Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1757519AbZFDJQz (ORCPT ); Thu, 4 Jun 2009 05:16:55 -0400 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1752091AbZFDJQr (ORCPT ); Thu, 4 Jun 2009 05:16:47 -0400 Received: from mail-qy0-f189.google.com ([209.85.221.189]:43649 "EHLO mail-qy0-f189.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752147AbZFDJQp convert rfc822-to-8bit (ORCPT ); Thu, 4 Jun 2009 05:16:45 -0400 DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=mime-version:in-reply-to:references:date:message-id:subject:from:to :cc:content-type:content-transfer-encoding; b=p/K8bYpXbV/bkYAaXD2zNV1CBJJX4x2/oQaS/J683kC2FrPdmtO+MqyfoU2l0tfPkn dKPO6xEghmd289nmkHI25kfCVpF751B4K58Nn9nFd526sXJuF++gSVbaju47EifsVD3W oQ/PW8++//GO3S0HDJeLFDktd0B0BezhUYhL0= MIME-Version: 1.0 In-Reply-To: <4A278C08.5000206@samsung.com> References: <4A278C08.5000206@samsung.com> Date: Thu, 4 Jun 2009 14:46:45 +0530 Message-ID: <5d5443650906040216h2314b7bbt1ae2e89c709b566e@mail.gmail.com> Subject: Re: [PATCH v2] add MAX17040 Fuel Gauge driver From: Trilok Soni To: Minkyu Kang Cc: linux-kernel@vger.kernel.org, linux-pm@lists.linux-foundation.org, kyungmin.park@samsung.com, Anton Vorontsov , Andrew Morton , linux-i2c@vger.kernel.org Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 8BIT Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 12772 Lines: 415 Hi Minkyu Kang, Adding linux-i2c mailing list, so not deleting any code. On Thu, Jun 4, 2009 at 2:25 PM, Minkyu Kang wrote: > The MAX17040 is a I2C interfaced Fuel Gauge systems for lithium-ion batteries > This patch adds support the MAX17040 Fuel Gauge > > Cc: Anton Vorontsov > Signed-off-by: Minkyu Kang > --- > ?drivers/power/Kconfig ? ? ? ? ? ?| ? ?8 + > ?drivers/power/Makefile ? ? ? ? ? | ? ?3 +- > ?drivers/power/max17040_battery.c | ?306 ++++++++++++++++++++++++++++++++++++++ > ?include/linux/max17040_battery.h | ? 19 +++ > ?4 files changed, 335 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..25303ba > --- /dev/null > +++ b/drivers/power/max17040_battery.c > @@ -0,0 +1,306 @@ > +/* > + * ?max17040_battery.c > + * ?fuel-gauge systems for lithium-ion (Li+) batteries > + * > + * ?Copyright (C) 2009 Samsung Electronics > + * ?Minkyu Kang > + * > + * 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 > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#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; May be you want to move this pdata under chip structure. > + > +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); Please add i2c_check_functionality check before doing any smbus read/write operations. > + ? ? ? max17040 = chip; This means that we support only one instance of this chip, right? > + > + ? ? ? 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"); > + ? ? ? ? ? ? ? cancel_delayed_work(&chip->work); > + ? ? ? ? ? ? ? i2c_set_clientdata(client, NULL); > + ? ? ? ? ? ? ? kfree(chip); > + ? ? ? ? ? ? ? max17040 = NULL; > + ? ? ? ? ? ? ? return -1; > + ? ? ? } > + > + ? ? ? return 0; > +} > + > +static int __devexit max17040_remove(struct i2c_client *client) > +{ > + ? ? ? struct max17040_chip *chip = i2c_get_clientdata(client); > + > + ? ? ? power_supply_unregister(&bat_ps); > + ? ? ? cancel_delayed_work(&chip->work); > + ? ? ? i2c_set_clientdata(client, NULL); > + ? ? ? kfree(chip); > + ? ? ? max17040 = NULL; > + ? ? ? 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 "); > +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 > + * > + * 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 > -- > To unsubscribe from this list: send the line "unsubscribe linux-kernel" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at ?http://vger.kernel.org/majordomo-info.html > Please read the FAQ at ?http://www.tux.org/lkml/ > -- ---Trilok Soni http://triloksoni.wordpress.com http://www.linkedin.com/in/triloksoni -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/