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...@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...@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


_______________________________________________
Mipsbook-devel mailing list
Mipsbook-devel@linuxtogo.org
http://lists.linuxtogo.org/cgi-bin/mailman/listinfo/mipsbook-devel

Reply via email to