Various small bugfixes and enhancements, plus two new drivers:

- A quite complex ab8500 charger driver, submitted by
   Arun Murthy @ ST-Ericsson;
 - Summit Microelectronics SMB347 Battery Charger, submitted by
   Bruce E. Robertson and Alan Cox @ Intel.
 
 And that's all.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2.0.18 (GNU/Linux)
 
 iQIcBAABAgAGBQJPdhj7AAoJEGgI9fZJve1bSKEQAKzKXPwqpBREXzS888YWzb34
 0r3Bly+F8eSRTjclbEEvEOVVCjxKOFpow5lN+eAkRNAMLDBcMamfBTR+rpoYb822
 d7R4Hy/stVnmD5nul0WMezZ0OZf6qRy8kHazmvb63Jk37ykof52TqoEInKdHL/ww
 afUNfCnAFAbZkjxEr/9MwpM+nzYKdlGIoQlu9CJLARJiT9CyICjgHG48MGmyxOws
 1LPha40jXuKLoyE+6nvG1Ji71NzZlFaVuFpzk75kjsWoB78zkRsMSv+zboHRz3QP
 PI+uS54auIkGhqeIeEuuSzPZx+1K2Fr2/qyut3tqM8AejBE4kETiBAy0b0BREBSd
 NqA4/Yw24c8xglIFP6OlNjHnLgCsExfHATzqzML2El/dXG8P2ggEayZzDh9bmsuU
 ZNKOmHZYgEj2eEorJcmlfZ23rJwlxuspl1m2nH2WEnqw6Z6TnskK4N1JSNlpGRPU
 j+iHbTPA04hultQkzNHCdPRR/jK1IC5eGdwc612QgxOhZPkvhmW92e8EOVz/3E3X
 TqKXfBiKxKtkIncLPYFh7oeZINKnlqZrXsYrHhxhlm9UstGOqfEIqbCGqlf0Q4P1
 WOhIQe1cP/JpB9T58e3mLF2dQIGScTqzQCRSW6/m+3HmDwZb8JyUQRw5188s3yJr
 umrBS5hqS+z3bISWrcGi
 =Y8fe
 -----END PGP SIGNATURE-----

Merge tag 'for-v3.4-rc1' of git://git.infradead.org/battery-2.6

Pull battery updates from Anton Vorontsov:
 "Various small bugfixes and enhancements, plus two new drivers:
   - A quite complex ab8500 charger driver, submitted by Arun Murthy @
     ST-Ericsson;
   - Summit Microelectronics SMB347 Battery Charger, submitted by Bruce
     E Robertson and Alan Cox @ Intel.

  And that's all."

* tag 'for-v3.4-rc1' of git://git.infradead.org/battery-2.6: (36 commits)
  max17042_battery: Clean up interrupt handling
  Revert "max8998_charger: Include linux/module.h just once"
  ab8500_fg: Fix some build warnings on x86_64
  max17042_battery: Fix CHARGE_FULL representation.
  max8998_charger: Include linux/module.h just once
  power_supply: Convert i2c drivers to module_i2c_driver
  lp8727_charger: Add MODULE_DEVICE_TABLE
  charger-manager: Simplify charger_get_property(), get rid of a warning
  charger-manager: Clean up for better readability
  da9052-battery: Convert to use module_platform_driver
  da9052-battery: Fix a memory leak when unload the module
  da9052-battery: Add missing platform_set_drvdata
  ab8500: Turn unneeded global symbols into local ones
  ab8500_fg: Fix copy-paste error
  ab8500_fg: Get rid of 'struct battery_type'
  ab8500_fg: Get rid of 'struct v_to_cap'
  ab8500_btemp: Get rid of 'enum adc_therm'
  ab8500_charger: Convert to the new USB OTG calls
  ab8500-btemp: AB8500 battery temperature driver
  ab8500-fg: A8500 fuel gauge driver
  ...
This commit is contained in:
Linus Torvalds 2012-03-30 16:09:02 -07:00
commit 919c840167
23 changed files with 11388 additions and 206 deletions

View file

@ -249,7 +249,7 @@ config CHARGER_TWL4030
Say Y here to enable support for TWL4030 Battery Charge Interface.
config CHARGER_LP8727
tristate "National Semiconductor LP8727 charger driver"
tristate "TI/National Semiconductor LP8727 charger driver"
depends on I2C
help
Say Y here to enable support for LP8727 Charger Driver.
@ -288,4 +288,23 @@ config CHARGER_MAX8998
Say Y to enable support for the battery charger control sysfs and
platform data of MAX8998/LP3974 PMICs.
config CHARGER_SMB347
tristate "Summit Microelectronics SMB347 Battery Charger"
depends on I2C
help
Say Y to include support for Summit Microelectronics SMB347
Battery Charger.
config AB8500_BM
bool "AB8500 Battery Management Driver"
depends on AB8500_CORE && AB8500_GPADC
help
Say Y to include support for AB5500 battery management.
config AB8500_BATTERY_THERM_ON_BATCTRL
bool "Thermistor connected on BATCTRL ADC"
depends on AB8500_BM
help
Say Y to enable battery temperature measurements using
thermistor connected on BATCTRL ADC.
endif # POWER_SUPPLY

View file

@ -34,6 +34,7 @@ obj-$(CONFIG_BATTERY_S3C_ADC) += s3c_adc_battery.o
obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o
obj-$(CONFIG_BATTERY_JZ4740) += jz4740-battery.o
obj-$(CONFIG_BATTERY_INTEL_MID) += intel_mid_battery.o
obj-$(CONFIG_AB8500_BM) += ab8500_charger.o ab8500_btemp.o ab8500_fg.o abx500_chargalg.o
obj-$(CONFIG_CHARGER_ISP1704) += isp1704_charger.o
obj-$(CONFIG_CHARGER_MAX8903) += max8903_charger.o
obj-$(CONFIG_CHARGER_TWL4030) += twl4030_charger.o
@ -42,3 +43,4 @@ obj-$(CONFIG_CHARGER_GPIO) += gpio-charger.o
obj-$(CONFIG_CHARGER_MANAGER) += charger-manager.o
obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o
obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o
obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o

1124
drivers/power/ab8500_btemp.c Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

2637
drivers/power/ab8500_fg.c Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -134,12 +134,11 @@ static int get_batt_uV(struct charger_manager *cm, int *uV)
union power_supply_propval val;
int ret;
if (cm->fuel_gauge)
ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
POWER_SUPPLY_PROP_VOLTAGE_NOW, &val);
else
if (!cm->fuel_gauge)
return -ENODEV;
ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
POWER_SUPPLY_PROP_VOLTAGE_NOW, &val);
if (ret)
return ret;
@ -245,9 +244,7 @@ static int try_charger_enable(struct charger_manager *cm, bool enable)
struct charger_desc *desc = cm->desc;
/* Ignore if it's redundent command */
if (enable && cm->charger_enabled)
return 0;
if (!enable && !cm->charger_enabled)
if (enable == cm->charger_enabled)
return 0;
if (enable) {
@ -309,9 +306,7 @@ static void uevent_notify(struct charger_manager *cm, const char *event)
if (!strncmp(env_str_save, event, UEVENT_BUF_SIZE))
return; /* Duplicated. */
else
strncpy(env_str_save, event, UEVENT_BUF_SIZE);
strncpy(env_str_save, event, UEVENT_BUF_SIZE);
return;
}
@ -387,8 +382,10 @@ static bool cm_monitor(void)
mutex_lock(&cm_list_mtx);
list_for_each_entry(cm, &cm_list, entry)
stop = stop || _cm_monitor(cm);
list_for_each_entry(cm, &cm_list, entry) {
if (_cm_monitor(cm))
stop = true;
}
mutex_unlock(&cm_list_mtx);
@ -402,7 +399,8 @@ static int charger_get_property(struct power_supply *psy,
struct charger_manager *cm = container_of(psy,
struct charger_manager, charger_psy);
struct charger_desc *desc = cm->desc;
int i, ret = 0, uV;
int ret = 0;
int uV;
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
@ -428,8 +426,7 @@ static int charger_get_property(struct power_supply *psy,
val->intval = 0;
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
ret = get_batt_uV(cm, &i);
val->intval = i;
ret = get_batt_uV(cm, &val->intval);
break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
@ -697,8 +694,10 @@ bool cm_suspend_again(void)
mutex_lock(&cm_list_mtx);
list_for_each_entry(cm, &cm_list, entry) {
if (cm->status_save_ext_pwr_inserted != is_ext_pwr_online(cm) ||
cm->status_save_batt != is_batt_present(cm))
cm->status_save_batt != is_batt_present(cm)) {
ret = false;
break;
}
}
mutex_unlock(&cm_list_mtx);
@ -855,11 +854,10 @@ static int charger_manager_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, cm);
memcpy(&cm->charger_psy, &psy_default,
sizeof(psy_default));
memcpy(&cm->charger_psy, &psy_default, sizeof(psy_default));
if (!desc->psy_name) {
strncpy(cm->psy_name_buf, psy_default.name,
PSY_NAME_MAX);
strncpy(cm->psy_name_buf, psy_default.name, PSY_NAME_MAX);
} else {
strncpy(cm->psy_name_buf, desc->psy_name, PSY_NAME_MAX);
}
@ -894,15 +892,15 @@ static int charger_manager_probe(struct platform_device *pdev)
POWER_SUPPLY_PROP_CURRENT_NOW;
cm->charger_psy.num_properties++;
}
if (!desc->measure_battery_temp) {
cm->charger_psy.properties[cm->charger_psy.num_properties] =
POWER_SUPPLY_PROP_TEMP_AMBIENT;
cm->charger_psy.num_properties++;
}
if (desc->measure_battery_temp) {
cm->charger_psy.properties[cm->charger_psy.num_properties] =
POWER_SUPPLY_PROP_TEMP;
cm->charger_psy.num_properties++;
} else {
cm->charger_psy.properties[cm->charger_psy.num_properties] =
POWER_SUPPLY_PROP_TEMP_AMBIENT;
cm->charger_psy.num_properties++;
}
ret = power_supply_register(NULL, &cm->charger_psy);
@ -933,9 +931,8 @@ static int charger_manager_probe(struct platform_device *pdev)
return 0;
err_chg_enable:
if (desc->charger_regulators)
regulator_bulk_free(desc->num_charger_regulators,
desc->charger_regulators);
regulator_bulk_free(desc->num_charger_regulators,
desc->charger_regulators);
err_bulk_get:
power_supply_unregister(&cm->charger_psy);
err_register:
@ -961,10 +958,8 @@ static int __devexit charger_manager_remove(struct platform_device *pdev)
list_del(&cm->entry);
mutex_unlock(&cm_list_mtx);
if (desc->charger_regulators)
regulator_bulk_free(desc->num_charger_regulators,
desc->charger_regulators);
regulator_bulk_free(desc->num_charger_regulators,
desc->charger_regulators);
power_supply_unregister(&cm->charger_psy);
kfree(cm->charger_psy.properties);
kfree(cm->charger_stat);
@ -982,9 +977,7 @@ MODULE_DEVICE_TABLE(platform, charger_manager_id);
static int cm_suspend_prepare(struct device *dev)
{
struct platform_device *pdev = container_of(dev, struct platform_device,
dev);
struct charger_manager *cm = platform_get_drvdata(pdev);
struct charger_manager *cm = dev_get_drvdata(dev);
if (!cm_suspended) {
if (rtc_dev) {
@ -1020,9 +1013,7 @@ static int cm_suspend_prepare(struct device *dev)
static void cm_suspend_complete(struct device *dev)
{
struct platform_device *pdev = container_of(dev, struct platform_device,
dev);
struct charger_manager *cm = platform_get_drvdata(pdev);
struct charger_manager *cm = dev_get_drvdata(dev);
if (cm_suspended) {
if (rtc_dev) {

View file

@ -612,6 +612,7 @@ static s32 __devinit da9052_bat_probe(struct platform_device *pdev)
if (ret)
goto err;
platform_set_drvdata(pdev, bat);
return 0;
err:
@ -633,6 +634,7 @@ static int __devexit da9052_bat_remove(struct platform_device *pdev)
free_irq(bat->da9052->irq_base + irq, bat);
}
power_supply_unregister(&bat->psy);
kfree(bat);
return 0;
}
@ -645,18 +647,7 @@ static struct platform_driver da9052_bat_driver = {
.owner = THIS_MODULE,
},
};
static int __init da9052_bat_init(void)
{
return platform_driver_register(&da9052_bat_driver);
}
module_init(da9052_bat_init);
static void __exit da9052_bat_exit(void)
{
platform_driver_unregister(&da9052_bat_driver);
}
module_exit(da9052_bat_exit);
module_platform_driver(da9052_bat_driver);
MODULE_DESCRIPTION("DA9052 BAT Device Driver");
MODULE_AUTHOR("David Dajun Chen <[email protected]>");

View file

@ -403,18 +403,7 @@ static struct i2c_driver ds278x_battery_driver = {
.remove = ds278x_battery_remove,
.id_table = ds278x_id,
};
static int __init ds278x_init(void)
{
return i2c_add_driver(&ds278x_battery_driver);
}
module_init(ds278x_init);
static void __exit ds278x_exit(void)
{
i2c_del_driver(&ds278x_battery_driver);
}
module_exit(ds278x_exit);
module_i2c_driver(ds278x_battery_driver);
MODULE_AUTHOR("Ryan Mallon");
MODULE_DESCRIPTION("Maxim/Dallas DS2782 Stand-Alone Fuel Gauage IC driver");

View file

@ -480,6 +480,7 @@ fail0:
dev_err(&pdev->dev, "failed to register isp1704 with error %d\n", ret);
isp1704_charger_set_power(isp, 0);
return ret;
}

View file

@ -1,6 +1,7 @@
/*
* Driver for LP8727 Micro/Mini USB IC with intergrated charger
* Driver for LP8727 Micro/Mini USB IC with integrated charger
*
* Copyright (C) 2011 Texas Instruments
* Copyright (C) 2011 National Semiconductor
*
* This program is free software; you can redistribute it and/or modify
@ -25,7 +26,7 @@
#define INT1 0x4
#define INT2 0x5
#define STATUS1 0x6
#define STATUS2 0x7
#define STATUS2 0x7
#define CHGCTRL2 0x9
/* CTRL1 register */
@ -91,7 +92,7 @@ struct lp8727_chg {
enum lp8727_dev_id devid;
};
static int lp8727_i2c_read(struct lp8727_chg *pchg, u8 reg, u8 *data, u8 len)
static int lp8727_read_bytes(struct lp8727_chg *pchg, u8 reg, u8 *data, u8 len)
{
s32 ret;
@ -102,29 +103,22 @@ static int lp8727_i2c_read(struct lp8727_chg *pchg, u8 reg, u8 *data, u8 len)
return (ret != len) ? -EIO : 0;
}
static int lp8727_i2c_write(struct lp8727_chg *pchg, u8 reg, u8 *data, u8 len)
static inline int lp8727_read_byte(struct lp8727_chg *pchg, u8 reg, u8 *data)
{
s32 ret;
return lp8727_read_bytes(pchg, reg, data, 1);
}
static int lp8727_write_byte(struct lp8727_chg *pchg, u8 reg, u8 data)
{
int ret;
mutex_lock(&pchg->xfer_lock);
ret = i2c_smbus_write_i2c_block_data(pchg->client, reg, len, data);
ret = i2c_smbus_write_byte_data(pchg->client, reg, data);
mutex_unlock(&pchg->xfer_lock);
return ret;
}
static inline int lp8727_i2c_read_byte(struct lp8727_chg *pchg, u8 reg,
u8 *data)
{
return lp8727_i2c_read(pchg, reg, data, 1);
}
static inline int lp8727_i2c_write_byte(struct lp8727_chg *pchg, u8 reg,
u8 *data)
{
return lp8727_i2c_write(pchg, reg, data, 1);
}
static int lp8727_is_charger_attached(const char *name, int id)
{
if (name) {
@ -137,37 +131,41 @@ static int lp8727_is_charger_attached(const char *name, int id)
return (id >= ID_TA && id <= ID_USB_CHG) ? 1 : 0;
}
static void lp8727_init_device(struct lp8727_chg *pchg)
static int lp8727_init_device(struct lp8727_chg *pchg)
{
u8 val;
int ret;
val = ID200_EN | ADC_EN | CP_EN;
if (lp8727_i2c_write_byte(pchg, CTRL1, &val))
dev_err(pchg->dev, "i2c write err : addr=0x%.2x\n", CTRL1);
ret = lp8727_write_byte(pchg, CTRL1, val);
if (ret)
return ret;
val = INT_EN | CHGDET_EN;
if (lp8727_i2c_write_byte(pchg, CTRL2, &val))
dev_err(pchg->dev, "i2c write err : addr=0x%.2x\n", CTRL2);
ret = lp8727_write_byte(pchg, CTRL2, val);
if (ret)
return ret;
return 0;
}
static int lp8727_is_dedicated_charger(struct lp8727_chg *pchg)
{
u8 val;
lp8727_i2c_read_byte(pchg, STATUS1, &val);
return (val & DCPORT);
lp8727_read_byte(pchg, STATUS1, &val);
return val & DCPORT;
}
static int lp8727_is_usb_charger(struct lp8727_chg *pchg)
{
u8 val;
lp8727_i2c_read_byte(pchg, STATUS1, &val);
return (val & CHPORT);
lp8727_read_byte(pchg, STATUS1, &val);
return val & CHPORT;
}
static void lp8727_ctrl_switch(struct lp8727_chg *pchg, u8 sw)
{
u8 val = sw;
lp8727_i2c_write_byte(pchg, SWCTRL, &val);
lp8727_write_byte(pchg, SWCTRL, sw);
}
static void lp8727_id_detection(struct lp8727_chg *pchg, u8 id, int vbusin)
@ -207,9 +205,9 @@ static void lp8727_enable_chgdet(struct lp8727_chg *pchg)
{
u8 val;
lp8727_i2c_read_byte(pchg, CTRL2, &val);
lp8727_read_byte(pchg, CTRL2, &val);
val |= CHGDET_EN;
lp8727_i2c_write_byte(pchg, CTRL2, &val);
lp8727_write_byte(pchg, CTRL2, val);
}
static void lp8727_delayed_func(struct work_struct *_work)
@ -218,7 +216,7 @@ static void lp8727_delayed_func(struct work_struct *_work)
struct lp8727_chg *pchg =
container_of(_work, struct lp8727_chg, work.work);
if (lp8727_i2c_read(pchg, INT1, intstat, 2)) {
if (lp8727_read_bytes(pchg, INT1, intstat, 2)) {
dev_err(pchg->dev, "can not read INT registers\n");
return;
}
@ -244,20 +242,22 @@ static irqreturn_t lp8727_isr_func(int irq, void *ptr)
return IRQ_HANDLED;
}
static void lp8727_intr_config(struct lp8727_chg *pchg)
static int lp8727_intr_config(struct lp8727_chg *pchg)
{
INIT_DELAYED_WORK(&pchg->work, lp8727_delayed_func);
pchg->irqthread = create_singlethread_workqueue("lp8727-irqthd");
if (!pchg->irqthread)
if (!pchg->irqthread) {
dev_err(pchg->dev, "can not create thread for lp8727\n");
if (request_threaded_irq(pchg->client->irq,
NULL,
lp8727_isr_func,
IRQF_TRIGGER_FALLING, "lp8727_irq", pchg)) {
dev_err(pchg->dev, "lp8727 irq can not be registered\n");
return -ENOMEM;
}
return request_threaded_irq(pchg->client->irq,
NULL,
lp8727_isr_func,
IRQF_TRIGGER_FALLING,
"lp8727_irq",
pchg);
}
static enum power_supply_property lp8727_charger_prop[] = {
@ -300,7 +300,7 @@ static int lp8727_battery_get_property(struct power_supply *psy,
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
if (lp8727_is_charger_attached(psy->name, pchg->devid)) {
lp8727_i2c_read_byte(pchg, STATUS1, &read);
lp8727_read_byte(pchg, STATUS1, &read);
if (((read & CHGSTAT) >> 4) == EOC)
val->intval = POWER_SUPPLY_STATUS_FULL;
else
@ -310,7 +310,7 @@ static int lp8727_battery_get_property(struct power_supply *psy,
}
break;
case POWER_SUPPLY_PROP_HEALTH:
lp8727_i2c_read_byte(pchg, STATUS2, &read);
lp8727_read_byte(pchg, STATUS2, &read);
read = (read & TEMP_STAT) >> 5;
if (read >= 0x1 && read <= 0x3)
val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
@ -351,7 +351,7 @@ static void lp8727_charger_changed(struct power_supply *psy)
eoc_level = pchg->chg_parm->eoc_level;
ichg = pchg->chg_parm->ichg;
val = (ichg << 4) | eoc_level;
lp8727_i2c_write_byte(pchg, CHGCTRL2, &val);
lp8727_write_byte(pchg, CHGCTRL2, val);
}
}
}
@ -439,15 +439,29 @@ static int lp8727_probe(struct i2c_client *cl, const struct i2c_device_id *id)
mutex_init(&pchg->xfer_lock);
lp8727_init_device(pchg);
lp8727_intr_config(pchg);
ret = lp8727_init_device(pchg);
if (ret) {
dev_err(pchg->dev, "i2c communication err: %d", ret);
goto error;
}
ret = lp8727_intr_config(pchg);
if (ret) {
dev_err(pchg->dev, "irq handler err: %d", ret);
goto error;
}
ret = lp8727_register_psy(pchg);
if (ret)
dev_err(pchg->dev,
"can not register power supplies. err=%d", ret);
if (ret) {
dev_err(pchg->dev, "power supplies register err: %d", ret);
goto error;
}
return 0;
error:
kfree(pchg);
return ret;
}
static int __devexit lp8727_remove(struct i2c_client *cl)
@ -466,6 +480,7 @@ static const struct i2c_device_id lp8727_ids[] = {
{"lp8727", 0},
{ }
};
MODULE_DEVICE_TABLE(i2c, lp8727_ids);
static struct i2c_driver lp8727_driver = {
.driver = {
@ -475,21 +490,9 @@ static struct i2c_driver lp8727_driver = {
.remove = __devexit_p(lp8727_remove),
.id_table = lp8727_ids,
};
module_i2c_driver(lp8727_driver);
static int __init lp8727_init(void)
{
return i2c_add_driver(&lp8727_driver);
}
static void __exit lp8727_exit(void)
{
i2c_del_driver(&lp8727_driver);
}
module_init(lp8727_init);
module_exit(lp8727_exit);
MODULE_DESCRIPTION("National Semiconductor LP8727 charger driver");
MODULE_AUTHOR
("Woogyom Kim <[email protected]>, Daniel Jeong <[email protected]>");
MODULE_DESCRIPTION("TI/National Semiconductor LP8727 charger driver");
MODULE_AUTHOR("Woogyom Kim <[email protected]>, "
"Daniel Jeong <[email protected]>");
MODULE_LICENSE("GPL");

View file

@ -290,18 +290,7 @@ static struct i2c_driver max17040_i2c_driver = {
.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_i2c_driver(max17040_i2c_driver);
MODULE_AUTHOR("Minkyu Kang <[email protected]>");
MODULE_DESCRIPTION("MAX17040 Fuel Gauge");

View file

@ -26,14 +26,47 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/mod_devicetable.h>
#include <linux/power_supply.h>
#include <linux/power/max17042_battery.h>
#include <linux/of.h>
/* Status register bits */
#define STATUS_POR_BIT (1 << 1)
#define STATUS_BST_BIT (1 << 3)
#define STATUS_VMN_BIT (1 << 8)
#define STATUS_TMN_BIT (1 << 9)
#define STATUS_SMN_BIT (1 << 10)
#define STATUS_BI_BIT (1 << 11)
#define STATUS_VMX_BIT (1 << 12)
#define STATUS_TMX_BIT (1 << 13)
#define STATUS_SMX_BIT (1 << 14)
#define STATUS_BR_BIT (1 << 15)
/* Interrupt mask bits */
#define CONFIG_ALRT_BIT_ENBL (1 << 2)
#define STATUS_INTR_SOCMIN_BIT (1 << 10)
#define STATUS_INTR_SOCMAX_BIT (1 << 14)
#define VFSOC0_LOCK 0x0000
#define VFSOC0_UNLOCK 0x0080
#define MODEL_UNLOCK1 0X0059
#define MODEL_UNLOCK2 0X00C4
#define MODEL_LOCK1 0X0000
#define MODEL_LOCK2 0X0000
#define dQ_ACC_DIV 0x4
#define dP_ACC_100 0x1900
#define dP_ACC_200 0x3200
struct max17042_chip {
struct i2c_client *client;
struct power_supply battery;
struct max17042_platform_data *pdata;
struct work_struct work;
int init_complete;
};
static int max17042_write_reg(struct i2c_client *client, u8 reg, u16 value)
@ -87,6 +120,9 @@ static int max17042_get_property(struct power_supply *psy,
struct max17042_chip, battery);
int ret;
if (!chip->init_complete)
return -EAGAIN;
switch (psp) {
case POWER_SUPPLY_PROP_PRESENT:
ret = max17042_read_reg(chip->client, MAX17042_STATUS);
@ -136,21 +172,18 @@ static int max17042_get_property(struct power_supply *psy,
val->intval = ret * 625 / 8;
break;
case POWER_SUPPLY_PROP_CAPACITY:
ret = max17042_read_reg(chip->client, MAX17042_SOC);
ret = max17042_read_reg(chip->client, MAX17042_RepSOC);
if (ret < 0)
return ret;
val->intval = ret >> 8;
break;
case POWER_SUPPLY_PROP_CHARGE_FULL:
ret = max17042_read_reg(chip->client, MAX17042_RepSOC);
ret = max17042_read_reg(chip->client, MAX17042_FullCAP);
if (ret < 0)
return ret;
if ((ret >> 8) >= MAX17042_BATTERY_FULL)
val->intval = 1;
else if (ret >= 0)
val->intval = 0;
val->intval = ret * 1000 / 2;
break;
case POWER_SUPPLY_PROP_TEMP:
ret = max17042_read_reg(chip->client, MAX17042_TEMP);
@ -210,22 +243,419 @@ static int max17042_get_property(struct power_supply *psy,
return 0;
}
static int max17042_write_verify_reg(struct i2c_client *client,
u8 reg, u16 value)
{
int retries = 8;
int ret;
u16 read_value;
do {
ret = i2c_smbus_write_word_data(client, reg, value);
read_value = max17042_read_reg(client, reg);
if (read_value != value) {
ret = -EIO;
retries--;
}
} while (retries && read_value != value);
if (ret < 0)
dev_err(&client->dev, "%s: err %d\n", __func__, ret);
return ret;
}
static inline void max17042_override_por(
struct i2c_client *client, u8 reg, u16 value)
{
if (value)
max17042_write_reg(client, reg, value);
}
static inline void max10742_unlock_model(struct max17042_chip *chip)
{
struct i2c_client *client = chip->client;
max17042_write_reg(client, MAX17042_MLOCKReg1, MODEL_UNLOCK1);
max17042_write_reg(client, MAX17042_MLOCKReg2, MODEL_UNLOCK2);
}
static inline void max10742_lock_model(struct max17042_chip *chip)
{
struct i2c_client *client = chip->client;
max17042_write_reg(client, MAX17042_MLOCKReg1, MODEL_LOCK1);
max17042_write_reg(client, MAX17042_MLOCKReg2, MODEL_LOCK2);
}
static inline void max17042_write_model_data(struct max17042_chip *chip,
u8 addr, int size)
{
struct i2c_client *client = chip->client;
int i;
for (i = 0; i < size; i++)
max17042_write_reg(client, addr + i,
chip->pdata->config_data->cell_char_tbl[i]);
}
static inline void max17042_read_model_data(struct max17042_chip *chip,
u8 addr, u16 *data, int size)
{
struct i2c_client *client = chip->client;
int i;
for (i = 0; i < size; i++)
data[i] = max17042_read_reg(client, addr + i);
}
static inline int max17042_model_data_compare(struct max17042_chip *chip,
u16 *data1, u16 *data2, int size)
{
int i;
if (memcmp(data1, data2, size)) {
dev_err(&chip->client->dev, "%s compare failed\n", __func__);
for (i = 0; i < size; i++)
dev_info(&chip->client->dev, "0x%x, 0x%x",
data1[i], data2[i]);
dev_info(&chip->client->dev, "\n");
return -EINVAL;
}
return 0;
}
static int max17042_init_model(struct max17042_chip *chip)
{
int ret;
int table_size =
sizeof(chip->pdata->config_data->cell_char_tbl)/sizeof(u16);
u16 *temp_data;
temp_data = kzalloc(table_size, GFP_KERNEL);
if (!temp_data)
return -ENOMEM;
max10742_unlock_model(chip);
max17042_write_model_data(chip, MAX17042_MODELChrTbl,
table_size);
max17042_read_model_data(chip, MAX17042_MODELChrTbl, temp_data,
table_size);
ret = max17042_model_data_compare(
chip,
chip->pdata->config_data->cell_char_tbl,
temp_data,
table_size);
max10742_lock_model(chip);
kfree(temp_data);
return ret;
}
static int max17042_verify_model_lock(struct max17042_chip *chip)
{
int i;
int table_size =
sizeof(chip->pdata->config_data->cell_char_tbl);
u16 *temp_data;
int ret = 0;
temp_data = kzalloc(table_size, GFP_KERNEL);
if (!temp_data)
return -ENOMEM;
max17042_read_model_data(chip, MAX17042_MODELChrTbl, temp_data,
table_size);
for (i = 0; i < table_size; i++)
if (temp_data[i])
ret = -EINVAL;
kfree(temp_data);
return ret;
}
static void max17042_write_config_regs(struct max17042_chip *chip)
{
struct max17042_config_data *config = chip->pdata->config_data;
max17042_write_reg(chip->client, MAX17042_CONFIG, config->config);
max17042_write_reg(chip->client, MAX17042_LearnCFG, config->learn_cfg);
max17042_write_reg(chip->client, MAX17042_FilterCFG,
config->filter_cfg);
max17042_write_reg(chip->client, MAX17042_RelaxCFG, config->relax_cfg);
}
static void max17042_write_custom_regs(struct max17042_chip *chip)
{
struct max17042_config_data *config = chip->pdata->config_data;
max17042_write_verify_reg(chip->client, MAX17042_RCOMP0,
config->rcomp0);
max17042_write_verify_reg(chip->client, MAX17042_TempCo,
config->tcompc0);
max17042_write_reg(chip->client, MAX17042_EmptyTempCo,
config->empty_tempco);
max17042_write_verify_reg(chip->client, MAX17042_K_empty0,
config->kempty0);
max17042_write_verify_reg(chip->client, MAX17042_ICHGTerm,
config->ichgt_term);
}
static void max17042_update_capacity_regs(struct max17042_chip *chip)
{
struct max17042_config_data *config = chip->pdata->config_data;
max17042_write_verify_reg(chip->client, MAX17042_FullCAP,
config->fullcap);
max17042_write_reg(chip->client, MAX17042_DesignCap,
config->design_cap);
max17042_write_verify_reg(chip->client, MAX17042_FullCAPNom,
config->fullcapnom);
}
static void max17042_reset_vfsoc0_reg(struct max17042_chip *chip)
{
u16 vfSoc;
vfSoc = max17042_read_reg(chip->client, MAX17042_VFSOC);
max17042_write_reg(chip->client, MAX17042_VFSOC0Enable, VFSOC0_UNLOCK);
max17042_write_verify_reg(chip->client, MAX17042_VFSOC0, vfSoc);
max17042_write_reg(chip->client, MAX17042_VFSOC0Enable, VFSOC0_LOCK);
}
static void max17042_load_new_capacity_params(struct max17042_chip *chip)
{
u16 full_cap0, rep_cap, dq_acc, vfSoc;
u32 rem_cap;
struct max17042_config_data *config = chip->pdata->config_data;
full_cap0 = max17042_read_reg(chip->client, MAX17042_FullCAP0);
vfSoc = max17042_read_reg(chip->client, MAX17042_VFSOC);
/* fg_vfSoc needs to shifted by 8 bits to get the
* perc in 1% accuracy, to get the right rem_cap multiply
* full_cap0, fg_vfSoc and devide by 100
*/
rem_cap = ((vfSoc >> 8) * full_cap0) / 100;
max17042_write_verify_reg(chip->client, MAX17042_RemCap, (u16)rem_cap);
rep_cap = (u16)rem_cap;
max17042_write_verify_reg(chip->client, MAX17042_RepCap, rep_cap);
/* Write dQ_acc to 200% of Capacity and dP_acc to 200% */
dq_acc = config->fullcap / dQ_ACC_DIV;
max17042_write_verify_reg(chip->client, MAX17042_dQacc, dq_acc);
max17042_write_verify_reg(chip->client, MAX17042_dPacc, dP_ACC_200);
max17042_write_verify_reg(chip->client, MAX17042_FullCAP,
config->fullcap);
max17042_write_reg(chip->client, MAX17042_DesignCap,
config->design_cap);
max17042_write_verify_reg(chip->client, MAX17042_FullCAPNom,
config->fullcapnom);
}
/*
* Block write all the override values coming from platform data.
* This function MUST be called before the POR initialization proceedure
* specified by maxim.
*/
static inline void max17042_override_por_values(struct max17042_chip *chip)
{
struct i2c_client *client = chip->client;
struct max17042_config_data *config = chip->pdata->config_data;
max17042_override_por(client, MAX17042_TGAIN, config->tgain);
max17042_override_por(client, MAx17042_TOFF, config->toff);
max17042_override_por(client, MAX17042_CGAIN, config->cgain);
max17042_override_por(client, MAX17042_COFF, config->coff);
max17042_override_por(client, MAX17042_VALRT_Th, config->valrt_thresh);
max17042_override_por(client, MAX17042_TALRT_Th, config->talrt_thresh);
max17042_override_por(client, MAX17042_SALRT_Th,
config->soc_alrt_thresh);
max17042_override_por(client, MAX17042_CONFIG, config->config);
max17042_override_por(client, MAX17042_SHDNTIMER, config->shdntimer);
max17042_override_por(client, MAX17042_DesignCap, config->design_cap);
max17042_override_por(client, MAX17042_ICHGTerm, config->ichgt_term);
max17042_override_por(client, MAX17042_AtRate, config->at_rate);
max17042_override_por(client, MAX17042_LearnCFG, config->learn_cfg);
max17042_override_por(client, MAX17042_FilterCFG, config->filter_cfg);
max17042_override_por(client, MAX17042_RelaxCFG, config->relax_cfg);
max17042_override_por(client, MAX17042_MiscCFG, config->misc_cfg);
max17042_override_por(client, MAX17042_MaskSOC, config->masksoc);
max17042_override_por(client, MAX17042_FullCAP, config->fullcap);
max17042_override_por(client, MAX17042_FullCAPNom, config->fullcapnom);
max17042_override_por(client, MAX17042_SOC_empty, config->socempty);
max17042_override_por(client, MAX17042_LAvg_empty, config->lavg_empty);
max17042_override_por(client, MAX17042_dQacc, config->dqacc);
max17042_override_por(client, MAX17042_dPacc, config->dpacc);
max17042_override_por(client, MAX17042_V_empty, config->vempty);
max17042_override_por(client, MAX17042_TempNom, config->temp_nom);
max17042_override_por(client, MAX17042_TempLim, config->temp_lim);
max17042_override_por(client, MAX17042_FCTC, config->fctc);
max17042_override_por(client, MAX17042_RCOMP0, config->rcomp0);
max17042_override_por(client, MAX17042_TempCo, config->tcompc0);
max17042_override_por(client, MAX17042_EmptyTempCo,
config->empty_tempco);
max17042_override_por(client, MAX17042_K_empty0, config->kempty0);
}
static int max17042_init_chip(struct max17042_chip *chip)
{
int ret;
int val;
max17042_override_por_values(chip);
/* After Power up, the MAX17042 requires 500mS in order
* to perform signal debouncing and initial SOC reporting
*/
msleep(500);
/* Initialize configaration */
max17042_write_config_regs(chip);
/* write cell characterization data */
ret = max17042_init_model(chip);
if (ret) {
dev_err(&chip->client->dev, "%s init failed\n",
__func__);
return -EIO;
}
max17042_verify_model_lock(chip);
if (ret) {
dev_err(&chip->client->dev, "%s lock verify failed\n",
__func__);
return -EIO;
}
/* write custom parameters */
max17042_write_custom_regs(chip);
/* update capacity params */
max17042_update_capacity_regs(chip);
/* delay must be atleast 350mS to allow VFSOC
* to be calculated from the new configuration
*/
msleep(350);
/* reset vfsoc0 reg */
max17042_reset_vfsoc0_reg(chip);
/* load new capacity params */
max17042_load_new_capacity_params(chip);
/* Init complete, Clear the POR bit */
val = max17042_read_reg(chip->client, MAX17042_STATUS);
max17042_write_reg(chip->client, MAX17042_STATUS,
val & (~STATUS_POR_BIT));
return 0;
}
static void max17042_set_soc_threshold(struct max17042_chip *chip, u16 off)
{
u16 soc, soc_tr;
/* program interrupt thesholds such that we should
* get interrupt for every 'off' perc change in the soc
*/
soc = max17042_read_reg(chip->client, MAX17042_RepSOC) >> 8;
soc_tr = (soc + off) << 8;
soc_tr |= (soc - off);
max17042_write_reg(chip->client, MAX17042_SALRT_Th, soc_tr);
}
static irqreturn_t max17042_thread_handler(int id, void *dev)
{
struct max17042_chip *chip = dev;
u16 val;
val = max17042_read_reg(chip->client, MAX17042_STATUS);
if ((val & STATUS_INTR_SOCMIN_BIT) ||
(val & STATUS_INTR_SOCMAX_BIT)) {
dev_info(&chip->client->dev, "SOC threshold INTR\n");
max17042_set_soc_threshold(chip, 1);
}
power_supply_changed(&chip->battery);
return IRQ_HANDLED;
}
static void max17042_init_worker(struct work_struct *work)
{
struct max17042_chip *chip = container_of(work,
struct max17042_chip, work);
int ret;
/* Initialize registers according to values from the platform data */
if (chip->pdata->enable_por_init && chip->pdata->config_data) {
ret = max17042_init_chip(chip);
if (ret)
return;
}
chip->init_complete = 1;
}
#ifdef CONFIG_OF
static struct max17042_platform_data *
max17042_get_pdata(struct device *dev)
{
struct device_node *np = dev->of_node;
u32 prop;
struct max17042_platform_data *pdata;
if (!np)
return dev->platform_data;
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return NULL;
/*
* Require current sense resistor value to be specified for
* current-sense functionality to be enabled at all.
*/
if (of_property_read_u32(np, "maxim,rsns-microohm", &prop) == 0) {
pdata->r_sns = prop;
pdata->enable_current_sense = true;
}
return pdata;
}
#else
static struct max17042_platform_data *
max17042_get_pdata(struct device *dev)
{
return dev->platform_data;
}
#endif
static int __devinit max17042_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
struct max17042_chip *chip;
int ret;
int reg;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA))
return -EIO;
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
return -ENOMEM;
chip->client = client;
chip->pdata = client->dev.platform_data;
chip->pdata = max17042_get_pdata(&client->dev);
if (!chip->pdata) {
dev_err(&client->dev, "no platform data provided\n");
return -EINVAL;
}
i2c_set_clientdata(client, chip);
@ -243,17 +673,9 @@ static int __devinit max17042_probe(struct i2c_client *client,
if (chip->pdata->r_sns == 0)
chip->pdata->r_sns = MAX17042_DEFAULT_SNS_RESISTOR;
ret = power_supply_register(&client->dev, &chip->battery);
if (ret) {
dev_err(&client->dev, "failed: power supply register\n");
kfree(chip);
return ret;
}
/* Initialize registers according to values from the platform data */
if (chip->pdata->init_data)
max17042_set_reg(client, chip->pdata->init_data,
chip->pdata->num_init_data);
chip->pdata->num_init_data);
if (!chip->pdata->enable_current_sense) {
max17042_write_reg(client, MAX17042_CGAIN, 0x0000);
@ -261,7 +683,34 @@ static int __devinit max17042_probe(struct i2c_client *client,
max17042_write_reg(client, MAX17042_LearnCFG, 0x0007);
}
return 0;
if (client->irq) {
ret = request_threaded_irq(client->irq, NULL,
max17042_thread_handler,
IRQF_TRIGGER_FALLING,
chip->battery.name, chip);
if (!ret) {
reg = max17042_read_reg(client, MAX17042_CONFIG);
reg |= CONFIG_ALRT_BIT_ENBL;
max17042_write_reg(client, MAX17042_CONFIG, reg);
max17042_set_soc_threshold(chip, 1);
} else
dev_err(&client->dev, "%s(): cannot get IRQ\n",
__func__);
}
reg = max17042_read_reg(chip->client, MAX17042_STATUS);
if (reg & STATUS_POR_BIT) {
INIT_WORK(&chip->work, max17042_init_worker);
schedule_work(&chip->work);
} else {
chip->init_complete = 1;
}
ret = power_supply_register(&client->dev, &chip->battery);
if (ret)
dev_err(&client->dev, "failed: power supply register\n");
return ret;
}
static int __devexit max17042_remove(struct i2c_client *client)
@ -269,10 +718,17 @@ static int __devexit max17042_remove(struct i2c_client *client)
struct max17042_chip *chip = i2c_get_clientdata(client);
power_supply_unregister(&chip->battery);
kfree(chip);
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id max17042_dt_match[] = {
{ .compatible = "maxim,max17042" },
{ },
};
MODULE_DEVICE_TABLE(of, max17042_dt_match);
#endif
static const struct i2c_device_id max17042_id[] = {
{ "max17042", 0 },
{ }
@ -282,23 +738,13 @@ MODULE_DEVICE_TABLE(i2c, max17042_id);
static struct i2c_driver max17042_i2c_driver = {
.driver = {
.name = "max17042",
.of_match_table = of_match_ptr(max17042_dt_match),
},
.probe = max17042_probe,
.remove = __devexit_p(max17042_remove),
.id_table = max17042_id,
};
static int __init max17042_init(void)
{
return i2c_add_driver(&max17042_i2c_driver);
}
module_init(max17042_init);
static void __exit max17042_exit(void)
{
i2c_del_driver(&max17042_i2c_driver);
}
module_exit(max17042_exit);
module_i2c_driver(max17042_i2c_driver);
MODULE_AUTHOR("MyungJoo Ham <[email protected]>");
MODULE_DESCRIPTION("MAX17042 Fuel Gauge");

View file

@ -852,18 +852,7 @@ static struct i2c_driver sbs_battery_driver = {
.of_match_table = sbs_dt_ids,
},
};
static int __init sbs_battery_init(void)
{
return i2c_add_driver(&sbs_battery_driver);
}
module_init(sbs_battery_init);
static void __exit sbs_battery_exit(void)
{
i2c_del_driver(&sbs_battery_driver);
}
module_exit(sbs_battery_exit);
module_i2c_driver(sbs_battery_driver);
MODULE_DESCRIPTION("SBS battery monitor driver");
MODULE_LICENSE("GPL");

File diff suppressed because it is too large Load diff

View file

@ -316,19 +316,7 @@ static struct i2c_driver z2_batt_driver = {
.remove = __devexit_p(z2_batt_remove),
.id_table = z2_batt_id,
};
static int __init z2_batt_init(void)
{
return i2c_add_driver(&z2_batt_driver);
}
static void __exit z2_batt_exit(void)
{
i2c_del_driver(&z2_batt_driver);
}
module_init(z2_batt_init);
module_exit(z2_batt_exit);
module_i2c_driver(z2_batt_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Peter Edwards <[email protected]>");