[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