From a5af092245a33965a4b089e8ba489c2db0f7db16 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 16 Nov 2012 17:09:22 +0800 Subject: generic-adc-battery: Fix checking return value of request_any_context_irq On failure, request_any_context_irq() returns a negative value. On success, it returns either IRQC_IS_HARDIRQ or IRQC_IS_NESTED. Signed-off-by: Axel Lin Signed-off-by: Anton Vorontsov --- drivers/power/generic-adc-battery.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/power/generic-adc-battery.c b/drivers/power/generic-adc-battery.c index 9bdf4447039..4a00ef7a879 100644 --- a/drivers/power/generic-adc-battery.c +++ b/drivers/power/generic-adc-battery.c @@ -327,7 +327,7 @@ static int __devinit gab_probe(struct platform_device *pdev) ret = request_any_context_irq(irq, gab_charged, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "battery charged", adc_bat); - if (ret) + if (ret < 0) goto err_gpio; } -- cgit v1.2.3 From a77d60aec70183faa68b7df4a3fa89fbaf592cf5 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 29 Sep 2012 10:13:46 +0300 Subject: generic-adc-battery: Pointer math issue in gab_probe() psy->properties is an enum (32 bit type) so adding sizeof() puts us four times further along than we intended. It should be cast to a char pointer before doing the math. Signed-off-by: Dan Carpenter Signed-off-by: Anton Vorontsov --- drivers/power/generic-adc-battery.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/power/generic-adc-battery.c b/drivers/power/generic-adc-battery.c index 4a00ef7a879..ecbf672a35d 100644 --- a/drivers/power/generic-adc-battery.c +++ b/drivers/power/generic-adc-battery.c @@ -279,7 +279,8 @@ static int __devinit gab_probe(struct platform_device *pdev) } memcpy(psy->properties, gab_props, sizeof(gab_props)); - properties = psy->properties + sizeof(gab_props); + properties = (enum power_supply_property *) + ((char *)psy->properties + sizeof(gab_props)); /* * getting channel from iio and copying the battery properties -- cgit v1.2.3 From ea2ce92e44dc83b7a69c2aedd9c52bfe7fee1a62 Mon Sep 17 00:00:00 2001 From: Ramakrishna Pallala Date: Tue, 9 Oct 2012 22:25:29 +0530 Subject: power_supply: Add support for CHARGE_CONTROL_* attributes Add support for power supply attributes CHARGE_CONTROL_LIMIT and CHARGE_CONTROL_LIMIT_MAX. These new attributes will enable the user space to implement custom charging algorithms based on platform state. Signed-off-by: Ramakrishna Pallala Signed-off-by: Anton Vorontsov --- Documentation/power/power_supply_class.txt | 3 +++ drivers/power/power_supply_sysfs.c | 2 ++ include/linux/power_supply.h | 2 ++ 3 files changed, 7 insertions(+) diff --git a/Documentation/power/power_supply_class.txt b/Documentation/power/power_supply_class.txt index 9c647bd7c5a..3f10b39b034 100644 --- a/Documentation/power/power_supply_class.txt +++ b/Documentation/power/power_supply_class.txt @@ -123,6 +123,9 @@ CONSTANT_CHARGE_VOLTAGE - constant charge voltage programmed by charger. CONSTANT_CHARGE_VOLTAGE_MAX - maximum charge voltage supported by the power supply object. +CHARGE_CONTROL_LIMIT - current charge control limit setting +CHARGE_CONTROL_LIMIT_MAX - maximum charge control limit setting + ENERGY_FULL, ENERGY_EMPTY - same as above but for energy. CAPACITY - capacity in percents. diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index 395c2cfa16c..40fa3b7cae5 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c @@ -164,6 +164,8 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(constant_charge_current_max), POWER_SUPPLY_ATTR(constant_charge_voltage), POWER_SUPPLY_ATTR(constant_charge_voltage_max), + POWER_SUPPLY_ATTR(charge_control_limit), + POWER_SUPPLY_ATTR(charge_control_limit_max), POWER_SUPPLY_ATTR(energy_full_design), POWER_SUPPLY_ATTR(energy_empty_design), POWER_SUPPLY_ATTR(energy_full), diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index e5ef45834c3..445b4b249af 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -114,6 +114,8 @@ enum power_supply_property { POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, + POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, + POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX, POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN, POWER_SUPPLY_PROP_ENERGY_FULL, -- cgit v1.2.3 From 952aeeb3ee28bc12a70744e40636de40688eb60d Mon Sep 17 00:00:00 2001 From: Ramakrishna Pallala Date: Tue, 9 Oct 2012 22:25:59 +0530 Subject: power_supply: Register power supply for thermal cooling device This patch registers the power supply as a cooling device if the power supply has support for charge throttling. Now with this change low level drivers need not register with thermal framework as it is automatically done by power supply framework. Signed-off-by: Ramakrishna Pallala Signed-off-by: Anton Vorontsov --- drivers/power/power_supply_core.c | 96 +++++++++++++++++++++++++++++++++++++++ include/linux/power_supply.h | 1 + 2 files changed, 97 insertions(+) diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c index 2436f135001..f984da1066e 100644 --- a/drivers/power/power_supply_core.c +++ b/drivers/power/power_supply_core.c @@ -216,6 +216,86 @@ static void psy_unregister_thermal(struct power_supply *psy) return; thermal_zone_device_unregister(psy->tzd); } + +/* thermal cooling device callbacks */ +static int ps_get_max_charge_cntl_limit(struct thermal_cooling_device *tcd, + unsigned long *state) +{ + struct power_supply *psy; + union power_supply_propval val; + int ret; + + psy = tcd->devdata; + ret = psy->get_property(psy, + POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX, &val); + if (!ret) + *state = val.intval; + + return ret; +} + +static int ps_get_cur_chrage_cntl_limit(struct thermal_cooling_device *tcd, + unsigned long *state) +{ + struct power_supply *psy; + union power_supply_propval val; + int ret; + + psy = tcd->devdata; + ret = psy->get_property(psy, + POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, &val); + if (!ret) + *state = val.intval; + + return ret; +} + +static int ps_set_cur_charge_cntl_limit(struct thermal_cooling_device *tcd, + unsigned long state) +{ + struct power_supply *psy; + union power_supply_propval val; + int ret; + + psy = tcd->devdata; + val.intval = state; + ret = psy->set_property(psy, + POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, &val); + + return ret; +} + +static struct thermal_cooling_device_ops psy_tcd_ops = { + .get_max_state = ps_get_max_charge_cntl_limit, + .get_cur_state = ps_get_cur_chrage_cntl_limit, + .set_cur_state = ps_set_cur_charge_cntl_limit, +}; + +static int psy_register_cooler(struct power_supply *psy) +{ + int i; + + /* Register for cooling device if psy can control charging */ + for (i = 0; i < psy->num_properties; i++) { + if (psy->properties[i] == + POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT) { + psy->tcd = thermal_cooling_device_register( + (char *)psy->name, + psy, &psy_tcd_ops); + if (IS_ERR(psy->tcd)) + return PTR_ERR(psy->tcd); + break; + } + } + return 0; +} + +static void psy_unregister_cooler(struct power_supply *psy) +{ + if (IS_ERR_OR_NULL(psy->tcd)) + return; + thermal_cooling_device_unregister(psy->tcd); +} #else static int psy_register_thermal(struct power_supply *psy) { @@ -225,6 +305,15 @@ static int psy_register_thermal(struct power_supply *psy) static void psy_unregister_thermal(struct power_supply *psy) { } + +static int psy_register_cooler(struct power_supply *psy) +{ + return 0; +} + +static void psy_unregister_cooler(struct power_supply *psy) +{ +} #endif int power_supply_register(struct device *parent, struct power_supply *psy) @@ -259,6 +348,10 @@ int power_supply_register(struct device *parent, struct power_supply *psy) if (rc) goto register_thermal_failed; + rc = psy_register_cooler(psy); + if (rc) + goto register_cooler_failed; + rc = power_supply_create_triggers(psy); if (rc) goto create_triggers_failed; @@ -268,6 +361,8 @@ int power_supply_register(struct device *parent, struct power_supply *psy) goto success; create_triggers_failed: + psy_unregister_cooler(psy); +register_cooler_failed: psy_unregister_thermal(psy); register_thermal_failed: device_del(dev); @@ -284,6 +379,7 @@ void power_supply_unregister(struct power_supply *psy) cancel_work_sync(&psy->changed_work); sysfs_remove_link(&psy->dev->kobj, "powers"); power_supply_remove_triggers(psy); + psy_unregister_cooler(psy); psy_unregister_thermal(psy); device_unregister(psy->dev); } diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index 445b4b249af..1f0ab90aff0 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -188,6 +188,7 @@ struct power_supply { struct work_struct changed_work; #ifdef CONFIG_THERMAL struct thermal_zone_device *tzd; + struct thermal_cooling_device *tcd; #endif #ifdef CONFIG_LEDS_TRIGGERS -- cgit v1.2.3 From 968a47836c7f9dc6a3987f846bb7e095d30b86ec Mon Sep 17 00:00:00 2001 From: "Kim, Milo" Date: Thu, 18 Oct 2012 06:11:33 +0000 Subject: lp8788-charger: Use consumer device name on setting IIO channels To get the ADC value for the battery voltage and temperature, LP8788 ADC driver is used. LP8788 charger driver is the consumer of LP8788 ADC driver. Thus, specific ADC driver name is required on getting the channel using iio_channel_get(). Signed-off-by: Milo(Woogyom) Kim Acked-by: Jonathan Cameron Signed-off-by: Anton Vorontsov --- drivers/power/lp8788-charger.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/power/lp8788-charger.c b/drivers/power/lp8788-charger.c index e852d12cd07..614e6aafc51 100644 --- a/drivers/power/lp8788-charger.c +++ b/drivers/power/lp8788-charger.c @@ -592,7 +592,8 @@ static void lp8788_irq_unregister(struct platform_device *pdev, } } -static void lp8788_setup_adc_channel(struct lp8788_charger *pchg) +static void lp8788_setup_adc_channel(const char *consumer_name, + struct lp8788_charger *pchg) { struct lp8788_charger_platform_data *pdata = pchg->pdata; struct device *dev = pchg->lp->dev; @@ -616,7 +617,7 @@ static void lp8788_setup_adc_channel(struct lp8788_charger *pchg) case LPADC_VBATT_5P5: case LPADC_VBATT_6P0: case LPADC_VBATT_5P0: - chan = iio_channel_get(NULL, chan_name[id]); + chan = iio_channel_get(consumer_name, chan_name[id]); pchg->chan[LP8788_VBATT] = IS_ERR(chan) ? NULL : chan; break; default: @@ -631,7 +632,7 @@ static void lp8788_setup_adc_channel(struct lp8788_charger *pchg) case LPADC_ADC2: case LPADC_ADC3: case LPADC_ADC4: - chan = iio_channel_get(NULL, chan_name[id]); + chan = iio_channel_get(consumer_name, chan_name[id]); pchg->chan[LP8788_BATT_TEMP] = IS_ERR(chan) ? NULL : chan; break; default: @@ -747,7 +748,7 @@ static __devinit int lp8788_charger_probe(struct platform_device *pdev) if (ret) return ret; - lp8788_setup_adc_channel(pchg); + lp8788_setup_adc_channel(pdev->name, pchg); ret = lp8788_psy_register(pdev, pchg); if (ret) -- cgit v1.2.3 From 340968de484967f931d89d20a1344d213fa86fb8 Mon Sep 17 00:00:00 2001 From: "Kim, Milo" Date: Fri, 19 Oct 2012 00:12:21 +0000 Subject: lp8788-charger: Fix wrong ADC conversion To get the battery voltage and temperature, IIO ADC functions are used. LP8788 ADC driver provides RAW and SCALE channel information. This patch fixes wrong ADC result. Signed-off-by: Milo(Woogyom) Kim Reviewed-by Lars-Peter Clausen Acked-by: Jonathan Cameron Signed-off-by: Anton Vorontsov --- drivers/power/lp8788-charger.c | 26 +++++++------------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/drivers/power/lp8788-charger.c b/drivers/power/lp8788-charger.c index 614e6aafc51..1afa5f7a535 100644 --- a/drivers/power/lp8788-charger.c +++ b/drivers/power/lp8788-charger.c @@ -235,25 +235,14 @@ static int lp8788_get_battery_present(struct lp8788_charger *pchg, return 0; } -static int lp8788_get_vbatt_adc(struct lp8788_charger *pchg, - unsigned int *result) +static int lp8788_get_vbatt_adc(struct lp8788_charger *pchg, int *result) { struct iio_channel *channel = pchg->chan[LP8788_VBATT]; - int scaleint; - int scalepart; - int ret; if (!channel) return -EINVAL; - ret = iio_read_channel_scale(channel, &scaleint, &scalepart); - if (ret != IIO_VAL_INT_PLUS_MICRO) - return -EINVAL; - - /* unit: mV */ - *result = (scaleint + scalepart * 1000000) / 1000; - - return 0; + return iio_read_channel_processed(channel, result); } static int lp8788_get_battery_voltage(struct lp8788_charger *pchg, @@ -268,7 +257,7 @@ static int lp8788_get_battery_capacity(struct lp8788_charger *pchg, struct lp8788 *lp = pchg->lp; struct lp8788_charger_platform_data *pdata = pchg->pdata; unsigned int max_vbatt; - unsigned int vbatt; + int vbatt; enum lp8788_charging_state state; u8 data; int ret; @@ -304,19 +293,18 @@ static int lp8788_get_battery_temperature(struct lp8788_charger *pchg, union power_supply_propval *val) { struct iio_channel *channel = pchg->chan[LP8788_BATT_TEMP]; - int scaleint; - int scalepart; + int result; int ret; if (!channel) return -EINVAL; - ret = iio_read_channel_scale(channel, &scaleint, &scalepart); - if (ret != IIO_VAL_INT_PLUS_MICRO) + ret = iio_read_channel_processed(channel, &result); + if (ret < 0) return -EINVAL; /* unit: 0.1 'C */ - val->intval = (scaleint + scalepart * 1000000) / 100; + val->intval = result * 10; return 0; } -- cgit v1.2.3 From 08d816b8cb09de1fd8268c8f1dbc97c3cc435d67 Mon Sep 17 00:00:00 2001 From: "Kim, Milo" Date: Mon, 22 Oct 2012 00:18:54 +0000 Subject: lp8788-charger: Fix ADC channel names The name of ADC channel is configurable in the platform side. This name is referenced in the IIO consumer driver. To get the IIO channel, specific name in the platform data is used as an parameter of the iio_channel_get(). Thus, lp8788_adc_id platform data are replaced with specific names. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Anton Vorontsov --- drivers/power/lp8788-charger.c | 44 ++++++------------------------------------ include/linux/mfd/lp8788.h | 8 ++++---- 2 files changed, 10 insertions(+), 42 deletions(-) diff --git a/drivers/power/lp8788-charger.c b/drivers/power/lp8788-charger.c index 1afa5f7a535..fb592bfbec6 100644 --- a/drivers/power/lp8788-charger.c +++ b/drivers/power/lp8788-charger.c @@ -584,50 +584,18 @@ static void lp8788_setup_adc_channel(const char *consumer_name, struct lp8788_charger *pchg) { struct lp8788_charger_platform_data *pdata = pchg->pdata; - struct device *dev = pchg->lp->dev; struct iio_channel *chan; - enum lp8788_adc_id id; - const char *chan_name[LPADC_MAX] = { - [LPADC_VBATT_5P5] = "vbatt-5p5", - [LPADC_VBATT_6P0] = "vbatt-6p0", - [LPADC_VBATT_5P0] = "vbatt-5p0", - [LPADC_ADC1] = "adc1", - [LPADC_ADC2] = "adc2", - [LPADC_ADC3] = "adc3", - [LPADC_ADC4] = "adc4", - }; if (!pdata) return; - id = pdata->vbatt_adc; - switch (id) { - case LPADC_VBATT_5P5: - case LPADC_VBATT_6P0: - case LPADC_VBATT_5P0: - chan = iio_channel_get(consumer_name, chan_name[id]); - pchg->chan[LP8788_VBATT] = IS_ERR(chan) ? NULL : chan; - break; - default: - dev_err(dev, "invalid ADC id for VBATT: %d\n", id); - pchg->chan[LP8788_VBATT] = NULL; - break; - } + /* ADC channel for battery voltage */ + chan = iio_channel_get(consumer_name, pdata->adc_vbatt); + pchg->chan[LP8788_VBATT] = IS_ERR(chan) ? NULL : chan; - id = pdata->batt_temp_adc; - switch (id) { - case LPADC_ADC1: - case LPADC_ADC2: - case LPADC_ADC3: - case LPADC_ADC4: - chan = iio_channel_get(consumer_name, chan_name[id]); - pchg->chan[LP8788_BATT_TEMP] = IS_ERR(chan) ? NULL : chan; - break; - default: - dev_err(dev, "invalid ADC id for BATT_TEMP : %d\n", id); - pchg->chan[LP8788_BATT_TEMP] = NULL; - break; - } + /* ADC channel for battery temperature */ + chan = iio_channel_get(consumer_name, pdata->adc_batt_temp); + pchg->chan[LP8788_BATT_TEMP] = IS_ERR(chan) ? NULL : chan; } static void lp8788_release_adc_channel(struct lp8788_charger *pchg) diff --git a/include/linux/mfd/lp8788.h b/include/linux/mfd/lp8788.h index cec364bdccf..2a32b16f79c 100644 --- a/include/linux/mfd/lp8788.h +++ b/include/linux/mfd/lp8788.h @@ -211,16 +211,16 @@ struct lp8788_chg_param { /* * struct lp8788_charger_platform_data - * @vbatt_adc : adc selection id for battery voltage - * @batt_temp_adc : adc selection id for battery temperature + * @adc_vbatt : adc channel name for battery voltage + * @adc_batt_temp : adc channel name for battery temperature * @max_vbatt_mv : used for calculating battery capacity * @chg_params : initial charging parameters * @num_chg_params : numbers of charging parameters * @charger_event : the charger event can be reported to the platform side */ struct lp8788_charger_platform_data { - enum lp8788_adc_id vbatt_adc; - enum lp8788_adc_id batt_temp_adc; + const char *adc_vbatt; + const char *adc_batt_temp; unsigned int max_vbatt_mv; struct lp8788_chg_param *chg_params; int num_chg_params; -- cgit v1.2.3 From 8511748ba1ab83a7d89d802a50335115358a8cfc Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 24 Oct 2012 10:12:29 +0300 Subject: ds2782_battery: Fix signedness bug in ds278x_read_reg16() We need to check for negative values before doing the swab16() for the error handling to work. Signed-off-by: Dan Carpenter Signed-off-by: Anton Vorontsov --- drivers/power/ds2782_battery.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/power/ds2782_battery.c b/drivers/power/ds2782_battery.c index 6bb6e2f5ea8..2fa9b6bf1f3 100644 --- a/drivers/power/ds2782_battery.c +++ b/drivers/power/ds2782_battery.c @@ -80,13 +80,13 @@ static inline int ds278x_read_reg16(struct ds278x_info *info, int reg_msb, { int ret; - ret = swab16(i2c_smbus_read_word_data(info->client, reg_msb)); + ret = i2c_smbus_read_word_data(info->client, reg_msb); if (ret < 0) { dev_err(&info->client->dev, "register read failed\n"); return ret; } - *val = ret; + *val = swab16(ret); return 0; } -- cgit v1.2.3 From a879f19fe7decb9a53d13a63edd2eced8c2895a2 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Sun, 18 Nov 2012 14:59:47 -0800 Subject: max17042_battery: Fix missing verify_model_lock() return value check The second error check is unreachable because the lock function isn't assigned to ret. Signed-off-by: Alan Cox Acked-by: Ramakrishna Pallala Signed-off-by: Anton Vorontsov --- drivers/power/max17042_battery.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/power/max17042_battery.c b/drivers/power/max17042_battery.c index 74abc6c755b..66b2c7b5091 100644 --- a/drivers/power/max17042_battery.c +++ b/drivers/power/max17042_battery.c @@ -572,7 +572,8 @@ static int max17042_init_chip(struct max17042_chip *chip) __func__); return -EIO; } - max17042_verify_model_lock(chip); + + ret = max17042_verify_model_lock(chip); if (ret) { dev_err(&chip->client->dev, "%s lock verify failed\n", __func__); -- cgit v1.2.3 From 04930baf28f08cd275e4afbf5a335b42335705df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Wed, 31 Oct 2012 10:48:40 +0100 Subject: power_supply: Add new Nokia RX-51 (N900) power supply battery driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This driver exporting battery design capacity, temperature and voltage for battery in Nokia RX-51. This driver is needed for open source battery management on Nokia RX-51 (N900). Signed-off-by: Pali Rohár Signed-off-by: Anton Vorontsov --- drivers/power/Kconfig | 7 ++ drivers/power/Makefile | 1 + drivers/power/rx51_battery.c | 251 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 259 insertions(+) create mode 100644 drivers/power/rx51_battery.c diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 49a89397231..5c3237f601d 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -245,6 +245,13 @@ config BATTERY_INTEL_MID Say Y here to enable the battery driver on Intel MID platforms. +config BATTERY_RX51 + tristate "Nokia RX-51 (N900) battery driver" + depends on TWL4030_MADC + help + Say Y here to enable support for battery information on Nokia + RX-51, also known as N900 tablet. + config CHARGER_ISP1704 tristate "ISP1704 USB Charger Detection" depends on USB_OTG_UTILS diff --git a/drivers/power/Makefile b/drivers/power/Makefile index b949cf85590..bc81fe2112c 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -37,6 +37,7 @@ obj-$(CONFIG_CHARGER_88PM860X) += 88pm860x_charger.o obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o obj-$(CONFIG_BATTERY_JZ4740) += jz4740-battery.o obj-$(CONFIG_BATTERY_INTEL_MID) += intel_mid_battery.o +obj-$(CONFIG_BATTERY_RX51) += rx51_battery.o obj-$(CONFIG_AB8500_BM) += ab8500_charger.o ab8500_btemp.o ab8500_fg.o abx500_chargalg.o obj-$(CONFIG_CHARGER_ISP1704) += isp1704_charger.o obj-$(CONFIG_CHARGER_MAX8903) += max8903_charger.o diff --git a/drivers/power/rx51_battery.c b/drivers/power/rx51_battery.c new file mode 100644 index 00000000000..ca49d6c0ee9 --- /dev/null +++ b/drivers/power/rx51_battery.c @@ -0,0 +1,251 @@ +/* + * Nokia RX-51 battery driver + * + * Copyright (C) 2012 Pali Rohár + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include + +struct rx51_device_info { + struct device *dev; + struct power_supply bat; +}; + +/* + * Read ADCIN channel value, code copied from maemo kernel + */ +static int rx51_battery_read_adc(int channel) +{ + struct twl4030_madc_request req; + + req.channels = 1 << channel; + req.do_avg = 1; + req.method = TWL4030_MADC_SW1; + req.func_cb = NULL; + req.type = TWL4030_MADC_WAIT; + + if (twl4030_madc_conversion(&req) <= 0) + return -ENODATA; + + return req.rbuf[channel]; +} + +/* + * Read ADCIN channel 12 (voltage) and convert RAW value to micro voltage + * This conversion formula was extracted from maemo program bsi-read + */ +static int rx51_battery_read_voltage(struct rx51_device_info *di) +{ + int voltage = rx51_battery_read_adc(12); + + if (voltage < 0) + return voltage; + + return 1000 * (10000 * voltage / 1705); +} + +/* + * Temperature look-up tables + * TEMP = (1/(t1 + 1/298) - 273.15) + * Where t1 = (1/B) * ln((RAW_ADC_U * 2.5)/(R * I * 255)) + * Formula is based on experimental data, RX-51 CAL data, maemo program bme + * and formula from da9052 driver with values R = 100, B = 3380, I = 0.00671 + */ + +/* + * Table1 (temperature for first 25 RAW values) + * Usage: TEMP = rx51_temp_table1[RAW] + * RAW is between 1 and 24 + * TEMP is between 201 C and 55 C + */ +static u8 rx51_temp_table1[] = { + 255, 201, 159, 138, 124, 114, 106, 99, 94, 89, 85, 82, 78, 75, + 73, 70, 68, 66, 64, 62, 61, 59, 57, 56, 55 +}; + +/* + * Table2 (lowest RAW value for temperature) + * Usage: RAW = rx51_temp_table2[TEMP-rx51_temp_table2_first] + * TEMP is between 53 C and -32 C + * RAW is between 25 and 993 + */ +#define rx51_temp_table2_first 53 +static u16 rx51_temp_table2[] = { + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 39, + 40, 41, 43, 44, 46, 48, 49, 51, 53, 55, 57, 59, 61, 64, + 66, 69, 71, 74, 77, 80, 83, 86, 90, 94, 97, 101, 106, 110, + 115, 119, 125, 130, 136, 141, 148, 154, 161, 168, 176, 184, 202, 211, + 221, 231, 242, 254, 266, 279, 293, 308, 323, 340, 357, 375, 395, 415, + 437, 460, 485, 511, 539, 568, 600, 633, 669, 706, 747, 790, 836, 885, + 937, 993, 1024 +}; + +/* + * Read ADCIN channel 0 (battery temp) and convert value to tenths of Celsius + * Use Temperature look-up tables for conversation + */ +static int rx51_battery_read_temperature(struct rx51_device_info *di) +{ + int min = 0; + int max = ARRAY_SIZE(rx51_temp_table2) - 1; + int raw = rx51_battery_read_adc(0); + + /* Zero and negative values are undefined */ + if (raw <= 0) + return INT_MAX; + + /* ADC channels are 10 bit, higher value are undefined */ + if (raw >= (1 << 10)) + return INT_MIN; + + /* First check for temperature in first direct table */ + if (raw < ARRAY_SIZE(rx51_temp_table1)) + return rx51_temp_table1[raw] * 100; + + /* Binary search RAW value in second inverse table */ + while (max - min > 1) { + int mid = (max + min) / 2; + if (rx51_temp_table2[mid] <= raw) + min = mid; + else if (rx51_temp_table2[mid] > raw) + max = mid; + if (rx51_temp_table2[mid] == raw) + break; + } + + return (rx51_temp_table2_first - min) * 100; +} + +/* + * Read ADCIN channel 4 (BSI) and convert RAW value to micro Ah + * This conversion formula was extracted from maemo program bsi-read + */ +static int rx51_battery_read_capacity(struct rx51_device_info *di) +{ + int capacity = rx51_battery_read_adc(4); + + if (capacity < 0) + return capacity; + + return 1280 * (1200 * capacity)/(1024 - capacity); +} + +/* + * Return power_supply property + */ +static int rx51_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct rx51_device_info *di = container_of((psy), + struct rx51_device_info, bat); + + switch (psp) { + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = POWER_SUPPLY_TECHNOLOGY_LION; + break; + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: + val->intval = 4200000; + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = rx51_battery_read_voltage(di) ? 1 : 0; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = rx51_battery_read_voltage(di); + break; + case POWER_SUPPLY_PROP_TEMP: + val->intval = rx51_battery_read_temperature(di); + break; + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + val->intval = rx51_battery_read_capacity(di); + break; + default: + return -EINVAL; + } + + if (val->intval == INT_MAX || val->intval == INT_MIN) + return -EINVAL; + + return 0; +} + +static enum power_supply_property rx51_battery_props[] = { + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, +}; + +static int __devinit rx51_battery_probe(struct platform_device *pdev) +{ + struct rx51_device_info *di; + int ret; + + di = kzalloc(sizeof(*di), GFP_KERNEL); + if (!di) + return -ENOMEM; + + platform_set_drvdata(pdev, di); + + di->bat.name = dev_name(&pdev->dev); + di->bat.type = POWER_SUPPLY_TYPE_BATTERY; + di->bat.properties = rx51_battery_props; + di->bat.num_properties = ARRAY_SIZE(rx51_battery_props); + di->bat.get_property = rx51_battery_get_property; + + ret = power_supply_register(di->dev, &di->bat); + if (ret) { + platform_set_drvdata(pdev, NULL); + kfree(di); + return ret; + } + + return 0; +} + +static int __devexit rx51_battery_remove(struct platform_device *pdev) +{ + struct rx51_device_info *di = platform_get_drvdata(pdev); + + power_supply_unregister(&di->bat); + platform_set_drvdata(pdev, NULL); + kfree(di); + + return 0; +} + +static struct platform_driver rx51_battery_driver = { + .probe = rx51_battery_probe, + .remove = __devexit_p(rx51_battery_remove), + .driver = { + .name = "rx51-battery", + .owner = THIS_MODULE, + }, +}; +module_platform_driver(rx51_battery_driver); + +MODULE_ALIAS("platform:rx51-battery"); +MODULE_AUTHOR("Pali Rohár "); +MODULE_DESCRIPTION("Nokia RX-51 battery driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 0bc98cc6155205b615a9d29a2d54d1b839521c04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Fri, 2 Nov 2012 20:18:11 +0100 Subject: power_supply: Add bq2415x charger driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The bq2415x charger driver is needed for example on Nokia N900 for charging battery. Driver is part of open source project to replace proprietary battery management. Signed-off-by: Pali Rohár Signed-off-by: Anton Vorontsov --- drivers/power/bq2415x_charger.c | 1644 +++++++++++++++++++++++++++++++++ include/linux/power/bq2415x_charger.h | 90 ++ 2 files changed, 1734 insertions(+) create mode 100644 drivers/power/bq2415x_charger.c create mode 100644 include/linux/power/bq2415x_charger.h diff --git a/drivers/power/bq2415x_charger.c b/drivers/power/bq2415x_charger.c new file mode 100644 index 00000000000..017b3c27f76 --- /dev/null +++ b/drivers/power/bq2415x_charger.c @@ -0,0 +1,1644 @@ +/* + bq2415x_charger.c - bq2415x charger driver + Copyright (C) 2011-2012 Pali Rohár + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +/* + Datasheets: + http://www.ti.com/product/bq24150 + http://www.ti.com/product/bq24150a + http://www.ti.com/product/bq24152 + http://www.ti.com/product/bq24153 + http://www.ti.com/product/bq24153a + http://www.ti.com/product/bq24155 +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* timeout for resetting chip timer */ +#define BQ2415X_TIMER_TIMEOUT 10 + +#define BQ2415X_REG_STATUS 0x00 +#define BQ2415X_REG_CONTROL 0x01 +#define BQ2415X_REG_VOLTAGE 0x02 +#define BQ2415X_REG_VENDER 0x03 +#define BQ2415X_REG_CURRENT 0x04 + +/* reset state for all registers */ +#define BQ2415X_RESET_STATUS BIT(6) +#define BQ2415X_RESET_CONTROL (BIT(4)|BIT(5)) +#define BQ2415X_RESET_VOLTAGE (BIT(1)|BIT(3)) +#define BQ2415X_RESET_CURRENT (BIT(0)|BIT(3)|BIT(7)) + +/* status register */ +#define BQ2415X_BIT_TMR_RST 7 +#define BQ2415X_BIT_OTG 7 +#define BQ2415X_BIT_EN_STAT 6 +#define BQ2415X_MASK_STAT (BIT(4)|BIT(5)) +#define BQ2415X_SHIFT_STAT 4 +#define BQ2415X_BIT_BOOST 3 +#define BQ2415X_MASK_FAULT (BIT(0)|BIT(1)|BIT(2)) +#define BQ2415X_SHIFT_FAULT 0 + +/* control register */ +#define BQ2415X_MASK_LIMIT (BIT(6)|BIT(7)) +#define BQ2415X_SHIFT_LIMIT 6 +#define BQ2415X_MASK_VLOWV (BIT(4)|BIT(5)) +#define BQ2415X_SHIFT_VLOWV 4 +#define BQ2415X_BIT_TE 3 +#define BQ2415X_BIT_CE 2 +#define BQ2415X_BIT_HZ_MODE 1 +#define BQ2415X_BIT_OPA_MODE 0 + +/* voltage register */ +#define BQ2415X_MASK_VO (BIT(2)|BIT(3)|BIT(4)|BIT(5)|BIT(6)|BIT(7)) +#define BQ2415X_SHIFT_VO 2 +#define BQ2415X_BIT_OTG_PL 1 +#define BQ2415X_BIT_OTG_EN 0 + +/* vender register */ +#define BQ2415X_MASK_VENDER (BIT(5)|BIT(6)|BIT(7)) +#define BQ2415X_SHIFT_VENDER 5 +#define BQ2415X_MASK_PN (BIT(3)|BIT(4)) +#define BQ2415X_SHIFT_PN 3 +#define BQ2415X_MASK_REVISION (BIT(0)|BIT(1)|BIT(2)) +#define BQ2415X_SHIFT_REVISION 0 + +/* current register */ +#define BQ2415X_MASK_RESET BIT(7) +#define BQ2415X_MASK_VI_CHRG (BIT(4)|BIT(5)|BIT(6)) +#define BQ2415X_SHIFT_VI_CHRG 4 +/* N/A BIT(3) */ +#define BQ2415X_MASK_VI_TERM (BIT(0)|BIT(1)|BIT(2)) +#define BQ2415X_SHIFT_VI_TERM 0 + + +enum bq2415x_command { + BQ2415X_TIMER_RESET, + BQ2415X_OTG_STATUS, + BQ2415X_STAT_PIN_STATUS, + BQ2415X_STAT_PIN_ENABLE, + BQ2415X_STAT_PIN_DISABLE, + BQ2415X_CHARGE_STATUS, + BQ2415X_BOOST_STATUS, + BQ2415X_FAULT_STATUS, + + BQ2415X_CHARGE_TERMINATION_STATUS, + BQ2415X_CHARGE_TERMINATION_ENABLE, + BQ2415X_CHARGE_TERMINATION_DISABLE, + BQ2415X_CHARGER_STATUS, + BQ2415X_CHARGER_ENABLE, + BQ2415X_CHARGER_DISABLE, + BQ2415X_HIGH_IMPEDANCE_STATUS, + BQ2415X_HIGH_IMPEDANCE_ENABLE, + BQ2415X_HIGH_IMPEDANCE_DISABLE, + BQ2415X_BOOST_MODE_STATUS, + BQ2415X_BOOST_MODE_ENABLE, + BQ2415X_BOOST_MODE_DISABLE, + + BQ2415X_OTG_LEVEL, + BQ2415X_OTG_ACTIVATE_HIGH, + BQ2415X_OTG_ACTIVATE_LOW, + BQ2415X_OTG_PIN_STATUS, + BQ2415X_OTG_PIN_ENABLE, + BQ2415X_OTG_PIN_DISABLE, + + BQ2415X_VENDER_CODE, + BQ2415X_PART_NUMBER, + BQ2415X_REVISION, +}; + +enum bq2415x_chip { + BQUNKNOWN, + BQ24150, + BQ24150A, + BQ24151, + BQ24151A, + BQ24152, + BQ24153, + BQ24153A, + BQ24155, + BQ24156, + BQ24156A, + BQ24158, +}; + +static char *bq2415x_chip_name[] = { + "unknown", + "bq24150", + "bq24150a", + "bq24151", + "bq24151a", + "bq24152", + "bq24153", + "bq24153a", + "bq24155", + "bq24156", + "bq24156a", + "bq24158", +}; + +struct bq2415x_device { + struct device *dev; + struct bq2415x_platform_data init_data; + struct power_supply charger; + struct delayed_work work; + enum bq2415x_mode reported_mode;/* mode reported by hook function */ + enum bq2415x_mode mode; /* current configured mode */ + enum bq2415x_chip chip; + const char *timer_error; + char *model; + char *name; + int autotimer; /* 1 - if driver automatically reset timer, 0 - not */ + int automode; /* 1 - enabled, 0 - disabled; -1 - not supported */ + int id; +}; + +/* each registered chip must have unique id */ +static DEFINE_IDR(bq2415x_id); + +static DEFINE_MUTEX(bq2415x_id_mutex); +static DEFINE_MUTEX(bq2415x_timer_mutex); +static DEFINE_MUTEX(bq2415x_i2c_mutex); + +/**** i2c read functions ****/ + +/* read value from register */ +static int bq2415x_i2c_read(struct bq2415x_device *bq, u8 reg) +{ + struct i2c_client *client = to_i2c_client(bq->dev); + struct i2c_msg msg[2]; + u8 val; + int ret; + + if (!client->adapter) + return -ENODEV; + + msg[0].addr = client->addr; + msg[0].flags = 0; + msg[0].buf = ® + msg[0].len = sizeof(reg); + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].buf = &val; + msg[1].len = sizeof(val); + + mutex_lock(&bq2415x_i2c_mutex); + ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); + mutex_unlock(&bq2415x_i2c_mutex); + + if (ret < 0) + return ret; + + return val; +} + +/* read value from register, apply mask and right shift it */ +static int bq2415x_i2c_read_mask(struct bq2415x_device *bq, u8 reg, + u8 mask, u8 shift) +{ + int ret; + + if (shift > 8) + return -EINVAL; + + ret = bq2415x_i2c_read(bq, reg); + if (ret < 0) + return ret; + else + return (ret & mask) >> shift; +} + +/* read value from register and return one specified bit */ +static int bq2415x_i2c_read_bit(struct bq2415x_device *bq, u8 reg, u8 bit) +{ + if (bit > 8) + return -EINVAL; + else + return bq2415x_i2c_read_mask(bq, reg, BIT(bit), bit); +} + +/**** i2c write functions ****/ + +/* write value to register */ +static int bq2415x_i2c_write(struct bq2415x_device *bq, u8 reg, u8 val) +{ + struct i2c_client *client = to_i2c_client(bq->dev); + struct i2c_msg msg[1]; + u8 data[2]; + int ret; + + data[0] = reg; + data[1] = val; + + msg[0].addr = client->addr; + msg[0].flags = 0; + msg[0].buf = data; + msg[0].len = ARRAY_SIZE(data); + + mutex_lock(&bq2415x_i2c_mutex); + ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); + mutex_unlock(&bq2415x_i2c_mutex); + + /* i2c_transfer returns number of messages transferred */ + if (ret < 0) + return ret; + else if (ret != 1) + return -EIO; + + return 0; +} + +/* read value from register, change it with mask left shifted and write back */ +static int bq2415x_i2c_write_mask(struct bq2415x_device *bq, u8 reg, u8 val, + u8 mask, u8 shift) +{ + int ret; + + if (shift > 8) + return -EINVAL; + + ret = bq2415x_i2c_read(bq, reg); + if (ret < 0) + return ret; + + ret &= ~mask; + ret |= val << shift; + + return bq2415x_i2c_write(bq, reg, ret); +} + +/* change only one bit in register */ +static int bq2415x_i2c_write_bit(struct bq2415x_device *bq, u8 reg, + bool val, u8 bit) +{ + if (bit > 8) + return -EINVAL; + else + return bq2415x_i2c_write_mask(bq, reg, val, BIT(bit), bit); +} + +/**** global functions ****/ + +/* exec command function */ +static int bq2415x_exec_command(struct bq2415x_device *bq, + enum bq2415x_command command) +{ + int ret; + switch (command) { + case BQ2415X_TIMER_RESET: + return bq2415x_i2c_write_bit(bq, BQ2415X_REG_STATUS, + 1, BQ2415X_BIT_TMR_RST); + case BQ2415X_OTG_STATUS: + return bq2415x_i2c_read_bit(bq, BQ2415X_REG_STATUS, + BQ2415X_BIT_OTG); + case BQ2415X_STAT_PIN_STATUS: + return bq2415x_i2c_read_bit(bq, BQ2415X_REG_STATUS, + BQ2415X_BIT_EN_STAT); + case BQ2415X_STAT_PIN_ENABLE: + return bq2415x_i2c_write_bit(bq, BQ2415X_REG_STATUS, 1, + BQ2415X_BIT_EN_STAT); + case BQ2415X_STAT_PIN_DISABLE: + return bq2415x_i2c_write_bit(bq, BQ2415X_REG_STATUS, 0, + BQ2415X_BIT_EN_STAT); + case BQ2415X_CHARGE_STATUS: + return bq2415x_i2c_read_mask(bq, BQ2415X_REG_STATUS, + BQ2415X_MASK_STAT, BQ2415X_SHIFT_STAT); + case BQ2415X_BOOST_STATUS: + return bq2415x_i2c_read_bit(bq, BQ2415X_REG_STATUS, + BQ2415X_BIT_BOOST); + case BQ2415X_FAULT_STATUS: + return bq2415x_i2c_read_mask(bq, BQ2415X_REG_STATUS, + BQ2415X_MASK_FAULT, BQ2415X_SHIFT_FAULT); + + case BQ2415X_CHARGE_TERMINATION_STATUS: + return bq2415x_i2c_read_bit(bq, BQ2415X_REG_CONTROL, + BQ2415X_BIT_TE); + case BQ2415X_CHARGE_TERMINATION_ENABLE: + return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL, + 1, BQ2415X_BIT_TE); + case BQ2415X_CHARGE_TERMINATION_DISABLE: + return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL, + 0, BQ2415X_BIT_TE); + case BQ2415X_CHARGER_STATUS: + ret = bq2415x_i2c_read_bit(bq, BQ2415X_REG_CONTROL, + BQ2415X_BIT_CE); + if (ret < 0) + return ret; + else + return ret > 0 ? 0 : 1; + case BQ2415X_CHARGER_ENABLE: + return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL, + 0, BQ2415X_BIT_CE); + case BQ2415X_CHARGER_DISABLE: + return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL, + 1, BQ2415X_BIT_CE); + case BQ2415X_HIGH_IMPEDANCE_STATUS: + return bq2415x_i2c_read_bit(bq, BQ2415X_REG_CONTROL, + BQ2415X_BIT_HZ_MODE); + case BQ2415X_HIGH_IMPEDANCE_ENABLE: + return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL, + 1, BQ2415X_BIT_HZ_MODE); + case BQ2415X_HIGH_IMPEDANCE_DISABLE: + return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL, + 0, BQ2415X_BIT_HZ_MODE); + case BQ2415X_BOOST_MODE_STATUS: + return bq2415x_i2c_read_bit(bq, BQ2415X_REG_CONTROL, + BQ2415X_BIT_OPA_MODE); + case BQ2415X_BOOST_MODE_ENABLE: + return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL, + 1, BQ2415X_BIT_OPA_MODE); + case BQ2415X_BOOST_MODE_DISABLE: + return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL, + 0, BQ2415X_BIT_OPA_MODE); + + case BQ2415X_OTG_LEVEL: + return bq2415x_i2c_read_bit(bq, BQ2415X_REG_VOLTAGE, + BQ2415X_BIT_OTG_PL); + case BQ2415X_OTG_ACTIVATE_HIGH: + return bq2415x_i2c_write_bit(bq, BQ2415X_REG_VOLTAGE, + 1, BQ2415X_BIT_OTG_PL); + case BQ2415X_OTG_ACTIVATE_LOW: + return bq2415x_i2c_write_bit(bq, BQ2415X_REG_VOLTAGE, + 0, BQ2415X_BIT_OTG_PL); + case BQ2415X_OTG_PIN_STATUS: + return bq2415x_i2c_read_bit(bq, BQ2415X_REG_VOLTAGE, + BQ2415X_BIT_OTG_EN); + case BQ2415X_OTG_PIN_ENABLE: + return bq2415x_i2c_write_bit(bq, BQ2415X_REG_VOLTAGE, + 1, BQ2415X_BIT_OTG_EN); + case BQ2415X_OTG_PIN_DISABLE: + return bq2415x_i2c_write_bit(bq, BQ2415X_REG_VOLTAGE, + 0, BQ2415X_BIT_OTG_EN); + + case BQ2415X_VENDER_CODE: + return bq2415x_i2c_read_mask(bq, BQ2415X_REG_VENDER, + BQ2415X_MASK_VENDER, BQ2415X_SHIFT_VENDER); + case BQ2415X_PART_NUMBER: + return bq2415x_i2c_read_mask(bq, BQ2415X_REG_VENDER, + BQ2415X_MASK_PN, BQ2415X_SHIFT_PN); + case BQ2415X_REVISION: + return bq2415x_i2c_read_mask(bq, BQ2415X_REG_VENDER, + BQ2415X_MASK_REVISION, BQ2415X_SHIFT_REVISION); + + default: + return -EINVAL; + } +} + +/* detect chip type */ +static enum bq2415x_chip bq2415x_detect_chip(struct bq2415x_device *bq) +{ + struct i2c_client *client = to_i2c_client(bq->dev); + int ret = bq2415x_exec_command(bq, BQ2415X_PART_NUMBER); + + if (ret < 0) + return ret; + + switch (client->addr) { + case 0x6b: + switch (ret) { + case 0: + if (bq->chip == BQ24151A) + return bq->chip; + else + return BQ24151; + case 1: + if (bq->chip == BQ24150A || + bq->chip == BQ24152 || + bq->chip == BQ24155) + return bq->chip; + else + return BQ24150; + case 2: + if (bq->chip == BQ24153A) + return bq->chip; + else + return BQ24153; + default: + return BQUNKNOWN; + } + break; + + case 0x6a: + switch (ret) { + case 0: + if (bq->chip == BQ24156A) + return bq->chip; + else + return BQ24156; + case 2: + return BQ24158; + default: + return BQUNKNOWN; + } + break; + } + + return BQUNKNOWN; +} + +/* detect chip revision */ +static int bq2415x_detect_revision(struct bq2415x_device *bq) +{ + int ret = bq2415x_exec_command(bq, BQ2415X_REVISION); + int chip = bq2415x_detect_chip(bq); + if (ret < 0 || chip < 0) + return -1; + + switch (chip) { + case BQ24150: + case BQ24150A: + case BQ24151: + case BQ24151A: + case BQ24152: + if (ret >= 0 && ret <= 3) + return ret; + else + return -1; + + case BQ24153: + case BQ24153A: + case BQ24156: + case BQ24156A: + case BQ24158: + if (ret == 3) + return 0; + else if (ret == 1) + return 1; + else + return -1; + + case BQ24155: + if (ret == 3) + return 3; + else + return -1; + + case BQUNKNOWN: + return -1; + } + + return -1; +} + +/* return chip vender code */ +static int bq2415x_get_vender_code(struct bq2415x_device *bq) +{ + int ret = bq2415x_exec_command(bq, BQ2415X_VENDER_CODE); + if (ret < 0) + return 0; + else /* convert to binary */ + return (ret & 0x1) + + ((ret >> 1) & 0x1) * 10 + + ((ret >> 2) & 0x1) * 100; +} + +/* reset all chip registers to default state */ +static void bq2415x_reset_chip(struct bq2415x_device *bq) +{ + bq2415x_i2c_write(bq, BQ2415X_REG_CURRENT, BQ2415X_RESET_CURRENT); + bq2415x_i2c_write(bq, BQ2415X_REG_VOLTAGE, BQ2415X_RESET_VOLTAGE); + bq2415x_i2c_write(bq, BQ2415X_REG_CONTROL, BQ2415X_RESET_CONTROL); + bq2415x_i2c_write(bq, BQ2415X_REG_STATUS, BQ2415X_RESET_STATUS); + bq->timer_error = NULL; +} + +/**** properties functions ****/ + +/* set current limit in mA */ +static int bq2415x_set_current_limit(struct bq2415x_device *bq, int mA) +{ + int val; + if (mA <= 100) + val = 0; + else if (mA <= 500) + val = 1; + else if (mA <= 800) + val = 2; + else + val = 3; + return bq2415x_i2c_write_mask(bq, BQ2415X_REG_CONTROL, val, + BQ2415X_MASK_LIMIT, BQ2415X_SHIFT_LIMIT); +} + +/* get current limit in mA */ +static int bq2415x_get_current_limit(struct bq2415x_device *bq) +{ + int ret = bq2415x_i2c_read_mask(bq, BQ2415X_REG_CONTROL, + BQ2415X_MASK_LIMIT, BQ2415X_SHIFT_LIMIT); + if (ret < 0) + return ret; + else if (ret == 0) + return 100; + else if (ret == 1) + return 500; + else if (ret == 2) + return 800; + else if (ret == 3) + return 1800; + else + return -EINVAL; +} + +/* set weak battery voltage in mV */ +static int bq2415x_set_weak_battery_voltage(struct bq2415x_device *bq, int mV) +{ + /* round to 100mV */ + int val; + if (mV <= 3400 + 50) + val = 0; + else if (mV <= 3500 + 50) + val = 1; + else if (mV <= 3600 + 50) + val = 2; + else + val = 3; + return bq2415x_i2c_write_mask(bq, BQ2415X_REG_CONTROL, val, + BQ2415X_MASK_VLOWV, BQ2415X_SHIFT_VLOWV); +} + +/* get weak battery voltage in mV */ +static int bq2415x_get_weak_battery_voltage(struct bq2415x_device *bq) +{ + int ret = bq2415x_i2c_read_mask(bq, BQ2415X_REG_CONTROL, + BQ2415X_MASK_VLOWV, BQ2415X_SHIFT_VLOWV); + if (ret < 0) + return ret; + else + return 100 * (34 + ret); +} + +/* set battery regulation voltage in mV */ +static int bq2415x_set_battery_regulation_voltage(struct bq2415x_device *bq, + int mV) +{ + int val = (mV/10 - 350) / 2; + + if (val < 0) + val = 0; + else if (val > 94) /* FIXME: Max is 94 or 122 ? Set max value ? */ + return -EINVAL; + + return bq2415x_i2c_write_mask(bq, BQ2415X_REG_VOLTAGE, val, + BQ2415X_MASK_VO, BQ2415X_SHIFT_VO); +} + +/* get battery regulation voltage in mV */ +static int bq2415x_get_battery_regulation_voltage(struct bq2415x_device *bq) +{ + int ret = bq2415x_i2c_read_mask(bq, BQ2415X_REG_VOLTAGE, + BQ2415X_MASK_VO, BQ2415X_SHIFT_VO); + if (ret < 0) + return ret; + else + return 10 * (350 + 2*ret); +} + +/* set charge current in mA (platform data must provide resistor sense) */ +static int bq2415x_set_charge_current(struct bq2415x_device *bq, int mA) +{ + int val; + if (bq->init_data.resistor_sense <= 0) + return -ENOSYS; + + val = (mA * bq->init_data.resistor_sense - 37400) / 6800; + + if (val < 0) + val = 0; + else if (val > 7) + val = 7; + + return bq2415x_i2c_write_mask(bq, BQ2415X_REG_CURRENT, val, + BQ2415X_MASK_VI_CHRG | BQ2415X_MASK_RESET, + BQ2415X_SHIFT_VI_CHRG); +} + +/* get charge current in mA (platform data must provide resistor sense) */ +static int bq2415x_get_charge_current(struct bq2415x_device *bq) +{ + int ret; + if (bq->init_data.resistor_sense <= 0) + return -ENOSYS; + + ret = bq2415x_i2c_read_mask(bq, BQ2415X_REG_CURRENT, + BQ2415X_MASK_VI_CHRG, BQ2415X_SHIFT_VI_CHRG); + if (ret < 0) + return ret; + else + return (37400 + 6800*ret) / bq->init_data.resistor_sense; +} + +/* set termination current in mA (platform data must provide resistor sense) */ +static int bq2415x_set_termination_current(struct bq2415x_device *bq, int mA) +{ + int val; + if (bq->init_data.resistor_sense <= 0) + return -ENOSYS; + + val = (mA * bq->init_data.resistor_sense - 3400) / 3400; + + if (val < 0) + val = 0; + else if (val > 7) + val = 7; + + return bq2415x_i2c_write_mask(bq, BQ2415X_REG_CURRENT, val, + BQ2415X_MASK_VI_TERM | BQ2415X_MASK_RESET, + BQ2415X_SHIFT_VI_TERM); +} + +/* get termination current in mA (platform data must provide resistor sense) */ +static int bq2415x_get_termination_current(struct bq2415x_device *bq) +{ + int ret; + if (bq->init_data.resistor_sense <= 0) + return -ENOSYS; + + ret = bq2415x_i2c_read_mask(bq, BQ2415X_REG_CURRENT, + BQ2415X_MASK_VI_TERM, BQ2415X_SHIFT_VI_TERM); + if (ret < 0) + return ret; + else + return (3400 + 3400*ret) / bq->init_data.resistor_sense; +} + +/* set default value of property */ +#define bq2415x_set_default_value(bq, prop) \ + do { \ + int ret = 0; \ + if (bq->init_data.prop != -1) \ + ret = bq2415x_set_##prop(bq, bq->init_data.prop); \ + if (ret < 0) \ + return ret; \ + } while (0) + +/* set default values of all properties */ +static int bq2415x_set_defaults(struct bq2415x_device *bq) +{ + bq2415x_exec_command(bq, BQ2415X_BOOST_MODE_DISABLE); + bq2415x_exec_command(bq, BQ2415X_CHARGER_DISABLE); + bq2415x_exec_command(bq, BQ2415X_CHARGE_TERMINATION_DISABLE); + bq2415x_set_default_value(bq, current_limit); + bq2415x_set_default_value(bq, weak_battery_voltage); + bq2415x_set_default_value(bq, battery_regulation_voltage); + if (bq->init_data.resistor_sense > 0) { + bq2415x_set_default_value(bq, charge_current); + bq2415x_set_default_value(bq, termination_current); + bq2415x_exec_command(bq, BQ2415X_CHARGE_TERMINATION_ENABLE); + } + bq2415x_exec_command(bq, BQ2415X_CHARGER_ENABLE); + return 0; +} + +/**** charger mode functions ****/ + +/* set charger mode */ +static int bq2415x_set_mode(struct bq2415x_device *bq, enum bq2415x_mode mode) +{ + int ret = 0; + int charger = 0; + int boost = 0; + + if (mode == BQ2415X_MODE_HOST_CHARGER || + mode == BQ2415X_MODE_DEDICATED_CHARGER) + charger = 1; + + if (mode == BQ2415X_MODE_BOOST) + boost = 1; + + if (!charger) + ret = bq2415x_exec_command(bq, BQ2415X_CHARGER_DISABLE); + + if (!boost) + ret = bq2415x_exec_command(bq, BQ2415X_BOOST_MODE_DISABLE); + + if (ret < 0) + return ret; + + switch (mode) { + case BQ2415X_MODE_NONE: + dev_dbg(bq->dev, "changing mode to: N/A\n"); + ret = bq2415x_set_current_limit(bq, 100); + break; + case BQ2415X_MODE_HOST_CHARGER: + dev_dbg(bq->dev, "changing mode to: Host/HUB charger\n"); + ret = bq2415x_set_current_limit(bq, 500); + break; + case BQ2415X_MODE_DEDICATED_CHARGER: + dev_dbg(bq->dev, "changing mode to: Dedicated charger\n"); + ret = bq2415x_set_current_limit(bq, 1800); + break; + case BQ2415X_MODE_BOOST: /* Boost mode */ + dev_dbg(bq->dev, "changing mode to: Boost\n"); + ret = bq2415x_set_current_limit(bq, 100); + break; + } + + if (ret < 0) + return ret; + + if (charger) + ret = bq2415x_exec_command(bq, BQ2415X_CHARGER_ENABLE); + else if (boost) + ret = bq2415x_exec_command(bq, BQ2415X_BOOST_MODE_ENABLE); + + if (ret < 0) + return ret; + + bq2415x_set_default_value(bq, weak_battery_voltage); + bq2415x_set_default_value(bq, battery_regulation_voltage); + + bq->mode = mode; + sysfs_notify(&bq->charger.dev->kobj, NULL, "mode"); + + return 0; + +} + +/* hook function called by other driver which set reported mode */ +static void bq2415x_hook_function(enum bq2415x_mode mode, void *data) +{ + struct bq2415x_device *bq = data; + + if (!bq) + return; + + dev_dbg(bq->dev, "hook function was called\n"); + bq->reported_mode = mode; + + /* if automode is not enabled do not tell about reported_mode */ + if (bq->automode < 1) + return; + + sysfs_notify(&bq->charger.dev->kobj, NULL, "reported_mode"); + bq2415x_set_mode(bq, bq->reported_mode); + +} + +/**** timer functions ****/ + +/* enable/disable auto resetting chip timer */ +static void bq2415x_set_autotimer(struct bq2415x_device *bq, int state) +{ + mutex_lock(&bq2415x_timer_mutex); + + if (bq->autotimer == state) { + mutex_unlock(&bq2415x_timer_mutex); + return; + } + + bq->autotimer = state; + + if (state) { + schedule_delayed_work(&bq->work, BQ2415X_TIMER_TIMEOUT * HZ); + bq2415x_exec_command(bq, BQ2415X_TIMER_RESET); + bq->timer_error = NULL; + } else { + cancel_delayed_work_sync(&bq->work); + } + + mutex_unlock(&bq2415x_timer_mutex); +} + +/* called by bq2415x_timer_work on timer error */ +static void bq2415x_timer_error(struct bq2415x_device *bq, const char *msg) +{ + bq->timer_error = msg; + sysfs_notify(&bq->charger.dev->kobj, NULL, "timer"); + dev_err(bq->dev, "%s\n", msg); + if (bq->automode > 0) + bq->automode = 0; + bq2415x_set_mode(bq, BQ2415X_MODE_NONE); + bq2415x_set_autotimer(bq, 0); +} + +/* delayed work function for auto resetting chip timer */ +static void bq2415x_timer_work(struct work_struct *work) +{ + struct bq2415x_device *bq = container_of(work, struct bq2415x_device, + work.work); + int ret, error, boost; + + if (!bq->autotimer) + return; + + ret = bq2415x_exec_command(bq, BQ2415X_TIMER_RESET); + if (ret < 0) { + bq2415x_timer_error(bq, "Resetting timer failed"); + return; + } + + boost = bq2415x_exec_command(bq, BQ2415X_BOOST_MODE_STATUS); + if (boost < 0) { + bq2415x_timer_error(bq, "Unknown error"); + return; + } + + error = bq2415x_exec_command(bq, BQ2415X_FAULT_STATUS); + if (error < 0) { + bq2415x_timer_error(bq, "Unknown error"); + return; + } + + if (boost) { + switch (error) { + /* Non fatal errors, chip is OK */ + case 0: /* No error */ + break; + case 6: /* Timer expired */ + dev_err(bq->dev, "Timer expired\n"); + break; + case 3: /* Battery voltage too low */ + dev_err(bq->dev, "Battery voltage to low\n"); + break; + + /* Fatal errors, disable and reset chip */ + case 1: /* Overvoltage protection (chip fried) */ + bq2415x_timer_error(bq, + "Overvoltage protection (chip fried)"); + return; + case 2: /* Overload */ + bq2415x_timer_error(bq, "Overload"); + return; + case 4: /* Battery overvoltage protection */ + bq2415x_timer_error(bq, + "Battery overvoltage protection"); + return; + case 5: /* Thermal shutdown (too hot) */ + bq2415x_timer_error(bq, + "Thermal shutdown (too hot)"); + return; + case 7: /* N/A */ + bq2415x_timer_error(bq, "Unknown error"); + return; + } + } else { + switch (error) { + /* Non fatal errors, chip is OK */ + case 0: /* No error */ + break; + case 2: /* Sleep mode */ + dev_err(bq->dev, "Sleep mode\n"); + break; + case 3: /* Poor input source */ + dev_err(bq->dev, "Poor input source\n"); + break; + case 6: /* Timer expired */ + dev_err(bq->dev, "Timer expired\n"); + break; + case 7: /* No battery */ + dev_err(bq->dev, "No battery\n"); + break; + + /* Fatal errors, disable and reset chip */ + case 1: /* Overvoltage protection (chip fried) */ + bq2415x_timer_error(bq, + "Overvoltage protection (chip fried)"); + return; + case 4: /* Battery overvoltage protection */ + bq2415x_timer_error(bq, + "Battery overvoltage protection"); + return; + case 5: /* Thermal shutdown (too hot) */ + bq2415x_timer_error(bq, + "Thermal shutdown (too hot)"); + return; + } + } + + schedule_delayed_work(&bq->work, BQ2415X_TIMER_TIMEOUT * HZ); +} + +/**** power supply interface code ****/ + +static enum power_supply_property bq2415x_power_supply_props[] = { + /* TODO: maybe add more power supply properties */ + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_MODEL_NAME, +}; + +static int bq2415x_power_supply_get_property(struct power_supply *psy, + enum power_supply_property psp, union power_supply_propval *val) +{ + struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, + charger); + int ret; + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + ret = bq2415x_exec_command(bq, BQ2415X_CHARGE_STATUS); + if (ret < 0) + return ret; + else if (ret == 0) /* Ready */ + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + else if (ret == 1) /* Charge in progress */ + val->intval = POWER_SUPPLY_STATUS_CHARGING; + else if (ret == 2) /* Charge done */ + val->intval = POWER_SUPPLY_STATUS_FULL; + else + val->intval = POWER_SUPPLY_STATUS_UNKNOWN; + break; + case POWER_SUPPLY_PROP_MODEL_NAME: + val->strval = bq->model; + break; + default: + return -EINVAL; + } + return 0; +} + +static int bq2415x_power_supply_init(struct bq2415x_device *bq) +{ + int ret; + int chip; + char revstr[8]; + + bq->charger.name = bq->name; + bq->charger.type = POWER_SUPPLY_TYPE_USB; + bq->charger.properties = bq2415x_power_supply_props; + bq->charger.num_properties = ARRAY_SIZE(bq2415x_power_supply_props); + bq->charger.get_property = bq2415x_power_supply_get_property; + + ret = bq2415x_detect_chip(bq); + if (ret < 0) + chip = BQUNKNOWN; + else + chip = ret; + + ret = bq2415x_detect_revision(bq); + if (ret < 0) + strcpy(revstr, "unknown"); + else + sprintf(revstr, "1.%d", ret); + + bq->model = kasprintf(GFP_KERNEL, + "chip %s, revision %s, vender code %.3d", + bq2415x_chip_name[chip], revstr, + bq2415x_get_vender_code(bq)); + if (!bq->model) { + dev_err(bq->dev, "failed to allocate model name\n"); + return -ENOMEM; + } + + ret = power_supply_register(bq->dev, &bq->charger); + if (ret) { + kfree(bq->model); + return ret; + } + + return 0; +} + +static void bq2415x_power_supply_exit(struct bq2415x_device *bq) +{ + bq->autotimer = 0; + if (bq->automode > 0) + bq->automode = 0; + cancel_delayed_work_sync(&bq->work); + power_supply_unregister(&bq->charger); + kfree(bq->model); +} + +/**** additional sysfs entries for power supply interface ****/ + +/* show *_status entries */ +static ssize_t bq2415x_sysfs_show_status(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct power_supply *psy = dev_get_drvdata(dev); + struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, + charger); + enum bq2415x_command command; + int ret; + + if (strcmp(attr->attr.name, "otg_status") == 0) + command = BQ2415X_OTG_STATUS; + else if (strcmp(attr->attr.name, "charge_status") == 0) + command = BQ2415X_CHARGE_STATUS; + else if (strcmp(attr->attr.name, "boost_status") == 0) + command = BQ2415X_BOOST_STATUS; + else if (strcmp(attr->attr.name, "fault_status") == 0) + command = BQ2415X_FAULT_STATUS; + else + return -EINVAL; + + ret = bq2415x_exec_command(bq, command); + if (ret < 0) + return ret; + else + return sprintf(buf, "%d\n", ret); +} + +/* set timer entry: + auto - enable auto mode + off - disable auto mode + (other values) - reset chip timer +*/ +static ssize_t bq2415x_sysfs_set_timer(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct power_supply *psy = dev_get_drvdata(dev); + struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, + charger); + int ret = 0; + + if (strncmp(buf, "auto", 4) == 0) + bq2415x_set_autotimer(bq, 1); + else if (strncmp(buf, "off", 3) == 0) + bq2415x_set_autotimer(bq, 0); + else + ret = bq2415x_exec_command(bq, BQ2415X_TIMER_RESET); + + if (ret < 0) + return ret; + else + return count; +} + +/* show timer entry (auto or off) */ +static ssize_t bq2415x_sysfs_show_timer(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct power_supply *psy = dev_get_drvdata(dev); + struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, + charger); + + if (bq->timer_error) + return sprintf(buf, "%s\n", bq->timer_error); + + if (bq->autotimer) + return sprintf(buf, "auto\n"); + else + return sprintf(buf, "off\n"); +} + +/* set mode entry: + auto - if automode is supported, enable it and set mode to reported + none - disable charger and boost mode + host - charging mode for host/hub chargers (current limit 500mA) + dedicated - charging mode for dedicated chargers (unlimited current limit) + boost - disable charger and enable boost mode +*/ +static ssize_t bq2415x_sysfs_set_mode(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct power_supply *psy = dev_get_drvdata(dev); + struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, + charger); + enum bq2415x_mode mode; + int ret = 0; + + if (strncmp(buf, "auto", 4) == 0) { + if (bq->automode < 0) + return -ENOSYS; + bq->automode = 1; + mode = bq->reported_mode; + } else if (strncmp(buf, "none", 4) == 0) { + if (bq->automode > 0) + bq->automode = 0; + mode = BQ2415X_MODE_NONE; + } else if (strncmp(buf, "host", 4) == 0) { + if (bq->automode > 0) + bq->automode = 0; + mode = BQ2415X_MODE_HOST_CHARGER; + } else if (strncmp(buf, "dedicated", 9) == 0) { + if (bq->automode > 0) + bq->automode = 0; + mode = BQ2415X_MODE_DEDICATED_CHARGER; + } else if (strncmp(buf, "boost", 5) == 0) { + if (bq->automode > 0) + bq->automode = 0; + mode = BQ2415X_MODE_BOOST; + } else if (strncmp(buf, "reset", 5) == 0) { + bq2415x_reset_chip(bq); + bq2415x_set_defaults(bq); + if (bq->automode <= 0) + return count; + bq->automode = 1; + mode = bq->reported_mode; + } else + return -EINVAL; + + ret = bq2415x_set_mode(bq, mode); + if (ret < 0) + return ret; + else + return count; +} + +/* show mode entry (auto, none, host, dedicated or boost) */ +static ssize_t bq2415x_sysfs_show_mode(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct power_supply *psy = dev_get_drvdata(dev); + struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, + charger); + ssize_t ret = 0; + + if (bq->automode > 0) + ret += sprintf(buf+ret, "auto ("); + + switch (bq->mode) { + case BQ2415X_MODE_NONE: + ret += sprintf(buf+ret, "none"); + break; + case BQ2415X_MODE_HOST_CHARGER: + ret += sprintf(buf+ret, "host"); + break; + case BQ2415X_MODE_DEDICATED_CHARGER: + ret += sprintf(buf+ret, "dedicated"); + break; + case BQ2415X_MODE_BOOST: + ret += sprintf(buf+ret, "boost"); + break; + } + + if (bq->automode > 0) + ret += sprintf(buf+ret, ")"); + + ret += sprintf(buf+ret, "\n"); + return ret; +} + +/* show reported_mode entry (none, host, dedicated or boost) */ +static ssize_t bq2415x_sysfs_show_reported_mode(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct power_supply *psy = dev_get_drvdata(dev); + struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, + charger); + + if (bq->automode < 0) + return -EINVAL; + + switch (bq->reported_mode) { + case BQ2415X_MODE_NONE: + return sprintf(buf, "none\n"); + case BQ2415X_MODE_HOST_CHARGER: + return sprintf(buf, "host\n"); + case BQ2415X_MODE_DEDICATED_CHARGER: + return sprintf(buf, "dedicated\n"); + case BQ2415X_MODE_BOOST: + return sprintf(buf, "boost\n"); + } + + return -EINVAL; +} + +/* directly set raw value to chip register, format: 'register value' */ +static ssize_t bq2415x_sysfs_set_registers(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct power_supply *psy = dev_get_drvdata(dev); + struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, + charger); + ssize_t ret = 0; + unsigned int reg; + unsigned int val; + + if (sscanf(buf, "%x %x", ®, &val) != 2) + return -EINVAL; + + if (reg > 4 || val > 255) + return -EINVAL; + + ret = bq2415x_i2c_write(bq, reg, val); + if (ret < 0) + return ret; + else + return count; +} + +/* print value of chip register, format: 'register=value' */ +static ssize_t bq2415x_sysfs_print_reg(struct bq2415x_device *bq, + u8 reg, char *buf) +{ + int ret = bq2415x_i2c_read(bq, reg); + if (ret < 0) + return sprintf(buf, "%#.2x=error %d\n", reg, ret); + else + return sprintf(buf, "%#.2x=%#.2x\n", reg, ret); +} + +/* show all raw values of chip register, format per line: 'register=value' */ +static ssize_t bq2415x_sysfs_show_registers(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct power_supply *psy = dev_get_drvdata(dev); + struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, + charger); + ssize_t ret = 0; + + ret += bq2415x_sysfs_print_reg(bq, BQ2415X_REG_STATUS, buf+ret); + ret += bq2415x_sysfs_print_reg(bq, BQ2415X_REG_CONTROL, buf+ret); + ret += bq2415x_sysfs_print_reg(bq, BQ2415X_REG_VOLTAGE, buf+ret); + ret += bq2415x_sysfs_print_reg(bq, BQ2415X_REG_VENDER, buf+ret); + ret += bq2415x_sysfs_print_reg(bq, BQ2415X_REG_CURRENT, buf+ret); + return ret; +} + +/* set current and voltage limit entries (in mA or mV) */ +static ssize_t bq2415x_sysfs_set_limit(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct power_supply *psy = dev_get_drvdata(dev); + struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, + charger); + long val; + int ret; + + if (kstrtol(buf, 10, &val) < 0) + return -EINVAL; + + if (strcmp(attr->attr.name, "current_limit") == 0) + ret = bq2415x_set_current_limit(bq, val); + else if (strcmp(attr->attr.name, "weak_battery_voltage") == 0) + ret = bq2415x_set_weak_battery_voltage(bq, val); + else if (strcmp(attr->attr.name, "battery_regulation_voltage") == 0) + ret = bq2415x_set_battery_regulation_voltage(bq, val); + else if (strcmp(attr->attr.name, "charge_current") == 0) + ret = bq2415x_set_charge_current(bq, val); + else if (strcmp(attr->attr.name, "termination_current") == 0) + ret = bq2415x_set_termination_current(bq, val); + else + return -EINVAL; + + if (ret < 0) + return ret; + else + return count; +} + +/* show current and voltage limit entries (in mA or mV) */ +static ssize_t bq2415x_sysfs_show_limit(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct power_supply *psy = dev_get_drvdata(dev); + struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, + charger); + int ret; + + if (strcmp(attr->attr.name, "current_limit") == 0) + ret = bq2415x_get_current_limit(bq); + else if (strcmp(attr->attr.name, "weak_battery_voltage") == 0) + ret = bq2415x_get_weak_battery_voltage(bq); + else if (strcmp(attr->attr.name, "battery_regulation_voltage") == 0) + ret = bq2415x_get_battery_regulation_voltage(bq); + else if (strcmp(attr->attr.name, "charge_current") == 0) + ret = bq2415x_get_charge_current(bq); + else if (strcmp(attr->attr.name, "termination_current") == 0) + ret = bq2415x_get_termination_current(bq); + else + return -EINVAL; + + if (ret < 0) + return ret; + else + return sprintf(buf, "%d\n", ret); +} + +/* set *_enable entries */ +static ssize_t bq2415x_sysfs_set_enable(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct power_supply *psy = dev_get_drvdata(dev); + struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, + charger); + enum bq2415x_command command; + long val; + int ret; + + if (kstrtol(buf, 10, &val) < 0) + return -EINVAL; + + if (strcmp(attr->attr.name, "charge_termination_enable") == 0) + command = val ? BQ2415X_CHARGE_TERMINATION_ENABLE : + BQ2415X_CHARGE_TERMINATION_DISABLE; + else if (strcmp(attr->attr.name, "high_impedance_enable") == 0) + command = val ? BQ2415X_HIGH_IMPEDANCE_ENABLE : + BQ2415X_HIGH_IMPEDANCE_DISABLE; + else if (strcmp(attr->attr.name, "otg_pin_enable") == 0) + command = val ? BQ2415X_OTG_PIN_ENABLE : + BQ2415X_OTG_PIN_DISABLE; + else if (strcmp(attr->attr.name, "stat_pin_enable") == 0) + command = val ? BQ2415X_STAT_PIN_ENABLE : + BQ2415X_STAT_PIN_DISABLE; + else + return -EINVAL; + + ret = bq2415x_exec_command(bq, command); + if (ret < 0) + return ret; + else + return count; +} + +/* show *_enable entries */ +static ssize_t bq2415x_sysfs_show_enable(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct power_supply *psy = dev_get_drvdata(dev); + struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, + charger); + enum bq2415x_command command; + int ret; + + if (strcmp(attr->attr.name, "charge_termination_enable") == 0) + command = BQ2415X_CHARGE_TERMINATION_STATUS; + else if (strcmp(attr->attr.name, "high_impedance_enable") == 0) + command = BQ2415X_HIGH_IMPEDANCE_STATUS; + else if (strcmp(attr->attr.name, "otg_pin_enable") == 0) + command = BQ2415X_OTG_PIN_STATUS; + else if (strcmp(attr->attr.name, "stat_pin_enable") == 0) + command = BQ2415X_STAT_PIN_STATUS; + else + return -EINVAL; + + ret = bq2415x_exec_command(bq, command); + if (ret < 0) + return ret; + else + return sprintf(buf, "%d\n", ret); +} + +static DEVICE_ATTR(current_limit, S_IWUSR | S_IRUGO, + bq2415x_sysfs_show_limit, bq2415x_sysfs_set_limit); +static DEVICE_ATTR(weak_battery_voltage, S_IWUSR | S_IRUGO, + bq2415x_sysfs_show_limit, bq2415x_sysfs_set_limit); +static DEVICE_ATTR(battery_regulation_voltage, S_IWUSR | S_IRUGO, + bq2415x_sysfs_show_limit, bq2415x_sysfs_set_limit); +static DEVICE_ATTR(charge_current, S_IWUSR | S_IRUGO, + bq2415x_sysfs_show_limit, bq2415x_sysfs_set_limit); +static DEVICE_ATTR(termination_current, S_IWUSR | S_IRUGO, + bq2415x_sysfs_show_limit, bq2415x_sysfs_set_limit); + +static DEVICE_ATTR(charge_termination_enable, S_IWUSR | S_IRUGO, + bq2415x_sysfs_show_enable, bq2415x_sysfs_set_enable); +static DEVICE_ATTR(high_impedance_enable, S_IWUSR | S_IRUGO, + bq2415x_sysfs_show_enable, bq2415x_sysfs_set_enable); +static DEVICE_ATTR(otg_pin_enable, S_IWUSR | S_IRUGO, + bq2415x_sysfs_show_enable, bq2415x_sysfs_set_enable); +static DEVICE_ATTR(stat_pin_enable, S_IWUSR | S_IRUGO, + bq2415x_sysfs_show_enable, bq2415x_sysfs_set_enable); + +static DEVICE_ATTR(reported_mode, S_IRUGO, + bq2415x_sysfs_show_reported_mode, NULL); +static DEVICE_ATTR(mode, S_IWUSR | S_IRUGO, + bq2415x_sysfs_show_mode, bq2415x_sysfs_set_mode); +static DEVICE_ATTR(timer, S_IWUSR | S_IRUGO, + bq2415x_sysfs_show_timer, bq2415x_sysfs_set_timer); + +static DEVICE_ATTR(registers, S_IWUSR | S_IRUGO, + bq2415x_sysfs_show_registers, bq2415x_sysfs_set_registers); + +static DEVICE_ATTR(otg_status, S_IRUGO, bq2415x_sysfs_show_status, NULL); +static DEVICE_ATTR(charge_status, S_IRUGO, bq2415x_sysfs_show_status, NULL); +static DEVICE_ATTR(boost_status, S_IRUGO, bq2415x_sysfs_show_status, NULL); +static DEVICE_ATTR(fault_status, S_IRUGO, bq2415x_sysfs_show_status, NULL); + +static struct attribute *bq2415x_sysfs_attributes[] = { + &dev_attr_current_limit.attr, + &dev_attr_weak_battery_voltage.attr, + &dev_attr_battery_regulation_voltage.attr, + &dev_attr_charge_current.attr, + &dev_attr_termination_current.attr, + + &dev_attr_charge_termination_enable.attr, + &dev_attr_high_impedance_enable.attr, + &dev_attr_otg_pin_enable.attr, + &dev_attr_stat_pin_enable.attr, + + &dev_attr_reported_mode.attr, + &dev_attr_mode.attr, + &dev_attr_timer.attr, + + &dev_attr_registers.attr, + + &dev_attr_otg_status.attr, + &dev_attr_charge_status.attr, + &dev_attr_boost_status.attr, + &dev_attr_fault_status.attr, + NULL, +}; + +static const struct attribute_group bq2415x_sysfs_attr_group = { + .attrs = bq2415x_sysfs_attributes, +}; + +static int bq2415x_sysfs_init(struct bq2415x_device *bq) +{ + return sysfs_create_group(&bq->charger.dev->kobj, + &bq2415x_sysfs_attr_group); +} + +static void bq2415x_sysfs_exit(struct bq2415x_device *bq) +{ + sysfs_remove_group(&bq->charger.dev->kobj, &bq2415x_sysfs_attr_group); +} + +/* main bq2415x probe function */ +static int bq2415x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret; + int num; + char *name; + struct bq2415x_device *bq; + + if (!client->dev.platform_data) { + dev_err(&client->dev, "platform data not set\n"); + return -ENODEV; + } + + /* Get new ID for the new device */ + ret = idr_pre_get(&bq2415x_id, GFP_KERNEL); + if (ret == 0) + return -ENOMEM; + + mutex_lock(&bq2415x_id_mutex); + ret = idr_get_new(&bq2415x_id, client, &num); + mutex_unlock(&bq2415x_id_mutex); + + if (ret < 0) + return ret; + + name = kasprintf(GFP_KERNEL, "%s-%d", id->name, num); + if (!name) { + dev_err(&client->dev, "failed to allocate device name\n"); + ret = -ENOMEM; + goto error_1; + } + + bq = kzalloc(sizeof(*bq), GFP_KERNEL); + if (!bq) { + dev_err(&client->dev, "failed to allocate device data\n"); + ret = -ENOMEM; + goto error_2; + } + + i2c_set_clientdata(client, bq); + + bq->id = num; + bq->dev = &client->dev; + bq->chip = id->driver_data; + bq->name = name; + bq->mode = BQ2415X_MODE_NONE; + bq->reported_mode = BQ2415X_MODE_NONE; + bq->autotimer = 0; + bq->automode = 0; + + memcpy(&bq->init_data, client->dev.platform_data, + sizeof(bq->init_data)); + + bq2415x_reset_chip(bq); + + ret = bq2415x_power_supply_init(bq); + if (ret) { + dev_err(bq->dev, "failed to register power supply: %d\n", ret); + goto error_3; + } + + ret = bq2415x_sysfs_init(bq); + if (ret) { + dev_err(bq->dev, "failed to create sysfs entries: %d\n", ret); + goto error_4; + } + + ret = bq2415x_set_defaults(bq); + if (ret) { + dev_err(bq->dev, "failed to set default values: %d\n", ret); + goto error_5; + } + + if (bq->init_data.set_mode_hook) { + if (bq->init_data.set_mode_hook( + bq2415x_hook_function, bq)) { + bq->automode = 1; + bq2415x_set_mode(bq, bq->reported_mode); + dev_info(bq->dev, "automode enabled\n"); + } else { + bq->automode = -1; + dev_info(bq->dev, "automode failed\n"); + } + } else { + bq->automode = -1; + dev_info(bq->dev, "automode not supported\n"); + } + + INIT_DELAYED_WORK(&bq->work, bq2415x_timer_work); + bq2415x_set_autotimer(bq, 1); + + dev_info(bq->dev, "driver registered\n"); + return 0; + +error_5: + bq2415x_sysfs_exit(bq); +error_4: + bq2415x_power_supply_exit(bq); +error_3: + kfree(bq); +error_2: + kfree(name); +error_1: + mutex_lock(&bq2415x_id_mutex); + idr_remove(&bq2415x_id, num); + mutex_unlock(&bq2415x_id_mutex); + + return ret; +} + +/* main bq2415x remove function */ + +static int bq2415x_remove(struct i2c_client *client) +{ + struct bq2415x_device *bq = i2c_get_clientdata(client); + + if (bq->init_data.set_mode_hook) + bq->init_data.set_mode_hook(NULL, NULL); + + bq2415x_sysfs_exit(bq); + bq2415x_power_supply_exit(bq); + + bq2415x_reset_chip(bq); + + mutex_lock(&bq2415x_id_mutex); + idr_remove(&bq2415x_id, bq->id); + mutex_unlock(&bq2415x_id_mutex); + + dev_info(bq->dev, "driver unregistered\n"); + + kfree(bq->name); + kfree(bq); + + return 0; +} + +static const struct i2c_device_id bq2415x_i2c_id_table[] = { + { "bq2415x", BQUNKNOWN }, + { "bq24150", BQ24150 }, + { "bq24150a", BQ24150A }, + { "bq24151", BQ24151 }, + { "bq24151a", BQ24151A }, + { "bq24152", BQ24152 }, + { "bq24153", BQ24153 }, + { "bq24153a", BQ24153A }, + { "bq24155", BQ24155 }, + { "bq24156", BQ24156 }, + { "bq24156a", BQ24156A }, + { "bq24158", BQ24158 }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, bq2415x_i2c_id_table); + +static struct i2c_driver bq2415x_driver = { + .driver = { + .name = "bq2415x-charger", + }, + .probe = bq2415x_probe, + .remove = bq2415x_remove, + .id_table = bq2415x_i2c_id_table, +}; + +static int __init bq2415x_init(void) +{ + return i2c_add_driver(&bq2415x_driver); +} +module_init(bq2415x_init); + +static void __exit bq2415x_exit(void) +{ + i2c_del_driver(&bq2415x_driver); +} +module_exit(bq2415x_exit); + +MODULE_AUTHOR("Pali Rohár "); +MODULE_DESCRIPTION("bq2415x charger driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/power/bq2415x_charger.h b/include/linux/power/bq2415x_charger.h new file mode 100644 index 00000000000..fb6bf4d8aea --- /dev/null +++ b/include/linux/power/bq2415x_charger.h @@ -0,0 +1,90 @@ +/* + bq2415x_charger.h - bq2415x charger driver + Copyright (C) 2011-2012 Pali Rohár + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef BQ2415X_CHARGER_H +#define BQ2415X_CHARGER_H + +/* + This is platform data for bq2415x chip. It contains default board voltages + and currents which can be also later configured via sysfs. If value is -1 + then default chip value (specified in datasheet) will be used. + + Value resistor_sense is needed for for configuring charge and termination + current. It it is less or equal to zero, configuring charge and termination + current will not be possible. + + Function set_mode_hook is needed for automode (setting correct current limit + when charger is connected/disconnected or setting boost mode). When is NULL, + automode function is disabled. When is not NULL, it must have this prototype: + + int (*set_mode_hook)( + void (*hook)(enum bq2415x_mode mode, void *data), + void *data) + + hook is hook function (see below) and data is pointer to driver private data + + bq2415x driver will call it as: + + platform_data->set_mode_hook(bq2415x_hook_function, bq2415x_device); + + Board/platform function set_mode_hook return non zero value when hook + function was successful registered. Platform code should call that hook + function (which get from pointer, with data) every time when charger was + connected/disconnected or require to enable boost mode. bq2415x driver then + will set correct current limit, enable/disable charger or boost mode. + + Hook function has this prototype: + + void hook(enum bq2415x_mode mode, void *data); + + mode is bq2415x mode (charger or boost) + data is pointer to driver private data (which get from set_charger_type_hook) + + When bq driver is being unloaded, it call function: + + platform_data->set_mode_hook(NULL, NULL); + + (hook function and driver private data are NULL) + + After that board/platform code must not call driver hook function! It is + possible that pointer to hook function will not be valid and calling will + cause undefined result. + +*/ + +/* Supported modes with maximal current limit */ +enum bq2415x_mode { + BQ2415X_MODE_NONE, /* unknown or no charger (100mA) */ + BQ2415X_MODE_HOST_CHARGER, /* usb host/hub charger (500mA) */ + BQ2415X_MODE_DEDICATED_CHARGER, /* dedicated charger (unlimited) */ + BQ2415X_MODE_BOOST, /* boost mode (charging disabled) */ +}; + +struct bq2415x_platform_data { + int current_limit; /* mA */ + int weak_battery_voltage; /* mV */ + int battery_regulation_voltage; /* mV */ + int charge_current; /* mA */ + int termination_current; /* mA */ + int resistor_sense; /* m ohm */ + int (*set_mode_hook)(void (*hook)(enum bq2415x_mode mode, void *data), + void *data); +}; + +#endif -- cgit v1.2.3 From 7ad85830736f3e0d854becf7621eba3a0c926d13 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Sun, 18 Nov 2012 15:48:52 -0800 Subject: bq2415x_charger: Add Kconfig/Makefile entries This commit adds Kconfig and Makefile entries so that we could actually build the driver. Signed-off-by: Anton Vorontsov --- drivers/power/Kconfig | 10 ++++++++++ drivers/power/Makefile | 1 + 2 files changed, 11 insertions(+) diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 5c3237f601d..523a58014ed 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -322,6 +322,16 @@ config CHARGER_MAX8998 Say Y to enable support for the battery charger control sysfs and platform data of MAX8998/LP3974 PMICs. +config CHARGER_BQ2415X + tristate "TI BQ2415x battery charger driver" + depends on I2C + help + Say Y to enable support for the TI BQ2415x battery charger + PMICs. + + You'll need this driver to charge batteries on e.g. Nokia + RX-51/N900. + config CHARGER_SMB347 tristate "Summit Microelectronics SMB347 Battery Charger" depends on I2C diff --git a/drivers/power/Makefile b/drivers/power/Makefile index bc81fe2112c..74dfd957015 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -48,5 +48,6 @@ obj-$(CONFIG_CHARGER_GPIO) += gpio-charger.o obj-$(CONFIG_CHARGER_MANAGER) += charger-manager.o obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o +obj-$(CONFIG_CHARGER_BQ2415X) += bq2415x_charger.o obj-$(CONFIG_POWER_AVS) += avs/ obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o -- cgit v1.2.3 From 6c47a3e00c6e4f3cdac7566c1480de34d9e32e07 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Sun, 18 Nov 2012 15:35:32 -0800 Subject: bq2415x_charger: Fix style issues I hate doing these style fixups myself, but I also hate inconsitent code. Normally I just ask to resubmit the patch with the issues fixed, but N900 is special: I have a selfish interest in it. :) Signed-off-by: Anton Vorontsov --- drivers/power/bq2415x_charger.c | 268 +++++++++++++++++++--------------- include/linux/power/bq2415x_charger.h | 131 +++++++++-------- 2 files changed, 215 insertions(+), 184 deletions(-) diff --git a/drivers/power/bq2415x_charger.c b/drivers/power/bq2415x_charger.c index 017b3c27f76..ee842b37f46 100644 --- a/drivers/power/bq2415x_charger.c +++ b/drivers/power/bq2415x_charger.c @@ -1,31 +1,32 @@ /* - bq2415x_charger.c - bq2415x charger driver - Copyright (C) 2011-2012 Pali Rohár - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ + * bq2415x charger driver + * + * Copyright (C) 2011-2012 Pali Rohár + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ /* - Datasheets: - http://www.ti.com/product/bq24150 - http://www.ti.com/product/bq24150a - http://www.ti.com/product/bq24152 - http://www.ti.com/product/bq24153 - http://www.ti.com/product/bq24153a - http://www.ti.com/product/bq24155 -*/ + * Datasheets: + * http://www.ti.com/product/bq24150 + * http://www.ti.com/product/bq24150a + * http://www.ti.com/product/bq24152 + * http://www.ti.com/product/bq24153 + * http://www.ti.com/product/bq24153a + * http://www.ti.com/product/bq24155 + */ #include #include @@ -222,7 +223,7 @@ static int bq2415x_i2c_read(struct bq2415x_device *bq, u8 reg) /* read value from register, apply mask and right shift it */ static int bq2415x_i2c_read_mask(struct bq2415x_device *bq, u8 reg, - u8 mask, u8 shift) + u8 mask, u8 shift) { int ret; @@ -232,8 +233,7 @@ static int bq2415x_i2c_read_mask(struct bq2415x_device *bq, u8 reg, ret = bq2415x_i2c_read(bq, reg); if (ret < 0) return ret; - else - return (ret & mask) >> shift; + return (ret & mask) >> shift; } /* read value from register and return one specified bit */ @@ -241,8 +241,7 @@ static int bq2415x_i2c_read_bit(struct bq2415x_device *bq, u8 reg, u8 bit) { if (bit > 8) return -EINVAL; - else - return bq2415x_i2c_read_mask(bq, reg, BIT(bit), bit); + return bq2415x_i2c_read_mask(bq, reg, BIT(bit), bit); } /**** i2c write functions ****/ @@ -278,7 +277,7 @@ static int bq2415x_i2c_write(struct bq2415x_device *bq, u8 reg, u8 val) /* read value from register, change it with mask left shifted and write back */ static int bq2415x_i2c_write_mask(struct bq2415x_device *bq, u8 reg, u8 val, - u8 mask, u8 shift) + u8 mask, u8 shift) { int ret; @@ -297,12 +296,11 @@ static int bq2415x_i2c_write_mask(struct bq2415x_device *bq, u8 reg, u8 val, /* change only one bit in register */ static int bq2415x_i2c_write_bit(struct bq2415x_device *bq, u8 reg, - bool val, u8 bit) + bool val, u8 bit) { if (bit > 8) return -EINVAL; - else - return bq2415x_i2c_write_mask(bq, reg, val, BIT(bit), bit); + return bq2415x_i2c_write_mask(bq, reg, val, BIT(bit), bit); } /**** global functions ****/ @@ -312,6 +310,7 @@ static int bq2415x_exec_command(struct bq2415x_device *bq, enum bq2415x_command command) { int ret; + switch (command) { case BQ2415X_TIMER_RESET: return bq2415x_i2c_write_bit(bq, BQ2415X_REG_STATUS, @@ -407,10 +406,8 @@ static int bq2415x_exec_command(struct bq2415x_device *bq, case BQ2415X_REVISION: return bq2415x_i2c_read_mask(bq, BQ2415X_REG_VENDER, BQ2415X_MASK_REVISION, BQ2415X_SHIFT_REVISION); - - default: - return -EINVAL; } + return -EINVAL; } /* detect chip type */ @@ -470,6 +467,7 @@ static int bq2415x_detect_revision(struct bq2415x_device *bq) { int ret = bq2415x_exec_command(bq, BQ2415X_REVISION); int chip = bq2415x_detect_chip(bq); + if (ret < 0 || chip < 0) return -1; @@ -483,7 +481,6 @@ static int bq2415x_detect_revision(struct bq2415x_device *bq) return ret; else return -1; - case BQ24153: case BQ24153A: case BQ24156: @@ -495,13 +492,11 @@ static int bq2415x_detect_revision(struct bq2415x_device *bq) return 1; else return -1; - case BQ24155: if (ret == 3) return 3; else return -1; - case BQUNKNOWN: return -1; } @@ -512,13 +507,16 @@ static int bq2415x_detect_revision(struct bq2415x_device *bq) /* return chip vender code */ static int bq2415x_get_vender_code(struct bq2415x_device *bq) { - int ret = bq2415x_exec_command(bq, BQ2415X_VENDER_CODE); + int ret; + + ret = bq2415x_exec_command(bq, BQ2415X_VENDER_CODE); if (ret < 0) return 0; - else /* convert to binary */ - return (ret & 0x1) + - ((ret >> 1) & 0x1) * 10 + - ((ret >> 2) & 0x1) * 100; + + /* convert to binary */ + return (ret & 0x1) + + ((ret >> 1) & 0x1) * 10 + + ((ret >> 2) & 0x1) * 100; } /* reset all chip registers to default state */ @@ -537,6 +535,7 @@ static void bq2415x_reset_chip(struct bq2415x_device *bq) static int bq2415x_set_current_limit(struct bq2415x_device *bq, int mA) { int val; + if (mA <= 100) val = 0; else if (mA <= 500) @@ -545,6 +544,7 @@ static int bq2415x_set_current_limit(struct bq2415x_device *bq, int mA) val = 2; else val = 3; + return bq2415x_i2c_write_mask(bq, BQ2415X_REG_CONTROL, val, BQ2415X_MASK_LIMIT, BQ2415X_SHIFT_LIMIT); } @@ -552,7 +552,9 @@ static int bq2415x_set_current_limit(struct bq2415x_device *bq, int mA) /* get current limit in mA */ static int bq2415x_get_current_limit(struct bq2415x_device *bq) { - int ret = bq2415x_i2c_read_mask(bq, BQ2415X_REG_CONTROL, + int ret; + + ret = bq2415x_i2c_read_mask(bq, BQ2415X_REG_CONTROL, BQ2415X_MASK_LIMIT, BQ2415X_SHIFT_LIMIT); if (ret < 0) return ret; @@ -564,15 +566,15 @@ static int bq2415x_get_current_limit(struct bq2415x_device *bq) return 800; else if (ret == 3) return 1800; - else - return -EINVAL; + return -EINVAL; } /* set weak battery voltage in mV */ static int bq2415x_set_weak_battery_voltage(struct bq2415x_device *bq, int mV) { - /* round to 100mV */ int val; + + /* round to 100mV */ if (mV <= 3400 + 50) val = 0; else if (mV <= 3500 + 50) @@ -581,6 +583,7 @@ static int bq2415x_set_weak_battery_voltage(struct bq2415x_device *bq, int mV) val = 2; else val = 3; + return bq2415x_i2c_write_mask(bq, BQ2415X_REG_CONTROL, val, BQ2415X_MASK_VLOWV, BQ2415X_SHIFT_VLOWV); } @@ -588,17 +591,18 @@ static int bq2415x_set_weak_battery_voltage(struct bq2415x_device *bq, int mV) /* get weak battery voltage in mV */ static int bq2415x_get_weak_battery_voltage(struct bq2415x_device *bq) { - int ret = bq2415x_i2c_read_mask(bq, BQ2415X_REG_CONTROL, + int ret; + + ret = bq2415x_i2c_read_mask(bq, BQ2415X_REG_CONTROL, BQ2415X_MASK_VLOWV, BQ2415X_SHIFT_VLOWV); if (ret < 0) return ret; - else - return 100 * (34 + ret); + return 100 * (34 + ret); } /* set battery regulation voltage in mV */ static int bq2415x_set_battery_regulation_voltage(struct bq2415x_device *bq, - int mV) + int mV) { int val = (mV/10 - 350) / 2; @@ -616,21 +620,21 @@ static int bq2415x_get_battery_regulation_voltage(struct bq2415x_device *bq) { int ret = bq2415x_i2c_read_mask(bq, BQ2415X_REG_VOLTAGE, BQ2415X_MASK_VO, BQ2415X_SHIFT_VO); + if (ret < 0) return ret; - else - return 10 * (350 + 2*ret); + return 10 * (350 + 2*ret); } /* set charge current in mA (platform data must provide resistor sense) */ static int bq2415x_set_charge_current(struct bq2415x_device *bq, int mA) { int val; + if (bq->init_data.resistor_sense <= 0) return -ENOSYS; val = (mA * bq->init_data.resistor_sense - 37400) / 6800; - if (val < 0) val = 0; else if (val > 7) @@ -645,6 +649,7 @@ static int bq2415x_set_charge_current(struct bq2415x_device *bq, int mA) static int bq2415x_get_charge_current(struct bq2415x_device *bq) { int ret; + if (bq->init_data.resistor_sense <= 0) return -ENOSYS; @@ -652,19 +657,18 @@ static int bq2415x_get_charge_current(struct bq2415x_device *bq) BQ2415X_MASK_VI_CHRG, BQ2415X_SHIFT_VI_CHRG); if (ret < 0) return ret; - else - return (37400 + 6800*ret) / bq->init_data.resistor_sense; + return (37400 + 6800*ret) / bq->init_data.resistor_sense; } /* set termination current in mA (platform data must provide resistor sense) */ static int bq2415x_set_termination_current(struct bq2415x_device *bq, int mA) { int val; + if (bq->init_data.resistor_sense <= 0) return -ENOSYS; val = (mA * bq->init_data.resistor_sense - 3400) / 3400; - if (val < 0) val = 0; else if (val > 7) @@ -679,6 +683,7 @@ static int bq2415x_set_termination_current(struct bq2415x_device *bq, int mA) static int bq2415x_get_termination_current(struct bq2415x_device *bq) { int ret; + if (bq->init_data.resistor_sense <= 0) return -ENOSYS; @@ -686,8 +691,7 @@ static int bq2415x_get_termination_current(struct bq2415x_device *bq) BQ2415X_MASK_VI_TERM, BQ2415X_SHIFT_VI_TERM); if (ret < 0) return ret; - else - return (3400 + 3400*ret) / bq->init_data.resistor_sense; + return (3400 + 3400*ret) / bq->init_data.resistor_sense; } /* set default value of property */ @@ -706,14 +710,17 @@ static int bq2415x_set_defaults(struct bq2415x_device *bq) bq2415x_exec_command(bq, BQ2415X_BOOST_MODE_DISABLE); bq2415x_exec_command(bq, BQ2415X_CHARGER_DISABLE); bq2415x_exec_command(bq, BQ2415X_CHARGE_TERMINATION_DISABLE); + bq2415x_set_default_value(bq, current_limit); bq2415x_set_default_value(bq, weak_battery_voltage); bq2415x_set_default_value(bq, battery_regulation_voltage); + if (bq->init_data.resistor_sense > 0) { bq2415x_set_default_value(bq, charge_current); bq2415x_set_default_value(bq, termination_current); bq2415x_exec_command(bq, BQ2415X_CHARGE_TERMINATION_ENABLE); } + bq2415x_exec_command(bq, BQ2415X_CHARGER_ENABLE); return 0; } @@ -844,8 +851,10 @@ static void bq2415x_timer_error(struct bq2415x_device *bq, const char *msg) static void bq2415x_timer_work(struct work_struct *work) { struct bq2415x_device *bq = container_of(work, struct bq2415x_device, - work.work); - int ret, error, boost; + work.work); + int ret; + int error; + int boost; if (!bq->autotimer) return; @@ -946,10 +955,11 @@ static enum power_supply_property bq2415x_power_supply_props[] = { }; static int bq2415x_power_supply_get_property(struct power_supply *psy, - enum power_supply_property psp, union power_supply_propval *val) + enum power_supply_property psp, + union power_supply_propval *val) { struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, - charger); + charger); int ret; switch (psp) { @@ -1031,7 +1041,8 @@ static void bq2415x_power_supply_exit(struct bq2415x_device *bq) /* show *_status entries */ static ssize_t bq2415x_sysfs_show_status(struct device *dev, - struct device_attribute *attr, char *buf) + struct device_attribute *attr, + char *buf) { struct power_supply *psy = dev_get_drvdata(dev); struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, @@ -1053,17 +1064,19 @@ static ssize_t bq2415x_sysfs_show_status(struct device *dev, ret = bq2415x_exec_command(bq, command); if (ret < 0) return ret; - else - return sprintf(buf, "%d\n", ret); + return sprintf(buf, "%d\n", ret); } -/* set timer entry: - auto - enable auto mode - off - disable auto mode - (other values) - reset chip timer -*/ +/* + * set timer entry: + * auto - enable auto mode + * off - disable auto mode + * (other values) - reset chip timer + */ static ssize_t bq2415x_sysfs_set_timer(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) + struct device_attribute *attr, + const char *buf, + size_t count) { struct power_supply *psy = dev_get_drvdata(dev); struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, @@ -1079,40 +1092,42 @@ static ssize_t bq2415x_sysfs_set_timer(struct device *dev, if (ret < 0) return ret; - else - return count; + return count; } /* show timer entry (auto or off) */ static ssize_t bq2415x_sysfs_show_timer(struct device *dev, - struct device_attribute *attr, char *buf) + struct device_attribute *attr, + char *buf) { struct power_supply *psy = dev_get_drvdata(dev); struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, - charger); + charger); if (bq->timer_error) return sprintf(buf, "%s\n", bq->timer_error); if (bq->autotimer) return sprintf(buf, "auto\n"); - else - return sprintf(buf, "off\n"); + return sprintf(buf, "off\n"); } -/* set mode entry: - auto - if automode is supported, enable it and set mode to reported - none - disable charger and boost mode - host - charging mode for host/hub chargers (current limit 500mA) - dedicated - charging mode for dedicated chargers (unlimited current limit) - boost - disable charger and enable boost mode -*/ +/* + * set mode entry: + * auto - if automode is supported, enable it and set mode to reported + * none - disable charger and boost mode + * host - charging mode for host/hub chargers (current limit 500mA) + * dedicated - charging mode for dedicated chargers (unlimited current limit) + * boost - disable charger and enable boost mode + */ static ssize_t bq2415x_sysfs_set_mode(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) + struct device_attribute *attr, + const char *buf, + size_t count) { struct power_supply *psy = dev_get_drvdata(dev); struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, - charger); + charger); enum bq2415x_mode mode; int ret = 0; @@ -1144,19 +1159,20 @@ static ssize_t bq2415x_sysfs_set_mode(struct device *dev, return count; bq->automode = 1; mode = bq->reported_mode; - } else + } else { return -EINVAL; + } ret = bq2415x_set_mode(bq, mode); if (ret < 0) return ret; - else - return count; + return count; } /* show mode entry (auto, none, host, dedicated or boost) */ static ssize_t bq2415x_sysfs_show_mode(struct device *dev, - struct device_attribute *attr, char *buf) + struct device_attribute *attr, + char *buf) { struct power_supply *psy = dev_get_drvdata(dev); struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, @@ -1190,11 +1206,12 @@ static ssize_t bq2415x_sysfs_show_mode(struct device *dev, /* show reported_mode entry (none, host, dedicated or boost) */ static ssize_t bq2415x_sysfs_show_reported_mode(struct device *dev, - struct device_attribute *attr, char *buf) + struct device_attribute *attr, + char *buf) { struct power_supply *psy = dev_get_drvdata(dev); struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, - charger); + charger); if (bq->automode < 0) return -EINVAL; @@ -1215,11 +1232,13 @@ static ssize_t bq2415x_sysfs_show_reported_mode(struct device *dev, /* directly set raw value to chip register, format: 'register value' */ static ssize_t bq2415x_sysfs_set_registers(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) + struct device_attribute *attr, + const char *buf, + size_t count) { struct power_supply *psy = dev_get_drvdata(dev); struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, - charger); + charger); ssize_t ret = 0; unsigned int reg; unsigned int val; @@ -1233,28 +1252,29 @@ static ssize_t bq2415x_sysfs_set_registers(struct device *dev, ret = bq2415x_i2c_write(bq, reg, val); if (ret < 0) return ret; - else - return count; + return count; } /* print value of chip register, format: 'register=value' */ static ssize_t bq2415x_sysfs_print_reg(struct bq2415x_device *bq, - u8 reg, char *buf) + u8 reg, + char *buf) { int ret = bq2415x_i2c_read(bq, reg); + if (ret < 0) return sprintf(buf, "%#.2x=error %d\n", reg, ret); - else - return sprintf(buf, "%#.2x=%#.2x\n", reg, ret); + return sprintf(buf, "%#.2x=%#.2x\n", reg, ret); } /* show all raw values of chip register, format per line: 'register=value' */ static ssize_t bq2415x_sysfs_show_registers(struct device *dev, - struct device_attribute *attr, char *buf) + struct device_attribute *attr, + char *buf) { struct power_supply *psy = dev_get_drvdata(dev); struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, - charger); + charger); ssize_t ret = 0; ret += bq2415x_sysfs_print_reg(bq, BQ2415X_REG_STATUS, buf+ret); @@ -1267,11 +1287,13 @@ static ssize_t bq2415x_sysfs_show_registers(struct device *dev, /* set current and voltage limit entries (in mA or mV) */ static ssize_t bq2415x_sysfs_set_limit(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) + struct device_attribute *attr, + const char *buf, + size_t count) { struct power_supply *psy = dev_get_drvdata(dev); struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, - charger); + charger); long val; int ret; @@ -1293,17 +1315,17 @@ static ssize_t bq2415x_sysfs_set_limit(struct device *dev, if (ret < 0) return ret; - else - return count; + return count; } /* show current and voltage limit entries (in mA or mV) */ static ssize_t bq2415x_sysfs_show_limit(struct device *dev, - struct device_attribute *attr, char *buf) + struct device_attribute *attr, + char *buf) { struct power_supply *psy = dev_get_drvdata(dev); struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, - charger); + charger); int ret; if (strcmp(attr->attr.name, "current_limit") == 0) @@ -1321,17 +1343,18 @@ static ssize_t bq2415x_sysfs_show_limit(struct device *dev, if (ret < 0) return ret; - else - return sprintf(buf, "%d\n", ret); + return sprintf(buf, "%d\n", ret); } /* set *_enable entries */ static ssize_t bq2415x_sysfs_set_enable(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) + struct device_attribute *attr, + const char *buf, + size_t count) { struct power_supply *psy = dev_get_drvdata(dev); struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, - charger); + charger); enum bq2415x_command command; long val; int ret; @@ -1357,17 +1380,17 @@ static ssize_t bq2415x_sysfs_set_enable(struct device *dev, ret = bq2415x_exec_command(bq, command); if (ret < 0) return ret; - else - return count; + return count; } /* show *_enable entries */ static ssize_t bq2415x_sysfs_show_enable(struct device *dev, - struct device_attribute *attr, char *buf) + struct device_attribute *attr, + char *buf) { struct power_supply *psy = dev_get_drvdata(dev); struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, - charger); + charger); enum bq2415x_command command; int ret; @@ -1385,8 +1408,7 @@ static ssize_t bq2415x_sysfs_show_enable(struct device *dev, ret = bq2415x_exec_command(bq, command); if (ret < 0) return ret; - else - return sprintf(buf, "%d\n", ret); + return sprintf(buf, "%d\n", ret); } static DEVICE_ATTR(current_limit, S_IWUSR | S_IRUGO, @@ -1425,6 +1447,10 @@ static DEVICE_ATTR(boost_status, S_IRUGO, bq2415x_sysfs_show_status, NULL); static DEVICE_ATTR(fault_status, S_IRUGO, bq2415x_sysfs_show_status, NULL); static struct attribute *bq2415x_sysfs_attributes[] = { + /* + * TODO: some (appropriate) of these attrs should be switched to + * use power supply class props. + */ &dev_attr_current_limit.attr, &dev_attr_weak_battery_voltage.attr, &dev_attr_battery_regulation_voltage.attr, @@ -1466,7 +1492,7 @@ static void bq2415x_sysfs_exit(struct bq2415x_device *bq) /* main bq2415x probe function */ static int bq2415x_probe(struct i2c_client *client, - const struct i2c_device_id *id) + const struct i2c_device_id *id) { int ret; int num; diff --git a/include/linux/power/bq2415x_charger.h b/include/linux/power/bq2415x_charger.h index fb6bf4d8aea..97a1665eaea 100644 --- a/include/linux/power/bq2415x_charger.h +++ b/include/linux/power/bq2415x_charger.h @@ -1,72 +1,77 @@ /* - bq2415x_charger.h - bq2415x charger driver - Copyright (C) 2011-2012 Pali Rohár - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ + * bq2415x charger driver + * + * Copyright (C) 2011-2012 Pali Rohár + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ #ifndef BQ2415X_CHARGER_H #define BQ2415X_CHARGER_H /* - This is platform data for bq2415x chip. It contains default board voltages - and currents which can be also later configured via sysfs. If value is -1 - then default chip value (specified in datasheet) will be used. - - Value resistor_sense is needed for for configuring charge and termination - current. It it is less or equal to zero, configuring charge and termination - current will not be possible. - - Function set_mode_hook is needed for automode (setting correct current limit - when charger is connected/disconnected or setting boost mode). When is NULL, - automode function is disabled. When is not NULL, it must have this prototype: - - int (*set_mode_hook)( - void (*hook)(enum bq2415x_mode mode, void *data), - void *data) - - hook is hook function (see below) and data is pointer to driver private data - - bq2415x driver will call it as: - - platform_data->set_mode_hook(bq2415x_hook_function, bq2415x_device); - - Board/platform function set_mode_hook return non zero value when hook - function was successful registered. Platform code should call that hook - function (which get from pointer, with data) every time when charger was - connected/disconnected or require to enable boost mode. bq2415x driver then - will set correct current limit, enable/disable charger or boost mode. - - Hook function has this prototype: - - void hook(enum bq2415x_mode mode, void *data); - - mode is bq2415x mode (charger or boost) - data is pointer to driver private data (which get from set_charger_type_hook) - - When bq driver is being unloaded, it call function: - - platform_data->set_mode_hook(NULL, NULL); - - (hook function and driver private data are NULL) - - After that board/platform code must not call driver hook function! It is - possible that pointer to hook function will not be valid and calling will - cause undefined result. - -*/ + * This is platform data for bq2415x chip. It contains default board + * voltages and currents which can be also later configured via sysfs. If + * value is -1 then default chip value (specified in datasheet) will be + * used. + * + * Value resistor_sense is needed for for configuring charge and + * termination current. It it is less or equal to zero, configuring charge + * and termination current will not be possible. + * + * Function set_mode_hook is needed for automode (setting correct current + * limit when charger is connected/disconnected or setting boost mode). + * When is NULL, automode function is disabled. When is not NULL, it must + * have this prototype: + * + * int (*set_mode_hook)( + * void (*hook)(enum bq2415x_mode mode, void *data), + * void *data) + * + * hook is hook function (see below) and data is pointer to driver private + * data + * + * bq2415x driver will call it as: + * + * platform_data->set_mode_hook(bq2415x_hook_function, bq2415x_device); + * + * Board/platform function set_mode_hook return non zero value when hook + * function was successful registered. Platform code should call that hook + * function (which get from pointer, with data) every time when charger + * was connected/disconnected or require to enable boost mode. bq2415x + * driver then will set correct current limit, enable/disable charger or + * boost mode. + * + * Hook function has this prototype: + * + * void hook(enum bq2415x_mode mode, void *data); + * + * mode is bq2415x mode (charger or boost) + * data is pointer to driver private data (which get from + * set_charger_type_hook) + * + * When bq driver is being unloaded, it call function: + * + * platform_data->set_mode_hook(NULL, NULL); + * + * (hook function and driver private data are NULL) + * + * After that board/platform code must not call driver hook function! It + * is possible that pointer to hook function will not be valid and calling + * will cause undefined result. + */ /* Supported modes with maximal current limit */ enum bq2415x_mode { -- cgit v1.2.3 From e59ec4a1899dfff007d91b30f00e7211731b816b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Sat, 10 Nov 2012 15:42:48 +0100 Subject: bq27x00_battery: Fixup nominal available capacity reporting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We should not not report nominal available capacity if battery is not calibrated, as we don't want to represent bogus values in the sysfs and confuse userland. Signed-off-by: Pali Rohár Signed-off-by: Anton Vorontsov --- drivers/power/bq27x00_battery.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index 5860d4dfbe9..41b3328ecfc 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -230,6 +230,14 @@ static int bq27x00_battery_read_charge(struct bq27x00_device_info *di, u8 reg) */ static inline int bq27x00_battery_read_nac(struct bq27x00_device_info *di) { + int flags; + bool is_bq27500 = di->chip == BQ27500; + bool is_higher = bq27xxx_is_chip_version_higher(di); + + flags = bq27x00_read(di, BQ27x00_REG_FLAGS, !is_bq27500); + if (flags >= 0 && !is_higher && (flags & BQ27000_FLAG_CI)) + return -ENODATA; + return bq27x00_battery_read_charge(di, BQ27x00_REG_NAC); } -- cgit v1.2.3 From ec073c9b319fee6564dcb9f1fb7a70eb6a8a06b5 Mon Sep 17 00:00:00 2001 From: Marcos Paulo de Souza Date: Tue, 6 Nov 2012 21:11:11 -0200 Subject: jz4740-battery: Use devm_kzalloc This is just a small cleanup, makes a logic a little bit more simplier. Signed-off-by: Marcos Paulo de Souza Acked-by: Lars-Peter Clausen Signed-off-by: Anton Vorontsov --- drivers/power/jz4740-battery.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/power/jz4740-battery.c b/drivers/power/jz4740-battery.c index ffbed5e5b94..529d22abe0e 100644 --- a/drivers/power/jz4740-battery.c +++ b/drivers/power/jz4740-battery.c @@ -250,7 +250,7 @@ static int __devinit jz_battery_probe(struct platform_device *pdev) return -ENXIO; } - jz_battery = kzalloc(sizeof(*jz_battery), GFP_KERNEL); + jz_battery = devm_kzalloc(&pdev->dev, sizeof(*jz_battery), GFP_KERNEL); if (!jz_battery) { dev_err(&pdev->dev, "Failed to allocate driver structure\n"); return -ENOMEM; @@ -260,24 +260,21 @@ static int __devinit jz_battery_probe(struct platform_device *pdev) jz_battery->irq = platform_get_irq(pdev, 0); if (jz_battery->irq < 0) { - ret = jz_battery->irq; dev_err(&pdev->dev, "Failed to get platform irq: %d\n", ret); - goto err_free; + return jz_battery->irq; } jz_battery->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!jz_battery->mem) { - ret = -ENOENT; dev_err(&pdev->dev, "Failed to get platform mmio resource\n"); - goto err_free; + return -ENOENT; } jz_battery->mem = request_mem_region(jz_battery->mem->start, resource_size(jz_battery->mem), pdev->name); if (!jz_battery->mem) { - ret = -EBUSY; dev_err(&pdev->dev, "Failed to request mmio memory region\n"); - goto err_free; + return -EBUSY; } jz_battery->base = ioremap_nocache(jz_battery->mem->start, @@ -371,8 +368,6 @@ err_iounmap: iounmap(jz_battery->base); err_release_mem_region: release_mem_region(jz_battery->mem->start, resource_size(jz_battery->mem)); -err_free: - kfree(jz_battery); return ret; } @@ -394,7 +389,6 @@ static int __devexit jz_battery_remove(struct platform_device *pdev) iounmap(jz_battery->base); release_mem_region(jz_battery->mem->start, resource_size(jz_battery->mem)); - kfree(jz_battery); return 0; } -- cgit v1.2.3 From 9790f98fdcfb1c60b953973ae256d00857b5ebb7 Mon Sep 17 00:00:00 2001 From: Marcos Paulo de Souza Date: Tue, 6 Nov 2012 21:11:12 -0200 Subject: jz4740-battery: Use devm_request_and_ioremap No functional changes. Just a cleanup. Signed-off-by: Marcos Paulo de Souza Acked-by: Lars-Peter Clausen Signed-off-by: Anton Vorontsov --- drivers/power/jz4740-battery.c | 33 ++++++--------------------------- 1 file changed, 6 insertions(+), 27 deletions(-) diff --git a/drivers/power/jz4740-battery.c b/drivers/power/jz4740-battery.c index 529d22abe0e..fc7550d4d56 100644 --- a/drivers/power/jz4740-battery.c +++ b/drivers/power/jz4740-battery.c @@ -33,7 +33,6 @@ struct jz_battery { struct jz_battery_platform_data *pdata; struct platform_device *pdev; - struct resource *mem; void __iomem *base; int irq; @@ -244,6 +243,7 @@ static int __devinit jz_battery_probe(struct platform_device *pdev) struct jz_battery_platform_data *pdata = pdev->dev.parent->platform_data; struct jz_battery *jz_battery; struct power_supply *battery; + struct resource *mem; if (!pdata) { dev_err(&pdev->dev, "No platform_data supplied\n"); @@ -264,26 +264,11 @@ static int __devinit jz_battery_probe(struct platform_device *pdev) return jz_battery->irq; } - jz_battery->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!jz_battery->mem) { - dev_err(&pdev->dev, "Failed to get platform mmio resource\n"); - return -ENOENT; - } + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - jz_battery->mem = request_mem_region(jz_battery->mem->start, - resource_size(jz_battery->mem), pdev->name); - if (!jz_battery->mem) { - dev_err(&pdev->dev, "Failed to request mmio memory region\n"); + jz_battery->base = devm_request_and_ioremap(&pdev->dev, mem); + if (!jz_battery->base) return -EBUSY; - } - - jz_battery->base = ioremap_nocache(jz_battery->mem->start, - resource_size(jz_battery->mem)); - if (!jz_battery->base) { - ret = -EBUSY; - dev_err(&pdev->dev, "Failed to ioremap mmio memory\n"); - goto err_release_mem_region; - } battery = &jz_battery->battery; battery->name = pdata->info.name; @@ -306,7 +291,7 @@ static int __devinit jz_battery_probe(struct platform_device *pdev) jz_battery); if (ret) { dev_err(&pdev->dev, "Failed to request irq %d\n", ret); - goto err_iounmap; + goto err; } disable_irq(jz_battery->irq); @@ -363,11 +348,8 @@ err_free_gpio: gpio_free(jz_battery->pdata->gpio_charge); err_free_irq: free_irq(jz_battery->irq, jz_battery); -err_iounmap: +err: platform_set_drvdata(pdev, NULL); - iounmap(jz_battery->base); -err_release_mem_region: - release_mem_region(jz_battery->mem->start, resource_size(jz_battery->mem)); return ret; } @@ -387,9 +369,6 @@ static int __devexit jz_battery_remove(struct platform_device *pdev) free_irq(jz_battery->irq, jz_battery); - iounmap(jz_battery->base); - release_mem_region(jz_battery->mem->start, resource_size(jz_battery->mem)); - return 0; } -- cgit v1.2.3 From e9f14c18b83ccf3ab291b83a1d6c640113ecf545 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 13 Nov 2012 10:42:11 +0100 Subject: twl4030_charger: Change TWL4030_MODULE_* ids to TWL_MODULE_* To facilitate upcoming cleanup in twl stack. No functional changes. Signed-off-by: Peter Ujfalusi Signed-off-by: Anton Vorontsov --- drivers/power/twl4030_charger.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index f9e70cf0819..a69d0d11b54 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -114,12 +114,12 @@ static int twl4030_clear_set(u8 mod_no, u8 clear, u8 set, u8 reg) static int twl4030_bci_read(u8 reg, u8 *val) { - return twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, val, reg); + return twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, val, reg); } static int twl4030_clear_set_boot_bci(u8 clear, u8 set) { - return twl4030_clear_set(TWL4030_MODULE_PM_MASTER, clear, + return twl4030_clear_set(TWL_MODULE_PM_MASTER, clear, TWL4030_CONFIG_DONE | TWL4030_BCIAUTOWEN | set, TWL4030_PM_MASTER_BOOT_BCI); } @@ -152,7 +152,7 @@ static int twl4030_bci_have_vbus(struct twl4030_bci *bci) int ret; u8 hwsts; - ret = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &hwsts, + ret = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &hwsts, TWL4030_PM_MASTER_STS_HW_CONDITIONS); if (ret < 0) return 0; @@ -199,7 +199,7 @@ static int twl4030_charger_enable_usb(struct twl4030_bci *bci, bool enable) return ret; /* forcing USBFASTMCHG(BCIMFSTS4[2]) to 1 */ - ret = twl4030_clear_set(TWL4030_MODULE_MAIN_CHARGE, 0, + ret = twl4030_clear_set(TWL_MODULE_MAIN_CHARGE, 0, TWL4030_USBFASTMCHG, TWL4030_BCIMFSTS4); } else { ret = twl4030_clear_set_boot_bci(TWL4030_BCIAUTOUSB, 0); @@ -238,7 +238,7 @@ static int twl4030_charger_enable_backup(int uvolt, int uamp) if (uvolt < 2500000 || uamp < 25) { /* disable charging of backup battery */ - ret = twl4030_clear_set(TWL4030_MODULE_PM_RECEIVER, + ret = twl4030_clear_set(TWL_MODULE_PM_RECEIVER, TWL4030_BBCHEN, 0, TWL4030_BB_CFG); return ret; } @@ -262,7 +262,7 @@ static int twl4030_charger_enable_backup(int uvolt, int uamp) else flags |= TWL4030_BBISEL_25uA; - ret = twl4030_clear_set(TWL4030_MODULE_PM_RECEIVER, + ret = twl4030_clear_set(TWL_MODULE_PM_RECEIVER, TWL4030_BBSEL_MASK | TWL4030_BBISEL_MASK, flags, TWL4030_BB_CFG); -- cgit v1.2.3 From e0f1abeba5c2d8a2183566717d99294fd1a29c2e Mon Sep 17 00:00:00 2001 From: "Rajanikanth H.V" Date: Sun, 18 Nov 2012 18:45:41 -0800 Subject: ab8500: Add devicetree support for fuelgauge - This patch adds device tree support for fuelgauge driver - optimize bm devices platform_data usage and of_probe(...) Note: of_probe() routine for battery managed devices is made common across all bm drivers. - test status: - interrupt numbers assigned differs between legacy and FDT mode. Signed-off-by: Rajanikanth H.V Signed-off-by: Anton Vorontsov --- Documentation/devicetree/bindings/mfd/ab8500.txt | 7 +- .../devicetree/bindings/power_supply/ab8500/fg.txt | 58 +++ arch/arm/boot/dts/dbx5x0.dtsi | 12 +- drivers/mfd/ab8500-core.c | 5 + drivers/power/Makefile | 2 +- drivers/power/ab8500_bmdata.c | 521 +++++++++++++++++++++ drivers/power/ab8500_btemp.c | 16 +- drivers/power/ab8500_charger.c | 16 +- drivers/power/ab8500_fg.c | 82 ++-- drivers/power/abx500_chargalg.c | 8 +- include/linux/mfd/abx500.h | 36 +- 11 files changed, 667 insertions(+), 96 deletions(-) create mode 100644 Documentation/devicetree/bindings/power_supply/ab8500/fg.txt create mode 100644 drivers/power/ab8500_bmdata.c diff --git a/Documentation/devicetree/bindings/mfd/ab8500.txt b/Documentation/devicetree/bindings/mfd/ab8500.txt index ce83c8d3c00..6ca8d817ef9 100644 --- a/Documentation/devicetree/bindings/mfd/ab8500.txt +++ b/Documentation/devicetree/bindings/mfd/ab8500.txt @@ -24,7 +24,12 @@ ab8500-bm : : : Battery Manager ab8500-btemp : : : Battery Temperature ab8500-charger : : : Battery Charger ab8500-codec : : : Audio Codec -ab8500-fg : : : Fuel Gauge +ab8500-fg : : vddadc : Fuel Gauge + : NCONV_ACCU : : Accumulate N Sample Conversion + : BATT_OVV : : Battery Over Voltage + : LOW_BAT_F : : LOW threshold battery voltage + : CC_INT_CALIB : : Coulomb Counter Internal Calibration + : CCEOC : : Coulomb Counter End of Conversion ab8500-gpadc : HW_CONV_END : vddadc : Analogue to Digital Converter SW_CONV_END : : ab8500-gpio : : : GPIO Controller diff --git a/Documentation/devicetree/bindings/power_supply/ab8500/fg.txt b/Documentation/devicetree/bindings/power_supply/ab8500/fg.txt new file mode 100644 index 00000000000..ccafcb9112f --- /dev/null +++ b/Documentation/devicetree/bindings/power_supply/ab8500/fg.txt @@ -0,0 +1,58 @@ +=== AB8500 Fuel Gauge Driver === + +AB8500 is a mixed signal multimedia and power management +device comprising: power and energy-management-module, +wall-charger, usb-charger, audio codec, general purpose adc, +tvout, clock management and sim card interface. + +Fuelgauge support is part of energy-management-modules, other +components of this module are: +main-charger, usb-combo-charger and battery-temperature-monitoring. + +The properties below describes the node for fuelgauge driver. + +Required Properties: +- compatible = This shall be: "stericsson,ab8500-fg" +- battery = Shall be battery specific information + Example: + ab8500_fg { + compatible = "stericsson,ab8500-fg"; + battery = <&ab8500_battery>; + }; + +dependent node: + ab8500_battery: ab8500_battery { + }; + This node will provide information on 'thermistor interface' and + 'battery technology type' used. + +Properties of this node are: +thermistor-on-batctrl: + A boolean value indicating thermistor interface to battery + + Note: + 'btemp' and 'batctrl' are the pins interfaced for battery temperature + measurement, 'btemp' signal is used when NTC(negative temperature + coefficient) resister is interfaced external to battery whereas + 'batctrl' pin is used when NTC resister is internal to battery. + + Example: + ab8500_battery: ab8500_battery { + thermistor-on-batctrl; + }; + indicates: NTC resister is internal to battery, 'batctrl' is used + for thermal measurement. + + The absence of property 'thermal-on-batctrl' indicates + NTC resister is external to battery and 'btemp' signal is used + for thermal measurement. + +battery-type: + This shall be the battery manufacturing technology type, + allowed types are: + "UNKNOWN" "NiMH" "LION" "LIPO" "LiFe" "NiCd" "LiMn" + Example: + ab8500_battery: ab8500_battery { + stericsson,battery-type = "LIPO"; + } + diff --git a/arch/arm/boot/dts/dbx5x0.dtsi b/arch/arm/boot/dts/dbx5x0.dtsi index 4b0e0ca08f4..0c81986904c 100644 --- a/arch/arm/boot/dts/dbx5x0.dtsi +++ b/arch/arm/boot/dts/dbx5x0.dtsi @@ -352,7 +352,17 @@ vddadc-supply = <&ab8500_ldo_tvout_reg>; }; - ab8500-usb { + ab8500_battery: ab8500_battery { + stericsson,battery-type = "LIPO"; + thermistor-on-batctrl; + }; + + ab8500_fg { + compatible = "stericsson,ab8500-fg"; + battery = <&ab8500_battery>; + }; + + ab8500_usb { compatible = "stericsson,ab8500-usb"; interrupts = < 90 0x4 96 0x4 diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index 1667c77b5cd..7c3017ba73e 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -1051,8 +1051,13 @@ static struct mfd_cell __devinitdata ab8500_bm_devs[] = { }, { .name = "ab8500-fg", + .of_compatible = "stericsson,ab8500-fg", .num_resources = ARRAY_SIZE(ab8500_fg_resources), .resources = ab8500_fg_resources, +#ifndef CONFIG_OF + .platform_data = &ab8500_bm_data, + .pdata_size = sizeof(ab8500_bm_data), +#endif }, { .name = "ab8500-chargalg", diff --git a/drivers/power/Makefile b/drivers/power/Makefile index 74dfd957015..696e3a960b3 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -38,7 +38,7 @@ obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o obj-$(CONFIG_BATTERY_JZ4740) += jz4740-battery.o obj-$(CONFIG_BATTERY_INTEL_MID) += intel_mid_battery.o obj-$(CONFIG_BATTERY_RX51) += rx51_battery.o -obj-$(CONFIG_AB8500_BM) += ab8500_charger.o ab8500_btemp.o ab8500_fg.o abx500_chargalg.o +obj-$(CONFIG_AB8500_BM) += ab8500_bmdata.o ab8500_charger.o ab8500_btemp.o ab8500_fg.o abx500_chargalg.o obj-$(CONFIG_CHARGER_ISP1704) += isp1704_charger.o obj-$(CONFIG_CHARGER_MAX8903) += max8903_charger.o obj-$(CONFIG_CHARGER_TWL4030) += twl4030_charger.o diff --git a/drivers/power/ab8500_bmdata.c b/drivers/power/ab8500_bmdata.c new file mode 100644 index 00000000000..e7639b6659f --- /dev/null +++ b/drivers/power/ab8500_bmdata.c @@ -0,0 +1,521 @@ +#include +#include +#include +#include +#include +#include + +/* + * These are the defined batteries that uses a NTC and ID resistor placed + * inside of the battery pack. + * Note that the res_to_temp table must be strictly sorted by falling resistance + * values to work. + */ +static struct abx500_res_to_temp temp_tbl_A_thermistor[] = { + {-5, 53407}, + { 0, 48594}, + { 5, 43804}, + {10, 39188}, + {15, 34870}, + {20, 30933}, + {25, 27422}, + {30, 24347}, + {35, 21694}, + {40, 19431}, + {45, 17517}, + {50, 15908}, + {55, 14561}, + {60, 13437}, + {65, 12500}, +}; + +static struct abx500_res_to_temp temp_tbl_B_thermistor[] = { + {-5, 165418}, + { 0, 159024}, + { 5, 151921}, + {10, 144300}, + {15, 136424}, + {20, 128565}, + {25, 120978}, + {30, 113875}, + {35, 107397}, + {40, 101629}, + {45, 96592}, + {50, 92253}, + {55, 88569}, + {60, 85461}, + {65, 82869}, +}; + +static struct abx500_v_to_cap cap_tbl_A_thermistor[] = { + {4171, 100}, + {4114, 95}, + {4009, 83}, + {3947, 74}, + {3907, 67}, + {3863, 59}, + {3830, 56}, + {3813, 53}, + {3791, 46}, + {3771, 33}, + {3754, 25}, + {3735, 20}, + {3717, 17}, + {3681, 13}, + {3664, 8}, + {3651, 6}, + {3635, 5}, + {3560, 3}, + {3408, 1}, + {3247, 0}, +}; + +static struct abx500_v_to_cap cap_tbl_B_thermistor[] = { + {4161, 100}, + {4124, 98}, + {4044, 90}, + {4003, 85}, + {3966, 80}, + {3933, 75}, + {3888, 67}, + {3849, 60}, + {3813, 55}, + {3787, 47}, + {3772, 30}, + {3751, 25}, + {3718, 20}, + {3681, 16}, + {3660, 14}, + {3589, 10}, + {3546, 7}, + {3495, 4}, + {3404, 2}, + {3250, 0}, +}; + +static struct abx500_v_to_cap cap_tbl[] = { + {4186, 100}, + {4163, 99}, + {4114, 95}, + {4068, 90}, + {3990, 80}, + {3926, 70}, + {3898, 65}, + {3866, 60}, + {3833, 55}, + {3812, 50}, + {3787, 40}, + {3768, 30}, + {3747, 25}, + {3730, 20}, + {3705, 15}, + {3699, 14}, + {3684, 12}, + {3672, 9}, + {3657, 7}, + {3638, 6}, + {3556, 4}, + {3424, 2}, + {3317, 1}, + {3094, 0}, +}; + +/* + * Note that the res_to_temp table must be strictly sorted by falling + * resistance values to work. + */ +static struct abx500_res_to_temp temp_tbl[] = { + {-5, 214834}, + { 0, 162943}, + { 5, 124820}, + {10, 96520}, + {15, 75306}, + {20, 59254}, + {25, 47000}, + {30, 37566}, + {35, 30245}, + {40, 24520}, + {45, 20010}, + {50, 16432}, + {55, 13576}, + {60, 11280}, + {65, 9425}, +}; + +/* + * Note that the batres_vs_temp table must be strictly sorted by falling + * temperature values to work. + */ +static struct batres_vs_temp temp_to_batres_tbl_thermistor[] = { + { 40, 120}, + { 30, 135}, + { 20, 165}, + { 10, 230}, + { 00, 325}, + {-10, 445}, + {-20, 595}, +}; + +/* + * Note that the batres_vs_temp table must be strictly sorted by falling + * temperature values to work. + */ +static struct batres_vs_temp temp_to_batres_tbl_ext_thermistor[] = { + { 60, 300}, + { 30, 300}, + { 20, 300}, + { 10, 300}, + { 00, 300}, + {-10, 300}, + {-20, 300}, +}; + +/* battery resistance table for LI ION 9100 battery */ +static struct batres_vs_temp temp_to_batres_tbl_9100[] = { + { 60, 180}, + { 30, 180}, + { 20, 180}, + { 10, 180}, + { 00, 180}, + {-10, 180}, + {-20, 180}, +}; + +static struct abx500_battery_type bat_type_thermistor[] = { +[BATTERY_UNKNOWN] = { + /* First element always represent the UNKNOWN battery */ + .name = POWER_SUPPLY_TECHNOLOGY_UNKNOWN, + .resis_high = 0, + .resis_low = 0, + .battery_resistance = 300, + .charge_full_design = 612, + .nominal_voltage = 3700, + .termination_vol = 4050, + .termination_curr = 200, + .recharge_vol = 3990, + .normal_cur_lvl = 400, + .normal_vol_lvl = 4100, + .maint_a_cur_lvl = 400, + .maint_a_vol_lvl = 4050, + .maint_a_chg_timer_h = 60, + .maint_b_cur_lvl = 400, + .maint_b_vol_lvl = 4000, + .maint_b_chg_timer_h = 200, + .low_high_cur_lvl = 300, + .low_high_vol_lvl = 4000, + .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl), + .r_to_t_tbl = temp_tbl, + .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl), + .v_to_cap_tbl = cap_tbl, + .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor), + .batres_tbl = temp_to_batres_tbl_thermistor, +}, +{ + .name = POWER_SUPPLY_TECHNOLOGY_LIPO, + .resis_high = 53407, + .resis_low = 12500, + .battery_resistance = 300, + .charge_full_design = 900, + .nominal_voltage = 3600, + .termination_vol = 4150, + .termination_curr = 80, + .recharge_vol = 4130, + .normal_cur_lvl = 700, + .normal_vol_lvl = 4200, + .maint_a_cur_lvl = 600, + .maint_a_vol_lvl = 4150, + .maint_a_chg_timer_h = 60, + .maint_b_cur_lvl = 600, + .maint_b_vol_lvl = 4100, + .maint_b_chg_timer_h = 200, + .low_high_cur_lvl = 300, + .low_high_vol_lvl = 4000, + .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl_A_thermistor), + .r_to_t_tbl = temp_tbl_A_thermistor, + .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl_A_thermistor), + .v_to_cap_tbl = cap_tbl_A_thermistor, + .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor), + .batres_tbl = temp_to_batres_tbl_thermistor, + +}, +{ + .name = POWER_SUPPLY_TECHNOLOGY_LIPO, + .resis_high = 165418, + .resis_low = 82869, + .battery_resistance = 300, + .charge_full_design = 900, + .nominal_voltage = 3600, + .termination_vol = 4150, + .termination_curr = 80, + .recharge_vol = 4130, + .normal_cur_lvl = 700, + .normal_vol_lvl = 4200, + .maint_a_cur_lvl = 600, + .maint_a_vol_lvl = 4150, + .maint_a_chg_timer_h = 60, + .maint_b_cur_lvl = 600, + .maint_b_vol_lvl = 4100, + .maint_b_chg_timer_h = 200, + .low_high_cur_lvl = 300, + .low_high_vol_lvl = 4000, + .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl_B_thermistor), + .r_to_t_tbl = temp_tbl_B_thermistor, + .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl_B_thermistor), + .v_to_cap_tbl = cap_tbl_B_thermistor, + .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor), + .batres_tbl = temp_to_batres_tbl_thermistor, +}, +}; + +static struct abx500_battery_type bat_type_ext_thermistor[] = { +[BATTERY_UNKNOWN] = { + /* First element always represent the UNKNOWN battery */ + .name = POWER_SUPPLY_TECHNOLOGY_UNKNOWN, + .resis_high = 0, + .resis_low = 0, + .battery_resistance = 300, + .charge_full_design = 612, + .nominal_voltage = 3700, + .termination_vol = 4050, + .termination_curr = 200, + .recharge_vol = 3990, + .normal_cur_lvl = 400, + .normal_vol_lvl = 4100, + .maint_a_cur_lvl = 400, + .maint_a_vol_lvl = 4050, + .maint_a_chg_timer_h = 60, + .maint_b_cur_lvl = 400, + .maint_b_vol_lvl = 4000, + .maint_b_chg_timer_h = 200, + .low_high_cur_lvl = 300, + .low_high_vol_lvl = 4000, + .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl), + .r_to_t_tbl = temp_tbl, + .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl), + .v_to_cap_tbl = cap_tbl, + .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor), + .batres_tbl = temp_to_batres_tbl_thermistor, +}, +/* + * These are the batteries that doesn't have an internal NTC resistor to measure + * its temperature. The temperature in this case is measure with a NTC placed + * near the battery but on the PCB. + */ +{ + .name = POWER_SUPPLY_TECHNOLOGY_LIPO, + .resis_high = 76000, + .resis_low = 53000, + .battery_resistance = 300, + .charge_full_design = 900, + .nominal_voltage = 3700, + .termination_vol = 4150, + .termination_curr = 100, + .recharge_vol = 4130, + .normal_cur_lvl = 700, + .normal_vol_lvl = 4200, + .maint_a_cur_lvl = 600, + .maint_a_vol_lvl = 4150, + .maint_a_chg_timer_h = 60, + .maint_b_cur_lvl = 600, + .maint_b_vol_lvl = 4100, + .maint_b_chg_timer_h = 200, + .low_high_cur_lvl = 300, + .low_high_vol_lvl = 4000, + .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl), + .r_to_t_tbl = temp_tbl, + .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl), + .v_to_cap_tbl = cap_tbl, + .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor), + .batres_tbl = temp_to_batres_tbl_thermistor, +}, +{ + .name = POWER_SUPPLY_TECHNOLOGY_LION, + .resis_high = 30000, + .resis_low = 10000, + .battery_resistance = 300, + .charge_full_design = 950, + .nominal_voltage = 3700, + .termination_vol = 4150, + .termination_curr = 100, + .recharge_vol = 4130, + .normal_cur_lvl = 700, + .normal_vol_lvl = 4200, + .maint_a_cur_lvl = 600, + .maint_a_vol_lvl = 4150, + .maint_a_chg_timer_h = 60, + .maint_b_cur_lvl = 600, + .maint_b_vol_lvl = 4100, + .maint_b_chg_timer_h = 200, + .low_high_cur_lvl = 300, + .low_high_vol_lvl = 4000, + .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl), + .r_to_t_tbl = temp_tbl, + .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl), + .v_to_cap_tbl = cap_tbl, + .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor), + .batres_tbl = temp_to_batres_tbl_thermistor, +}, +{ + .name = POWER_SUPPLY_TECHNOLOGY_LION, + .resis_high = 95000, + .resis_low = 76001, + .battery_resistance = 300, + .charge_full_design = 950, + .nominal_voltage = 3700, + .termination_vol = 4150, + .termination_curr = 100, + .recharge_vol = 4130, + .normal_cur_lvl = 700, + .normal_vol_lvl = 4200, + .maint_a_cur_lvl = 600, + .maint_a_vol_lvl = 4150, + .maint_a_chg_timer_h = 60, + .maint_b_cur_lvl = 600, + .maint_b_vol_lvl = 4100, + .maint_b_chg_timer_h = 200, + .low_high_cur_lvl = 300, + .low_high_vol_lvl = 4000, + .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl), + .r_to_t_tbl = temp_tbl, + .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl), + .v_to_cap_tbl = cap_tbl, + .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor), + .batres_tbl = temp_to_batres_tbl_thermistor, +}, +}; + +static const struct abx500_bm_capacity_levels cap_levels = { + .critical = 2, + .low = 10, + .normal = 70, + .high = 95, + .full = 100, +}; + +static const struct abx500_fg_parameters fg = { + .recovery_sleep_timer = 10, + .recovery_total_time = 100, + .init_timer = 1, + .init_discard_time = 5, + .init_total_time = 40, + .high_curr_time = 60, + .accu_charging = 30, + .accu_high_curr = 30, + .high_curr_threshold = 50, + .lowbat_threshold = 3100, + .battok_falling_th_sel0 = 2860, + .battok_raising_th_sel1 = 2860, + .user_cap_limit = 15, + .maint_thres = 97, +}; + +static const struct abx500_maxim_parameters maxi_params = { + .ena_maxi = true, + .chg_curr = 910, + .wait_cycles = 10, + .charger_curr_step = 100, +}; + +static const struct abx500_bm_charger_parameters chg = { + .usb_volt_max = 5500, + .usb_curr_max = 1500, + .ac_volt_max = 7500, + .ac_curr_max = 1500, +}; + +struct abx500_bm_data ab8500_bm_data = { + .temp_under = 3, + .temp_low = 8, + .temp_high = 43, + .temp_over = 48, + .main_safety_tmr_h = 4, + .temp_interval_chg = 20, + .temp_interval_nochg = 120, + .usb_safety_tmr_h = 4, + .bkup_bat_v = BUP_VCH_SEL_2P6V, + .bkup_bat_i = BUP_ICH_SEL_150UA, + .no_maintenance = false, + .adc_therm = ABx500_ADC_THERM_BATCTRL, + .chg_unknown_bat = false, + .enable_overshoot = false, + .fg_res = 100, + .cap_levels = &cap_levels, + .bat_type = bat_type_thermistor, + .n_btypes = 3, + .batt_id = 0, + .interval_charging = 5, + .interval_not_charging = 120, + .temp_hysteresis = 3, + .gnd_lift_resistance = 34, + .maxi = &maxi_params, + .chg_params = &chg, + .fg_params = &fg, +}; + +int __devinit +bmdevs_of_probe(struct device *dev, + struct device_node *np, + struct abx500_bm_data **battery) +{ + struct abx500_battery_type *btype; + struct device_node *np_bat_supply; + struct abx500_bm_data *bat; + const char *btech; + char bat_tech[8]; + int i, thermistor; + + *battery = &ab8500_bm_data; + + /* get phandle to 'battery-info' node */ + np_bat_supply = of_parse_phandle(np, "battery", 0); + if (!np_bat_supply) { + dev_err(dev, "missing property battery\n"); + return -EINVAL; + } + if (of_property_read_bool(np_bat_supply, + "thermistor-on-batctrl")) + thermistor = NTC_INTERNAL; + else + thermistor = NTC_EXTERNAL; + + bat = *battery; + if (thermistor == NTC_EXTERNAL) { + bat->n_btypes = 4; + bat->bat_type = bat_type_ext_thermistor; + bat->adc_therm = ABx500_ADC_THERM_BATTEMP; + } + btech = of_get_property(np_bat_supply, + "stericsson,battery-type", NULL); + if (!btech) { + dev_warn(dev, "missing property battery-name/type\n"); + strcpy(bat_tech, "UNKNOWN"); + } else { + strcpy(bat_tech, btech); + } + + if (strncmp(bat_tech, "LION", 4) == 0) { + bat->no_maintenance = true; + bat->chg_unknown_bat = true; + bat->bat_type[BATTERY_UNKNOWN].charge_full_design = 2600; + bat->bat_type[BATTERY_UNKNOWN].termination_vol = 4150; + bat->bat_type[BATTERY_UNKNOWN].recharge_vol = 4130; + bat->bat_type[BATTERY_UNKNOWN].normal_cur_lvl = 520; + bat->bat_type[BATTERY_UNKNOWN].normal_vol_lvl = 4200; + } + /* select the battery resolution table */ + for (i = 0; i < bat->n_btypes; ++i) { + btype = (bat->bat_type + i); + if (thermistor == NTC_EXTERNAL) { + btype->batres_tbl = + temp_to_batres_tbl_ext_thermistor; + } else if (strncmp(bat_tech, "LION", 4) == 0) { + btype->batres_tbl = + temp_to_batres_tbl_9100; + } else { + btype->batres_tbl = + temp_to_batres_tbl_thermistor; + } + } + of_node_put(np_bat_supply); + return 0; +} diff --git a/drivers/power/ab8500_btemp.c b/drivers/power/ab8500_btemp.c index e3b6395b20d..abc2abc16a5 100644 --- a/drivers/power/ab8500_btemp.c +++ b/drivers/power/ab8500_btemp.c @@ -93,7 +93,7 @@ struct ab8500_btemp { struct ab8500 *parent; struct ab8500_gpadc *gpadc; struct ab8500_fg *fg; - struct abx500_btemp_platform_data *pdata; + struct abx500_bmdevs_plat_data *pdata; struct abx500_bm_data *bat; struct power_supply btemp_psy; struct ab8500_btemp_events events; @@ -962,10 +962,10 @@ static int __devexit ab8500_btemp_remove(struct platform_device *pdev) static int __devinit ab8500_btemp_probe(struct platform_device *pdev) { + struct abx500_bmdevs_plat_data *plat_data = pdev->dev.platform_data; + struct ab8500_btemp *di; int irq, i, ret = 0; u8 val; - 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"); @@ -982,21 +982,13 @@ static int __devinit ab8500_btemp_probe(struct platform_device *pdev) di->gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); /* get btemp specific platform data */ - di->pdata = plat_data->btemp; + di->pdata = plat_data; if (!di->pdata) { dev_err(di->dev, "no btemp platform data supplied\n"); ret = -EINVAL; goto free_device_info; } - /* get battery specific platform data */ - di->bat = plat_data->battery; - if (!di->bat) { - dev_err(di->dev, "no battery platform data supplied\n"); - ret = -EINVAL; - goto free_device_info; - } - /* BTEMP supply */ di->btemp_psy.name = "ab8500_btemp"; di->btemp_psy.type = POWER_SUPPLY_TYPE_BATTERY; diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c index 26ff759e222..723edb47b1d 100644 --- a/drivers/power/ab8500_charger.c +++ b/drivers/power/ab8500_charger.c @@ -220,7 +220,7 @@ struct ab8500_charger { bool autopower; struct ab8500 *parent; struct ab8500_gpadc *gpadc; - struct abx500_charger_platform_data *pdata; + struct abx500_bmdevs_plat_data *pdata; struct abx500_bm_data *bat; struct ab8500_charger_event_flags flags; struct ab8500_charger_usb_state usb_state; @@ -2533,9 +2533,9 @@ 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 = pdev->dev.platform_data; + struct abx500_bmdevs_plat_data *plat_data = pdev->dev.platform_data; struct ab8500_charger *di; + int irq, i, charger_status, ret = 0; if (!plat_data) { dev_err(&pdev->dev, "No platform data\n"); @@ -2555,21 +2555,13 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev) spin_lock_init(&di->usb_state.usb_lock); /* get charger specific platform data */ - di->pdata = plat_data->charger; + di->pdata = plat_data; if (!di->pdata) { dev_err(di->dev, "no charger platform data supplied\n"); ret = -EINVAL; goto free_device_info; } - /* get battery specific platform data */ - di->bat = plat_data->battery; - if (!di->bat) { - dev_err(di->dev, "no battery platform data supplied\n"); - ret = -EINVAL; - goto free_device_info; - } - di->autopower = false; /* AC supply */ diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c index 2db8cc25439..ed62ef788eb 100644 --- a/drivers/power/ab8500_fg.c +++ b/drivers/power/ab8500_fg.c @@ -22,15 +22,16 @@ #include #include #include -#include -#include #include -#include #include -#include -#include #include +#include #include +#include +#include +#include +#include +#include #define MILLI_TO_MICRO 1000 #define FG_LSB_IN_MA 1627 @@ -172,7 +173,6 @@ struct inst_curr_result_list { * @avg_cap: Average capacity filter * @parent: Pointer to the struct ab8500 * @gpadc: Pointer to the struct gpadc - * @pdata: Pointer to the abx500_fg platform data * @bat: Pointer to the abx500_bm platform data * @fg_psy: Structure that holds the FG specific battery properties * @fg_wq: Work queue for running the FG algorithm @@ -212,7 +212,6 @@ struct ab8500_fg { struct ab8500_fg_avg_cap avg_cap; struct ab8500 *parent; struct ab8500_gpadc *gpadc; - struct abx500_fg_platform_data *pdata; struct abx500_bm_data *bat; struct power_supply fg_psy; struct workqueue_struct *fg_wq; @@ -2429,7 +2428,6 @@ static int __devexit ab8500_fg_remove(struct platform_device *pdev) flush_scheduled_work(); power_supply_unregister(&di->fg_psy); platform_set_drvdata(pdev, NULL); - kfree(di); return ret; } @@ -2442,21 +2440,39 @@ static struct ab8500_fg_interrupts ab8500_fg_irq[] = { {"CCEOC", ab8500_fg_cc_data_end_handler}, }; +static char *supply_interface[] = { + "ab8500_chargalg", + "ab8500_usb", +}; + static int __devinit ab8500_fg_probe(struct platform_device *pdev) { + struct device_node *np = pdev->dev.of_node; + struct ab8500_fg *di; int i, irq; int ret = 0; - 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; - } - di = kzalloc(sizeof(*di), GFP_KERNEL); - if (!di) + di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL); + if (!di) { + dev_err(&pdev->dev, "%s no mem for ab8500_fg\n", __func__); return -ENOMEM; + } + di->bat = pdev->mfd_cell->platform_data; + if (!di->bat) { + if (np) { + ret = bmdevs_of_probe(&pdev->dev, np, &di->bat); + if (ret) { + dev_err(&pdev->dev, + "failed to get battery information\n"); + return ret; + } + } else { + dev_err(&pdev->dev, "missing dt node for ab8500_fg\n"); + return -EINVAL; + } + } else { + dev_info(&pdev->dev, "falling back to legacy platform data\n"); + } mutex_init(&di->cc_lock); @@ -2465,29 +2481,13 @@ static int __devinit ab8500_fg_probe(struct platform_device *pdev) di->parent = dev_get_drvdata(pdev->dev.parent); di->gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - /* get fg specific platform data */ - di->pdata = plat_data->fg; - if (!di->pdata) { - dev_err(di->dev, "no fg platform data supplied\n"); - ret = -EINVAL; - goto free_device_info; - } - - /* get battery specific platform data */ - di->bat = plat_data->battery; - if (!di->bat) { - dev_err(di->dev, "no battery platform data supplied\n"); - ret = -EINVAL; - goto free_device_info; - } - di->fg_psy.name = "ab8500_fg"; di->fg_psy.type = POWER_SUPPLY_TYPE_BATTERY; di->fg_psy.properties = ab8500_fg_props; di->fg_psy.num_properties = ARRAY_SIZE(ab8500_fg_props); di->fg_psy.get_property = ab8500_fg_get_property; - di->fg_psy.supplied_to = di->pdata->supplied_to; - di->fg_psy.num_supplicants = di->pdata->num_supplicants; + di->fg_psy.supplied_to = supply_interface; + di->fg_psy.num_supplicants = ARRAY_SIZE(supply_interface), di->fg_psy.external_power_changed = ab8500_fg_external_power_changed; di->bat_cap.max_mah_design = MILLI_TO_MICRO * @@ -2506,8 +2506,7 @@ static int __devinit ab8500_fg_probe(struct platform_device *pdev) di->fg_wq = create_singlethread_workqueue("ab8500_fg_wq"); if (di->fg_wq == NULL) { dev_err(di->dev, "failed to create work queue\n"); - ret = -ENOMEM; - goto free_device_info; + return -ENOMEM; } /* Init work for running the fg algorithm instantly */ @@ -2606,12 +2605,14 @@ free_irq: } free_inst_curr_wq: destroy_workqueue(di->fg_wq); -free_device_info: - kfree(di); - return ret; } +static const struct of_device_id ab8500_fg_match[] = { + { .compatible = "stericsson,ab8500-fg", }, + { }, +}; + static struct platform_driver ab8500_fg_driver = { .probe = ab8500_fg_probe, .remove = __devexit_p(ab8500_fg_remove), @@ -2620,6 +2621,7 @@ static struct platform_driver ab8500_fg_driver = { .driver = { .name = "ab8500-fg", .owner = THIS_MODULE, + .of_match_table = ab8500_fg_match, }, }; diff --git a/drivers/power/abx500_chargalg.c b/drivers/power/abx500_chargalg.c index 4d302803ffc..758ea76de2f 100644 --- a/drivers/power/abx500_chargalg.c +++ b/drivers/power/abx500_chargalg.c @@ -231,7 +231,7 @@ struct abx500_chargalg { struct abx500_chargalg_charger_info chg_info; struct abx500_chargalg_battery_data batt_data; struct abx500_chargalg_suspension_status susp_status; - struct abx500_chargalg_platform_data *pdata; + struct abx500_bmdevs_plat_data *pdata; struct abx500_bm_data *bat; struct power_supply chargalg_psy; struct ux500_charger *ac_chg; @@ -1802,7 +1802,7 @@ static int __devexit abx500_chargalg_remove(struct platform_device *pdev) static int __devinit abx500_chargalg_probe(struct platform_device *pdev) { - struct abx500_bm_plat_data *plat_data; + struct abx500_bmdevs_plat_data *plat_data; int ret = 0; struct abx500_chargalg *di = @@ -1812,10 +1812,8 @@ static int __devinit abx500_chargalg_probe(struct platform_device *pdev) /* get device struct */ di->dev = &pdev->dev; - plat_data = pdev->dev.platform_data; - di->pdata = plat_data->chargalg; - di->bat = plat_data->battery; + di->pdata = plat_data; /* chargalg supply */ di->chargalg_psy.name = "abx500_chargalg"; diff --git a/include/linux/mfd/abx500.h b/include/linux/mfd/abx500.h index 5d5298d5602..33f2c58554f 100644 --- a/include/linux/mfd/abx500.h +++ b/include/linux/mfd/abx500.h @@ -267,39 +267,27 @@ struct abx500_bm_data { int gnd_lift_resistance; const struct abx500_maxim_parameters *maxi; const struct abx500_bm_capacity_levels *cap_levels; - const struct abx500_battery_type *bat_type; + struct abx500_battery_type *bat_type; const struct abx500_bm_charger_parameters *chg_params; const struct abx500_fg_parameters *fg_params; }; -struct abx500_chargalg_platform_data { - char **supplied_to; - size_t num_supplicants; -}; - -struct abx500_charger_platform_data { - char **supplied_to; - size_t num_supplicants; - bool autopower_cfg; -}; +extern struct abx500_bm_data ab8500_bm_data; -struct abx500_btemp_platform_data { - char **supplied_to; - size_t num_supplicants; +struct abx500_bmdevs_plat_data { + char **supplied_to; + size_t num_supplicants; + bool autopower_cfg; }; -struct abx500_fg_platform_data { - char **supplied_to; - size_t num_supplicants; +enum { + NTC_EXTERNAL = 0, + NTC_INTERNAL, }; -struct abx500_bm_plat_data { - struct abx500_bm_data *battery; - struct abx500_charger_platform_data *charger; - struct abx500_btemp_platform_data *btemp; - struct abx500_fg_platform_data *fg; - struct abx500_chargalg_platform_data *chargalg; -}; +int bmdevs_of_probe(struct device *dev, + struct device_node *np, + struct abx500_bm_data **battery); int abx500_set_register_interruptible(struct device *dev, u8 bank, u8 reg, u8 value); -- cgit v1.2.3 From bd9e8ab2d58d2183aa723bf0eb8e07d4362b4975 Mon Sep 17 00:00:00 2001 From: "Rajanikanth H.V" Date: Sun, 18 Nov 2012 19:16:58 -0800 Subject: ab8500: Add devicetree support for btemp This patch adds device tree support for battery-temperature-monitor driver Signed-off-by: Rajanikanth H.V Signed-off-by: Anton Vorontsov --- Documentation/devicetree/bindings/mfd/ab8500.txt | 6 ++ .../bindings/power_supply/ab8500/btemp.txt | 16 ++++++ arch/arm/boot/dts/dbx5x0.dtsi | 5 ++ drivers/mfd/ab8500-core.c | 5 ++ drivers/power/Kconfig | 6 -- drivers/power/ab8500_bmdata.c | 4 +- drivers/power/ab8500_btemp.c | 67 +++++++++++++--------- 7 files changed, 73 insertions(+), 36 deletions(-) create mode 100644 Documentation/devicetree/bindings/power_supply/ab8500/btemp.txt diff --git a/Documentation/devicetree/bindings/mfd/ab8500.txt b/Documentation/devicetree/bindings/mfd/ab8500.txt index 6ca8d817ef9..f2ee0e7a5da 100644 --- a/Documentation/devicetree/bindings/mfd/ab8500.txt +++ b/Documentation/devicetree/bindings/mfd/ab8500.txt @@ -30,6 +30,12 @@ ab8500-fg : : vddadc : Fuel Gauge : LOW_BAT_F : : LOW threshold battery voltage : CC_INT_CALIB : : Coulomb Counter Internal Calibration : CCEOC : : Coulomb Counter End of Conversion +ab8500-btemp : : vtvout : Battery Temperature + : BAT_CTRL_INDB : : Battery Removal Indicator + : BTEMP_LOW : : Btemp < BtempLow, if battery temperature is lower than -10°C + : BTEMP_LOW_MEDIUM : : BtempLow < Btemp < BtempMedium,if battery temperature is between -10 and 0°C + : BTEMP_MEDIUM_HIGH : : BtempMedium < Btemp < BtempHigh,if battery temperature is between 0°C and“MaxTemp + : BTEMP_HIGH : : Btemp > BtempHigh, if battery temperature is higher than “MaxTemp ab8500-gpadc : HW_CONV_END : vddadc : Analogue to Digital Converter SW_CONV_END : : ab8500-gpio : : : GPIO Controller diff --git a/Documentation/devicetree/bindings/power_supply/ab8500/btemp.txt b/Documentation/devicetree/bindings/power_supply/ab8500/btemp.txt new file mode 100644 index 00000000000..0ba1bcc7f33 --- /dev/null +++ b/Documentation/devicetree/bindings/power_supply/ab8500/btemp.txt @@ -0,0 +1,16 @@ +=== AB8500 Battery Temperature Monitor Driver === + +The properties below describes the node for btemp driver. + +Required Properties: +- compatible = Shall be: "stericsson,ab8500-btemp" +- battery = Shall be battery specific information + + Example: + ab8500_btemp { + compatible = "stericsson,ab8500-btemp"; + battery = <&ab8500_battery>; + }; + +For information on battery specific node, Ref: +Documentation/devicetree/bindings/power_supply/ab8500/fg.txt diff --git a/arch/arm/boot/dts/dbx5x0.dtsi b/arch/arm/boot/dts/dbx5x0.dtsi index 0c81986904c..d170fadd7dd 100644 --- a/arch/arm/boot/dts/dbx5x0.dtsi +++ b/arch/arm/boot/dts/dbx5x0.dtsi @@ -362,6 +362,11 @@ battery = <&ab8500_battery>; }; + ab8500_btemp { + compatible = "stericsson,ab8500-btemp"; + battery = <&ab8500_battery>; + }; + ab8500_usb { compatible = "stericsson,ab8500-usb"; interrupts = < 90 0x4 diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index 7c3017ba73e..94d45be5c1b 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -1046,8 +1046,13 @@ static struct mfd_cell __devinitdata ab8500_bm_devs[] = { }, { .name = "ab8500-btemp", + .of_compatible = "stericsson,ab8500-btemp", .num_resources = ARRAY_SIZE(ab8500_btemp_resources), .resources = ab8500_btemp_resources, +#ifndef CONFIG_OF + .platform_data = &ab8500_bm_data, + .pdata_size = sizeof(ab8500_bm_data), +#endif }, { .name = "ab8500-fg", diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 523a58014ed..263499f8709 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -346,12 +346,6 @@ config AB8500_BM help Say Y to include support for AB8500 battery management. -config AB8500_BATTERY_THERM_ON_BATCTRL - bool "Thermistor connected on BATCTRL ADC" - depends on AB8500_BM - help - Say Y to enable battery temperature measurements using - thermistor connected on BATCTRL ADC. endif # POWER_SUPPLY source "drivers/power/avs/Kconfig" diff --git a/drivers/power/ab8500_bmdata.c b/drivers/power/ab8500_bmdata.c index e7639b6659f..03cc528425c 100644 --- a/drivers/power/ab8500_bmdata.c +++ b/drivers/power/ab8500_bmdata.c @@ -30,7 +30,7 @@ static struct abx500_res_to_temp temp_tbl_A_thermistor[] = { }; static struct abx500_res_to_temp temp_tbl_B_thermistor[] = { - {-5, 165418}, + {-5, 200000}, { 0, 159024}, { 5, 151921}, {10, 144300}, @@ -240,7 +240,7 @@ static struct abx500_battery_type bat_type_thermistor[] = { }, { .name = POWER_SUPPLY_TECHNOLOGY_LIPO, - .resis_high = 165418, + .resis_high = 200000, .resis_low = 82869, .battery_resistance = 300, .charge_full_design = 900, diff --git a/drivers/power/ab8500_btemp.c b/drivers/power/ab8500_btemp.c index abc2abc16a5..8135542ee74 100644 --- a/drivers/power/ab8500_btemp.c +++ b/drivers/power/ab8500_btemp.c @@ -20,11 +20,13 @@ #include #include #include -#include +#include +#include +#include #include +#include #include #include -#include #define VTVOUT_V 1800 @@ -76,7 +78,6 @@ struct ab8500_btemp_ranges { * @parent: Pointer to the struct ab8500 * @gpadc: Pointer to the struct gpadc * @fg: Pointer to the struct fg - * @pdata: Pointer to the abx500_btemp platform data * @bat: Pointer to the abx500_bm platform data * @btemp_psy: Structure for BTEMP specific battery properties * @events: Structure for information about events triggered @@ -93,7 +94,6 @@ struct ab8500_btemp { struct ab8500 *parent; struct ab8500_gpadc *gpadc; struct ab8500_fg *fg; - struct abx500_bmdevs_plat_data *pdata; struct abx500_bm_data *bat; struct power_supply btemp_psy; struct ab8500_btemp_events events; @@ -955,48 +955,57 @@ static int __devexit ab8500_btemp_remove(struct platform_device *pdev) flush_scheduled_work(); power_supply_unregister(&di->btemp_psy); platform_set_drvdata(pdev, NULL); - kfree(di); return 0; } +static char *supply_interface[] = { + "ab8500_chargalg", + "ab8500_fg", +}; + static int __devinit ab8500_btemp_probe(struct platform_device *pdev) { - struct abx500_bmdevs_plat_data *plat_data = pdev->dev.platform_data; + struct device_node *np = pdev->dev.of_node; struct ab8500_btemp *di; int irq, i, ret = 0; u8 val; - if (!plat_data) { - dev_err(&pdev->dev, "No platform data\n"); - return -EINVAL; - } - - di = kzalloc(sizeof(*di), GFP_KERNEL); - if (!di) + di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL); + if (!di) { + dev_err(&pdev->dev, "%s no mem for ab8500_btemp\n", __func__); return -ENOMEM; + } + di->bat = pdev->mfd_cell->platform_data; + if (!di->bat) { + if (np) { + ret = bmdevs_of_probe(&pdev->dev, np, &di->bat); + if (ret) { + dev_err(&pdev->dev, + "failed to get battery information\n"); + return ret; + } + } else { + dev_err(&pdev->dev, "missing dt node for ab8500_btemp\n"); + return -EINVAL; + } + } else { + dev_info(&pdev->dev, "falling back to legacy platform data\n"); + } /* get parent data */ di->dev = &pdev->dev; di->parent = dev_get_drvdata(pdev->dev.parent); di->gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - /* get btemp specific platform data */ - di->pdata = plat_data; - if (!di->pdata) { - dev_err(di->dev, "no btemp platform data supplied\n"); - ret = -EINVAL; - goto free_device_info; - } - /* BTEMP supply */ di->btemp_psy.name = "ab8500_btemp"; di->btemp_psy.type = POWER_SUPPLY_TYPE_BATTERY; di->btemp_psy.properties = ab8500_btemp_props; di->btemp_psy.num_properties = ARRAY_SIZE(ab8500_btemp_props); di->btemp_psy.get_property = ab8500_btemp_get_property; - di->btemp_psy.supplied_to = di->pdata->supplied_to; - di->btemp_psy.num_supplicants = di->pdata->num_supplicants; + di->btemp_psy.supplied_to = supply_interface; + di->btemp_psy.num_supplicants = ARRAY_SIZE(supply_interface); di->btemp_psy.external_power_changed = ab8500_btemp_external_power_changed; @@ -1006,8 +1015,7 @@ static int __devinit ab8500_btemp_probe(struct platform_device *pdev) create_singlethread_workqueue("ab8500_btemp_wq"); if (di->btemp_wq == NULL) { dev_err(di->dev, "failed to create work queue\n"); - ret = -ENOMEM; - goto free_device_info; + return -ENOMEM; } /* Init work for measuring temperature periodically */ @@ -1085,12 +1093,14 @@ free_irq: } free_btemp_wq: destroy_workqueue(di->btemp_wq); -free_device_info: - kfree(di); - return ret; } +static const struct of_device_id ab8500_btemp_match[] = { + { .compatible = "stericsson,ab8500-btemp", }, + { }, +}; + static struct platform_driver ab8500_btemp_driver = { .probe = ab8500_btemp_probe, .remove = __devexit_p(ab8500_btemp_remove), @@ -1099,6 +1109,7 @@ static struct platform_driver ab8500_btemp_driver = { .driver = { .name = "ab8500-btemp", .owner = THIS_MODULE, + .of_match_table = ab8500_btemp_match, }, }; -- cgit v1.2.3 From 4aef72dbb2e8997e627dd94ae2b9109dc09fffaa Mon Sep 17 00:00:00 2001 From: "Rajanikanth H.V" Date: Sun, 18 Nov 2012 19:17:47 -0800 Subject: ab8500: Add devicetree support for charger This patch adds device tree support for ab8500-charger driver Signed-off-by: Rajanikanth H.V Signed-off-by: Anton Vorontsov --- Documentation/devicetree/bindings/mfd/ab8500.txt | 14 ++++ .../bindings/power_supply/ab8500/charger.txt | 25 ++++++++ arch/arm/boot/dts/dbx5x0.dtsi | 6 ++ drivers/mfd/ab8500-core.c | 5 ++ drivers/power/ab8500_charger.c | 74 +++++++++++++--------- 5 files changed, 95 insertions(+), 29 deletions(-) create mode 100644 Documentation/devicetree/bindings/power_supply/ab8500/charger.txt diff --git a/Documentation/devicetree/bindings/mfd/ab8500.txt b/Documentation/devicetree/bindings/mfd/ab8500.txt index f2ee0e7a5da..13b707b7355 100644 --- a/Documentation/devicetree/bindings/mfd/ab8500.txt +++ b/Documentation/devicetree/bindings/mfd/ab8500.txt @@ -36,6 +36,20 @@ ab8500-btemp : : vtvout : Battery Temperature : BTEMP_LOW_MEDIUM : : BtempLow < Btemp < BtempMedium,if battery temperature is between -10 and 0°C : BTEMP_MEDIUM_HIGH : : BtempMedium < Btemp < BtempHigh,if battery temperature is between 0°C and“MaxTemp : BTEMP_HIGH : : Btemp > BtempHigh, if battery temperature is higher than “MaxTemp +ab8500-charger : : vddadc : Charger interface + : MAIN_CH_UNPLUG_DET : : main charger unplug detection management (not in 8505) + : MAIN_CHARGE_PLUG_DET : : main charger plug detection management (not in 8505) + : MAIN_EXT_CH_NOT_OK : : main charger not OK + : MAIN_CH_TH_PROT_R : : Die temp is above main charger + : MAIN_CH_TH_PROT_F : : Die temp is below main charger + : VBUS_DET_F : : VBUS falling detected + : VBUS_DET_R : : VBUS rising detected + : USB_LINK_STATUS : : USB link status has changed + : USB_CH_TH_PROT_R : : Die temp is above usb charger + : USB_CH_TH_PROT_F : : Die temp is below usb charger + : USB_CHARGER_NOT_OKR : : allowed USB charger not ok detection + : VBUS_OVV : : Overvoltage on Vbus ball detected (USB charge is stopped) + : CH_WD_EXP : : Charger watchdog detected ab8500-gpadc : HW_CONV_END : vddadc : Analogue to Digital Converter SW_CONV_END : : ab8500-gpio : : : GPIO Controller diff --git a/Documentation/devicetree/bindings/power_supply/ab8500/charger.txt b/Documentation/devicetree/bindings/power_supply/ab8500/charger.txt new file mode 100644 index 00000000000..6bdbb08ea9e --- /dev/null +++ b/Documentation/devicetree/bindings/power_supply/ab8500/charger.txt @@ -0,0 +1,25 @@ +=== AB8500 Charger Driver === + +Required Properties: +- compatible = Shall be "stericsson,ab8500-charger" +- battery = Shall be battery specific information + Example: + ab8500_charger { + compatible = "stericsson,ab8500-charger"; + battery = <&ab8500_battery>; + }; + +- vddadc-supply: Supply for USB and Main charger + Example: + ab8500-charger { + vddadc-supply = <&ab8500_ldo_tvout_reg>; + } +- autopower_cfg: + Boolean value depicting the presence of 'automatic poweron after powerloss' + Example: + ab8500-charger { + autopower_cfg; + }; + +For information on battery specific node, Ref: +Documentation/devicetree/bindings/power_supply/ab8500/fg.txt diff --git a/arch/arm/boot/dts/dbx5x0.dtsi b/arch/arm/boot/dts/dbx5x0.dtsi index d170fadd7dd..1d4ad3098ae 100644 --- a/arch/arm/boot/dts/dbx5x0.dtsi +++ b/arch/arm/boot/dts/dbx5x0.dtsi @@ -367,6 +367,12 @@ battery = <&ab8500_battery>; }; + ab8500_charger { + compatible = "stericsson,ab8500-charger"; + battery = <&ab8500_battery>; + vddadc-supply = <&ab8500_ldo_tvout_reg>; + }; + ab8500_usb { compatible = "stericsson,ab8500-usb"; interrupts = < 90 0x4 diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index 94d45be5c1b..c7a120bfd50 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -1041,8 +1041,13 @@ static struct mfd_cell __devinitdata abx500_common_devs[] = { static struct mfd_cell __devinitdata ab8500_bm_devs[] = { { .name = "ab8500-charger", + .of_compatible = "stericsson,ab8500-charger", .num_resources = ARRAY_SIZE(ab8500_charger_resources), .resources = ab8500_charger_resources, +#ifndef CONFIG_OF + .platform_data = &ab8500_bm_data, + .pdata_size = sizeof(ab8500_bm_data), +#endif }, { .name = "ab8500-btemp", diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c index 723edb47b1d..dddc9473f72 100644 --- a/drivers/power/ab8500_charger.c +++ b/drivers/power/ab8500_charger.c @@ -23,6 +23,8 @@ #include #include #include +#include +#include #include #include #include @@ -181,9 +183,9 @@ struct ab8500_charger_usb_state { * @vbat Battery voltage * @old_vbat Previously measured battery voltage * @autopower Indicate if we should have automatic pwron after pwrloss + * @autopower_cfg platform specific power config support for "pwron after pwrloss" * @parent: Pointer to the struct ab8500 * @gpadc: Pointer to the struct gpadc - * @pdata: Pointer to the abx500_charger platform data * @bat: Pointer to the abx500_bm platform data * @flags: Structure for information about events triggered * @usb_state: Structure for usb stack information @@ -218,9 +220,9 @@ struct ab8500_charger { int vbat; int old_vbat; bool autopower; + bool autopower_cfg; struct ab8500 *parent; struct ab8500_gpadc *gpadc; - struct abx500_bmdevs_plat_data *pdata; struct abx500_bm_data *bat; struct ab8500_charger_event_flags flags; struct ab8500_charger_usb_state usb_state; @@ -322,7 +324,7 @@ static void ab8500_power_loss_handling(struct ab8500_charger *di) static void ab8500_power_supply_changed(struct ab8500_charger *di, struct power_supply *psy) { - if (di->pdata->autopower_cfg) { + if (di->autopower_cfg) { if (!di->usb.charger_connected && !di->ac.charger_connected && di->autopower) { @@ -2526,25 +2528,45 @@ static int __devexit ab8500_charger_remove(struct platform_device *pdev) power_supply_unregister(&di->usb_chg.psy); power_supply_unregister(&di->ac_chg.psy); platform_set_drvdata(pdev, NULL); - kfree(di); return 0; } +static char *supply_interface[] = { + "ab8500_chargalg", + "ab8500_fg", + "ab8500_btemp", +}; + static int __devinit ab8500_charger_probe(struct platform_device *pdev) { - struct abx500_bmdevs_plat_data *plat_data = pdev->dev.platform_data; + struct device_node *np = pdev->dev.of_node; struct ab8500_charger *di; int irq, i, charger_status, ret = 0; - if (!plat_data) { - dev_err(&pdev->dev, "No platform data\n"); - return -EINVAL; - } - - di = kzalloc(sizeof(*di), GFP_KERNEL); - if (!di) + di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL); + if (!di) { + dev_err(&pdev->dev, "%s no mem for ab8500_charger\n", __func__); return -ENOMEM; + } + di->bat = pdev->mfd_cell->platform_data; + if (!di->bat) { + if (np) { + ret = bmdevs_of_probe(&pdev->dev, np, &di->bat); + if (ret) { + dev_err(&pdev->dev, + "failed to get battery information\n"); + return ret; + } + di->autopower_cfg = of_property_read_bool(np, "autopower_cfg"); + } else { + dev_err(&pdev->dev, "missing dt node for ab8500_charger\n"); + return -EINVAL; + } + } else { + dev_info(&pdev->dev, "falling back to legacy platform data\n"); + di->autopower_cfg = false; + } /* get parent data */ di->dev = &pdev->dev; @@ -2554,14 +2576,6 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev) /* initialize lock */ spin_lock_init(&di->usb_state.usb_lock); - /* get charger specific platform data */ - di->pdata = plat_data; - if (!di->pdata) { - dev_err(di->dev, "no charger platform data supplied\n"); - ret = -EINVAL; - goto free_device_info; - } - di->autopower = false; /* AC supply */ @@ -2571,8 +2585,8 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev) di->ac_chg.psy.properties = ab8500_charger_ac_props; di->ac_chg.psy.num_properties = ARRAY_SIZE(ab8500_charger_ac_props); di->ac_chg.psy.get_property = ab8500_charger_ac_get_property; - di->ac_chg.psy.supplied_to = di->pdata->supplied_to; - di->ac_chg.psy.num_supplicants = di->pdata->num_supplicants; + di->ac_chg.psy.supplied_to = supply_interface; + di->ac_chg.psy.num_supplicants = ARRAY_SIZE(supply_interface), /* ux500_charger sub-class */ di->ac_chg.ops.enable = &ab8500_charger_ac_en; di->ac_chg.ops.kick_wd = &ab8500_charger_watchdog_kick; @@ -2589,8 +2603,8 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev) di->usb_chg.psy.properties = ab8500_charger_usb_props; di->usb_chg.psy.num_properties = ARRAY_SIZE(ab8500_charger_usb_props); di->usb_chg.psy.get_property = ab8500_charger_usb_get_property; - di->usb_chg.psy.supplied_to = di->pdata->supplied_to; - di->usb_chg.psy.num_supplicants = di->pdata->num_supplicants; + di->usb_chg.psy.supplied_to = supply_interface; + di->usb_chg.psy.num_supplicants = ARRAY_SIZE(supply_interface), /* ux500_charger sub-class */ di->usb_chg.ops.enable = &ab8500_charger_usb_en; di->usb_chg.ops.kick_wd = &ab8500_charger_watchdog_kick; @@ -2606,8 +2620,7 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev) create_singlethread_workqueue("ab8500_charger_wq"); if (di->charger_wq == NULL) { dev_err(di->dev, "failed to create work queue\n"); - ret = -ENOMEM; - goto free_device_info; + return -ENOMEM; } /* Init work for HW failure check */ @@ -2749,12 +2762,14 @@ free_regulator: regulator_put(di->regu); free_charger_wq: destroy_workqueue(di->charger_wq); -free_device_info: - kfree(di); - return ret; } +static const struct of_device_id ab8500_charger_match[] = { + { .compatible = "stericsson,ab8500-charger", }, + { }, +}; + static struct platform_driver ab8500_charger_driver = { .probe = ab8500_charger_probe, .remove = __devexit_p(ab8500_charger_remove), @@ -2763,6 +2778,7 @@ static struct platform_driver ab8500_charger_driver = { .driver = { .name = "ab8500-charger", .owner = THIS_MODULE, + .of_match_table = ab8500_charger_match, }, }; -- cgit v1.2.3 From a12810ab9fcf0c9fd5e50b5e350a3ffbeaa571be Mon Sep 17 00:00:00 2001 From: "Rajanikanth H.V" Date: Wed, 31 Oct 2012 15:40:33 +0000 Subject: ab8500: Add devicetree support for chargalg This patch adds device tree support for charging algorithm driver Signed-off-by: Rajanikanth H.V Signed-off-by: Anton Vorontsov --- .../bindings/power_supply/ab8500/chargalg.txt | 16 +++++++ arch/arm/boot/dts/dbx5x0.dtsi | 5 ++ drivers/mfd/ab8500-core.c | 5 ++ drivers/power/abx500_chargalg.c | 54 +++++++++++++++------- include/linux/mfd/abx500.h | 6 --- 5 files changed, 64 insertions(+), 22 deletions(-) create mode 100644 Documentation/devicetree/bindings/power_supply/ab8500/chargalg.txt diff --git a/Documentation/devicetree/bindings/power_supply/ab8500/chargalg.txt b/Documentation/devicetree/bindings/power_supply/ab8500/chargalg.txt new file mode 100644 index 00000000000..ef532837112 --- /dev/null +++ b/Documentation/devicetree/bindings/power_supply/ab8500/chargalg.txt @@ -0,0 +1,16 @@ +=== AB8500 Charging Algorithm Driver === + +The properties below describes the node for chargalg driver. + +Required Properties: +- compatible = Shall be: "stericsson,ab8500-chargalg" +- battery = Shall be battery specific information + +Example: +ab8500_chargalg { + compatible = "stericsson,ab8500-chargalg"; + battery = <&ab8500_battery>; +}; + +For information on battery specific node, Ref: +Documentation/devicetree/bindings/power_supply/ab8500/fg.txt diff --git a/arch/arm/boot/dts/dbx5x0.dtsi b/arch/arm/boot/dts/dbx5x0.dtsi index 1d4ad3098ae..12a68af4490 100644 --- a/arch/arm/boot/dts/dbx5x0.dtsi +++ b/arch/arm/boot/dts/dbx5x0.dtsi @@ -373,6 +373,11 @@ vddadc-supply = <&ab8500_ldo_tvout_reg>; }; + ab8500_chargalg { + compatible = "stericsson,ab8500-chargalg"; + battery = <&ab8500_battery>; + }; + ab8500_usb { compatible = "stericsson,ab8500-usb"; interrupts = < 90 0x4 diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index c7a120bfd50..5ec70f26b9d 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -1071,8 +1071,13 @@ static struct mfd_cell __devinitdata ab8500_bm_devs[] = { }, { .name = "ab8500-chargalg", + .of_compatible = "stericsson,ab8500-chargalg", .num_resources = ARRAY_SIZE(ab8500_chargalg_resources), .resources = ab8500_chargalg_resources, +#ifndef CONFIG_OF + .platform_data = &ab8500_bm_data, + .pdata_size = sizeof(ab8500_bm_data), +#endif }, }; diff --git a/drivers/power/abx500_chargalg.c b/drivers/power/abx500_chargalg.c index 758ea76de2f..dcdc4393b9e 100644 --- a/drivers/power/abx500_chargalg.c +++ b/drivers/power/abx500_chargalg.c @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include #include #include @@ -205,7 +207,6 @@ enum maxim_ret { * @chg_info: information about connected charger types * @batt_data: data of the battery * @susp_status: current charger suspension status - * @pdata: pointer to the abx500_chargalg platform data * @bat: pointer to the abx500_bm platform data * @chargalg_psy: structure that holds the battery properties exposed by * the charging algorithm @@ -231,7 +232,6 @@ struct abx500_chargalg { struct abx500_chargalg_charger_info chg_info; struct abx500_chargalg_battery_data batt_data; struct abx500_chargalg_suspension_status susp_status; - struct abx500_bmdevs_plat_data *pdata; struct abx500_bm_data *bat; struct power_supply chargalg_psy; struct ux500_charger *ac_chg; @@ -1795,25 +1795,44 @@ static int __devexit abx500_chargalg_remove(struct platform_device *pdev) flush_scheduled_work(); power_supply_unregister(&di->chargalg_psy); platform_set_drvdata(pdev, NULL); - kfree(di); return 0; } +static char *supply_interface[] = { + "ab8500_fg", +}; + static int __devinit abx500_chargalg_probe(struct platform_device *pdev) { - struct abx500_bmdevs_plat_data *plat_data; + struct device_node *np = pdev->dev.of_node; + struct abx500_chargalg *di; int ret = 0; - struct abx500_chargalg *di = - kzalloc(sizeof(struct abx500_chargalg), GFP_KERNEL); - if (!di) + di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL); + if (!di) { + dev_err(&pdev->dev, "%s no mem for ab8500_chargalg\n", __func__); return -ENOMEM; + } + di->bat = pdev->mfd_cell->platform_data; + if (!di->bat) { + if (np) { + ret = bmdevs_of_probe(&pdev->dev, np, &di->bat); + if (ret) { + dev_err(&pdev->dev, + "failed to get battery information\n"); + return ret; + } + } else { + dev_err(&pdev->dev, "missing dt node for ab8500_chargalg\n"); + return -EINVAL; + } + } else { + dev_info(&pdev->dev, "falling back to legacy platform data\n"); + } /* get device struct */ di->dev = &pdev->dev; - plat_data = pdev->dev.platform_data; - di->pdata = plat_data; /* chargalg supply */ di->chargalg_psy.name = "abx500_chargalg"; @@ -1821,8 +1840,8 @@ static int __devinit abx500_chargalg_probe(struct platform_device *pdev) di->chargalg_psy.properties = abx500_chargalg_props; di->chargalg_psy.num_properties = ARRAY_SIZE(abx500_chargalg_props); di->chargalg_psy.get_property = abx500_chargalg_get_property; - di->chargalg_psy.supplied_to = di->pdata->supplied_to; - di->chargalg_psy.num_supplicants = di->pdata->num_supplicants; + di->chargalg_psy.supplied_to = supply_interface; + di->chargalg_psy.num_supplicants = ARRAY_SIZE(supply_interface), di->chargalg_psy.external_power_changed = abx500_chargalg_external_power_changed; @@ -1842,7 +1861,7 @@ static int __devinit abx500_chargalg_probe(struct platform_device *pdev) create_singlethread_workqueue("abx500_chargalg_wq"); if (di->chargalg_wq == NULL) { dev_err(di->dev, "failed to create work queue\n"); - goto free_device_info; + return -ENOMEM; } /* Init work for chargalg */ @@ -1883,20 +1902,23 @@ free_psy: power_supply_unregister(&di->chargalg_psy); free_chargalg_wq: destroy_workqueue(di->chargalg_wq); -free_device_info: - kfree(di); - return ret; } +static const struct of_device_id ab8500_chargalg_match[] = { + { .compatible = "stericsson,ab8500-chargalg", }, + { }, +}; + static struct platform_driver abx500_chargalg_driver = { .probe = abx500_chargalg_probe, .remove = __devexit_p(abx500_chargalg_remove), .suspend = abx500_chargalg_suspend, .resume = abx500_chargalg_resume, .driver = { - .name = "abx500-chargalg", + .name = "ab8500-chargalg", .owner = THIS_MODULE, + .of_match_table = ab8500_chargalg_match, }, }; diff --git a/include/linux/mfd/abx500.h b/include/linux/mfd/abx500.h index 33f2c58554f..2138bd33021 100644 --- a/include/linux/mfd/abx500.h +++ b/include/linux/mfd/abx500.h @@ -274,12 +274,6 @@ struct abx500_bm_data { extern struct abx500_bm_data ab8500_bm_data; -struct abx500_bmdevs_plat_data { - char **supplied_to; - size_t num_supplicants; - bool autopower_cfg; -}; - enum { NTC_EXTERNAL = 0, NTC_INTERNAL, -- cgit v1.2.3 From eba3b670a9166a91be5a11fe33290dca6b9457a2 Mon Sep 17 00:00:00 2001 From: Qing Xu Date: Mon, 19 Nov 2012 15:39:12 +0800 Subject: max8925_power: Add support for device-tree initialization This patch adds support for initializing platform data from the device-tree description. Signed-off-by: Qing Xu Signed-off-by: Anton Vorontsov --- drivers/power/max8925_power.c | 51 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/drivers/power/max8925_power.c b/drivers/power/max8925_power.c index daa333bd7eb..b5a3ccb16a1 100644 --- a/drivers/power/max8925_power.c +++ b/drivers/power/max8925_power.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -426,6 +427,54 @@ static __devexit int max8925_deinit_charger(struct max8925_power_info *info) return 0; } +#ifdef CONFIG_OF +static struct max8925_power_pdata * +max8925_power_dt_init(struct platform_device *pdev) +{ + struct device_node *nproot = pdev->dev.parent->of_node; + struct device_node *np; + int batt_detect; + int topoff_threshold; + int fast_charge; + int no_temp_support; + int no_insert_detect; + struct max8925_power_pdata *pdata; + + if (!nproot) + return pdev->dev.platform_data; + + np = of_find_node_by_name(nproot, "charger"); + if (!np) { + dev_err(&pdev->dev, "failed to find charger node\n"); + return NULL; + } + + pdata = devm_kzalloc(&pdev->dev, + sizeof(struct max8925_power_pdata), + GFP_KERNEL); + + of_property_read_u32(np, "topoff-threshold", &topoff_threshold); + of_property_read_u32(np, "batt-detect", &batt_detect); + of_property_read_u32(np, "fast-charge", &fast_charge); + of_property_read_u32(np, "no-insert-detect", &no_insert_detect); + of_property_read_u32(np, "no-temp-support", &no_temp_support); + + pdata->batt_detect = batt_detect; + pdata->fast_charge = fast_charge; + pdata->topoff_threshold = topoff_threshold; + pdata->no_insert_detect = no_insert_detect; + pdata->no_temp_support = no_temp_support; + + return pdata; +} +#else +static struct max8925_power_pdata * +max8925_power_dt_init(struct platform_device *pdev) +{ + return pdev->dev.platform_data; +} +#endif + static __devinit int max8925_power_probe(struct platform_device *pdev) { struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent); @@ -433,7 +482,7 @@ static __devinit int max8925_power_probe(struct platform_device *pdev) struct max8925_power_info *info; int ret; - pdata = pdev->dev.platform_data; + pdata = max8925_power_dt_init(pdev); if (!pdata) { dev_err(&pdev->dev, "platform data isn't assigned to " "power supply\n"); -- cgit v1.2.3