The MAX17040 is a I2C interfaced Fuel Gauge systems for lithium-ion batteries This patch adds support the MAX17040 Fuel Gauge Signed-off-by: Minkyu Kang <mk7.kang@xxxxxxxxxxx> --- drivers/power/Kconfig | 8 + drivers/power/Makefile | 3 +- drivers/power/max17040_battery.c | 304 ++++++++++++++++++++++++++++++++++++++ include/linux/max17040_battery.h | 19 +++ 4 files changed, 333 insertions(+), 1 deletions(-) create mode 100644 drivers/power/max17040_battery.c create mode 100644 include/linux/max17040_battery.h diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 33da112..6af5798 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -88,4 +88,12 @@ config CHARGER_PCF50633 help Say Y to include support for NXP PCF50633 Main Battery Charger. +config BATTERY_MAX17040 + tristate "Maxim MAX17040 Fuel Gauge" + depends on I2C + help + MAX17040 is fuel-gauge systems for lithium-ion (Li+) batteries + in handheld and portable equipment. The MAX17040 is configured + to operate with a single lithium cell + endif # POWER_SUPPLY diff --git a/drivers/power/Makefile b/drivers/power/Makefile index 2fcf41d..9c48995 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -25,4 +25,5 @@ obj-$(CONFIG_BATTERY_TOSA) += tosa_battery.o obj-$(CONFIG_BATTERY_WM97XX) += wm97xx_battery.o obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00_battery.o obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o -obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o \ No newline at end of file +obj-$(CONFIG_BATTERY_MAX17040)) += max17040_battery.o +obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o diff --git a/drivers/power/max17040_battery.c b/drivers/power/max17040_battery.c new file mode 100644 index 0000000..9bee171 --- /dev/null +++ b/drivers/power/max17040_battery.c @@ -0,0 +1,304 @@ +/* + * max17040_battery.c + * fuel-gauge systems for lithium-ion (Li+) batteries + * + * Copyright (C) 2009 Samsung Electronics + * Minkyu Kang <mk7.kang@xxxxxxxxxxx> + * + * 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/module.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/mutex.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/delay.h> +#include <linux/power_supply.h> +#include <linux/max17040_battery.h> + +#define MAX17040_VCELL_MSB 0x02 +#define MAX17040_VCELL_LSB 0x03 +#define MAX17040_SOC_MSB 0x04 +#define MAX17040_SOC_LSB 0x05 +#define MAX17040_MODE_MSB 0x06 +#define MAX17040_MODE_LSB 0x07 +#define MAX17040_VER_MSB 0x08 +#define MAX17040_VER_LSB 0x09 +#define MAX17040_RCOMP_MSB 0x0C +#define MAX17040_RCOMP_LSB 0x0D +#define MAX17040_CMD_MSB 0xFE +#define MAX17040_CMD_LSB 0xFF + +#define MAX17040_DELAY 1000 +#define MAX17040_BATTERY_FULL 95 + +struct max17040_chip { + struct i2c_client *client; + struct delayed_work work; + + /* State Of Connect */ + int online; + /* battery voltage */ + int vcell; + /* battery capacity */ + int soc; + /* State Of Charge */ + int status; +}; + +static struct max17040_chip *max17040; +static struct max17040_platform_data *pdata; + +static int max17040_get_property(struct power_supply *bat_ps, + enum power_supply_property psp, + union power_supply_propval *val) +{ + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + val->intval = max17040->status; + break; + case POWER_SUPPLY_PROP_ONLINE: + val->intval = max17040->online; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = max17040->vcell; + break; + case POWER_SUPPLY_PROP_CAPACITY: + val->intval = max17040->soc; + break; + default: + return -EINVAL; + } + return 0; +} + +static struct power_supply bat_ps = { + .name = "battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .get_property = max17040_get_property, +}; + +static int max17040_write_reg(int reg, u8 value) +{ + int ret; + + ret = i2c_smbus_write_byte_data(max17040->client, reg, value); + + if (ret < 0) { + dev_err(&max17040->client->dev, + "%s: reg 0x%x, val 0x%x, err %d\n", + __func__, reg, value, ret); + } + + return ret; +} + +static u8 max17040_read_reg(int reg) +{ + int ret; + + ret = i2c_smbus_read_byte_data(max17040->client, reg); + + if (ret < 0) { + dev_err(&max17040->client->dev, + "%s: reg 0x%x, err %d\n", + __func__, reg, ret); + } + + return ret; +} + +static void max17040_reset(void) +{ + max17040_write_reg(MAX17040_CMD_MSB, 0x54); + max17040_write_reg(MAX17040_CMD_LSB, 0x00); +} + +static void max17040_set_rcomp(u16 val) +{ + max17040_write_reg(MAX17040_RCOMP_MSB, val >> 8); + max17040_write_reg(MAX17040_RCOMP_LSB, (val << 8) >> 8); +} + +static void max17040_get_vcell(void) +{ + u8 msb; + u8 lsb; + + msb = max17040_read_reg(MAX17040_VCELL_MSB); + lsb = max17040_read_reg(MAX17040_VCELL_LSB); + + max17040->vcell = (msb << 4) + (lsb >> 4); +} + +static void max17040_get_soc(void) +{ + u8 msb; + u8 lsb; + + msb = max17040_read_reg(MAX17040_SOC_MSB); + lsb = max17040_read_reg(MAX17040_SOC_LSB); + + max17040->soc = msb; +} + +static void max17040_get_version(void) +{ + u8 msb; + u8 lsb; + + msb = max17040_read_reg(MAX17040_VER_MSB); + lsb = max17040_read_reg(MAX17040_VER_LSB); + + dev_info(&max17040->client->dev, + "MAX17040 Fuel-Gauge Ver %d%d\n", msb, lsb); +} + +static void max17040_get_online(void) +{ + if (pdata->battery_online) + max17040->online = pdata->battery_online(); + else + max17040->online = 1; +} + +static void max17040_get_status(void) +{ + if (!pdata->charger_online || !pdata->charger_enable) { + max17040->status = POWER_SUPPLY_STATUS_UNKNOWN; + return; + } + + if (pdata->charger_online()) { + if (pdata->charger_enable()) + max17040->status = POWER_SUPPLY_STATUS_CHARGING; + else + max17040->status = POWER_SUPPLY_STATUS_NOT_CHARGING; + } else { + max17040->status = POWER_SUPPLY_STATUS_DISCHARGING; + } + + if (max17040->soc > MAX17040_BATTERY_FULL) + max17040->status = POWER_SUPPLY_STATUS_FULL; +} + +static void max17040_work(struct work_struct *work) +{ + max17040_get_vcell(); + max17040_get_soc(); + max17040_get_online(); + max17040_get_status(); + + schedule_delayed_work(&max17040->work, MAX17040_DELAY); +} + +static enum power_supply_property max17040_battery_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CAPACITY, +}; + +static int __devinit max17040_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct max17040_chip *chip; + int ret; + + chip = kzalloc(sizeof(struct max17040_chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->client = client; + pdata = client->dev.platform_data; + + i2c_set_clientdata(client, chip); + max17040 = chip; + + max17040_reset(); + + max17040_get_version(); + + INIT_DELAYED_WORK_DEFERRABLE(&chip->work, max17040_work); + schedule_delayed_work(&chip->work, MAX17040_DELAY); + + bat_ps.properties = max17040_battery_props; + bat_ps.num_properties = ARRAY_SIZE(max17040_battery_props); + + ret = power_supply_register(&client->dev, &bat_ps); + if (ret) { + dev_err(&max17040->client->dev, + "failed: power supply register\n"); + kfree(chip); + max17040 = NULL; + i2c_set_clientdata(client, NULL); + return -1; + } + + return 0; +} + +static int __devexit max17040_remove(struct i2c_client *client) +{ + struct max17040_chip *chip = i2c_get_clientdata(client); + + kfree(chip); + max17040 = NULL; + i2c_set_clientdata(client, NULL); + power_supply_unregister(&bat_ps); + return 0; +} + +static int max17040_suspend(struct i2c_client *client, + pm_message_t state) +{ + struct max17040_chip *chip = i2c_get_clientdata(client); + + cancel_delayed_work(&chip->work); + return 0; +} + +static int max17040_resume(struct i2c_client *client) +{ + struct max17040_chip *chip = i2c_get_clientdata(client); + + schedule_delayed_work(&chip->work, MAX17040_DELAY); + return 0; +} + +static const struct i2c_device_id max17040_id[] = { + { "max17040", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max17040_id); + +static struct i2c_driver max17040_i2c_driver = { + .driver = { + .name = "max17040", + }, + .probe = max17040_probe, + .remove = __devexit_p(max17040_remove), + .suspend = max17040_suspend, + .resume = max17040_resume, + .id_table = max17040_id, +}; + +static int __init max17040_init(void) +{ + return i2c_add_driver(&max17040_i2c_driver); +} +module_init(max17040_init); + +static void __exit max17040_exit(void) +{ + i2c_del_driver(&max17040_i2c_driver); +} +module_exit(max17040_exit); + +MODULE_AUTHOR("Minkyu Kang <mk7.kang@xxxxxxxxxxx>"); +MODULE_DESCRIPTION("MAX17040 Fuel Gauge"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/max17040_battery.h b/include/linux/max17040_battery.h new file mode 100644 index 0000000..ad97b06 --- /dev/null +++ b/include/linux/max17040_battery.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2009 Samsung Electronics + * Minkyu Kang <mk7.kang@xxxxxxxxxxx> + * + * 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. + */ + +#ifndef __MAX17040_BATTERY_H_ +#define __MAX17040_BATTERY_H_ + +struct max17040_platform_data { + int (*battery_online)(void); + int (*charger_online)(void); + int (*charger_enable)(void); +}; + +#endif -- 1.5.4.3 _______________________________________________ linux-pm mailing list linux-pm@xxxxxxxxxxxxxxxxxxxxxxxxxx https://lists.linux-foundation.org/mailman/listinfo/linux-pm