From 1ef3d8fb4deb77ee020b246d217dd4dfb28d88d5 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 15 Mar 2012 14:37:32 +0300 Subject: max17042_battery: Fix a couple buffer overflows There are a couple issues here caused by confusion between sizeof() and ARRAY_SIZE(). "table_size" should be the number of elements, but we should allocate it with kcalloc() so that we allocate the correct number of bytes. In max17042_init_model() we don't allocate enough space so we go past the end of the array in max17042_read_model_data() and max17042_model_data_compare(). In max17042_verify_model_lock() we allocate the right amount of space but we call max17042_read_model_data() with the wrong number of elements and also in the for loop we go past the end of the array. Signed-off-by: Dan Carpenter Acked-by: Dirk Brandewie Signed-off-by: Anton Vorontsov --- drivers/power/max17042_battery.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/max17042_battery.c b/drivers/power/max17042_battery.c index 04620c2cb388..39dd610994ac 100644 --- a/drivers/power/max17042_battery.c +++ b/drivers/power/max17042_battery.c @@ -325,11 +325,10 @@ static inline int max17042_model_data_compare(struct max17042_chip *chip, static int max17042_init_model(struct max17042_chip *chip) { int ret; - int table_size = - sizeof(chip->pdata->config_data->cell_char_tbl)/sizeof(u16); + int table_size = ARRAY_SIZE(chip->pdata->config_data->cell_char_tbl); u16 *temp_data; - temp_data = kzalloc(table_size, GFP_KERNEL); + temp_data = kcalloc(table_size, sizeof(*temp_data), GFP_KERNEL); if (!temp_data) return -ENOMEM; @@ -354,12 +353,11 @@ static int max17042_init_model(struct max17042_chip *chip) static int max17042_verify_model_lock(struct max17042_chip *chip) { int i; - int table_size = - sizeof(chip->pdata->config_data->cell_char_tbl); + int table_size = ARRAY_SIZE(chip->pdata->config_data->cell_char_tbl); u16 *temp_data; int ret = 0; - temp_data = kzalloc(table_size, GFP_KERNEL); + temp_data = kcalloc(table_size, sizeof(*temp_data), GFP_KERNEL); if (!temp_data) return -ENOMEM; -- cgit v1.2.3 From bb28da90f4f973529f81be01547ebde7bf270042 Mon Sep 17 00:00:00 2001 From: Ramakrishna Pallala Date: Mon, 26 Mar 2012 15:38:26 +0530 Subject: max17042_battery: Fix driver exit function This patch fixes driver's remove function: it should free the IRQ. Signed-off-by: Ramakrishna Pallala Signed-off-by: Anton Vorontsov --- drivers/power/max17042_battery.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/power') diff --git a/drivers/power/max17042_battery.c b/drivers/power/max17042_battery.c index 39dd610994ac..93fd13c9be68 100644 --- a/drivers/power/max17042_battery.c +++ b/drivers/power/max17042_battery.c @@ -715,6 +715,8 @@ static int __devexit max17042_remove(struct i2c_client *client) { struct max17042_chip *chip = i2c_get_clientdata(client); + if (client->irq) + free_irq(client->irq, chip); power_supply_unregister(&chip->battery); return 0; } -- cgit v1.2.3 From 48bc177441d68c0ba70631beb544c3d695328d56 Mon Sep 17 00:00:00 2001 From: Ramakrishna Pallala Date: Tue, 27 Mar 2012 02:23:40 +0530 Subject: max17042_battery: Add suspend/resume hooks This patch adds suspend/resume methods to the driver. In suspend method irq line is disabled to avoid i2c read/write errors from the interrupt handler as the i2c bus itself could be in suspend state. In resume function irq line will be re-enabled. Signed-off-by: Ramakrishna Pallala Signed-off-by: Anton Vorontsov --- drivers/power/max17042_battery.c | 41 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) (limited to 'drivers/power') diff --git a/drivers/power/max17042_battery.c b/drivers/power/max17042_battery.c index 93fd13c9be68..07dee974a70e 100644 --- a/drivers/power/max17042_battery.c +++ b/drivers/power/max17042_battery.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -721,6 +722,40 @@ static int __devexit max17042_remove(struct i2c_client *client) return 0; } +#ifdef CONFIG_PM +static int max17042_suspend(struct device *dev) +{ + struct max17042_chip *chip = dev_get_drvdata(dev); + + /* disable the irq and enable irq_wake + * capability to the interrupt line. + */ + if (chip->client->irq) { + disable_irq(chip->client->irq); + enable_irq_wake(chip->client->irq); + } + + return 0; +} + +static int max17042_resume(struct device *dev) +{ + struct max17042_chip *chip = dev_get_drvdata(dev); + + if (chip->client->irq) { + disable_irq_wake(chip->client->irq); + enable_irq(chip->client->irq); + /* re-program the SOC thresholds to 1% change */ + max17042_set_soc_threshold(chip, 1); + } + + return 0; +} +#else +#define max17042_suspend NULL +#define max17042_resume NULL +#endif + #ifdef CONFIG_OF static const struct of_device_id max17042_dt_match[] = { { .compatible = "maxim,max17042" }, @@ -735,10 +770,16 @@ static const struct i2c_device_id max17042_id[] = { }; MODULE_DEVICE_TABLE(i2c, max17042_id); +static const struct dev_pm_ops max17042_pm_ops = { + .suspend = max17042_suspend, + .resume = max17042_resume, +}; + static struct i2c_driver max17042_i2c_driver = { .driver = { .name = "max17042", .of_match_table = of_match_ptr(max17042_dt_match), + .pm = &max17042_pm_ops, }, .probe = max17042_probe, .remove = __devexit_p(max17042_remove), -- cgit v1.2.3 From 48e41c70c10f10541d922fc67e7952f06ad59d9a Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Fri, 4 May 2012 20:38:13 -0700 Subject: max17042_battery: Move dev_pm_ops struct under CONFIG_PM This is what we do for the rest of the drivers, saves some bytes. Plus a small style change while at it. Signed-off-by: Anton Vorontsov --- drivers/power/max17042_battery.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/max17042_battery.c b/drivers/power/max17042_battery.c index 07dee974a70e..738648d1d9fc 100644 --- a/drivers/power/max17042_battery.c +++ b/drivers/power/max17042_battery.c @@ -727,7 +727,8 @@ static int max17042_suspend(struct device *dev) { struct max17042_chip *chip = dev_get_drvdata(dev); - /* disable the irq and enable irq_wake + /* + * disable the irq and enable irq_wake * capability to the interrupt line. */ if (chip->client->irq) { @@ -751,9 +752,15 @@ static int max17042_resume(struct device *dev) return 0; } + +static const struct dev_pm_ops max17042_pm_ops = { + .suspend = max17042_suspend, + .resume = max17042_resume, +}; + +#define MAX17042_PM_OPS (&max17042_pm_ops) #else -#define max17042_suspend NULL -#define max17042_resume NULL +#define MAX17042_PM_OPS NULL #endif #ifdef CONFIG_OF @@ -770,16 +777,11 @@ static const struct i2c_device_id max17042_id[] = { }; MODULE_DEVICE_TABLE(i2c, max17042_id); -static const struct dev_pm_ops max17042_pm_ops = { - .suspend = max17042_suspend, - .resume = max17042_resume, -}; - static struct i2c_driver max17042_i2c_driver = { .driver = { .name = "max17042", .of_match_table = of_match_ptr(max17042_dt_match), - .pm = &max17042_pm_ops, + .pm = MAX17042_PM_OPS, }, .probe = max17042_probe, .remove = __devexit_p(max17042_remove), -- cgit v1.2.3 From a2ebfe2fc6e088a70d06cd15a5bc9bcb621cc195 Mon Sep 17 00:00:00 2001 From: Ramakrishna Pallala Date: Tue, 10 Apr 2012 16:21:20 +0530 Subject: power_supply: Add voltage_ocv property and use it for max17042 driver This adds a new sysfs file called 'voltage_ocv' which gives the Open Circuit Voltage of the battery. This property can be used for platform shutdown policies and can be useful for initial capacity estimations. Note: This patch is generated against linux-next branch. Signed-off-by: Ramakrishna Pallala Signed-off-by: Anton Vorontsov --- drivers/power/max17042_battery.c | 8 ++++++++ drivers/power/power_supply_sysfs.c | 1 + 2 files changed, 9 insertions(+) (limited to 'drivers/power') diff --git a/drivers/power/max17042_battery.c b/drivers/power/max17042_battery.c index 738648d1d9fc..42c4be9a664b 100644 --- a/drivers/power/max17042_battery.c +++ b/drivers/power/max17042_battery.c @@ -106,6 +106,7 @@ static enum power_supply_property max17042_battery_props[] = { POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_VOLTAGE_AVG, + POWER_SUPPLY_PROP_VOLTAGE_OCV, POWER_SUPPLY_PROP_CAPACITY, POWER_SUPPLY_PROP_CHARGE_FULL, POWER_SUPPLY_PROP_TEMP, @@ -170,6 +171,13 @@ static int max17042_get_property(struct power_supply *psy, if (ret < 0) return ret; + val->intval = ret * 625 / 8; + break; + case POWER_SUPPLY_PROP_VOLTAGE_OCV: + ret = max17042_read_reg(chip->client, MAX17042_OCVInternal); + if (ret < 0) + return ret; + val->intval = ret * 625 / 8; break; case POWER_SUPPLY_PROP_CAPACITY: diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index 4368e7d61316..4150747f9186 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c @@ -146,6 +146,7 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(voltage_min_design), POWER_SUPPLY_ATTR(voltage_now), POWER_SUPPLY_ATTR(voltage_avg), + POWER_SUPPLY_ATTR(voltage_ocv), POWER_SUPPLY_ATTR(current_max), POWER_SUPPLY_ATTR(current_now), POWER_SUPPLY_ATTR(current_avg), -- cgit v1.2.3 From 0d4ed4e27a4cb180af395fa3d7aa98d79f3d3015 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Fri, 4 May 2012 21:06:19 -0700 Subject: power_supply: Make the core a boolean instead of a tristate On Mon, Apr 02, 2012 at 01:53:23PM +1000, Benjamin Herrenschmidt wrote: > > drivers/built-in.o: In function `.nouveau_pm_trigger': > > (.text+0xa56e8): undefined reference to `.power_supply_is_system_supplied' > > > > nouveau probably needs to depends on CONFIG_POWER_SUPPLY to force a module > > build with the latter is =m > > Ok, not that trivial... > > The problem is more like POWER_SUPPLY should be a bool, not a tristate. > > If you think about it: you don't want things like nouveau to depend on a > random subsystem like that, people will never get it. In fact, > POWER_SUPPLY provides empty inline stubs when not enabled, so that's > really designed to not have depends... > > However that -cannot- work if POWER_SUPPLY is modular and the drivers > who use it are not. > > The only fixes here that make sense I can think of > that don't also involve Kconfig horrors are: > > - Ugly: in power_supply.h, use the extern variant if > > defined(CONFIG_POWER_SUPPLY) || > (defined(CONFIG_POWER_SUPPLY_MODULE) && defined(MODULE)) > > IE. use the stub if power supply is a module and what is being built is > built-in. Of course that's not only ugly, it somewhat sucks from a user > perspective as the subsystem now exists but can't be used by some > drivers... > > - Better: Just make the bloody thing a bool :-) The power supply > framework itself is small enough, just make it a boolean option and > avoid the problem entirely. The actual power supply sub drivers can > remain modular of course. Suggested-by: Benjamin Herrenschmidt Signed-off-by: Anton Vorontsov --- drivers/power/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/power') diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 99dc29f2f2f2..0c52a4079124 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -1,5 +1,5 @@ menuconfig POWER_SUPPLY - tristate "Power supply class support" + bool "Power supply class support" help Say Y here to enable power supply class support. This allows power supply (batteries, AC, USB) monitoring by userspace -- cgit v1.2.3 From 243e3527ffbda96434429e618324f80ca0d98902 Mon Sep 17 00:00:00 2001 From: Ramakrishna Pallala Date: Sat, 5 May 2012 03:08:37 +0530 Subject: max17042_battery: Fix power supply and irq registration ordering IRQ registration should happen only after power supply object usable. This patch fixes the ordering of power supply and irq registration calls. Signed-off-by: Ramakrishna Pallala Signed-off-by: Anton Vorontsov --- drivers/power/max17042_battery.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/max17042_battery.c b/drivers/power/max17042_battery.c index 42c4be9a664b..b4c8af016278 100644 --- a/drivers/power/max17042_battery.c +++ b/drivers/power/max17042_battery.c @@ -690,6 +690,12 @@ static int __devinit max17042_probe(struct i2c_client *client, max17042_write_reg(client, MAX17042_LearnCFG, 0x0007); } + ret = power_supply_register(&client->dev, &chip->battery); + if (ret) { + dev_err(&client->dev, "failed: power supply register\n"); + return ret; + } + if (client->irq) { ret = request_threaded_irq(client->irq, NULL, max17042_thread_handler, @@ -706,7 +712,6 @@ static int __devinit max17042_probe(struct i2c_client *client, } reg = max17042_read_reg(chip->client, MAX17042_STATUS); - if (reg & STATUS_POR_BIT) { INIT_WORK(&chip->work, max17042_init_worker); schedule_work(&chip->work); @@ -714,10 +719,7 @@ static int __devinit max17042_probe(struct i2c_client *client, 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; + return 0; } static int __devexit max17042_remove(struct i2c_client *client) -- cgit v1.2.3 From e5ba50bc3b9311256ae6a6620e7708fb25d1f6e2 Mon Sep 17 00:00:00 2001 From: Ramakrishna Pallala Date: Sat, 5 May 2012 04:43:10 +0530 Subject: max17042_battery: Handle irq request failure case suspend/resume functions take action based upon the fuel gauge interrupt. If the rquest irq fails we should assign 0 to client->irq. Signed-off-by: Ramakrishna Pallala Signed-off-by: Anton Vorontsov --- drivers/power/max17042_battery.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/power') diff --git a/drivers/power/max17042_battery.c b/drivers/power/max17042_battery.c index b4c8af016278..8d28006322e9 100644 --- a/drivers/power/max17042_battery.c +++ b/drivers/power/max17042_battery.c @@ -706,9 +706,11 @@ static int __devinit max17042_probe(struct i2c_client *client, reg |= CONFIG_ALRT_BIT_ENBL; max17042_write_reg(client, MAX17042_CONFIG, reg); max17042_set_soc_threshold(chip, 1); - } else + } else { + client->irq = 0; dev_err(&client->dev, "%s(): cannot get IRQ\n", __func__); + } } reg = max17042_read_reg(chip->client, MAX17042_STATUS); -- cgit v1.2.3 From cc28e17100a6b051ef966de81bb2d4a97b2a6645 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 13 Apr 2012 10:15:49 +0200 Subject: ab8500_charger: Harden platform data check If no platform data at all is supplied the driver crashes, extend the checks to be more careful so we can compile in the driver and boot also without platform data present. Acked-by: Arun Murthy Signed-off-by: Linus Walleij Signed-off-by: Anton Vorontsov --- drivers/power/ab8500_charger.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c index e2b4accbec88..79dc584a53ed 100644 --- a/drivers/power/ab8500_charger.c +++ b/drivers/power/ab8500_charger.c @@ -2551,13 +2551,12 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev) /* get charger specific platform data */ plat_data = pdev->dev.platform_data; - di->pdata = plat_data->charger; - - if (!di->pdata) { + if (!plat_data || !plat_data->charger) { dev_err(di->dev, "no charger platform data supplied\n"); ret = -EINVAL; goto free_device_info; } + di->pdata = plat_data->charger; /* get battery specific platform data */ di->bat = plat_data->battery; -- cgit v1.2.3 From ec511672b97383107d87e86921b1f0392bc1d000 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 13 Apr 2012 10:16:06 +0200 Subject: ab8500_btemp: Harden platform data check If no platform data at all is supplied the driver crashes, extend the checks to be more careful so we can compile in the driver and boot also without platform data present. Acked-by: Arun Murthy Signed-off-by: Linus Walleij Signed-off-by: Anton Vorontsov --- drivers/power/ab8500_btemp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/ab8500_btemp.c b/drivers/power/ab8500_btemp.c index d8bb99394ac0..e266f038a110 100644 --- a/drivers/power/ab8500_btemp.c +++ b/drivers/power/ab8500_btemp.c @@ -978,12 +978,12 @@ static int __devinit ab8500_btemp_probe(struct platform_device *pdev) /* get btemp specific platform data */ plat_data = pdev->dev.platform_data; - di->pdata = plat_data->btemp; - if (!di->pdata) { + if (!plat_data || !plat_data->btemp) { dev_err(di->dev, "no btemp platform data supplied\n"); ret = -EINVAL; goto free_device_info; } + di->pdata = plat_data->btemp; /* get battery specific platform data */ di->bat = plat_data->battery; -- cgit v1.2.3 From 5f98eb393c6d7a5de3202b7ce9e4439292e952fb Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 13 Apr 2012 10:16:13 +0200 Subject: ab8500_fg: Harden platform data check If no platform data at all is supplied the driver crashes, extend the checks to be more careful so we can compile in the driver and boot also without platform data present. Acked-by: Arun Murthy Signed-off-by: Linus Walleij Signed-off-by: Anton Vorontsov --- drivers/power/ab8500_fg.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c index c22f2f05657e..0ebea39e73fe 100644 --- a/drivers/power/ab8500_fg.c +++ b/drivers/power/ab8500_fg.c @@ -2462,12 +2462,12 @@ static int __devinit ab8500_fg_probe(struct platform_device *pdev) /* get fg specific platform data */ plat_data = pdev->dev.platform_data; - di->pdata = plat_data->fg; - if (!di->pdata) { + if (!plat_data || !plat_data->fg) { dev_err(di->dev, "no fg platform data supplied\n"); ret = -EINVAL; goto free_device_info; } + di->pdata = plat_data->fg; /* get battery specific platform data */ di->bat = plat_data->battery; -- cgit v1.2.3 From 2aac3de19b72608f474c90034185c2be4908728f Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Sat, 5 May 2012 04:38:19 -0700 Subject: ab8500: Clean up probe routines These patches clean up some ugliness and brings the variable initialisation formatting more into line with other drivers. Signed-off-by: Lee Jones Signed-off-by: Anton Vorontsov --- drivers/power/ab8500_btemp.c | 16 ++++++++++------ drivers/power/ab8500_charger.c | 16 ++++++++++------ drivers/power/ab8500_fg.c | 16 ++++++++++------ 3 files changed, 30 insertions(+), 18 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/ab8500_btemp.c b/drivers/power/ab8500_btemp.c index e266f038a110..bba3ccac72fe 100644 --- a/drivers/power/ab8500_btemp.c +++ b/drivers/power/ab8500_btemp.c @@ -964,10 +964,15 @@ static int __devinit ab8500_btemp_probe(struct platform_device *pdev) { int irq, i, ret = 0; u8 val; - struct abx500_bm_plat_data *plat_data; + struct abx500_bm_plat_data *plat_data = pdev->dev.platform_data; + struct ab8500_btemp *di; + + if (!plat_data) { + dev_err(&pdev->dev, "No platform data\n"); + return -EINVAL; + } - struct ab8500_btemp *di = - kzalloc(sizeof(struct ab8500_btemp), GFP_KERNEL); + di = kzalloc(sizeof(*di), GFP_KERNEL); if (!di) return -ENOMEM; @@ -977,13 +982,12 @@ static int __devinit ab8500_btemp_probe(struct platform_device *pdev) di->gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); /* get btemp specific platform data */ - plat_data = pdev->dev.platform_data; - if (!plat_data || !plat_data->btemp) { + di->pdata = plat_data->btemp; + if (!di->pdata) { dev_err(di->dev, "no btemp platform data supplied\n"); ret = -EINVAL; goto free_device_info; } - di->pdata = plat_data->btemp; /* get battery specific platform data */ di->bat = plat_data->battery; diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c index 79dc584a53ed..d2303d0b7c75 100644 --- a/drivers/power/ab8500_charger.c +++ b/drivers/power/ab8500_charger.c @@ -2534,10 +2534,15 @@ static int __devexit ab8500_charger_remove(struct platform_device *pdev) static int __devinit ab8500_charger_probe(struct platform_device *pdev) { int irq, i, charger_status, ret = 0; - struct abx500_bm_plat_data *plat_data; + struct abx500_bm_plat_data *plat_data = pdev->dev.platform_data; + struct ab8500_charger *di; - struct ab8500_charger *di = - kzalloc(sizeof(struct ab8500_charger), GFP_KERNEL); + if (!plat_data) { + dev_err(&pdev->dev, "No platform data\n"); + return -EINVAL; + } + + di = kzalloc(sizeof(*di), GFP_KERNEL); if (!di) return -ENOMEM; @@ -2550,13 +2555,12 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev) spin_lock_init(&di->usb_state.usb_lock); /* get charger specific platform data */ - plat_data = pdev->dev.platform_data; - if (!plat_data || !plat_data->charger) { + di->pdata = plat_data->charger; + if (!di->pdata) { dev_err(di->dev, "no charger platform data supplied\n"); ret = -EINVAL; goto free_device_info; } - di->pdata = plat_data->charger; /* get battery specific platform data */ di->bat = plat_data->battery; diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c index 0ebea39e73fe..bf022255994c 100644 --- a/drivers/power/ab8500_fg.c +++ b/drivers/power/ab8500_fg.c @@ -2446,10 +2446,15 @@ static int __devinit ab8500_fg_probe(struct platform_device *pdev) { int i, irq; int ret = 0; - struct abx500_bm_plat_data *plat_data; + struct abx500_bm_plat_data *plat_data = pdev->dev.platform_data; + struct ab8500_fg *di; + + if (!plat_data) { + dev_err(&pdev->dev, "No platform data\n"); + return -EINVAL; + } - struct ab8500_fg *di = - kzalloc(sizeof(struct ab8500_fg), GFP_KERNEL); + di = kzalloc(sizeof(*di), GFP_KERNEL); if (!di) return -ENOMEM; @@ -2461,13 +2466,12 @@ static int __devinit ab8500_fg_probe(struct platform_device *pdev) di->gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); /* get fg specific platform data */ - plat_data = pdev->dev.platform_data; - if (!plat_data || !plat_data->fg) { + di->pdata = plat_data->fg; + if (!di->pdata) { dev_err(di->dev, "no fg platform data supplied\n"); ret = -EINVAL; goto free_device_info; } - di->pdata = plat_data->fg; /* get battery specific platform data */ di->bat = plat_data->battery; -- cgit v1.2.3 From e6fe3597fa17dcd7ee82a5198beadb19a457a478 Mon Sep 17 00:00:00 2001 From: Ramakrishna Pallala Date: Thu, 19 Apr 2012 10:00:18 +0530 Subject: smb347_charger: Cleanup power supply registration code in probe This patch checks if the usb or mains charging is enabled by the platform before registering with the power supply class. Signed-off-by: Ramakrishna Pallala Signed-off-by: Anton Vorontsov --- drivers/power/smb347-charger.c | 70 ++++++++++++++++++++++++------------------ 1 file changed, 40 insertions(+), 30 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/smb347-charger.c b/drivers/power/smb347-charger.c index ce1694d1a365..3e9df6ef8537 100644 --- a/drivers/power/smb347-charger.c +++ b/drivers/power/smb347-charger.c @@ -816,8 +816,10 @@ static irqreturn_t smb347_interrupt(int irq, void *data) if (irqstat_e & (IRQSTAT_E_USBIN_UV_IRQ | IRQSTAT_E_DCIN_UV_IRQ)) { if (smb347_update_status(smb) > 0) { smb347_update_online(smb); - power_supply_changed(&smb->mains); - power_supply_changed(&smb->usb); + if (smb->pdata->use_mains) + power_supply_changed(&smb->mains); + if (smb->pdata->use_usb) + power_supply_changed(&smb->usb); } ret = IRQ_HANDLED; } @@ -1185,21 +1187,34 @@ static int smb347_probe(struct i2c_client *client, if (ret < 0) return ret; - smb->mains.name = "smb347-mains"; - smb->mains.type = POWER_SUPPLY_TYPE_MAINS; - smb->mains.get_property = smb347_mains_get_property; - smb->mains.properties = smb347_mains_properties; - smb->mains.num_properties = ARRAY_SIZE(smb347_mains_properties); - smb->mains.supplied_to = battery; - smb->mains.num_supplicants = ARRAY_SIZE(battery); - - smb->usb.name = "smb347-usb"; - smb->usb.type = POWER_SUPPLY_TYPE_USB; - smb->usb.get_property = smb347_usb_get_property; - smb->usb.properties = smb347_usb_properties; - smb->usb.num_properties = ARRAY_SIZE(smb347_usb_properties); - smb->usb.supplied_to = battery; - smb->usb.num_supplicants = ARRAY_SIZE(battery); + if (smb->pdata->use_mains) { + smb->mains.name = "smb347-mains"; + smb->mains.type = POWER_SUPPLY_TYPE_MAINS; + smb->mains.get_property = smb347_mains_get_property; + smb->mains.properties = smb347_mains_properties; + smb->mains.num_properties = ARRAY_SIZE(smb347_mains_properties); + smb->mains.supplied_to = battery; + smb->mains.num_supplicants = ARRAY_SIZE(battery); + ret = power_supply_register(dev, &smb->mains); + if (ret < 0) + return ret; + } + + if (smb->pdata->use_usb) { + smb->usb.name = "smb347-usb"; + smb->usb.type = POWER_SUPPLY_TYPE_USB; + smb->usb.get_property = smb347_usb_get_property; + smb->usb.properties = smb347_usb_properties; + smb->usb.num_properties = ARRAY_SIZE(smb347_usb_properties); + smb->usb.supplied_to = battery; + smb->usb.num_supplicants = ARRAY_SIZE(battery); + ret = power_supply_register(dev, &smb->usb); + if (ret < 0) { + if (smb->pdata->use_mains) + power_supply_unregister(&smb->mains); + return ret; + } + } smb->battery.name = "smb347-battery"; smb->battery.type = POWER_SUPPLY_TYPE_BATTERY; @@ -1207,20 +1222,13 @@ static int smb347_probe(struct i2c_client *client, smb->battery.properties = smb347_battery_properties; smb->battery.num_properties = ARRAY_SIZE(smb347_battery_properties); - ret = power_supply_register(dev, &smb->mains); - if (ret < 0) - return ret; - - ret = power_supply_register(dev, &smb->usb); - if (ret < 0) { - power_supply_unregister(&smb->mains); - return ret; - } ret = power_supply_register(dev, &smb->battery); if (ret < 0) { - power_supply_unregister(&smb->usb); - power_supply_unregister(&smb->mains); + if (smb->pdata->use_usb) + power_supply_unregister(&smb->usb); + if (smb->pdata->use_mains) + power_supply_unregister(&smb->mains); return ret; } @@ -1255,8 +1263,10 @@ static int smb347_remove(struct i2c_client *client) } power_supply_unregister(&smb->battery); - power_supply_unregister(&smb->usb); - power_supply_unregister(&smb->mains); + if (smb->pdata->use_usb) + power_supply_unregister(&smb->usb); + if (smb->pdata->use_mains) + power_supply_unregister(&smb->mains); return 0; } -- cgit v1.2.3 From b75ef1d807e20919a00bed16045537e76e0497a7 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 16 Apr 2012 11:48:38 +0300 Subject: smb347-charger: Convert to use module_i2c_driver() This reduces the amount of boilerplate code in the driver and makes it a bit simpler. Signed-off-by: Mika Westerberg Signed-off-by: Anton Vorontsov --- drivers/power/smb347-charger.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/smb347-charger.c b/drivers/power/smb347-charger.c index 3e9df6ef8537..0dfe063f4178 100644 --- a/drivers/power/smb347-charger.c +++ b/drivers/power/smb347-charger.c @@ -1285,17 +1285,7 @@ static struct i2c_driver smb347_driver = { .id_table = smb347_id, }; -static int __init smb347_init(void) -{ - return i2c_add_driver(&smb347_driver); -} -module_init(smb347_init); - -static void __exit smb347_exit(void) -{ - i2c_del_driver(&smb347_driver); -} -module_exit(smb347_exit); +module_i2c_driver(smb347_driver); MODULE_AUTHOR("Bruce E. Robertson "); MODULE_AUTHOR("Mika Westerberg "); -- cgit v1.2.3 From 055d7f0f29b2af6ec8fd5da9f0ce27b9e9e63436 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 16 Apr 2012 11:48:39 +0300 Subject: smb347-charger: Rename few functions to match better what they are doing The naming used in the driver for some functions is not very clear what the functions are really doing. To make this a bit easier to understand we rename few functions which were badly named. Signed-off-by: Mika Westerberg Signed-off-by: Anton Vorontsov --- drivers/power/smb347-charger.c | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/smb347-charger.c b/drivers/power/smb347-charger.c index 0dfe063f4178..18d53748e66d 100644 --- a/drivers/power/smb347-charger.c +++ b/drivers/power/smb347-charger.c @@ -235,14 +235,14 @@ static int smb347_write(struct smb347_charger *smb, u8 reg, u8 val) } /** - * smb347_update_status - updates the charging status + * smb347_update_ps_status - refreshes the power source status * @smb: pointer to smb347 charger instance * - * Function checks status of the charging and updates internal state - * accordingly. Returns %0 if there is no change in status, %1 if the - * status has changed and negative errno in case of failure. + * Function checks whether any power source is connected to the charger and + * updates internal state accordingly. If there is a change to previous state + * function returns %1, otherwise %0 and negative errno in case of errror. */ -static int smb347_update_status(struct smb347_charger *smb) +static int smb347_update_ps_status(struct smb347_charger *smb) { bool usb = false; bool dc = false; @@ -271,15 +271,15 @@ static int smb347_update_status(struct smb347_charger *smb) } /* - * smb347_is_online - returns whether input power source is connected + * smb347_is_ps_online - returns whether input power source is connected * @smb: pointer to smb347 charger instance * * Returns %true if input power source is connected. Note that this is * dependent on what platform has configured for usable power sources. For - * example if USB is disabled, this will return %false even if the USB - * cable is connected. + * example if USB is disabled, this will return %false even if the USB cable + * is connected. */ -static bool smb347_is_online(struct smb347_charger *smb) +static bool smb347_is_ps_online(struct smb347_charger *smb) { bool ret; @@ -301,7 +301,7 @@ static int smb347_charging_status(struct smb347_charger *smb) { int ret; - if (!smb347_is_online(smb)) + if (!smb347_is_ps_online(smb)) return 0; ret = smb347_read(smb, STAT_C); @@ -351,7 +351,7 @@ static inline int smb347_charging_disable(struct smb347_charger *smb) return smb347_charging_set(smb, false); } -static int smb347_update_online(struct smb347_charger *smb) +static int smb347_start_stop_charging(struct smb347_charger *smb) { int ret; @@ -360,7 +360,7 @@ static int smb347_update_online(struct smb347_charger *smb) * disable or enable the charging. We do it manually because it * depends on how the platform has configured the valid inputs. */ - if (smb347_is_online(smb)) { + if (smb347_is_ps_online(smb)) { ret = smb347_charging_enable(smb); if (ret < 0) dev_err(&smb->client->dev, @@ -749,11 +749,11 @@ static int smb347_hw_init(struct smb347_charger *smb) if (ret < 0) goto fail; - ret = smb347_update_status(smb); + ret = smb347_update_ps_status(smb); if (ret < 0) goto fail; - ret = smb347_update_online(smb); + ret = smb347_start_stop_charging(smb); fail: smb347_set_writable(smb, false); @@ -814,8 +814,8 @@ static irqreturn_t smb347_interrupt(int irq, void *data) * was connected or disconnected. */ if (irqstat_e & (IRQSTAT_E_USBIN_UV_IRQ | IRQSTAT_E_DCIN_UV_IRQ)) { - if (smb347_update_status(smb) > 0) { - smb347_update_online(smb); + if (smb347_update_ps_status(smb) > 0) { + smb347_start_stop_charging(smb); if (smb->pdata->use_mains) power_supply_changed(&smb->mains); if (smb->pdata->use_usb) @@ -989,13 +989,13 @@ static int smb347_battery_get_property(struct power_supply *psy, const struct smb347_charger_platform_data *pdata = smb->pdata; int ret; - ret = smb347_update_status(smb); + ret = smb347_update_ps_status(smb); if (ret < 0) return ret; switch (prop) { case POWER_SUPPLY_PROP_STATUS: - if (!smb347_is_online(smb)) { + if (!smb347_is_ps_online(smb)) { val->intval = POWER_SUPPLY_STATUS_DISCHARGING; break; } @@ -1006,7 +1006,7 @@ static int smb347_battery_get_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_CHARGE_TYPE: - if (!smb347_is_online(smb)) + if (!smb347_is_ps_online(smb)) return -ENODATA; /* @@ -1039,7 +1039,7 @@ static int smb347_battery_get_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_VOLTAGE_NOW: - if (!smb347_is_online(smb)) + if (!smb347_is_ps_online(smb)) return -ENODATA; ret = smb347_read(smb, STAT_A); if (ret < 0) @@ -1053,7 +1053,7 @@ static int smb347_battery_get_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_CURRENT_NOW: - if (!smb347_is_online(smb)) + if (!smb347_is_ps_online(smb)) return -ENODATA; ret = smb347_read(smb, STAT_B); -- cgit v1.2.3 From d72bade76d11da661d76aec5801567d573f39fea Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 16 Apr 2012 11:48:40 +0300 Subject: smb347-charger: Move IRQ enabling to the end of probe There is a potential problem if we call smb347_irq_enable() from smb347_irq_init() because smb347_irq_enable() makes the device registers read-only once it returns and smb347_irq_init() expects them to still be read-write. Currently no harm happens because it is the last call we make in smb347_irq_init(). Anyway a better place for enabling IRQs is at the end of probe function and this is also symmetric to call smb347_irq_disable() which is done at the beginning of remove function. Signed-off-by: Mika Westerberg Signed-off-by: Anton Vorontsov --- drivers/power/smb347-charger.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/smb347-charger.c b/drivers/power/smb347-charger.c index 18d53748e66d..de6898f5d166 100644 --- a/drivers/power/smb347-charger.c +++ b/drivers/power/smb347-charger.c @@ -925,10 +925,6 @@ static int smb347_irq_init(struct smb347_charger *smb) if (ret < 0) goto fail_readonly; - ret = smb347_irq_enable(smb); - if (ret < 0) - goto fail_readonly; - smb347_set_writable(smb, false); smb->client->irq = irq; return 0; @@ -1241,6 +1237,8 @@ static int smb347_probe(struct i2c_client *client, if (ret < 0) { dev_warn(dev, "failed to initialize IRQ: %d\n", ret); dev_warn(dev, "disabling IRQ support\n"); + } else { + smb347_irq_enable(smb); } } -- cgit v1.2.3 From 34298d40e5853bc195c9db012fc1ddccac9b6f7f Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 16 Apr 2012 11:48:41 +0300 Subject: smb347-charger: Convert to regmap API The smb347-charger driver does a lot of read-modify-write to the device registers. Instead of open-coding everything we can take advantage of regmap API which provides nice functions to do this kind of things. In addition there is no need for custom debugfs file for dumping registers as this is already provided by the regmap API. Signed-off-by: Mika Westerberg Signed-off-by: Anton Vorontsov --- drivers/power/Kconfig | 1 + drivers/power/smb347-charger.c | 558 +++++++++++++++++------------------------ 2 files changed, 232 insertions(+), 327 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 0c52a4079124..2259dea525e0 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -291,6 +291,7 @@ config CHARGER_MAX8998 config CHARGER_SMB347 tristate "Summit Microelectronics SMB347 Battery Charger" depends on I2C + select REGMAP_I2C help Say Y to include support for Summit Microelectronics SMB347 Battery Charger. diff --git a/drivers/power/smb347-charger.c b/drivers/power/smb347-charger.c index de6898f5d166..cf31b31cc653 100644 --- a/drivers/power/smb347-charger.c +++ b/drivers/power/smb347-charger.c @@ -11,7 +11,6 @@ * published by the Free Software Foundation. */ -#include #include #include #include @@ -21,7 +20,7 @@ #include #include #include -#include +#include /* * Configuration registers. These are mirrored to volatile RAM and can be @@ -39,6 +38,7 @@ #define CFG_CURRENT_LIMIT_DC_SHIFT 4 #define CFG_CURRENT_LIMIT_USB_MASK 0x0f #define CFG_FLOAT_VOLTAGE 0x03 +#define CFG_FLOAT_VOLTAGE_FLOAT_MASK 0x3f #define CFG_FLOAT_VOLTAGE_THRESHOLD_MASK 0xc0 #define CFG_FLOAT_VOLTAGE_THRESHOLD_SHIFT 6 #define CFG_STAT 0x05 @@ -113,29 +113,31 @@ #define STAT_C_CHARGER_ERROR BIT(6) #define STAT_E 0x3f +#define SMB347_MAX_REGISTER 0x3f + /** * struct smb347_charger - smb347 charger instance * @lock: protects concurrent access to online variables - * @client: pointer to i2c client + * @dev: pointer to device + * @regmap: pointer to driver regmap * @mains: power_supply instance for AC/DC power * @usb: power_supply instance for USB power * @battery: power_supply instance for battery * @mains_online: is AC/DC input connected * @usb_online: is USB input connected * @charging_enabled: is charging enabled - * @dentry: for debugfs * @pdata: pointer to platform data */ struct smb347_charger { struct mutex lock; - struct i2c_client *client; + struct device *dev; + struct regmap *regmap; struct power_supply mains; struct power_supply usb; struct power_supply battery; bool mains_online; bool usb_online; bool charging_enabled; - struct dentry *dentry; const struct smb347_charger_platform_data *pdata; }; @@ -212,28 +214,6 @@ static int current_to_hw(const unsigned int *tbl, size_t size, unsigned int val) return i > 0 ? i - 1 : -EINVAL; } -static int smb347_read(struct smb347_charger *smb, u8 reg) -{ - int ret; - - ret = i2c_smbus_read_byte_data(smb->client, reg); - if (ret < 0) - dev_warn(&smb->client->dev, "failed to read reg 0x%x: %d\n", - reg, ret); - return ret; -} - -static int smb347_write(struct smb347_charger *smb, u8 reg, u8 val) -{ - int ret; - - ret = i2c_smbus_write_byte_data(smb->client, reg, val); - if (ret < 0) - dev_warn(&smb->client->dev, "failed to write reg 0x%x: %d\n", - reg, ret); - return ret; -} - /** * smb347_update_ps_status - refreshes the power source status * @smb: pointer to smb347 charger instance @@ -246,9 +226,10 @@ static int smb347_update_ps_status(struct smb347_charger *smb) { bool usb = false; bool dc = false; + unsigned int val; int ret; - ret = smb347_read(smb, IRQSTAT_E); + ret = regmap_read(smb->regmap, IRQSTAT_E, &val); if (ret < 0) return ret; @@ -257,9 +238,9 @@ static int smb347_update_ps_status(struct smb347_charger *smb) * platform data _and_ whether corresponding undervoltage is set. */ if (smb->pdata->use_mains) - dc = !(ret & IRQSTAT_E_DCIN_UV_STAT); + dc = !(val & IRQSTAT_E_DCIN_UV_STAT); if (smb->pdata->use_usb) - usb = !(ret & IRQSTAT_E_USBIN_UV_STAT); + usb = !(val & IRQSTAT_E_USBIN_UV_STAT); mutex_lock(&smb->lock); ret = smb->mains_online != dc || smb->usb_online != usb; @@ -299,16 +280,17 @@ static bool smb347_is_ps_online(struct smb347_charger *smb) */ static int smb347_charging_status(struct smb347_charger *smb) { + unsigned int val; int ret; if (!smb347_is_ps_online(smb)) return 0; - ret = smb347_read(smb, STAT_C); + ret = regmap_read(smb->regmap, STAT_C, &val); if (ret < 0) return 0; - return (ret & STAT_C_CHG_MASK) >> STAT_C_CHG_SHIFT; + return (val & STAT_C_CHG_MASK) >> STAT_C_CHG_SHIFT; } static int smb347_charging_set(struct smb347_charger *smb, bool enable) @@ -316,27 +298,17 @@ static int smb347_charging_set(struct smb347_charger *smb, bool enable) int ret = 0; if (smb->pdata->enable_control != SMB347_CHG_ENABLE_SW) { - dev_dbg(&smb->client->dev, - "charging enable/disable in SW disabled\n"); + dev_dbg(smb->dev, "charging enable/disable in SW disabled\n"); return 0; } mutex_lock(&smb->lock); if (smb->charging_enabled != enable) { - ret = smb347_read(smb, CMD_A); - if (ret < 0) - goto out; - - smb->charging_enabled = enable; - - if (enable) - ret |= CMD_A_CHG_ENABLED; - else - ret &= ~CMD_A_CHG_ENABLED; - - ret = smb347_write(smb, CMD_A, ret); + ret = regmap_update_bits(smb->regmap, CMD_A, CMD_A_CHG_ENABLED, + enable ? CMD_A_CHG_ENABLED : 0); + if (!ret) + smb->charging_enabled = enable; } -out: mutex_unlock(&smb->lock); return ret; } @@ -363,13 +335,11 @@ static int smb347_start_stop_charging(struct smb347_charger *smb) if (smb347_is_ps_online(smb)) { ret = smb347_charging_enable(smb); if (ret < 0) - dev_err(&smb->client->dev, - "failed to enable charging\n"); + dev_err(smb->dev, "failed to enable charging\n"); } else { ret = smb347_charging_disable(smb); if (ret < 0) - dev_err(&smb->client->dev, - "failed to disable charging\n"); + dev_err(smb->dev, "failed to disable charging\n"); } return ret; @@ -377,112 +347,120 @@ static int smb347_start_stop_charging(struct smb347_charger *smb) static int smb347_set_charge_current(struct smb347_charger *smb) { - int ret, val; - - ret = smb347_read(smb, CFG_CHARGE_CURRENT); - if (ret < 0) - return ret; + int ret; if (smb->pdata->max_charge_current) { - val = current_to_hw(fcc_tbl, ARRAY_SIZE(fcc_tbl), + ret = current_to_hw(fcc_tbl, ARRAY_SIZE(fcc_tbl), smb->pdata->max_charge_current); - if (val < 0) - return val; + if (ret < 0) + return ret; - ret &= ~CFG_CHARGE_CURRENT_FCC_MASK; - ret |= val << CFG_CHARGE_CURRENT_FCC_SHIFT; + ret = regmap_update_bits(smb->regmap, CFG_CHARGE_CURRENT, + CFG_CHARGE_CURRENT_FCC_MASK, + ret << CFG_CHARGE_CURRENT_FCC_SHIFT); + if (ret < 0) + return ret; } if (smb->pdata->pre_charge_current) { - val = current_to_hw(pcc_tbl, ARRAY_SIZE(pcc_tbl), + ret = current_to_hw(pcc_tbl, ARRAY_SIZE(pcc_tbl), smb->pdata->pre_charge_current); - if (val < 0) - return val; + if (ret < 0) + return ret; - ret &= ~CFG_CHARGE_CURRENT_PCC_MASK; - ret |= val << CFG_CHARGE_CURRENT_PCC_SHIFT; + ret = regmap_update_bits(smb->regmap, CFG_CHARGE_CURRENT, + CFG_CHARGE_CURRENT_PCC_MASK, + ret << CFG_CHARGE_CURRENT_PCC_SHIFT); + if (ret < 0) + return ret; } if (smb->pdata->termination_current) { - val = current_to_hw(tc_tbl, ARRAY_SIZE(tc_tbl), + ret = current_to_hw(tc_tbl, ARRAY_SIZE(tc_tbl), smb->pdata->termination_current); - if (val < 0) - return val; + if (ret < 0) + return ret; - ret &= ~CFG_CHARGE_CURRENT_TC_MASK; - ret |= val; + ret = regmap_update_bits(smb->regmap, CFG_CHARGE_CURRENT, + CFG_CHARGE_CURRENT_TC_MASK, ret); + if (ret < 0) + return ret; } - return smb347_write(smb, CFG_CHARGE_CURRENT, ret); + return 0; } static int smb347_set_current_limits(struct smb347_charger *smb) { - int ret, val; - - ret = smb347_read(smb, CFG_CURRENT_LIMIT); - if (ret < 0) - return ret; + int ret; if (smb->pdata->mains_current_limit) { - val = current_to_hw(icl_tbl, ARRAY_SIZE(icl_tbl), + ret = current_to_hw(icl_tbl, ARRAY_SIZE(icl_tbl), smb->pdata->mains_current_limit); - if (val < 0) - return val; + if (ret < 0) + return ret; - ret &= ~CFG_CURRENT_LIMIT_DC_MASK; - ret |= val << CFG_CURRENT_LIMIT_DC_SHIFT; + ret = regmap_update_bits(smb->regmap, CFG_CURRENT_LIMIT, + CFG_CURRENT_LIMIT_DC_MASK, + ret << CFG_CURRENT_LIMIT_DC_SHIFT); + if (ret < 0) + return ret; } if (smb->pdata->usb_hc_current_limit) { - val = current_to_hw(icl_tbl, ARRAY_SIZE(icl_tbl), + ret = current_to_hw(icl_tbl, ARRAY_SIZE(icl_tbl), smb->pdata->usb_hc_current_limit); - if (val < 0) - return val; + if (ret < 0) + return ret; - ret &= ~CFG_CURRENT_LIMIT_USB_MASK; - ret |= val; + ret = regmap_update_bits(smb->regmap, CFG_CURRENT_LIMIT, + CFG_CURRENT_LIMIT_USB_MASK, ret); + if (ret < 0) + return ret; } - return smb347_write(smb, CFG_CURRENT_LIMIT, ret); + return 0; } static int smb347_set_voltage_limits(struct smb347_charger *smb) { - int ret, val; - - ret = smb347_read(smb, CFG_FLOAT_VOLTAGE); - if (ret < 0) - return ret; + int ret; if (smb->pdata->pre_to_fast_voltage) { - val = smb->pdata->pre_to_fast_voltage; + ret = smb->pdata->pre_to_fast_voltage; /* uV */ - val = clamp_val(val, 2400000, 3000000) - 2400000; - val /= 200000; + ret = clamp_val(ret, 2400000, 3000000) - 2400000; + ret /= 200000; - ret &= ~CFG_FLOAT_VOLTAGE_THRESHOLD_MASK; - ret |= val << CFG_FLOAT_VOLTAGE_THRESHOLD_SHIFT; + ret = regmap_update_bits(smb->regmap, CFG_FLOAT_VOLTAGE, + CFG_FLOAT_VOLTAGE_THRESHOLD_MASK, + ret << CFG_FLOAT_VOLTAGE_THRESHOLD_SHIFT); + if (ret < 0) + return ret; } if (smb->pdata->max_charge_voltage) { - val = smb->pdata->max_charge_voltage; + ret = smb->pdata->max_charge_voltage; /* uV */ - val = clamp_val(val, 3500000, 4500000) - 3500000; - val /= 20000; + ret = clamp_val(ret, 3500000, 4500000) - 3500000; + ret /= 20000; - ret |= val; + ret = regmap_update_bits(smb->regmap, CFG_FLOAT_VOLTAGE, + CFG_FLOAT_VOLTAGE_FLOAT_MASK, ret); + if (ret < 0) + return ret; } - return smb347_write(smb, CFG_FLOAT_VOLTAGE, ret); + return 0; } static int smb347_set_temp_limits(struct smb347_charger *smb) { bool enable_therm_monitor = false; - int ret, val; + int ret = 0; + int val; if (smb->pdata->chip_temp_threshold) { val = smb->pdata->chip_temp_threshold; @@ -491,22 +469,13 @@ static int smb347_set_temp_limits(struct smb347_charger *smb) val = clamp_val(val, 100, 130) - 100; val /= 10; - ret = smb347_read(smb, CFG_OTG); - if (ret < 0) - return ret; - - ret &= ~CFG_OTG_TEMP_THRESHOLD_MASK; - ret |= val << CFG_OTG_TEMP_THRESHOLD_SHIFT; - - ret = smb347_write(smb, CFG_OTG, ret); + ret = regmap_update_bits(smb->regmap, CFG_OTG, + CFG_OTG_TEMP_THRESHOLD_MASK, + val << CFG_OTG_TEMP_THRESHOLD_SHIFT); if (ret < 0) return ret; } - ret = smb347_read(smb, CFG_TEMP_LIMIT); - if (ret < 0) - return ret; - if (smb->pdata->soft_cold_temp_limit != SMB347_TEMP_USE_DEFAULT) { val = smb->pdata->soft_cold_temp_limit; @@ -515,8 +484,11 @@ static int smb347_set_temp_limits(struct smb347_charger *smb) /* this goes from higher to lower so invert the value */ val = ~val & 0x3; - ret &= ~CFG_TEMP_LIMIT_SOFT_COLD_MASK; - ret |= val << CFG_TEMP_LIMIT_SOFT_COLD_SHIFT; + ret = regmap_update_bits(smb->regmap, CFG_TEMP_LIMIT, + CFG_TEMP_LIMIT_SOFT_COLD_MASK, + val << CFG_TEMP_LIMIT_SOFT_COLD_SHIFT); + if (ret < 0) + return ret; enable_therm_monitor = true; } @@ -527,8 +499,11 @@ static int smb347_set_temp_limits(struct smb347_charger *smb) val = clamp_val(val, 40, 55) - 40; val /= 5; - ret &= ~CFG_TEMP_LIMIT_SOFT_HOT_MASK; - ret |= val << CFG_TEMP_LIMIT_SOFT_HOT_SHIFT; + ret = regmap_update_bits(smb->regmap, CFG_TEMP_LIMIT, + CFG_TEMP_LIMIT_SOFT_HOT_MASK, + val << CFG_TEMP_LIMIT_SOFT_HOT_SHIFT); + if (ret < 0) + return ret; enable_therm_monitor = true; } @@ -541,8 +516,11 @@ static int smb347_set_temp_limits(struct smb347_charger *smb) /* this goes from higher to lower so invert the value */ val = ~val & 0x3; - ret &= ~CFG_TEMP_LIMIT_HARD_COLD_MASK; - ret |= val << CFG_TEMP_LIMIT_HARD_COLD_SHIFT; + ret = regmap_update_bits(smb->regmap, CFG_TEMP_LIMIT, + CFG_TEMP_LIMIT_HARD_COLD_MASK, + val << CFG_TEMP_LIMIT_HARD_COLD_SHIFT); + if (ret < 0) + return ret; enable_therm_monitor = true; } @@ -553,16 +531,15 @@ static int smb347_set_temp_limits(struct smb347_charger *smb) val = clamp_val(val, 50, 65) - 50; val /= 5; - ret &= ~CFG_TEMP_LIMIT_HARD_HOT_MASK; - ret |= val << CFG_TEMP_LIMIT_HARD_HOT_SHIFT; + ret = regmap_update_bits(smb->regmap, CFG_TEMP_LIMIT, + CFG_TEMP_LIMIT_HARD_HOT_MASK, + val << CFG_TEMP_LIMIT_HARD_HOT_SHIFT); + if (ret < 0) + return ret; enable_therm_monitor = true; } - ret = smb347_write(smb, CFG_TEMP_LIMIT, ret); - if (ret < 0) - return ret; - /* * If any of the temperature limits are set, we also enable the * thermistor monitoring. @@ -574,25 +551,15 @@ static int smb347_set_temp_limits(struct smb347_charger *smb) * depending on the configuration. */ if (enable_therm_monitor) { - ret = smb347_read(smb, CFG_THERM); - if (ret < 0) - return ret; - - ret &= ~CFG_THERM_MONITOR_DISABLED; - - ret = smb347_write(smb, CFG_THERM, ret); + ret = regmap_update_bits(smb->regmap, CFG_THERM, + CFG_THERM_MONITOR_DISABLED, 0); if (ret < 0) return ret; } if (smb->pdata->suspend_on_hard_temp_limit) { - ret = smb347_read(smb, CFG_SYSOK); - if (ret < 0) - return ret; - - ret &= ~CFG_SYSOK_SUSPEND_HARD_LIMIT_DISABLED; - - ret = smb347_write(smb, CFG_SYSOK, ret); + ret = regmap_update_bits(smb->regmap, CFG_SYSOK, + CFG_SYSOK_SUSPEND_HARD_LIMIT_DISABLED, 0); if (ret < 0) return ret; } @@ -601,17 +568,15 @@ static int smb347_set_temp_limits(struct smb347_charger *smb) SMB347_SOFT_TEMP_COMPENSATE_DEFAULT) { val = smb->pdata->soft_temp_limit_compensation & 0x3; - ret = smb347_read(smb, CFG_THERM); + ret = regmap_update_bits(smb->regmap, CFG_THERM, + CFG_THERM_SOFT_HOT_COMPENSATION_MASK, + val << CFG_THERM_SOFT_HOT_COMPENSATION_SHIFT); if (ret < 0) return ret; - ret &= ~CFG_THERM_SOFT_HOT_COMPENSATION_MASK; - ret |= val << CFG_THERM_SOFT_HOT_COMPENSATION_SHIFT; - - ret &= ~CFG_THERM_SOFT_COLD_COMPENSATION_MASK; - ret |= val << CFG_THERM_SOFT_COLD_COMPENSATION_SHIFT; - - ret = smb347_write(smb, CFG_THERM, ret); + ret = regmap_update_bits(smb->regmap, CFG_THERM, + CFG_THERM_SOFT_COLD_COMPENSATION_MASK, + val << CFG_THERM_SOFT_COLD_COMPENSATION_SHIFT); if (ret < 0) return ret; } @@ -622,14 +587,9 @@ static int smb347_set_temp_limits(struct smb347_charger *smb) if (val < 0) return val; - ret = smb347_read(smb, CFG_OTG); - if (ret < 0) - return ret; - - ret &= ~CFG_OTG_CC_COMPENSATION_MASK; - ret |= (val & 0x3) << CFG_OTG_CC_COMPENSATION_SHIFT; - - ret = smb347_write(smb, CFG_OTG, ret); + ret = regmap_update_bits(smb->regmap, CFG_OTG, + CFG_OTG_CC_COMPENSATION_MASK, + (val & 0x3) << CFG_OTG_CC_COMPENSATION_SHIFT); if (ret < 0) return ret; } @@ -648,22 +608,13 @@ static int smb347_set_temp_limits(struct smb347_charger *smb) */ static int smb347_set_writable(struct smb347_charger *smb, bool writable) { - int ret; - - ret = smb347_read(smb, CMD_A); - if (ret < 0) - return ret; - - if (writable) - ret |= CMD_A_ALLOW_WRITE; - else - ret &= ~CMD_A_ALLOW_WRITE; - - return smb347_write(smb, CMD_A, ret); + return regmap_update_bits(smb->regmap, CMD_A, CMD_A_ALLOW_WRITE, + writable ? CMD_A_ALLOW_WRITE : 0); } static int smb347_hw_init(struct smb347_charger *smb) { + unsigned int val; int ret; ret = smb347_set_writable(smb, true); @@ -692,34 +643,19 @@ static int smb347_hw_init(struct smb347_charger *smb) /* If USB charging is disabled we put the USB in suspend mode */ if (!smb->pdata->use_usb) { - ret = smb347_read(smb, CMD_A); - if (ret < 0) - goto fail; - - ret |= CMD_A_SUSPEND_ENABLED; - - ret = smb347_write(smb, CMD_A, ret); + ret = regmap_update_bits(smb->regmap, CMD_A, + CMD_A_SUSPEND_ENABLED, + CMD_A_SUSPEND_ENABLED); if (ret < 0) goto fail; } - ret = smb347_read(smb, CFG_OTHER); - if (ret < 0) - goto fail; - /* * If configured by platform data, we enable hardware Auto-OTG * support for driving VBUS. Otherwise we disable it. */ - ret &= ~CFG_OTHER_RID_MASK; - if (smb->pdata->use_usb_otg) - ret |= CFG_OTHER_RID_ENABLED_AUTO_OTG; - - ret = smb347_write(smb, CFG_OTHER, ret); - if (ret < 0) - goto fail; - - ret = smb347_read(smb, CFG_PIN); + ret = regmap_update_bits(smb->regmap, CFG_OTHER, CFG_OTHER_RID_MASK, + smb->pdata->use_usb_otg ? CFG_OTHER_RID_ENABLED_AUTO_OTG : 0); if (ret < 0) goto fail; @@ -728,24 +664,25 @@ static int smb347_hw_init(struct smb347_charger *smb) * command register unless pin control is specified in the platform * data. */ - ret &= ~CFG_PIN_EN_CTRL_MASK; - switch (smb->pdata->enable_control) { - case SMB347_CHG_ENABLE_SW: - /* Do nothing, 0 means i2c control */ - break; case SMB347_CHG_ENABLE_PIN_ACTIVE_LOW: - ret |= CFG_PIN_EN_CTRL_ACTIVE_LOW; + val = CFG_PIN_EN_CTRL_ACTIVE_LOW; break; case SMB347_CHG_ENABLE_PIN_ACTIVE_HIGH: - ret |= CFG_PIN_EN_CTRL_ACTIVE_HIGH; + val = CFG_PIN_EN_CTRL_ACTIVE_HIGH; + break; + default: + val = 0; break; } - /* Disable Automatic Power Source Detection (APSD) interrupt. */ - ret &= ~CFG_PIN_EN_APSD_IRQ; + ret = regmap_update_bits(smb->regmap, CFG_PIN, CFG_PIN_EN_CTRL_MASK, + val); + if (ret < 0) + goto fail; - ret = smb347_write(smb, CFG_PIN, ret); + /* Disable Automatic Power Source Detection (APSD) interrupt. */ + ret = regmap_update_bits(smb->regmap, CFG_PIN, CFG_PIN_EN_APSD_IRQ, 0); if (ret < 0) goto fail; @@ -763,24 +700,25 @@ fail: static irqreturn_t smb347_interrupt(int irq, void *data) { struct smb347_charger *smb = data; - int stat_c, irqstat_e, irqstat_c; - irqreturn_t ret = IRQ_NONE; + unsigned int stat_c, irqstat_e, irqstat_c; + bool handled = false; + int ret; - stat_c = smb347_read(smb, STAT_C); - if (stat_c < 0) { - dev_warn(&smb->client->dev, "reading STAT_C failed\n"); + ret = regmap_read(smb->regmap, STAT_C, &stat_c); + if (ret < 0) { + dev_warn(smb->dev, "reading STAT_C failed\n"); return IRQ_NONE; } - irqstat_c = smb347_read(smb, IRQSTAT_C); - if (irqstat_c < 0) { - dev_warn(&smb->client->dev, "reading IRQSTAT_C failed\n"); + ret = regmap_read(smb->regmap, IRQSTAT_C, &irqstat_c); + if (ret < 0) { + dev_warn(smb->dev, "reading IRQSTAT_C failed\n"); return IRQ_NONE; } - irqstat_e = smb347_read(smb, IRQSTAT_E); - if (irqstat_e < 0) { - dev_warn(&smb->client->dev, "reading IRQSTAT_E failed\n"); + ret = regmap_read(smb->regmap, IRQSTAT_E, &irqstat_e); + if (ret < 0) { + dev_warn(smb->dev, "reading IRQSTAT_E failed\n"); return IRQ_NONE; } @@ -789,13 +727,11 @@ static irqreturn_t smb347_interrupt(int irq, void *data) * disable charging. */ if (stat_c & STAT_C_CHARGER_ERROR) { - dev_err(&smb->client->dev, - "error in charger, disabling charging\n"); + dev_err(smb->dev, "error in charger, disabling charging\n"); smb347_charging_disable(smb); power_supply_changed(&smb->battery); - - ret = IRQ_HANDLED; + handled = true; } /* @@ -806,7 +742,7 @@ static irqreturn_t smb347_interrupt(int irq, void *data) if (irqstat_c & (IRQSTAT_C_TERMINATION_IRQ | IRQSTAT_C_TAPER_IRQ)) { if (irqstat_c & IRQSTAT_C_TERMINATION_STAT) power_supply_changed(&smb->battery); - ret = IRQ_HANDLED; + handled = true; } /* @@ -821,10 +757,10 @@ static irqreturn_t smb347_interrupt(int irq, void *data) if (smb->pdata->use_usb) power_supply_changed(&smb->usb); } - ret = IRQ_HANDLED; + handled = true; } - return ret; + return handled ? IRQ_HANDLED : IRQ_NONE; } static int smb347_irq_set(struct smb347_charger *smb, bool enable) @@ -841,41 +777,18 @@ static int smb347_irq_set(struct smb347_charger *smb, bool enable) * - termination current reached * - charger error */ - if (enable) { - ret = smb347_write(smb, CFG_FAULT_IRQ, CFG_FAULT_IRQ_DCIN_UV); - if (ret < 0) - goto fail; - - ret = smb347_write(smb, CFG_STATUS_IRQ, - CFG_STATUS_IRQ_TERMINATION_OR_TAPER); - if (ret < 0) - goto fail; - - ret = smb347_read(smb, CFG_PIN); - if (ret < 0) - goto fail; - - ret |= CFG_PIN_EN_CHARGER_ERROR; - - ret = smb347_write(smb, CFG_PIN, ret); - } else { - ret = smb347_write(smb, CFG_FAULT_IRQ, 0); - if (ret < 0) - goto fail; - - ret = smb347_write(smb, CFG_STATUS_IRQ, 0); - if (ret < 0) - goto fail; - - ret = smb347_read(smb, CFG_PIN); - if (ret < 0) - goto fail; - - ret &= ~CFG_PIN_EN_CHARGER_ERROR; + ret = regmap_update_bits(smb->regmap, CFG_FAULT_IRQ, 0xff, + enable ? CFG_FAULT_IRQ_DCIN_UV : 0); + if (ret < 0) + goto fail; - ret = smb347_write(smb, CFG_PIN, ret); - } + ret = regmap_update_bits(smb->regmap, CFG_STATUS_IRQ, 0xff, + enable ? CFG_STATUS_IRQ_TERMINATION_OR_TAPER : 0); + if (ret < 0) + goto fail; + ret = regmap_update_bits(smb->regmap, CFG_PIN, CFG_PIN_EN_CHARGER_ERROR, + enable ? CFG_PIN_EN_CHARGER_ERROR : 0); fail: smb347_set_writable(smb, false); return ret; @@ -891,18 +804,18 @@ static inline int smb347_irq_disable(struct smb347_charger *smb) return smb347_irq_set(smb, false); } -static int smb347_irq_init(struct smb347_charger *smb) +static int smb347_irq_init(struct smb347_charger *smb, + struct i2c_client *client) { const struct smb347_charger_platform_data *pdata = smb->pdata; int ret, irq = gpio_to_irq(pdata->irq_gpio); - ret = gpio_request_one(pdata->irq_gpio, GPIOF_IN, smb->client->name); + ret = gpio_request_one(pdata->irq_gpio, GPIOF_IN, client->name); if (ret < 0) goto fail; ret = request_threaded_irq(irq, NULL, smb347_interrupt, - IRQF_TRIGGER_FALLING, smb->client->name, - smb); + IRQF_TRIGGER_FALLING, client->name, smb); if (ret < 0) goto fail_gpio; @@ -914,19 +827,14 @@ static int smb347_irq_init(struct smb347_charger *smb) * Configure the STAT output to be suitable for interrupts: disable * all other output (except interrupts) and make it active low. */ - ret = smb347_read(smb, CFG_STAT); - if (ret < 0) - goto fail_readonly; - - ret &= ~CFG_STAT_ACTIVE_HIGH; - ret |= CFG_STAT_DISABLED; - - ret = smb347_write(smb, CFG_STAT, ret); + ret = regmap_update_bits(smb->regmap, CFG_STAT, + CFG_STAT_ACTIVE_HIGH | CFG_STAT_DISABLED, + CFG_STAT_DISABLED); if (ret < 0) goto fail_readonly; smb347_set_writable(smb, false); - smb->client->irq = irq; + client->irq = irq; return 0; fail_readonly: @@ -936,7 +844,7 @@ fail_irq: fail_gpio: gpio_free(pdata->irq_gpio); fail: - smb->client->irq = 0; + client->irq = 0; return ret; } @@ -983,6 +891,7 @@ static int smb347_battery_get_property(struct power_supply *psy, struct smb347_charger *smb = container_of(psy, struct smb347_charger, battery); const struct smb347_charger_platform_data *pdata = smb->pdata; + unsigned int v; int ret; ret = smb347_update_ps_status(smb); @@ -1037,22 +946,22 @@ static int smb347_battery_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_VOLTAGE_NOW: if (!smb347_is_ps_online(smb)) return -ENODATA; - ret = smb347_read(smb, STAT_A); + ret = regmap_read(smb->regmap, STAT_A, &v); if (ret < 0) return ret; - ret &= STAT_A_FLOAT_VOLTAGE_MASK; - if (ret > 0x3d) - ret = 0x3d; + v &= STAT_A_FLOAT_VOLTAGE_MASK; + if (v > 0x3d) + v = 0x3d; - val->intval = 3500000 + ret * 20000; + val->intval = 3500000 + v * 20000; break; case POWER_SUPPLY_PROP_CURRENT_NOW: if (!smb347_is_ps_online(smb)) return -ENODATA; - ret = smb347_read(smb, STAT_B); + ret = regmap_read(smb->regmap, STAT_B, &v); if (ret < 0) return ret; @@ -1060,15 +969,15 @@ static int smb347_battery_get_property(struct power_supply *psy, * The current value is composition of FCC and PCC values * and we can detect which table to use from bit 5. */ - if (ret & 0x20) { + if (v & 0x20) { val->intval = hw_to_current(fcc_tbl, ARRAY_SIZE(fcc_tbl), - ret & 7); + v & 7); } else { - ret >>= 3; + v >>= 3; val->intval = hw_to_current(pcc_tbl, ARRAY_SIZE(pcc_tbl), - ret & 7); + v & 7); } break; @@ -1099,58 +1008,54 @@ static enum power_supply_property smb347_battery_properties[] = { POWER_SUPPLY_PROP_MODEL_NAME, }; -static int smb347_debugfs_show(struct seq_file *s, void *data) +static bool smb347_volatile_reg(struct device *dev, unsigned int reg) { - struct smb347_charger *smb = s->private; - int ret; - u8 reg; - - seq_printf(s, "Control registers:\n"); - seq_printf(s, "==================\n"); - for (reg = CFG_CHARGE_CURRENT; reg <= CFG_ADDRESS; reg++) { - ret = smb347_read(smb, reg); - seq_printf(s, "0x%02x:\t0x%02x\n", reg, ret); - } - seq_printf(s, "\n"); - - seq_printf(s, "Command registers:\n"); - seq_printf(s, "==================\n"); - ret = smb347_read(smb, CMD_A); - seq_printf(s, "0x%02x:\t0x%02x\n", CMD_A, ret); - ret = smb347_read(smb, CMD_B); - seq_printf(s, "0x%02x:\t0x%02x\n", CMD_B, ret); - ret = smb347_read(smb, CMD_C); - seq_printf(s, "0x%02x:\t0x%02x\n", CMD_C, ret); - seq_printf(s, "\n"); - - seq_printf(s, "Interrupt status registers:\n"); - seq_printf(s, "===========================\n"); - for (reg = IRQSTAT_A; reg <= IRQSTAT_F; reg++) { - ret = smb347_read(smb, reg); - seq_printf(s, "0x%02x:\t0x%02x\n", reg, ret); - } - seq_printf(s, "\n"); - - seq_printf(s, "Status registers:\n"); - seq_printf(s, "=================\n"); - for (reg = STAT_A; reg <= STAT_E; reg++) { - ret = smb347_read(smb, reg); - seq_printf(s, "0x%02x:\t0x%02x\n", reg, ret); + switch (reg) { + case IRQSTAT_A: + case IRQSTAT_C: + case IRQSTAT_E: + case IRQSTAT_F: + case STAT_A: + case STAT_B: + case STAT_C: + case STAT_E: + return true; } - return 0; + return false; } -static int smb347_debugfs_open(struct inode *inode, struct file *file) +static bool smb347_readable_reg(struct device *dev, unsigned int reg) { - return single_open(file, smb347_debugfs_show, inode->i_private); + switch (reg) { + case CFG_CHARGE_CURRENT: + case CFG_CURRENT_LIMIT: + case CFG_FLOAT_VOLTAGE: + case CFG_STAT: + case CFG_PIN: + case CFG_THERM: + case CFG_SYSOK: + case CFG_OTHER: + case CFG_OTG: + case CFG_TEMP_LIMIT: + case CFG_FAULT_IRQ: + case CFG_STATUS_IRQ: + case CFG_ADDRESS: + case CMD_A: + case CMD_B: + case CMD_C: + return true; + } + + return smb347_volatile_reg(dev, reg); } -static const struct file_operations smb347_debugfs_fops = { - .open = smb347_debugfs_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, +static const struct regmap_config smb347_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = SMB347_MAX_REGISTER, + .volatile_reg = smb347_volatile_reg, + .readable_reg = smb347_readable_reg, }; static int smb347_probe(struct i2c_client *client, @@ -1176,9 +1081,13 @@ static int smb347_probe(struct i2c_client *client, i2c_set_clientdata(client, smb); mutex_init(&smb->lock); - smb->client = client; + smb->dev = &client->dev; smb->pdata = pdata; + smb->regmap = devm_regmap_init_i2c(client, &smb347_regmap); + if (IS_ERR(smb->regmap)) + return PTR_ERR(smb->regmap); + ret = smb347_hw_init(smb); if (ret < 0) return ret; @@ -1233,7 +1142,7 @@ static int smb347_probe(struct i2c_client *client, * interrupt support here. */ if (pdata->irq_gpio >= 0) { - ret = smb347_irq_init(smb); + ret = smb347_irq_init(smb, client); if (ret < 0) { dev_warn(dev, "failed to initialize IRQ: %d\n", ret); dev_warn(dev, "disabling IRQ support\n"); @@ -1242,8 +1151,6 @@ static int smb347_probe(struct i2c_client *client, } } - smb->dentry = debugfs_create_file("smb347-regs", S_IRUSR, NULL, smb, - &smb347_debugfs_fops); return 0; } @@ -1251,9 +1158,6 @@ static int smb347_remove(struct i2c_client *client) { struct smb347_charger *smb = i2c_get_clientdata(client); - if (!IS_ERR_OR_NULL(smb->dentry)) - debugfs_remove(smb->dentry); - if (client->irq) { smb347_irq_disable(smb); free_irq(client->irq, smb); -- cgit v1.2.3 From d829dc75bafb10754f35fb8895e5143d20267b04 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Sat, 5 May 2012 06:24:10 -0700 Subject: charger-manager: Poll battery health in normal state Charger-Manager needs to check battery health in normal state as well as suspend-to-RAM state. When the battery is fully charged, Charger-Manager needs to determine when the chargers restart charging. This patch allows Charger-Manager to monitor battery health in normal state and handle operation for chargers after battery is fully charged. Signed-off-by: MyungJoo Ham Signed-off-by: Donggeun Kim Signed-off-by: Kyungmin Park Signed-off-by: Anton Vorontsov --- drivers/power/charger-manager.c | 229 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 229 insertions(+) (limited to 'drivers/power') diff --git a/drivers/power/charger-manager.c b/drivers/power/charger-manager.c index 9eca9f1ff0ea..959062d16bac 100644 --- a/drivers/power/charger-manager.c +++ b/drivers/power/charger-manager.c @@ -57,6 +57,12 @@ static bool cm_suspended; static bool cm_rtc_set; static unsigned long cm_suspend_duration_ms; +/* About normal (not suspended) monitoring */ +static unsigned long polling_jiffy = ULONG_MAX; /* ULONG_MAX: no polling */ +static unsigned long next_polling; /* Next appointed polling time */ +static struct workqueue_struct *cm_wq; /* init at driver add */ +static struct delayed_work cm_monitor_work; /* init at driver add */ + /* Global charger-manager description */ static struct charger_global_desc *g_desc; /* init with setup_charger_manager */ @@ -71,6 +77,11 @@ static bool is_batt_present(struct charger_manager *cm) int i, ret; switch (cm->desc->battery_present) { + case CM_BATTERY_PRESENT: + present = true; + break; + case CM_NO_BATTERY: + break; case CM_FUEL_GAUGE: ret = cm->fuel_gauge->get_property(cm->fuel_gauge, POWER_SUPPLY_PROP_PRESENT, &val); @@ -278,6 +289,26 @@ static int try_charger_enable(struct charger_manager *cm, bool enable) return err; } +/** + * try_charger_restart - Restart charging. + * @cm: the Charger Manager representing the battery. + * + * Restart charging by turning off and on the charger. + */ +static int try_charger_restart(struct charger_manager *cm) +{ + int err; + + if (cm->emergency_stop) + return -EAGAIN; + + err = try_charger_enable(cm, false); + if (err) + return err; + + return try_charger_enable(cm, true); +} + /** * uevent_notify - Let users know something has changed. * @cm: the Charger Manager representing the battery. @@ -333,6 +364,46 @@ static void uevent_notify(struct charger_manager *cm, const char *event) dev_info(cm->dev, event); } +/** + * fullbatt_vchk - Check voltage drop some times after "FULL" event. + * @work: the work_struct appointing the function + * + * If a user has designated "fullbatt_vchkdrop_ms/uV" values with + * charger_desc, Charger Manager checks voltage drop after the battery + * "FULL" event. It checks whether the voltage has dropped more than + * fullbatt_vchkdrop_uV by calling this function after fullbatt_vchkrop_ms. + */ +static void fullbatt_vchk(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct charger_manager *cm = container_of(dwork, + struct charger_manager, fullbatt_vchk_work); + struct charger_desc *desc = cm->desc; + int batt_uV, err, diff; + + /* remove the appointment for fullbatt_vchk */ + cm->fullbatt_vchk_jiffies_at = 0; + + if (!desc->fullbatt_vchkdrop_uV || !desc->fullbatt_vchkdrop_ms) + return; + + err = get_batt_uV(cm, &batt_uV); + if (err) { + dev_err(cm->dev, "%s: get_batt_uV error(%d).\n", __func__, err); + return; + } + + diff = cm->fullbatt_vchk_uV; + diff -= batt_uV; + + dev_dbg(cm->dev, "VBATT dropped %duV after full-batt.\n", diff); + + if (diff > desc->fullbatt_vchkdrop_uV) { + try_charger_restart(cm); + uevent_notify(cm, "Recharge"); + } +} + /** * _cm_monitor - Monitor the temperature and return true for exceptions. * @cm: the Charger Manager representing the battery. @@ -392,6 +463,68 @@ static bool cm_monitor(void) return stop; } +/** + * _setup_polling - Setup the next instance of polling. + * @work: work_struct of the function _setup_polling. + */ +static void _setup_polling(struct work_struct *work) +{ + unsigned long min = ULONG_MAX; + struct charger_manager *cm; + bool keep_polling = false; + unsigned long _next_polling; + + mutex_lock(&cm_list_mtx); + + list_for_each_entry(cm, &cm_list, entry) { + if (is_polling_required(cm) && cm->desc->polling_interval_ms) { + keep_polling = true; + + if (min > cm->desc->polling_interval_ms) + min = cm->desc->polling_interval_ms; + } + } + + polling_jiffy = msecs_to_jiffies(min); + if (polling_jiffy <= CM_JIFFIES_SMALL) + polling_jiffy = CM_JIFFIES_SMALL + 1; + + if (!keep_polling) + polling_jiffy = ULONG_MAX; + if (polling_jiffy == ULONG_MAX) + goto out; + + WARN(cm_wq == NULL, "charger-manager: workqueue not initialized" + ". try it later. %s\n", __func__); + + _next_polling = jiffies + polling_jiffy; + + if (!delayed_work_pending(&cm_monitor_work) || + (delayed_work_pending(&cm_monitor_work) && + time_after(next_polling, _next_polling))) { + cancel_delayed_work_sync(&cm_monitor_work); + next_polling = jiffies + polling_jiffy; + queue_delayed_work(cm_wq, &cm_monitor_work, polling_jiffy); + } + +out: + mutex_unlock(&cm_list_mtx); +} +static DECLARE_WORK(setup_polling, _setup_polling); + +/** + * cm_monitor_poller - The Monitor / Poller. + * @work: work_struct of the function cm_monitor_poller + * + * During non-suspended state, cm_monitor_poller is used to poll and monitor + * the batteries. + */ +static void cm_monitor_poller(struct work_struct *work) +{ + cm_monitor(); + schedule_work(&setup_polling); +} + static int charger_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) @@ -613,6 +746,21 @@ static bool cm_setup_timer(void) mutex_lock(&cm_list_mtx); list_for_each_entry(cm, &cm_list, entry) { + unsigned int fbchk_ms = 0; + + /* fullbatt_vchk is required. setup timer for that */ + if (cm->fullbatt_vchk_jiffies_at) { + fbchk_ms = jiffies_to_msecs(cm->fullbatt_vchk_jiffies_at + - jiffies); + if (time_is_before_eq_jiffies( + cm->fullbatt_vchk_jiffies_at) || + msecs_to_jiffies(fbchk_ms) < CM_JIFFIES_SMALL) { + fullbatt_vchk(&cm->fullbatt_vchk_work.work); + fbchk_ms = 0; + } + } + CM_MIN_VALID(wakeup_ms, fbchk_ms); + /* Skip if polling is not required for this CM */ if (!is_polling_required(cm) && !cm->emergency_stop) continue; @@ -672,6 +820,23 @@ static bool cm_setup_timer(void) return false; } +static void _cm_fbchk_in_suspend(struct charger_manager *cm) +{ + unsigned long jiffy_now = jiffies; + + if (!cm->fullbatt_vchk_jiffies_at) + return; + + if (g_desc && g_desc->assume_timer_stops_in_suspend) + jiffy_now += msecs_to_jiffies(cm_suspend_duration_ms); + + /* Execute now if it's going to be executed not too long after */ + jiffy_now += CM_JIFFIES_SMALL; + + if (time_after_eq(jiffy_now, cm->fullbatt_vchk_jiffies_at)) + fullbatt_vchk(&cm->fullbatt_vchk_work.work); +} + /** * cm_suspend_again - Determine whether suspend again or not * @@ -693,6 +858,8 @@ bool cm_suspend_again(void) ret = true; mutex_lock(&cm_list_mtx); list_for_each_entry(cm, &cm_list, entry) { + _cm_fbchk_in_suspend(cm); + if (cm->status_save_ext_pwr_inserted != is_ext_pwr_online(cm) || cm->status_save_batt != is_batt_present(cm)) { ret = false; @@ -796,6 +963,21 @@ static int charger_manager_probe(struct platform_device *pdev) memcpy(cm->desc, desc, sizeof(struct charger_desc)); cm->last_temp_mC = INT_MIN; /* denotes "unmeasured, yet" */ + /* + * The following two do not need to be errors. + * Users may intentionally ignore those two features. + */ + if (desc->fullbatt_uV == 0) { + dev_info(&pdev->dev, "Ignoring full-battery voltage threshold" + " as it is not supplied."); + } + if (!desc->fullbatt_vchkdrop_ms || !desc->fullbatt_vchkdrop_uV) { + dev_info(&pdev->dev, "Disabling full-battery voltage drop " + "checking mechanism as it is not supplied."); + desc->fullbatt_vchkdrop_ms = 0; + desc->fullbatt_vchkdrop_uV = 0; + } + if (!desc->charger_regulators || desc->num_charger_regulators < 1) { ret = -EINVAL; dev_err(&pdev->dev, "charger_regulators undefined.\n"); @@ -903,6 +1085,8 @@ static int charger_manager_probe(struct platform_device *pdev) cm->charger_psy.num_properties++; } + INIT_DELAYED_WORK(&cm->fullbatt_vchk_work, fullbatt_vchk); + ret = power_supply_register(NULL, &cm->charger_psy); if (ret) { dev_err(&pdev->dev, "Cannot register charger-manager with" @@ -928,6 +1112,8 @@ static int charger_manager_probe(struct platform_device *pdev) list_add(&cm->entry, &cm_list); mutex_unlock(&cm_list_mtx); + schedule_work(&setup_polling); + return 0; err_chg_enable: @@ -958,9 +1144,17 @@ static int __devexit charger_manager_remove(struct platform_device *pdev) list_del(&cm->entry); mutex_unlock(&cm_list_mtx); + if (work_pending(&setup_polling)) + cancel_work_sync(&setup_polling); + if (delayed_work_pending(&cm_monitor_work)) + cancel_delayed_work_sync(&cm_monitor_work); + regulator_bulk_free(desc->num_charger_regulators, desc->charger_regulators); power_supply_unregister(&cm->charger_psy); + + try_charger_enable(cm, false); + kfree(cm->charger_psy.properties); kfree(cm->charger_stat); kfree(cm->desc); @@ -1000,6 +1194,8 @@ static int cm_suspend_prepare(struct device *dev) cm_suspended = true; } + if (delayed_work_pending(&cm->fullbatt_vchk_work)) + cancel_delayed_work(&cm->fullbatt_vchk_work); cm->status_save_ext_pwr_inserted = is_ext_pwr_online(cm); cm->status_save_batt = is_batt_present(cm); @@ -1027,6 +1223,33 @@ static void cm_suspend_complete(struct device *dev) cm_rtc_set = false; } + /* Re-enqueue delayed work (fullbatt_vchk_work) */ + if (cm->fullbatt_vchk_jiffies_at) { + unsigned long delay = 0; + unsigned long now = jiffies + CM_JIFFIES_SMALL; + + if (time_after_eq(now, cm->fullbatt_vchk_jiffies_at)) { + delay = (unsigned long)((long)now + - (long)(cm->fullbatt_vchk_jiffies_at)); + delay = jiffies_to_msecs(delay); + } else { + delay = 0; + } + + /* + * Account for cm_suspend_duration_ms if + * assume_timer_stops_in_suspend is active + */ + if (g_desc && g_desc->assume_timer_stops_in_suspend) { + if (delay > cm_suspend_duration_ms) + delay -= cm_suspend_duration_ms; + else + delay = 0; + } + + queue_delayed_work(cm_wq, &cm->fullbatt_vchk_work, + msecs_to_jiffies(delay)); + } uevent_notify(cm, NULL); } @@ -1048,12 +1271,18 @@ static struct platform_driver charger_manager_driver = { static int __init charger_manager_init(void) { + cm_wq = create_freezable_workqueue("charger_manager"); + INIT_DELAYED_WORK(&cm_monitor_work, cm_monitor_poller); + return platform_driver_register(&charger_manager_driver); } late_initcall(charger_manager_init); static void __exit charger_manager_cleanup(void) { + destroy_workqueue(cm_wq); + cm_wq = NULL; + platform_driver_unregister(&charger_manager_driver); } module_exit(charger_manager_cleanup); -- cgit v1.2.3 From dfeccb12b4614befc49a92eb121c2211294ca669 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Sat, 5 May 2012 06:26:47 -0700 Subject: charger-manager: Provide cm_notify_event function for in-kernel use By using cm_notify_event function, charger driver can report several charger events (e.g. battery full and external power in/out, etc) to Charger-Manager. Charger-Manager can properly and immediately control chargers by the reported event. Signed-off-by: MyungJoo Ham Signed-off-by: Donggeun Kim Signed-off-by: Kyungmin Park Signed-off-by: Anton Vorontsov --- drivers/power/charger-manager.c | 163 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 163 insertions(+) (limited to 'drivers/power') diff --git a/drivers/power/charger-manager.c b/drivers/power/charger-manager.c index 959062d16bac..86935ec18954 100644 --- a/drivers/power/charger-manager.c +++ b/drivers/power/charger-manager.c @@ -23,6 +23,16 @@ #include #include +static const char * const default_event_names[] = { + [CM_EVENT_UNKNOWN] = "Unknown", + [CM_EVENT_BATT_FULL] = "Battery Full", + [CM_EVENT_BATT_IN] = "Battery Inserted", + [CM_EVENT_BATT_OUT] = "Battery Pulled Out", + [CM_EVENT_EXT_PWR_IN_OUT] = "External Power Attach/Detach", + [CM_EVENT_CHG_START_STOP] = "Charging Start/Stop", + [CM_EVENT_OTHERS] = "Other battery events" +}; + /* * Regard CM_JIFFIES_SMALL jiffies is small enough to ignore for * delayed works so that we can run delayed works with CM_JIFFIES_SMALL @@ -525,6 +535,69 @@ static void cm_monitor_poller(struct work_struct *work) schedule_work(&setup_polling); } +/** + * fullbatt_handler - Event handler for CM_EVENT_BATT_FULL + * @cm: the Charger Manager representing the battery. + */ +static void fullbatt_handler(struct charger_manager *cm) +{ + struct charger_desc *desc = cm->desc; + + if (!desc->fullbatt_vchkdrop_uV || !desc->fullbatt_vchkdrop_ms) + goto out; + + if (cm_suspended) + device_set_wakeup_capable(cm->dev, true); + + if (delayed_work_pending(&cm->fullbatt_vchk_work)) + cancel_delayed_work(&cm->fullbatt_vchk_work); + queue_delayed_work(cm_wq, &cm->fullbatt_vchk_work, + msecs_to_jiffies(desc->fullbatt_vchkdrop_ms)); + cm->fullbatt_vchk_jiffies_at = jiffies + msecs_to_jiffies( + desc->fullbatt_vchkdrop_ms); + + if (cm->fullbatt_vchk_jiffies_at == 0) + cm->fullbatt_vchk_jiffies_at = 1; + +out: + dev_info(cm->dev, "EVENT_HANDLE: Battery Fully Charged.\n"); + uevent_notify(cm, default_event_names[CM_EVENT_BATT_FULL]); +} + +/** + * battout_handler - Event handler for CM_EVENT_BATT_OUT + * @cm: the Charger Manager representing the battery. + */ +static void battout_handler(struct charger_manager *cm) +{ + if (cm_suspended) + device_set_wakeup_capable(cm->dev, true); + + if (!is_batt_present(cm)) { + dev_emerg(cm->dev, "Battery Pulled Out!\n"); + uevent_notify(cm, default_event_names[CM_EVENT_BATT_OUT]); + } else { + uevent_notify(cm, "Battery Reinserted?"); + } +} + +/** + * misc_event_handler - Handler for other evnets + * @cm: the Charger Manager representing the battery. + * @type: the Charger Manager representing the battery. + */ +static void misc_event_handler(struct charger_manager *cm, + enum cm_event_types type) +{ + if (cm_suspended) + device_set_wakeup_capable(cm->dev, true); + + if (!delayed_work_pending(&cm_monitor_work) && + is_polling_required(cm) && cm->desc->polling_interval_ms) + schedule_work(&setup_polling); + uevent_notify(cm, default_event_names[type]); +} + static int charger_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) @@ -1112,6 +1185,13 @@ static int charger_manager_probe(struct platform_device *pdev) list_add(&cm->entry, &cm_list); mutex_unlock(&cm_list_mtx); + /* + * Charger-manager is capable of waking up the systme from sleep + * when event is happend through cm_notify_event() + */ + device_init_wakeup(&pdev->dev, true); + device_set_wakeup_capable(&pdev->dev, false); + schedule_work(&setup_polling); return 0; @@ -1169,6 +1249,18 @@ static const struct platform_device_id charger_manager_id[] = { }; MODULE_DEVICE_TABLE(platform, charger_manager_id); +static int cm_suspend_noirq(struct device *dev) +{ + int ret = 0; + + if (device_may_wakeup(dev)) { + device_set_wakeup_capable(dev, false); + ret = -EAGAIN; + } + + return ret; +} + static int cm_suspend_prepare(struct device *dev) { struct charger_manager *cm = dev_get_drvdata(dev); @@ -1250,11 +1342,13 @@ static void cm_suspend_complete(struct device *dev) queue_delayed_work(cm_wq, &cm->fullbatt_vchk_work, msecs_to_jiffies(delay)); } + device_set_wakeup_capable(cm->dev, false); uevent_notify(cm, NULL); } static const struct dev_pm_ops charger_manager_pm = { .prepare = cm_suspend_prepare, + .suspend_noirq = cm_suspend_noirq, .complete = cm_suspend_complete, }; @@ -1287,6 +1381,75 @@ static void __exit charger_manager_cleanup(void) } module_exit(charger_manager_cleanup); +/** + * find_power_supply - find the associated power_supply of charger + * @cm: the Charger Manager representing the battery + * @psy: pointer to instance of charger's power_supply + */ +static bool find_power_supply(struct charger_manager *cm, + struct power_supply *psy) +{ + int i; + bool found = false; + + for (i = 0; cm->charger_stat[i]; i++) { + if (psy == cm->charger_stat[i]) { + found = true; + break; + } + } + + return found; +} + +/** + * cm_notify_event - charger driver notify Charger Manager of charger event + * @psy: pointer to instance of charger's power_supply + * @type: type of charger event + * @msg: optional message passed to uevent_notify fuction + */ +void cm_notify_event(struct power_supply *psy, enum cm_event_types type, + char *msg) +{ + struct charger_manager *cm; + bool found_power_supply = false; + + if (psy == NULL) + return; + + mutex_lock(&cm_list_mtx); + list_for_each_entry(cm, &cm_list, entry) { + found_power_supply = find_power_supply(cm, psy); + if (found_power_supply) + break; + } + mutex_unlock(&cm_list_mtx); + + if (!found_power_supply) + return; + + switch (type) { + case CM_EVENT_BATT_FULL: + fullbatt_handler(cm); + break; + case CM_EVENT_BATT_OUT: + battout_handler(cm); + break; + case CM_EVENT_BATT_IN: + case CM_EVENT_EXT_PWR_IN_OUT ... CM_EVENT_CHG_START_STOP: + misc_event_handler(cm, type); + break; + case CM_EVENT_UNKNOWN: + case CM_EVENT_OTHERS: + uevent_notify(cm, msg ? msg : default_event_names[type]); + break; + default: + dev_err(cm->dev, "%s type not specified.\n", __func__); + break; + } +} +EXPORT_SYMBOL_GPL(cm_notify_event); + MODULE_AUTHOR("MyungJoo Ham "); MODULE_DESCRIPTION("Charger Manager"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 840b91bda91ebaa6230caec0ccf005daf9305fb6 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sat, 17 Mar 2012 15:33:10 +0800 Subject: power_supply: Fix a typo in BATTERY_DS2781 Kconfig entry Signed-off-by: Axel Lin Acked-by: Evgeniy Polyakov Signed-off-by: Anton Vorontsov --- drivers/power/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/power') diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 2259dea525e0..b711795c6664 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -77,7 +77,7 @@ config BATTERY_DS2780 Say Y here to enable support for batteries with ds2780 chip. config BATTERY_DS2781 - tristate "2781 battery driver" + tristate "DS2781 battery driver" depends on HAS_IOMEM select W1 select W1_SLAVE_DS2781 -- cgit v1.2.3 From 13f2483cd5e3df425cfa492cc62cf8fada5d01b9 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sat, 17 Mar 2012 15:37:31 +0800 Subject: ds2781_battery: Use DS2781_PARAM_EEPROM_SIZE and DS2781_USER_EEPROM_SIZE Since we have defined DS2781_PARAM_EEPROM_SIZE and DS2781_USER_EEPROM_SIZE, use them to simplify the code. Signed-off-by: Axel Lin Acked-by: Evgeniy Polyakov Signed-off-by: Anton Vorontsov --- drivers/power/ds2781_battery.c | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/ds2781_battery.c b/drivers/power/ds2781_battery.c index ca0d653d0a7a..975684a40f15 100644 --- a/drivers/power/ds2781_battery.c +++ b/drivers/power/ds2781_battery.c @@ -643,9 +643,7 @@ static ssize_t ds2781_read_param_eeprom_bin(struct file *filp, struct power_supply *psy = to_power_supply(dev); struct ds2781_device_info *dev_info = to_ds2781_device_info(psy); - count = min_t(loff_t, count, - DS2781_EEPROM_BLOCK1_END - - DS2781_EEPROM_BLOCK1_START + 1 - off); + count = min_t(loff_t, count, DS2781_PARAM_EEPROM_SIZE - off); return ds2781_read_block(dev_info, buf, DS2781_EEPROM_BLOCK1_START + off, count); @@ -661,9 +659,7 @@ static ssize_t ds2781_write_param_eeprom_bin(struct file *filp, struct ds2781_device_info *dev_info = to_ds2781_device_info(psy); int ret; - count = min_t(loff_t, count, - DS2781_EEPROM_BLOCK1_END - - DS2781_EEPROM_BLOCK1_START + 1 - off); + count = min_t(loff_t, count, DS2781_PARAM_EEPROM_SIZE - off); ret = ds2781_write(dev_info, buf, DS2781_EEPROM_BLOCK1_START + off, count); @@ -682,7 +678,7 @@ static struct bin_attribute ds2781_param_eeprom_bin_attr = { .name = "param_eeprom", .mode = S_IRUGO | S_IWUSR, }, - .size = DS2781_EEPROM_BLOCK1_END - DS2781_EEPROM_BLOCK1_START + 1, + .size = DS2781_PARAM_EEPROM_SIZE, .read = ds2781_read_param_eeprom_bin, .write = ds2781_write_param_eeprom_bin, }; @@ -696,9 +692,7 @@ static ssize_t ds2781_read_user_eeprom_bin(struct file *filp, struct power_supply *psy = to_power_supply(dev); struct ds2781_device_info *dev_info = to_ds2781_device_info(psy); - count = min_t(loff_t, count, - DS2781_EEPROM_BLOCK0_END - - DS2781_EEPROM_BLOCK0_START + 1 - off); + count = min_t(loff_t, count, DS2781_USER_EEPROM_SIZE - off); return ds2781_read_block(dev_info, buf, DS2781_EEPROM_BLOCK0_START + off, count); @@ -715,9 +709,7 @@ static ssize_t ds2781_write_user_eeprom_bin(struct file *filp, struct ds2781_device_info *dev_info = to_ds2781_device_info(psy); int ret; - count = min_t(loff_t, count, - DS2781_EEPROM_BLOCK0_END - - DS2781_EEPROM_BLOCK0_START + 1 - off); + count = min_t(loff_t, count, DS2781_USER_EEPROM_SIZE - off); ret = ds2781_write(dev_info, buf, DS2781_EEPROM_BLOCK0_START + off, count); @@ -736,7 +728,7 @@ static struct bin_attribute ds2781_user_eeprom_bin_attr = { .name = "user_eeprom", .mode = S_IRUGO | S_IWUSR, }, - .size = DS2781_EEPROM_BLOCK0_END - DS2781_EEPROM_BLOCK0_START + 1, + .size = DS2781_USER_EEPROM_SIZE, .read = ds2781_read_user_eeprom_bin, .write = ds2781_write_user_eeprom_bin, }; -- cgit v1.2.3 From 81a08382aeb802ad642ab7f2a7786baa7bafaaa3 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 27 Mar 2012 23:22:13 +0300 Subject: isp1704_charger: Use after free on probe error We can't use "isp" after freeing it. Signed-off-by: Dan Carpenter Reviewed-by: Felipe Contreras Signed-off-by: Anton Vorontsov --- drivers/power/isp1704_charger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/power') diff --git a/drivers/power/isp1704_charger.c b/drivers/power/isp1704_charger.c index 39eb50f35f09..e5ccd2979773 100644 --- a/drivers/power/isp1704_charger.c +++ b/drivers/power/isp1704_charger.c @@ -474,13 +474,13 @@ static int __devinit isp1704_charger_probe(struct platform_device *pdev) fail2: power_supply_unregister(&isp->psy); fail1: + isp1704_charger_set_power(isp, 0); usb_put_transceiver(isp->phy); fail0: kfree(isp); dev_err(&pdev->dev, "failed to register isp1704 with error %d\n", ret); - isp1704_charger_set_power(isp, 0); return ret; } -- cgit v1.2.3 From b1f092f6480e0a9d8d5f99d3363e022952d1af83 Mon Sep 17 00:00:00 2001 From: Nikolaus Voss Date: Wed, 25 Apr 2012 08:59:03 +0200 Subject: sbs-battery.c: Capacity attr = remaining relative capacity Currently, the capacity exported by this driver refers to reg 0x0e, which is the absolute state of charge which according to SBS refers to the design capacity/ energy of the battery. It can be > 100 % and drops below 100 % for a fully charged battery with the battery aging. This is not what the user exspects of a remaining capacity indication between 0 and 100 % with 100 % referring to a fully charged battery. This is provided by SBS reg 0x0d, which is the relative state of charge referring to the full charge capacity. Signed-off-by: Nikolaus Voss Acked-by: Rhyland Klein Signed-off-by: Anton Vorontsov --- drivers/power/sbs-battery.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/power') diff --git a/drivers/power/sbs-battery.c b/drivers/power/sbs-battery.c index 06b659d91790..a5b6849d4123 100644 --- a/drivers/power/sbs-battery.c +++ b/drivers/power/sbs-battery.c @@ -89,7 +89,7 @@ static const struct chip_data { [REG_CURRENT] = SBS_DATA(POWER_SUPPLY_PROP_CURRENT_NOW, 0x0A, -32768, 32767), [REG_CAPACITY] = - SBS_DATA(POWER_SUPPLY_PROP_CAPACITY, 0x0E, 0, 100), + SBS_DATA(POWER_SUPPLY_PROP_CAPACITY, 0x0D, 0, 100), [REG_REMAINING_CAPACITY] = SBS_DATA(POWER_SUPPLY_PROP_ENERGY_NOW, 0x0F, 0, 65535), [REG_REMAINING_CAPACITY_CHARGE] = -- cgit v1.2.3 From 9a8422d205ea142a27c2573e5ca3d2cc87d75260 Mon Sep 17 00:00:00 2001 From: Ramakrishna Pallala Date: Sat, 5 May 2012 14:34:26 +0530 Subject: max17042_battery: Add support for max17047/50 chip max17047 is improved version of max17042 chip. It has few HW bug fixes with minor changes in register set. max17050 is same as max17047 chip except its silicon packging. So from driver's point of view there is no difference btw max1047 and max1050. This patch adds the support to dynamically detect the chip type and adds steps to initialize the max17047 chip. Signed-off-by: Ramakrishna Pallala Signed-off-by: Anton Vorontsov --- drivers/power/Kconfig | 5 +-- drivers/power/max17042_battery.c | 69 +++++++++++++++++++++++++++++++++------- 2 files changed, 61 insertions(+), 13 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index b711795c6664..e3a3b4956f08 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -181,14 +181,15 @@ config BATTERY_MAX17040 to operate with a single lithium cell config BATTERY_MAX17042 - tristate "Maxim MAX17042/8997/8966 Fuel Gauge" + tristate "Maxim MAX17042/17047/17050/8997/8966 Fuel Gauge" depends on I2C help MAX17042 is fuel-gauge systems for lithium-ion (Li+) batteries in handheld and portable equipment. The MAX17042 is configured to operate with a single lithium cell. MAX8997 and MAX8966 are multi-function devices that include fuel gauages that are compatible - with MAX17042. + with MAX17042. This driver also supports max17047/50 chips which are + improved version of max17042. config BATTERY_Z2 tristate "Z2 battery driver" diff --git a/drivers/power/max17042_battery.c b/drivers/power/max17042_battery.c index 8d28006322e9..140788b309f8 100644 --- a/drivers/power/max17042_battery.c +++ b/drivers/power/max17042_battery.c @@ -62,9 +62,13 @@ #define dP_ACC_100 0x1900 #define dP_ACC_200 0x3200 +#define MAX17042_IC_VERSION 0x0092 +#define MAX17047_IC_VERSION 0x00AC /* same for max17050 */ + struct max17042_chip { struct i2c_client *client; struct power_supply battery; + enum max170xx_chip_type chip_type; struct max17042_platform_data *pdata; struct work_struct work; int init_complete; @@ -152,7 +156,10 @@ static int max17042_get_property(struct power_supply *psy, val->intval *= 20000; /* Units of LSB = 20mV */ break; case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: - ret = max17042_read_reg(chip->client, MAX17042_V_empty); + if (chip->chip_type == MAX17042) + ret = max17042_read_reg(chip->client, MAX17042_V_empty); + else + ret = max17042_read_reg(chip->client, MAX17047_V_empty); if (ret < 0) return ret; @@ -389,6 +396,9 @@ static void max17042_write_config_regs(struct max17042_chip *chip) max17042_write_reg(chip->client, MAX17042_FilterCFG, config->filter_cfg); max17042_write_reg(chip->client, MAX17042_RelaxCFG, config->relax_cfg); + if (chip->chip_type == MAX17047) + max17042_write_reg(chip->client, MAX17047_FullSOCThr, + config->full_soc_thresh); } static void max17042_write_custom_regs(struct max17042_chip *chip) @@ -399,12 +409,23 @@ static void max17042_write_custom_regs(struct max17042_chip *chip) 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); + if (chip->chip_type == MAX17042) { + max17042_write_reg(chip->client, MAX17042_EmptyTempCo, + config->empty_tempco); + max17042_write_verify_reg(chip->client, MAX17042_K_empty0, + config->kempty0); + } else { + max17042_write_verify_reg(chip->client, MAX17047_QRTbl00, + config->qrtbl00); + max17042_write_verify_reg(chip->client, MAX17047_QRTbl10, + config->qrtbl10); + max17042_write_verify_reg(chip->client, MAX17047_QRTbl20, + config->qrtbl20); + max17042_write_verify_reg(chip->client, MAX17047_QRTbl30, + config->qrtbl30); + } } static void max17042_update_capacity_regs(struct max17042_chip *chip) @@ -460,6 +481,8 @@ static void max17042_load_new_capacity_params(struct max17042_chip *chip) config->design_cap); max17042_write_verify_reg(chip->client, MAX17042_FullCAPNom, config->fullcapnom); + /* Update SOC register with new SOC */ + max17042_write_reg(chip->client, MAX17042_RepSOC, vfSoc); } /* @@ -496,20 +519,28 @@ static inline void max17042_override_por_values(struct max17042_chip *chip) 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); + if (chip->chip_type == MAX17042) + 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); + if (chip->chip_type == MAX17042) + max17042_override_por(client, MAX17042_V_empty, config->vempty); + else + max17042_override_por(client, MAX17047_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); + if (chip->chip_type) { + 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) @@ -666,7 +697,19 @@ static int __devinit max17042_probe(struct i2c_client *client, i2c_set_clientdata(client, chip); - chip->battery.name = "max17042_battery"; + ret = max17042_read_reg(chip->client, MAX17042_DevName); + if (ret == MAX17042_IC_VERSION) { + dev_dbg(&client->dev, "chip type max17042 detected\n"); + chip->chip_type = MAX17042; + } else if (ret == MAX17047_IC_VERSION) { + dev_dbg(&client->dev, "chip type max17047/50 detected\n"); + chip->chip_type = MAX17047; + } else { + dev_err(&client->dev, "device version mismatch: %x\n", ret); + return -EIO; + } + + chip->battery.name = "max170xx_battery"; chip->battery.type = POWER_SUPPLY_TYPE_BATTERY; chip->battery.get_property = max17042_get_property; chip->battery.properties = max17042_battery_props; @@ -778,6 +821,8 @@ static const struct dev_pm_ops max17042_pm_ops = { #ifdef CONFIG_OF static const struct of_device_id max17042_dt_match[] = { { .compatible = "maxim,max17042" }, + { .compatible = "maxim,max17047" }, + { .compatible = "maxim,max17050" }, { }, }; MODULE_DEVICE_TABLE(of, max17042_dt_match); @@ -785,6 +830,8 @@ MODULE_DEVICE_TABLE(of, max17042_dt_match); static const struct i2c_device_id max17042_id[] = { { "max17042", 0 }, + { "max17047", 1 }, + { "max17050", 2 }, { } }; MODULE_DEVICE_TABLE(i2c, max17042_id); -- cgit v1.2.3 From fcc015cda7dfe1af26e17bbed21eecb742183b7e Mon Sep 17 00:00:00 2001 From: Ramakrishna Pallala Date: Sat, 5 May 2012 21:33:54 +0530 Subject: smb347-charger: Clean up battery attributes CURRENT_NOW and VOLTAGE_NOW should be instantaneous readings from power supply(ex: battery). smb347 charger driver reports charge voltage for VOLTAGE_NOW and charge current for CURRENT_NOW attributes which are not instantaneous readings. This patch removes the battery VOLTAGE_NOW and CURRENT_NOW properties from the driver and also removes hw_to_current() which is not required anymore. Signed-off-by: Ramakrishna Pallala Acked-by: Mika Westerberg Signed-off-by: Anton Vorontsov --- drivers/power/smb347-charger.c | 49 ------------------------------------------ 1 file changed, 49 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/smb347-charger.c b/drivers/power/smb347-charger.c index cf31b31cc653..09d19d241eed 100644 --- a/drivers/power/smb347-charger.c +++ b/drivers/power/smb347-charger.c @@ -195,14 +195,6 @@ static const unsigned int ccc_tbl[] = { 1200000, }; -/* Convert register value to current using lookup table */ -static int hw_to_current(const unsigned int *tbl, size_t size, unsigned int val) -{ - if (val >= size) - return -EINVAL; - return tbl[val]; -} - /* Convert current to register value using lookup table */ static int current_to_hw(const unsigned int *tbl, size_t size, unsigned int val) { @@ -891,7 +883,6 @@ static int smb347_battery_get_property(struct power_supply *psy, struct smb347_charger *smb = container_of(psy, struct smb347_charger, battery); const struct smb347_charger_platform_data *pdata = smb->pdata; - unsigned int v; int ret; ret = smb347_update_ps_status(smb); @@ -943,44 +934,6 @@ static int smb347_battery_get_property(struct power_supply *psy, val->intval = pdata->battery_info.voltage_max_design; break; - case POWER_SUPPLY_PROP_VOLTAGE_NOW: - if (!smb347_is_ps_online(smb)) - return -ENODATA; - ret = regmap_read(smb->regmap, STAT_A, &v); - if (ret < 0) - return ret; - - v &= STAT_A_FLOAT_VOLTAGE_MASK; - if (v > 0x3d) - v = 0x3d; - - val->intval = 3500000 + v * 20000; - break; - - case POWER_SUPPLY_PROP_CURRENT_NOW: - if (!smb347_is_ps_online(smb)) - return -ENODATA; - - ret = regmap_read(smb->regmap, STAT_B, &v); - if (ret < 0) - return ret; - - /* - * The current value is composition of FCC and PCC values - * and we can detect which table to use from bit 5. - */ - if (v & 0x20) { - val->intval = hw_to_current(fcc_tbl, - ARRAY_SIZE(fcc_tbl), - v & 7); - } else { - v >>= 3; - val->intval = hw_to_current(pcc_tbl, - ARRAY_SIZE(pcc_tbl), - v & 7); - } - break; - case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: val->intval = pdata->battery_info.charge_full_design; break; @@ -1002,8 +955,6 @@ static enum power_supply_property smb347_battery_properties[] = { POWER_SUPPLY_PROP_TECHNOLOGY, POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, - POWER_SUPPLY_PROP_VOLTAGE_NOW, - POWER_SUPPLY_PROP_CURRENT_NOW, POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, POWER_SUPPLY_PROP_MODEL_NAME, }; -- cgit v1.2.3 From 96facd23e45e6d5020be135c8ab392ba9e044fa4 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 7 May 2012 11:26:37 +0300 Subject: smb347-charger: Include missing Without the include we get build errors like: drivers/power/smb347-charger.c: In function 'smb347_probe': drivers/power/smb347-charger.c:1039:2: error: implicit declaration of function 'IS_ERR' [-Werror=implicit-function-declaration] drivers/power/smb347-charger.c:1040:3: error: implicit declaration of function 'PTR_ERR' [-Werror=implicit-function-declaration] Reported-by: Stephen Rothwell Signed-off-by: Mika Westerberg Signed-off-by: Anton Vorontsov --- drivers/power/smb347-charger.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/power') diff --git a/drivers/power/smb347-charger.c b/drivers/power/smb347-charger.c index 09d19d241eed..f8eedd8a676f 100644 --- a/drivers/power/smb347-charger.c +++ b/drivers/power/smb347-charger.c @@ -11,6 +11,7 @@ * published by the Free Software Foundation. */ +#include #include #include #include -- cgit v1.2.3