[Lenny400] [PATCH] power: add driver for PM MCU in Letux 400
Daniel Glöckner
daniel-gl at gmx.net
Sat Apr 4 18:43:52 CEST 2015
The Letux 400 MIPS netbook contains an LPC915 microcontroller to measure
the battery voltage, detect the presence of a charger, and to power the
device off. It is controlled via I2C address 0x28.
Signed-off-by: Daniel Glöckner <daniel-gl at gmx.net>
---
drivers/power/minipc-mcu.c | 167 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 167 insertions(+)
create mode 100644 drivers/power/minipc-mcu.c
diff --git a/drivers/power/minipc-mcu.c b/drivers/power/minipc-mcu.c
new file mode 100644
index 0000000..29d779a
--- /dev/null
+++ b/drivers/power/minipc-mcu.c
@@ -0,0 +1,167 @@
+/*
+ * An I2C driver for the MCU used on the Letux 400
+ * Copyright 2012 Daniel Gloeckner
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/i2c.h>
+#include <linux/power_supply.h>
+#include <linux/pm.h>
+
+#define DRV_VERSION "1.0"
+
+static struct i2c_client *mcu;
+
+static int minipc_battery_get_properties(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ int ret = 0;
+ switch (psp) {
+ case POWER_SUPPLY_PROP_TECHNOLOGY:
+ val->intval = POWER_SUPPLY_TECHNOLOGY_LIPO;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+ val->intval = 7000000;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+ val->intval = 8400000;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ ret = i2c_smbus_read_byte_data(mcu, 0xdb);
+ if (ret >= 0) {
+ /* resistor divider scales 8.4V to 3V */
+ /* lpc915 is powered from 3.3V */
+ /* ergo voltage = value * 2.8 * 3.3 / 255 */
+ val->intval = ret * 616000 / 17;
+ ret = 0;
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static enum power_supply_property minipc_battery_properties[] = {
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+};
+
+static struct power_supply minipc_battery = {
+ .name = "battery",
+ .type = POWER_SUPPLY_TYPE_BATTERY,
+ .properties = minipc_battery_properties,
+ .num_properties = ARRAY_SIZE(minipc_battery_properties),
+ .get_property = minipc_battery_get_properties,
+};
+
+static int minipc_psu_get_properties(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ int ret = 0;
+ switch (psp) {
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = i2c_smbus_read_byte_data(mcu, 0xd9) & 1;
+ if (val->intval < 0)
+ ret = val->intval;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static enum power_supply_property minipc_psu_properties[] = {
+ POWER_SUPPLY_PROP_ONLINE,
+};
+
+static struct power_supply minipc_psu = {
+ .name = "psu",
+ .type = POWER_SUPPLY_TYPE_MAINS,
+ .properties = minipc_psu_properties,
+ .num_properties = ARRAY_SIZE(minipc_psu_properties),
+ .get_property = minipc_psu_get_properties,
+};
+
+static void minipc_mcu_power_off(void)
+{
+ i2c_smbus_write_byte_data(mcu, 0xd8, 0x01);
+}
+
+static int minipc_mcu_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ dev_dbg(&client->dev, "%s\n", __func__);
+
+ if (mcu)
+ return -EBUSY;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n");
+
+ minipc_battery.use_for_apm = power_supply_register(&client->dev,
+ &minipc_battery);
+ minipc_psu.use_for_apm = power_supply_register(&client->dev,
+ &minipc_psu);
+
+ mcu = client;
+ pm_power_off = minipc_mcu_power_off;
+
+ return 0;
+}
+
+static int minipc_mcu_remove(struct i2c_client *client)
+{
+ if (!mcu)
+ return 0;
+ if (!minipc_battery.use_for_apm)
+ power_supply_unregister(&minipc_battery);
+ if (!minipc_psu.use_for_apm)
+ power_supply_unregister(&minipc_psu);
+ pm_power_off = NULL;
+ mcu = NULL;
+
+ return 0;
+}
+
+static const struct i2c_device_id minipc_mcu_id[] = {
+ { "minipc-mcu", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, minipc_mcu_id);
+
+static struct i2c_driver minipc_mcu_driver = {
+ .driver = {
+ .name = "minipc-mcu",
+ },
+ .probe = minipc_mcu_probe,
+ .remove = minipc_mcu_remove,
+ .id_table = minipc_mcu_id,
+};
+
+static int __init minipc_mcu_init(void)
+{
+ return i2c_add_driver(&minipc_mcu_driver);
+}
+
+static void __exit minipc_mcu_exit(void)
+{
+ i2c_del_driver(&minipc_mcu_driver);
+}
+
+MODULE_AUTHOR("Daniel Gloeckner <daniel-gl at gmx.net>");
+MODULE_DESCRIPTION("Letux 400 LPC915 MCU driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+
+module_init(minipc_mcu_init);
+module_exit(minipc_mcu_exit);
--
1.8.3.4
More information about the Lenny400
mailing list