From f07c11ea32eebf81b7c1e59da2d119bf023882f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Sun, 8 Sep 2013 10:50:37 +0200 Subject: [PATCH 01/15] isp1704_charger: Fix driver to work with changes introduced in v3.5 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * omap musb driver does not report USB_EVENT_ENUMERATED event anymore * omap musb driver reporting USB_EVENT_VBUS when charger is connected * read last event from phy->last_event (instead from ulpi register) * do not call wall charger detection more times Signed-off-by: Pali Rohár Signed-off-by: Anton Vorontsov --- drivers/power/isp1704_charger.c | 95 +++++++++++++++------------------ 1 file changed, 42 insertions(+), 53 deletions(-) diff --git a/drivers/power/isp1704_charger.c b/drivers/power/isp1704_charger.c index fc04d191579b..1bb3a91b1acc 100644 --- a/drivers/power/isp1704_charger.c +++ b/drivers/power/isp1704_charger.c @@ -2,6 +2,7 @@ * ISP1704 USB Charger Detection driver * * Copyright (C) 2010 Nokia Corporation + * Copyright (C) 2012 - 2013 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 @@ -65,10 +66,6 @@ struct isp1704_charger { unsigned present:1; unsigned online:1; unsigned current_max; - - /* temp storage variables */ - unsigned long event; - unsigned max_power; }; static inline int isp1704_read(struct isp1704_charger *isp, u32 reg) @@ -231,56 +228,59 @@ static inline int isp1704_charger_detect(struct isp1704_charger *isp) return ret; } +static inline int isp1704_charger_detect_dcp(struct isp1704_charger *isp) +{ + if (isp1704_charger_detect(isp) && + isp1704_charger_type(isp) == POWER_SUPPLY_TYPE_USB_DCP) + return true; + else + return false; +} + static void isp1704_charger_work(struct work_struct *data) { - int detect; - unsigned long event; - unsigned power; struct isp1704_charger *isp = container_of(data, struct isp1704_charger, work); static DEFINE_MUTEX(lock); - event = isp->event; - power = isp->max_power; - mutex_lock(&lock); - if (event != USB_EVENT_NONE) - isp1704_charger_set_power(isp, 1); - - switch (event) { + switch (isp->phy->last_event) { case USB_EVENT_VBUS: - isp->online = true; + /* do not call wall charger detection more times */ + if (!isp->present) { + isp->online = true; + isp->present = 1; + isp1704_charger_set_power(isp, 1); - /* detect charger */ - detect = isp1704_charger_detect(isp); + /* detect wall charger */ + if (isp1704_charger_detect_dcp(isp)) { + isp->psy.type = POWER_SUPPLY_TYPE_USB_DCP; + isp->current_max = 1800; + } else { + isp->psy.type = POWER_SUPPLY_TYPE_USB; + isp->current_max = 500; + } - if (detect) { - isp->present = detect; - isp->psy.type = isp1704_charger_type(isp); - } - - switch (isp->psy.type) { - case POWER_SUPPLY_TYPE_USB_DCP: - isp->current_max = 1800; - break; - case POWER_SUPPLY_TYPE_USB_CDP: - /* - * Only 500mA here or high speed chirp - * handshaking may break - */ - isp->current_max = 500; - /* FALLTHROUGH */ - case POWER_SUPPLY_TYPE_USB: - default: /* enable data pullups */ if (isp->phy->otg->gadget) usb_gadget_connect(isp->phy->otg->gadget); } + + if (isp->psy.type != POWER_SUPPLY_TYPE_USB_DCP) { + /* + * Only 500mA here or high speed chirp + * handshaking may break + */ + if (isp->current_max > 500) + isp->current_max = 500; + + if (isp->current_max > 100) + isp->psy.type = POWER_SUPPLY_TYPE_USB_CDP; + } break; case USB_EVENT_NONE: isp->online = false; - isp->current_max = 0; isp->present = 0; isp->current_max = 0; isp->psy.type = POWER_SUPPLY_TYPE_USB; @@ -298,12 +298,6 @@ static void isp1704_charger_work(struct work_struct *data) isp1704_charger_set_power(isp, 0); break; - case USB_EVENT_ENUMERATED: - if (isp->present) - isp->current_max = 1800; - else - isp->current_max = power; - break; default: goto out; } @@ -314,16 +308,11 @@ out: } static int isp1704_notifier_call(struct notifier_block *nb, - unsigned long event, void *power) + unsigned long val, void *v) { struct isp1704_charger *isp = container_of(nb, struct isp1704_charger, nb); - isp->event = event; - - if (power) - isp->max_power = *((unsigned *)power); - schedule_work(&isp->work); return NOTIFY_OK; @@ -462,13 +451,13 @@ static int isp1704_charger_probe(struct platform_device *pdev) if (isp->phy->otg->gadget) usb_gadget_disconnect(isp->phy->otg->gadget); + if (isp->phy->last_event == USB_EVENT_NONE) + isp1704_charger_set_power(isp, 0); + /* Detect charger if VBUS is valid (the cable was already plugged). */ - ret = isp1704_read(isp, ULPI_USB_INT_STS); - isp1704_charger_set_power(isp, 0); - if ((ret & ULPI_INT_VBUS_VALID) && !isp->phy->otg->default_a) { - isp->event = USB_EVENT_VBUS; + if (isp->phy->last_event == USB_EVENT_VBUS && + !isp->phy->otg->default_a) schedule_work(&isp->work); - } return 0; fail2: From 6de7ef3e309acb1e55ae122b85a2665064832b0c Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 6 Sep 2013 13:31:09 +0530 Subject: [PATCH 02/15] ab8500-charger: Check return value of regulator_enable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Check the return value of regulator_enable to silence the following type of warnings: drivers/power/ab8500_charger.c:1390:20: warning: ignoring return value of ‘regulator_enable’, declared with attribute warn_unused_result [-Wunused-result] Signed-off-by: Sachin Kamat Acked-by: Lee Jones Signed-off-by: Anton Vorontsov --- drivers/power/ab8500_charger.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c index a4c4a10b3a41..fd350189b632 100644 --- a/drivers/power/ab8500_charger.c +++ b/drivers/power/ab8500_charger.c @@ -1387,8 +1387,12 @@ static int ab8500_charger_ac_en(struct ux500_charger *charger, * the GPADC module independant of the AB8500 chargers */ if (!di->vddadc_en_ac) { - regulator_enable(di->regu); - di->vddadc_en_ac = true; + ret = regulator_enable(di->regu); + if (ret) + dev_warn(di->dev, + "Failed to enable regulator\n"); + else + di->vddadc_en_ac = true; } /* Check if the requested voltage or current is valid */ @@ -1556,8 +1560,12 @@ static int ab8500_charger_usb_en(struct ux500_charger *charger, * the GPADC module independant of the AB8500 chargers */ if (!di->vddadc_en_usb) { - regulator_enable(di->regu); - di->vddadc_en_usb = true; + ret = regulator_enable(di->regu); + if (ret) + dev_warn(di->dev, + "Failed to enable regulator\n"); + else + di->vddadc_en_usb = true; } /* Enable USB charging */ From 08a5c7e6842f9c4c1556f147af2a561056345a45 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 6 Sep 2013 13:31:10 +0530 Subject: [PATCH 03/15] ab8500-charger: Remove redundant break Each of the if-else blocks has a break statement. Remove the additional one which is unreachable. Signed-off-by: Sachin Kamat Acked-by: Lee Jones Signed-off-by: Anton Vorontsov --- drivers/power/ab8500_charger.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c index fd350189b632..19110aa613a1 100644 --- a/drivers/power/ab8500_charger.c +++ b/drivers/power/ab8500_charger.c @@ -766,7 +766,6 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di, ret = -ENXIO; break; } - break; case USB_STAT_CARKIT_1: case USB_STAT_CARKIT_2: case USB_STAT_ACA_DOCK_CHARGER: From 508b2c13499aa459ff34123ec5641a72ca46777c Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 6 Sep 2013 13:31:11 +0530 Subject: [PATCH 04/15] pm2301-charger: Check return value of regulator_enable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Check the return value of regulator_enable to silence the following warning: drivers/power/pm2301_charger.c:725:20: warning: ignoring return value of ‘regulator_enable’, declared with attribute warn_unused_result [-Wunused-result] Signed-off-by: Sachin Kamat Acked-by: Lee Jones Signed-off-by: Anton Vorontsov --- drivers/power/pm2301_charger.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/power/pm2301_charger.c b/drivers/power/pm2301_charger.c index ffa10ed83eb1..7214b667f940 100644 --- a/drivers/power/pm2301_charger.c +++ b/drivers/power/pm2301_charger.c @@ -722,8 +722,12 @@ static int pm2xxx_charger_ac_en(struct ux500_charger *charger, dev_dbg(pm2->dev, "Enable AC: %dmV %dmA\n", vset, iset); if (!pm2->vddadc_en_ac) { - regulator_enable(pm2->regu); - pm2->vddadc_en_ac = true; + ret = regulator_enable(pm2->regu); + if (ret) + dev_warn(pm2->dev, + "Failed to enable vddadc regulator\n"); + else + pm2->vddadc_en_ac = true; } ret = pm2xxx_charging_init(pm2); From 83bd57aeb06b1d6db2729082dd90a0286f3e3674 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 6 Sep 2013 13:31:12 +0530 Subject: [PATCH 05/15] pm2301-charger: Staticize pm2xxx_charger_die_therm_mngt pm2xxx_charger_die_therm_mngt is used only in this file. Make it static. Signed-off-by: Sachin Kamat Acked-by: Lee Jones Signed-off-by: Anton Vorontsov --- drivers/power/pm2301_charger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/power/pm2301_charger.c b/drivers/power/pm2301_charger.c index 7214b667f940..b733c692a17f 100644 --- a/drivers/power/pm2301_charger.c +++ b/drivers/power/pm2301_charger.c @@ -205,7 +205,7 @@ static int pm2xxx_charger_batt_therm_mngt(struct pm2xxx_charger *pm2, int val) } -int pm2xxx_charger_die_therm_mngt(struct pm2xxx_charger *pm2, int val) +static int pm2xxx_charger_die_therm_mngt(struct pm2xxx_charger *pm2, int val) { queue_work(pm2->charger_wq, &pm2->check_main_thermal_prot_work); From ce77399226313a72578b5b0d67e289d3f165b8ba Mon Sep 17 00:00:00 2001 From: Darbha Sriharsha Date: Fri, 11 Oct 2013 17:15:43 -0400 Subject: [PATCH 06/15] power_supply: Add support for bq24735 charger Adds support for the bq24735 charger chipset. The bq24735 is a high-efficiency, synchronous battery charger. It allows control of the charging current, input current, and the charger voltage DAC's through SMBus. Signed-off-by: Darbha Sriharsha Signed-off-by: Rhyland Klein Thanks-to: Stephen Warren Thanks-to: Thierry Reding Thanks-to: Manish Badarkhe Signed-off-by: Anton Vorontsov --- .../bindings/power_supply/ti,bq24735.txt | 32 ++ drivers/power/Kconfig | 6 + drivers/power/Makefile | 1 + drivers/power/bq24735-charger.c | 419 ++++++++++++++++++ include/linux/power/bq24735-charger.h | 39 ++ 5 files changed, 497 insertions(+) create mode 100644 Documentation/devicetree/bindings/power_supply/ti,bq24735.txt create mode 100644 drivers/power/bq24735-charger.c create mode 100644 include/linux/power/bq24735-charger.h diff --git a/Documentation/devicetree/bindings/power_supply/ti,bq24735.txt b/Documentation/devicetree/bindings/power_supply/ti,bq24735.txt new file mode 100644 index 000000000000..4f6a550184d0 --- /dev/null +++ b/Documentation/devicetree/bindings/power_supply/ti,bq24735.txt @@ -0,0 +1,32 @@ +TI BQ24735 Charge Controller +~~~~~~~~~~ + +Required properties : + - compatible : "ti,bq24735" + +Optional properties : + - interrupts : Specify the interrupt to be used to trigger when the AC + adapter is either plugged in or removed. + - ti,ac-detect-gpios : This GPIO is optionally used to read the AC adapter + presence. This is a Host GPIO that is configured as an input and + connected to the bq24735. + - ti,charge-current : Used to control and set the charging current. This value + must be between 128mA and 8.128A with a 64mA step resolution. The POR value + is 0x0000h. This number is in mA (e.g. 8192), see spec for more information + about the ChargeCurrent (0x14h) register. + - ti,charge-voltage : Used to control and set the charging voltage. This value + must be between 1.024V and 19.2V with a 16mV step resolution. The POR value + is 0x0000h. This number is in mV (e.g. 19200), see spec for more information + about the ChargeVoltage (0x15h) register. + - ti,input-current : Used to control and set the charger input current. This + value must be between 128mA and 8.064A with a 128mA step resolution. The + POR value is 0x1000h. This number is in mA (e.g. 8064), see the spec for + more information about the InputCurrent (0x3fh) register. + +Example: + + bq24735@9 { + compatible = "ti,bq24735"; + reg = <0x9>; + ti,ac-detect-gpios = <&gpio 72 0x1>; + } diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index e6f92b450913..5e2054afe840 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -346,6 +346,12 @@ config CHARGER_BQ24190 help Say Y to enable support for the TI BQ24190 battery charger. +config CHARGER_BQ24735 + tristate "TI BQ24735 battery charger support" + depends on I2C && GPIOLIB + help + Say Y to enable support for the TI BQ24735 battery charger. + config CHARGER_SMB347 tristate "Summit Microelectronics SMB347 Battery Charger" depends on I2C diff --git a/drivers/power/Makefile b/drivers/power/Makefile index a4b74177706f..372b4e8ab598 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -52,6 +52,7 @@ obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o obj-$(CONFIG_CHARGER_BQ2415X) += bq2415x_charger.o obj-$(CONFIG_CHARGER_BQ24190) += bq24190_charger.o +obj-$(CONFIG_CHARGER_BQ24735) += bq24735-charger.o obj-$(CONFIG_POWER_AVS) += avs/ obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o obj-$(CONFIG_CHARGER_TPS65090) += tps65090-charger.o diff --git a/drivers/power/bq24735-charger.c b/drivers/power/bq24735-charger.c new file mode 100644 index 000000000000..d022b823305b --- /dev/null +++ b/drivers/power/bq24735-charger.c @@ -0,0 +1,419 @@ +/* + * Battery charger driver for TI BQ24735 + * + * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved. + * + * 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; + * + * 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 +#include +#include +#include +#include +#include + +#include + +#define BQ24735_CHG_OPT 0x12 +#define BQ24735_CHG_OPT_CHARGE_DISABLE (1 << 0) +#define BQ24735_CHG_OPT_AC_PRESENT (1 << 4) +#define BQ24735_CHARGE_CURRENT 0x14 +#define BQ24735_CHARGE_CURRENT_MASK 0x1fc0 +#define BQ24735_CHARGE_VOLTAGE 0x15 +#define BQ24735_CHARGE_VOLTAGE_MASK 0x7ff0 +#define BQ24735_INPUT_CURRENT 0x3f +#define BQ24735_INPUT_CURRENT_MASK 0x1f80 +#define BQ24735_MANUFACTURER_ID 0xfe +#define BQ24735_DEVICE_ID 0xff + +struct bq24735 { + struct power_supply charger; + struct i2c_client *client; + struct bq24735_platform *pdata; +}; + +static inline struct bq24735 *to_bq24735(struct power_supply *psy) +{ + return container_of(psy, struct bq24735, charger); +} + +static enum power_supply_property bq24735_charger_properties[] = { + POWER_SUPPLY_PROP_ONLINE, +}; + +static inline int bq24735_write_word(struct i2c_client *client, u8 reg, + u16 value) +{ + return i2c_smbus_write_word_data(client, reg, le16_to_cpu(value)); +} + +static inline int bq24735_read_word(struct i2c_client *client, u8 reg) +{ + s32 ret = i2c_smbus_read_word_data(client, reg); + + return ret < 0 ? ret : le16_to_cpu(ret); +} + +static int bq24735_update_word(struct i2c_client *client, u8 reg, + u16 mask, u16 value) +{ + unsigned int tmp; + int ret; + + ret = bq24735_read_word(client, reg); + if (ret < 0) + return ret; + + tmp = ret & ~mask; + tmp |= value & mask; + + return bq24735_write_word(client, reg, tmp); +} + +static inline int bq24735_enable_charging(struct bq24735 *charger) +{ + return bq24735_update_word(charger->client, BQ24735_CHG_OPT, + BQ24735_CHG_OPT_CHARGE_DISABLE, + ~BQ24735_CHG_OPT_CHARGE_DISABLE); +} + +static inline int bq24735_disable_charging(struct bq24735 *charger) +{ + return bq24735_update_word(charger->client, BQ24735_CHG_OPT, + BQ24735_CHG_OPT_CHARGE_DISABLE, + BQ24735_CHG_OPT_CHARGE_DISABLE); +} + +static int bq24735_config_charger(struct bq24735 *charger) +{ + struct bq24735_platform *pdata = charger->pdata; + int ret; + u16 value; + + if (pdata->charge_current) { + value = pdata->charge_current & BQ24735_CHARGE_CURRENT_MASK; + + ret = bq24735_write_word(charger->client, + BQ24735_CHARGE_CURRENT, value); + if (ret < 0) { + dev_err(&charger->client->dev, + "Failed to write charger current : %d\n", + ret); + return ret; + } + } + + if (pdata->charge_voltage) { + value = pdata->charge_voltage & BQ24735_CHARGE_VOLTAGE_MASK; + + ret = bq24735_write_word(charger->client, + BQ24735_CHARGE_VOLTAGE, value); + if (ret < 0) { + dev_err(&charger->client->dev, + "Failed to write charger voltage : %d\n", + ret); + return ret; + } + } + + if (pdata->input_current) { + value = pdata->input_current & BQ24735_INPUT_CURRENT_MASK; + + ret = bq24735_write_word(charger->client, + BQ24735_INPUT_CURRENT, value); + if (ret < 0) { + dev_err(&charger->client->dev, + "Failed to write input current : %d\n", + ret); + return ret; + } + } + + return 0; +} + +static bool bq24735_charger_is_present(struct bq24735 *charger) +{ + struct bq24735_platform *pdata = charger->pdata; + int ret; + + if (pdata->status_gpio_valid) { + ret = gpio_get_value_cansleep(pdata->status_gpio); + return ret ^= pdata->status_gpio_active_low == 0; + } else { + int ac = 0; + + ac = bq24735_read_word(charger->client, BQ24735_CHG_OPT); + if (ac < 0) { + dev_err(&charger->client->dev, + "Failed to read charger options : %d\n", + ac); + return false; + } + return (ac & BQ24735_CHG_OPT_AC_PRESENT) ? true : false; + } + + return false; +} + +static irqreturn_t bq24735_charger_isr(int irq, void *devid) +{ + struct power_supply *psy = devid; + struct bq24735 *charger = to_bq24735(psy); + + if (bq24735_charger_is_present(charger)) + bq24735_enable_charging(charger); + else + bq24735_disable_charging(charger); + + power_supply_changed(psy); + + return IRQ_HANDLED; +} + +static int bq24735_charger_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct bq24735 *charger; + + charger = container_of(psy, struct bq24735, charger); + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + val->intval = bq24735_charger_is_present(charger) ? 1 : 0; + break; + default: + return -EINVAL; + } + + return 0; +} + +static struct bq24735_platform *bq24735_parse_dt_data(struct i2c_client *client) +{ + struct bq24735_platform *pdata; + struct device_node *np = client->dev.of_node; + u32 val; + int ret; + enum of_gpio_flags flags; + + pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + dev_err(&client->dev, + "Memory alloc for bq24735 pdata failed\n"); + return NULL; + } + + pdata->status_gpio = of_get_named_gpio_flags(np, "ti,ac-detect-gpios", + 0, &flags); + + if (flags & OF_GPIO_ACTIVE_LOW) + pdata->status_gpio_active_low = 1; + + ret = of_property_read_u32(np, "ti,charge-current", &val); + if (!ret) + pdata->charge_current = val; + + ret = of_property_read_u32(np, "ti,charge-voltage", &val); + if (!ret) + pdata->charge_voltage = val; + + ret = of_property_read_u32(np, "ti,input-current", &val); + if (!ret) + pdata->input_current = val; + + return pdata; +} + +static int bq24735_charger_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret; + struct bq24735 *charger; + struct power_supply *supply; + char *name; + + charger = devm_kzalloc(&client->dev, sizeof(*charger), GFP_KERNEL); + if (!charger) + return -ENOMEM; + + charger->pdata = client->dev.platform_data; + + if (IS_ENABLED(CONFIG_OF) && !charger->pdata && client->dev.of_node) + charger->pdata = bq24735_parse_dt_data(client); + + if (!charger->pdata) { + dev_err(&client->dev, "no platform data provided\n"); + return -EINVAL; + } + + name = (char *)charger->pdata->name; + if (!name) { + name = kasprintf(GFP_KERNEL, "bq24735@%s", + dev_name(&client->dev)); + if (!name) { + dev_err(&client->dev, "Failed to alloc device name\n"); + return -ENOMEM; + } + } + + charger->client = client; + + supply = &charger->charger; + + supply->name = name; + supply->type = POWER_SUPPLY_TYPE_MAINS; + supply->properties = bq24735_charger_properties; + supply->num_properties = ARRAY_SIZE(bq24735_charger_properties); + supply->get_property = bq24735_charger_get_property; + supply->supplied_to = charger->pdata->supplied_to; + supply->num_supplicants = charger->pdata->num_supplicants; + supply->of_node = client->dev.of_node; + + i2c_set_clientdata(client, charger); + + ret = bq24735_read_word(client, BQ24735_MANUFACTURER_ID); + if (ret < 0) { + dev_err(&client->dev, "Failed to read manufacturer id : %d\n", + ret); + goto err_free_name; + } else if (ret != 0x0040) { + dev_err(&client->dev, + "manufacturer id mismatch. 0x0040 != 0x%04x\n", ret); + ret = -ENODEV; + goto err_free_name; + } + + ret = bq24735_read_word(client, BQ24735_DEVICE_ID); + if (ret < 0) { + dev_err(&client->dev, "Failed to read device id : %d\n", ret); + goto err_free_name; + } else if (ret != 0x000B) { + dev_err(&client->dev, + "device id mismatch. 0x000b != 0x%04x\n", ret); + ret = -ENODEV; + goto err_free_name; + } + + if (gpio_is_valid(charger->pdata->status_gpio)) { + ret = devm_gpio_request(&client->dev, + charger->pdata->status_gpio, + name); + if (ret) { + dev_err(&client->dev, + "Failed GPIO request for GPIO %d: %d\n", + charger->pdata->status_gpio, ret); + } + + charger->pdata->status_gpio_valid = !ret; + } + + ret = bq24735_config_charger(charger); + if (ret < 0) { + dev_err(&client->dev, "failed in configuring charger"); + goto err_free_name; + } + + /* check for AC adapter presence */ + if (bq24735_charger_is_present(charger)) { + ret = bq24735_enable_charging(charger); + if (ret < 0) { + dev_err(&client->dev, "Failed to enable charging\n"); + goto err_free_name; + } + } + + ret = power_supply_register(&client->dev, supply); + if (ret < 0) { + dev_err(&client->dev, "Failed to register power supply: %d\n", + ret); + goto err_free_name; + } + + if (client->irq) { + ret = devm_request_threaded_irq(&client->dev, client->irq, + NULL, bq24735_charger_isr, + IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, + supply->name, supply); + if (ret) { + dev_err(&client->dev, + "Unable to register IRQ %d err %d\n", + client->irq, ret); + goto err_unregister_supply; + } + } + + return 0; +err_unregister_supply: + power_supply_unregister(supply); +err_free_name: + if (name != charger->pdata->name) + kfree(name); + + return ret; +} + +static int bq24735_charger_remove(struct i2c_client *client) +{ + struct bq24735 *charger = i2c_get_clientdata(client); + + if (charger->client->irq) + devm_free_irq(&charger->client->dev, charger->client->irq, + &charger->charger); + + power_supply_unregister(&charger->charger); + + if (charger->charger.name != charger->pdata->name) + kfree(charger->charger.name); + + return 0; +} + +static const struct i2c_device_id bq24735_charger_id[] = { + { "bq24735-charger", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, bq24735_charger_id); + +static const struct of_device_id bq24735_match_ids[] = { + { .compatible = "ti,bq24735", }, + { /* end */ } +}; +MODULE_DEVICE_TABLE(of, bq24735_match_ids); + +static struct i2c_driver bq24735_charger_driver = { + .driver = { + .name = "bq24735-charger", + .owner = THIS_MODULE, + .of_match_table = bq24735_match_ids, + }, + .probe = bq24735_charger_probe, + .remove = bq24735_charger_remove, + .id_table = bq24735_charger_id, +}; + +module_i2c_driver(bq24735_charger_driver); + +MODULE_DESCRIPTION("bq24735 battery charging driver"); +MODULE_AUTHOR("Darbha Sriharsha "); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/power/bq24735-charger.h b/include/linux/power/bq24735-charger.h new file mode 100644 index 000000000000..f536164a6069 --- /dev/null +++ b/include/linux/power/bq24735-charger.h @@ -0,0 +1,39 @@ +/* + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __CHARGER_BQ24735_H_ +#define __CHARGER_BQ24735_H_ + +#include +#include + +struct bq24735_platform { + uint32_t charge_current; + uint32_t charge_voltage; + uint32_t input_current; + + const char *name; + + int status_gpio; + int status_gpio_active_low; + bool status_gpio_valid; + + char **supplied_to; + size_t num_supplicants; +}; + +#endif /* __CHARGER_BQ24735_H_ */ From 0ed81393235fbf9d3379b55d2ceece1885ea3fae Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Mon, 23 Sep 2013 18:04:19 +0800 Subject: [PATCH 07/15] tps65090-charger: Drop devm_free_irq of devm_ allocated irq The devm_request_irq function allocates irq that is released when a driver detaches. Thus, there is no reason to explicitly call devm_free_irq in probe or remove functions. Signed-off-by: Wei Yongjun Signed-off-by: Anton Vorontsov --- drivers/power/tps65090-charger.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/power/tps65090-charger.c b/drivers/power/tps65090-charger.c index bdd7b9b2546a..14b6e79e1fc0 100644 --- a/drivers/power/tps65090-charger.c +++ b/drivers/power/tps65090-charger.c @@ -277,13 +277,13 @@ static int tps65090_charger_probe(struct platform_device *pdev) if (ret) { dev_err(cdata->dev, "Unable to register irq %d err %d\n", irq, ret); - goto fail_free_irq; + goto fail_unregister_supply; } ret = tps65090_config_charger(cdata); if (ret < 0) { dev_err(&pdev->dev, "charger config failed, err %d\n", ret); - goto fail_free_irq; + goto fail_unregister_supply; } /* Check for charger presence */ @@ -292,14 +292,14 @@ static int tps65090_charger_probe(struct platform_device *pdev) if (ret < 0) { dev_err(cdata->dev, "%s(): Error in reading reg 0x%x", __func__, TPS65090_REG_CG_STATUS1); - goto fail_free_irq; + goto fail_unregister_supply; } if (status1 != 0) { ret = tps65090_enable_charging(cdata); if (ret < 0) { dev_err(cdata->dev, "error enabling charger\n"); - goto fail_free_irq; + goto fail_unregister_supply; } cdata->ac_online = 1; power_supply_changed(&cdata->ac); @@ -307,8 +307,6 @@ static int tps65090_charger_probe(struct platform_device *pdev) return 0; -fail_free_irq: - devm_free_irq(cdata->dev, irq, cdata); fail_unregister_supply: power_supply_unregister(&cdata->ac); @@ -319,7 +317,6 @@ static int tps65090_charger_remove(struct platform_device *pdev) { struct tps65090_charger *cdata = platform_get_drvdata(pdev); - devm_free_irq(cdata->dev, cdata->irq, cdata); power_supply_unregister(&cdata->ac); return 0; From e47bcba4a189ab1ba23072ee2f457b1d72cd22fe Mon Sep 17 00:00:00 2001 From: Manish Badarkhe Date: Sat, 28 Sep 2013 10:02:24 +0530 Subject: [PATCH 08/15] tps65090-charger: Use "IS_ENABLED(CONFIG_OF)" for DT code Instead of "#if defined(CONFIG_OF)" use "IS_ENABLED(CONFIG_OF)" option for DT code to avoid if-deffery in code. Also, arranged header files in alphabetically. Signed-off-by: Manish Badarkhe Acked-by: Rhyland Klein Signed-off-by: Anton Vorontsov --- drivers/power/tps65090-charger.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/drivers/power/tps65090-charger.c b/drivers/power/tps65090-charger.c index 14b6e79e1fc0..8fc9d6df87f6 100644 --- a/drivers/power/tps65090-charger.c +++ b/drivers/power/tps65090-charger.c @@ -15,15 +15,17 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +#include #include #include #include #include #include -#include -#include +#include #include #include +#include + #include #define TPS65090_REG_INTR_STS 0x00 @@ -185,10 +187,6 @@ static irqreturn_t tps65090_charger_isr(int irq, void *dev_id) return IRQ_HANDLED; } -#if defined(CONFIG_OF) - -#include - static struct tps65090_platform_data * tps65090_parse_dt_charger_data(struct platform_device *pdev) { @@ -210,13 +208,6 @@ static struct tps65090_platform_data * return pdata; } -#else -static struct tps65090_platform_data * - tps65090_parse_dt_charger_data(struct platform_device *pdev) -{ - return NULL; -} -#endif static int tps65090_charger_probe(struct platform_device *pdev) { @@ -228,7 +219,7 @@ static int tps65090_charger_probe(struct platform_device *pdev) pdata = dev_get_platdata(pdev->dev.parent); - if (!pdata && pdev->dev.of_node) + if (IS_ENABLED(CONFIG_OF) && !pdata && pdev->dev.of_node) pdata = tps65090_parse_dt_charger_data(pdev); if (!pdata) { From 3ed5cd79b82e9a055e0df3275eeb471ea0f1e2d7 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Wed, 16 Oct 2013 16:08:31 +0200 Subject: [PATCH 09/15] bq2415x_charger: Fix max battery regulation voltage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As per the datasheets, maximum battery regulation voltage is 4440mV. The formula is (voltage - offset) / step, so the maximum value is: (4440 - 3500) / 20 = 47 Signed-off-by: Alexandre Belloni Thanks-to: Pali Rohár Signed-off-by: Anton Vorontsov --- drivers/power/bq2415x_charger.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/power/bq2415x_charger.c b/drivers/power/bq2415x_charger.c index 0727f9256138..df893dd1447d 100644 --- a/drivers/power/bq2415x_charger.c +++ b/drivers/power/bq2415x_charger.c @@ -605,9 +605,13 @@ static int bq2415x_set_battery_regulation_voltage(struct bq2415x_device *bq, { int val = (mV/10 - 350) / 2; + /* + * According to datasheet, maximum battery regulation voltage is + * 4440mV which is b101111 = 47. + */ if (val < 0) val = 0; - else if (val > 94) /* FIXME: Max is 94 or 122 ? Set max value ? */ + else if (val > 47) return -EINVAL; return bq2415x_i2c_write_mask(bq, BQ2415X_REG_VOLTAGE, val, From 883c10a9dd8099e7519cdc43b75ac4066a6ae98d Mon Sep 17 00:00:00 2001 From: Jonghwa Lee Date: Fri, 25 Oct 2013 11:47:31 +0900 Subject: [PATCH 10/15] charger-manager : Replace kzalloc to devm_kzalloc and remove uneccessary code Use devm function for dynamic memory allocation. Signed-off-by: Jonghwa Lee Signed-off-by: Myungjoo Ham Signed-off-by: Anton Vorontsov --- drivers/power/charger-manager.c | 85 +++++++++++---------------------- 1 file changed, 27 insertions(+), 58 deletions(-) diff --git a/drivers/power/charger-manager.c b/drivers/power/charger-manager.c index e30e847600bb..7287c0efd6bf 100644 --- a/drivers/power/charger-manager.c +++ b/drivers/power/charger-manager.c @@ -1378,7 +1378,8 @@ static int charger_manager_register_sysfs(struct charger_manager *cm) charger = &desc->charger_regulators[i]; snprintf(buf, 10, "charger.%d", i); - str = kzalloc(sizeof(char) * (strlen(buf) + 1), GFP_KERNEL); + str = devm_kzalloc(cm->dev, + sizeof(char) * (strlen(buf) + 1), GFP_KERNEL); if (!str) { ret = -ENOMEM; goto err; @@ -1452,30 +1453,23 @@ static int charger_manager_probe(struct platform_device *pdev) rtc_dev = NULL; dev_err(&pdev->dev, "Cannot get RTC %s\n", g_desc->rtc_name); - ret = -ENODEV; - goto err_alloc; + return -ENODEV; } } if (!desc) { dev_err(&pdev->dev, "No platform data (desc) found\n"); - ret = -ENODEV; - goto err_alloc; + return -ENODEV; } - cm = kzalloc(sizeof(struct charger_manager), GFP_KERNEL); - if (!cm) { - ret = -ENOMEM; - goto err_alloc; - } + cm = devm_kzalloc(&pdev->dev, + sizeof(struct charger_manager), GFP_KERNEL); + if (!cm) + return -ENOMEM; /* Basic Values. Unspecified are Null or 0 */ cm->dev = &pdev->dev; - cm->desc = kmemdup(desc, sizeof(struct charger_desc), GFP_KERNEL); - if (!cm->desc) { - ret = -ENOMEM; - goto err_alloc_desc; - } + cm->desc = desc; cm->last_temp_mC = INT_MIN; /* denotes "unmeasured, yet" */ /* @@ -1498,27 +1492,23 @@ static int charger_manager_probe(struct platform_device *pdev) } if (!desc->charger_regulators || desc->num_charger_regulators < 1) { - ret = -EINVAL; dev_err(&pdev->dev, "charger_regulators undefined\n"); - goto err_no_charger; + return -EINVAL; } if (!desc->psy_charger_stat || !desc->psy_charger_stat[0]) { dev_err(&pdev->dev, "No power supply defined\n"); - ret = -EINVAL; - goto err_no_charger_stat; + return -EINVAL; } /* Counting index only */ while (desc->psy_charger_stat[i]) i++; - cm->charger_stat = kzalloc(sizeof(struct power_supply *) * (i + 1), - GFP_KERNEL); - if (!cm->charger_stat) { - ret = -ENOMEM; - goto err_no_charger_stat; - } + cm->charger_stat = devm_kzalloc(&pdev->dev, + sizeof(struct power_supply *) * i, GFP_KERNEL); + if (!cm->charger_stat) + return -ENOMEM; for (i = 0; desc->psy_charger_stat[i]; i++) { cm->charger_stat[i] = power_supply_get_by_name( @@ -1526,8 +1516,7 @@ static int charger_manager_probe(struct platform_device *pdev) if (!cm->charger_stat[i]) { dev_err(&pdev->dev, "Cannot find power supply \"%s\"\n", desc->psy_charger_stat[i]); - ret = -ENODEV; - goto err_chg_stat; + return -ENODEV; } } @@ -1535,21 +1524,18 @@ static int charger_manager_probe(struct platform_device *pdev) if (!cm->fuel_gauge) { dev_err(&pdev->dev, "Cannot find power supply \"%s\"\n", desc->psy_fuel_gauge); - ret = -ENODEV; - goto err_chg_stat; + return -ENODEV; } if (desc->polling_interval_ms == 0 || msecs_to_jiffies(desc->polling_interval_ms) <= CM_JIFFIES_SMALL) { dev_err(&pdev->dev, "polling_interval_ms is too small\n"); - ret = -EINVAL; - goto err_chg_stat; + return -EINVAL; } if (!desc->temperature_out_of_range) { dev_err(&pdev->dev, "there is no temperature_out_of_range\n"); - ret = -EINVAL; - goto err_chg_stat; + return -EINVAL; } if (!desc->charging_max_duration_ms || @@ -1570,14 +1556,13 @@ static int charger_manager_probe(struct platform_device *pdev) cm->charger_psy.name = cm->psy_name_buf; /* Allocate for psy properties because they may vary */ - cm->charger_psy.properties = kzalloc(sizeof(enum power_supply_property) + cm->charger_psy.properties = devm_kzalloc(&pdev->dev, + sizeof(enum power_supply_property) * (ARRAY_SIZE(default_charger_props) + - NUM_CHARGER_PSY_OPTIONAL), - GFP_KERNEL); - if (!cm->charger_psy.properties) { - ret = -ENOMEM; - goto err_chg_stat; - } + NUM_CHARGER_PSY_OPTIONAL), GFP_KERNEL); + if (!cm->charger_psy.properties) + return -ENOMEM; + memcpy(cm->charger_psy.properties, default_charger_props, sizeof(enum power_supply_property) * ARRAY_SIZE(default_charger_props)); @@ -1614,7 +1599,7 @@ static int charger_manager_probe(struct platform_device *pdev) if (ret) { dev_err(&pdev->dev, "Cannot register charger-manager with name \"%s\"\n", cm->charger_psy.name); - goto err_register; + return ret; } /* Register extcon device for charger cable */ @@ -1655,8 +1640,6 @@ err_reg_sysfs: charger = &desc->charger_regulators[i]; sysfs_remove_group(&cm->charger_psy.dev->kobj, &charger->attr_g); - - kfree(charger->attr_g.name); } err_reg_extcon: for (i = 0; i < desc->num_charger_regulators; i++) { @@ -1674,16 +1657,7 @@ err_reg_extcon: } power_supply_unregister(&cm->charger_psy); -err_register: - kfree(cm->charger_psy.properties); -err_chg_stat: - kfree(cm->charger_stat); -err_no_charger_stat: -err_no_charger: - kfree(cm->desc); -err_alloc_desc: - kfree(cm); -err_alloc: + return ret; } @@ -1718,11 +1692,6 @@ static int charger_manager_remove(struct platform_device *pdev) try_charger_enable(cm, false); - kfree(cm->charger_psy.properties); - kfree(cm->charger_stat); - kfree(cm->desc); - kfree(cm); - return 0; } From 3d23c7f457ed86f27425cb3f229b76720855a26d Mon Sep 17 00:00:00 2001 From: Manish Badarkhe Date: Mon, 14 Oct 2013 10:56:51 +0530 Subject: [PATCH 11/15] max17042_battery: Use SIMPLE_DEV_PM_OPS Use the SIMPLE_DEV_PM_OPS macro to declare the driver's pm_ops. Signed-off-by: Manish Badarkhe Signed-off-by: Anton Vorontsov --- drivers/power/max17042_battery.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/drivers/power/max17042_battery.c b/drivers/power/max17042_battery.c index d664ef58afa7..bd72b0f7f729 100644 --- a/drivers/power/max17042_battery.c +++ b/drivers/power/max17042_battery.c @@ -786,7 +786,7 @@ static int max17042_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int max17042_suspend(struct device *dev) { struct max17042_chip *chip = dev_get_drvdata(dev); @@ -816,17 +816,11 @@ static int max17042_resume(struct device *dev) return 0; } - -static const struct dev_pm_ops max17042_pm_ops = { - .suspend = max17042_suspend, - .resume = max17042_resume, -}; - -#define MAX17042_PM_OPS (&max17042_pm_ops) -#else -#define MAX17042_PM_OPS NULL #endif +static SIMPLE_DEV_PM_OPS(max17042_pm_ops, max17042_suspend, + max17042_resume); + #ifdef CONFIG_OF static const struct of_device_id max17042_dt_match[] = { { .compatible = "maxim,max17042" }, @@ -849,7 +843,7 @@ static struct i2c_driver max17042_i2c_driver = { .driver = { .name = "max17042", .of_match_table = of_match_ptr(max17042_dt_match), - .pm = MAX17042_PM_OPS, + .pm = &max17042_pm_ops, }, .probe = max17042_probe, .remove = max17042_remove, From 39e7213edc4f31b0650dcc5630e175a6bd278b9b Mon Sep 17 00:00:00 2001 From: Jonghwa Lee Date: Fri, 25 Oct 2013 13:55:02 +0900 Subject: [PATCH 12/15] max17042_battery: Support regmap to access device's registers This patch makes max17042 fuelguage driver uses regmap API to access its device's registers. Signed-off-by: Jonghwa Lee Signed-off-by: Myungjoo Ham Signed-off-by: Anton Vorontsov --- drivers/power/max17042_battery.c | 349 +++++++++++++++---------------- 1 file changed, 170 insertions(+), 179 deletions(-) diff --git a/drivers/power/max17042_battery.c b/drivers/power/max17042_battery.c index bd72b0f7f729..e0b22f9b6fdd 100644 --- a/drivers/power/max17042_battery.c +++ b/drivers/power/max17042_battery.c @@ -33,6 +33,7 @@ #include #include #include +#include /* Status register bits */ #define STATUS_POR_BIT (1 << 1) @@ -67,6 +68,7 @@ struct max17042_chip { struct i2c_client *client; + struct regmap *regmap; struct power_supply battery; enum max170xx_chip_type chip_type; struct max17042_platform_data *pdata; @@ -74,35 +76,6 @@ struct max17042_chip { int init_complete; }; -static int max17042_write_reg(struct i2c_client *client, u8 reg, u16 value) -{ - int ret = i2c_smbus_write_word_data(client, reg, value); - - if (ret < 0) - dev_err(&client->dev, "%s: err %d\n", __func__, ret); - - return ret; -} - -static int max17042_read_reg(struct i2c_client *client, u8 reg) -{ - int ret = i2c_smbus_read_word_data(client, reg); - - if (ret < 0) - dev_err(&client->dev, "%s: err %d\n", __func__, ret); - - return ret; -} - -static void max17042_set_reg(struct i2c_client *client, - struct max17042_reg_data *data, int size) -{ - int i; - - for (i = 0; i < size; i++) - max17042_write_reg(client, data[i].addr, data[i].data); -} - static enum power_supply_property max17042_battery_props[] = { POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_CYCLE_COUNT, @@ -125,96 +98,98 @@ static int max17042_get_property(struct power_supply *psy, { struct max17042_chip *chip = container_of(psy, struct max17042_chip, battery); + struct regmap *map = chip->regmap; int ret; + u32 data; if (!chip->init_complete) return -EAGAIN; switch (psp) { case POWER_SUPPLY_PROP_PRESENT: - ret = max17042_read_reg(chip->client, MAX17042_STATUS); + ret = regmap_read(map, MAX17042_STATUS, &data); if (ret < 0) return ret; - if (ret & MAX17042_STATUS_BattAbsent) + if (data & MAX17042_STATUS_BattAbsent) val->intval = 0; else val->intval = 1; break; case POWER_SUPPLY_PROP_CYCLE_COUNT: - ret = max17042_read_reg(chip->client, MAX17042_Cycles); + ret = regmap_read(map, MAX17042_Cycles, &data); if (ret < 0) return ret; - val->intval = ret; + val->intval = data; break; case POWER_SUPPLY_PROP_VOLTAGE_MAX: - ret = max17042_read_reg(chip->client, MAX17042_MinMaxVolt); + ret = regmap_read(map, MAX17042_MinMaxVolt, &data); if (ret < 0) return ret; - val->intval = ret >> 8; + val->intval = data >> 8; val->intval *= 20000; /* Units of LSB = 20mV */ break; case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: if (chip->chip_type == MAX17042) - ret = max17042_read_reg(chip->client, MAX17042_V_empty); + ret = regmap_read(map, MAX17042_V_empty, &data); else - ret = max17042_read_reg(chip->client, MAX17047_V_empty); + ret = regmap_read(map, MAX17047_V_empty, &data); if (ret < 0) return ret; - val->intval = ret >> 7; + val->intval = data >> 7; val->intval *= 10000; /* Units of LSB = 10mV */ break; case POWER_SUPPLY_PROP_VOLTAGE_NOW: - ret = max17042_read_reg(chip->client, MAX17042_VCELL); + ret = regmap_read(map, MAX17042_VCELL, &data); if (ret < 0) return ret; - val->intval = ret * 625 / 8; + val->intval = data * 625 / 8; break; case POWER_SUPPLY_PROP_VOLTAGE_AVG: - ret = max17042_read_reg(chip->client, MAX17042_AvgVCELL); + ret = regmap_read(map, MAX17042_AvgVCELL, &data); if (ret < 0) return ret; - val->intval = ret * 625 / 8; + val->intval = data * 625 / 8; break; case POWER_SUPPLY_PROP_VOLTAGE_OCV: - ret = max17042_read_reg(chip->client, MAX17042_OCVInternal); + ret = regmap_read(map, MAX17042_OCVInternal, &data); if (ret < 0) return ret; - val->intval = ret * 625 / 8; + val->intval = data * 625 / 8; break; case POWER_SUPPLY_PROP_CAPACITY: - ret = max17042_read_reg(chip->client, MAX17042_RepSOC); + ret = regmap_read(map, MAX17042_RepSOC, &data); if (ret < 0) return ret; - val->intval = ret >> 8; + val->intval = data >> 8; break; case POWER_SUPPLY_PROP_CHARGE_FULL: - ret = max17042_read_reg(chip->client, MAX17042_FullCAP); + ret = regmap_read(map, MAX17042_FullCAP, &data); if (ret < 0) return ret; - val->intval = ret * 1000 / 2; + val->intval = data * 1000 / 2; break; case POWER_SUPPLY_PROP_CHARGE_COUNTER: - ret = max17042_read_reg(chip->client, MAX17042_QH); + ret = regmap_read(map, MAX17042_QH, &data); if (ret < 0) return ret; - val->intval = ret * 1000 / 2; + val->intval = data * 1000 / 2; break; case POWER_SUPPLY_PROP_TEMP: - ret = max17042_read_reg(chip->client, MAX17042_TEMP); + ret = regmap_read(map, MAX17042_TEMP, &data); if (ret < 0) return ret; - val->intval = ret; + val->intval = data; /* The value is signed. */ if (val->intval & 0x8000) { val->intval = (0x7fff & ~val->intval) + 1; @@ -226,11 +201,11 @@ static int max17042_get_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_CURRENT_NOW: if (chip->pdata->enable_current_sense) { - ret = max17042_read_reg(chip->client, MAX17042_Current); + ret = regmap_read(map, MAX17042_Current, &data); if (ret < 0) return ret; - val->intval = ret; + val->intval = data; if (val->intval & 0x8000) { /* Negative */ val->intval = ~val->intval & 0x7fff; @@ -244,12 +219,11 @@ static int max17042_get_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_CURRENT_AVG: if (chip->pdata->enable_current_sense) { - ret = max17042_read_reg(chip->client, - MAX17042_AvgCurrent); + ret = regmap_read(map, MAX17042_AvgCurrent, &data); if (ret < 0) return ret; - val->intval = ret; + val->intval = data; if (val->intval & 0x8000) { /* Negative */ val->intval = ~val->intval & 0x7fff; @@ -267,16 +241,15 @@ static int max17042_get_property(struct power_supply *psy, return 0; } -static int max17042_write_verify_reg(struct i2c_client *client, - u8 reg, u16 value) +static int max17042_write_verify_reg(struct regmap *map, u8 reg, u32 value) { int retries = 8; int ret; - u16 read_value; + u32 read_value; do { - ret = i2c_smbus_write_word_data(client, reg, value); - read_value = max17042_read_reg(client, reg); + ret = regmap_write(map, reg, value); + regmap_read(map, reg, &read_value); if (read_value != value) { ret = -EIO; retries--; @@ -284,50 +257,51 @@ static int max17042_write_verify_reg(struct i2c_client *client, } while (retries && read_value != value); if (ret < 0) - dev_err(&client->dev, "%s: err %d\n", __func__, ret); + pr_err("%s: err %d\n", __func__, ret); return ret; } -static inline void max17042_override_por( - struct i2c_client *client, u8 reg, u16 value) +static inline void max17042_override_por(struct regmap *map, + u8 reg, u16 value) { if (value) - max17042_write_reg(client, reg, value); + regmap_write(map, reg, value); } static inline void max10742_unlock_model(struct max17042_chip *chip) { - struct i2c_client *client = chip->client; - max17042_write_reg(client, MAX17042_MLOCKReg1, MODEL_UNLOCK1); - max17042_write_reg(client, MAX17042_MLOCKReg2, MODEL_UNLOCK2); + struct regmap *map = chip->regmap; + regmap_write(map, MAX17042_MLOCKReg1, MODEL_UNLOCK1); + regmap_write(map, MAX17042_MLOCKReg2, MODEL_UNLOCK2); } static inline void max10742_lock_model(struct max17042_chip *chip) { - struct i2c_client *client = chip->client; - max17042_write_reg(client, MAX17042_MLOCKReg1, MODEL_LOCK1); - max17042_write_reg(client, MAX17042_MLOCKReg2, MODEL_LOCK2); + struct regmap *map = chip->regmap; + + regmap_write(map, MAX17042_MLOCKReg1, MODEL_LOCK1); + regmap_write(map, MAX17042_MLOCKReg2, MODEL_LOCK2); } static inline void max17042_write_model_data(struct max17042_chip *chip, u8 addr, int size) { - struct i2c_client *client = chip->client; + struct regmap *map = chip->regmap; int i; for (i = 0; i < size; i++) - max17042_write_reg(client, addr + i, - chip->pdata->config_data->cell_char_tbl[i]); + regmap_write(map, addr + i, + chip->pdata->config_data->cell_char_tbl[i]); } static inline void max17042_read_model_data(struct max17042_chip *chip, - u8 addr, u16 *data, int size) + u8 addr, u32 *data, int size) { - struct i2c_client *client = chip->client; + struct regmap *map = chip->regmap; int i; for (i = 0; i < size; i++) - data[i] = max17042_read_reg(client, addr + i); + regmap_read(map, addr + i, &data[i]); } static inline int max17042_model_data_compare(struct max17042_chip *chip, @@ -350,7 +324,7 @@ static int max17042_init_model(struct max17042_chip *chip) { int ret; int table_size = ARRAY_SIZE(chip->pdata->config_data->cell_char_tbl); - u16 *temp_data; + u32 *temp_data; temp_data = kcalloc(table_size, sizeof(*temp_data), GFP_KERNEL); if (!temp_data) @@ -365,7 +339,7 @@ static int max17042_init_model(struct max17042_chip *chip) ret = max17042_model_data_compare( chip, chip->pdata->config_data->cell_char_tbl, - temp_data, + (u16 *)temp_data, table_size); max10742_lock_model(chip); @@ -378,7 +352,7 @@ static int max17042_verify_model_lock(struct max17042_chip *chip) { int i; int table_size = ARRAY_SIZE(chip->pdata->config_data->cell_char_tbl); - u16 *temp_data; + u32 *temp_data; int ret = 0; temp_data = kcalloc(table_size, sizeof(*temp_data), GFP_KERNEL); @@ -398,40 +372,38 @@ static int max17042_verify_model_lock(struct max17042_chip *chip) static void max17042_write_config_regs(struct max17042_chip *chip) { struct max17042_config_data *config = chip->pdata->config_data; + struct regmap *map = chip->regmap; - max17042_write_reg(chip->client, MAX17042_CONFIG, config->config); - max17042_write_reg(chip->client, MAX17042_LearnCFG, config->learn_cfg); - max17042_write_reg(chip->client, MAX17042_FilterCFG, + regmap_write(map, MAX17042_CONFIG, config->config); + regmap_write(map, MAX17042_LearnCFG, config->learn_cfg); + regmap_write(map, MAX17042_FilterCFG, config->filter_cfg); - max17042_write_reg(chip->client, MAX17042_RelaxCFG, config->relax_cfg); + regmap_write(map, MAX17042_RelaxCFG, config->relax_cfg); if (chip->chip_type == MAX17047) - max17042_write_reg(chip->client, MAX17047_FullSOCThr, + regmap_write(map, MAX17047_FullSOCThr, config->full_soc_thresh); } static void max17042_write_custom_regs(struct max17042_chip *chip) { struct max17042_config_data *config = chip->pdata->config_data; + struct regmap *map = chip->regmap; - max17042_write_verify_reg(chip->client, MAX17042_RCOMP0, - config->rcomp0); - max17042_write_verify_reg(chip->client, MAX17042_TempCo, - config->tcompc0); - max17042_write_verify_reg(chip->client, MAX17042_ICHGTerm, - config->ichgt_term); + max17042_write_verify_reg(map, MAX17042_RCOMP0, config->rcomp0); + max17042_write_verify_reg(map, MAX17042_TempCo, config->tcompc0); + max17042_write_verify_reg(map, MAX17042_ICHGTerm, config->ichgt_term); if (chip->chip_type == MAX17042) { - max17042_write_reg(chip->client, MAX17042_EmptyTempCo, - config->empty_tempco); - max17042_write_verify_reg(chip->client, MAX17042_K_empty0, + regmap_write(map, MAX17042_EmptyTempCo, config->empty_tempco); + max17042_write_verify_reg(map, MAX17042_K_empty0, config->kempty0); } else { - max17042_write_verify_reg(chip->client, MAX17047_QRTbl00, + max17042_write_verify_reg(map, MAX17047_QRTbl00, config->qrtbl00); - max17042_write_verify_reg(chip->client, MAX17047_QRTbl10, + max17042_write_verify_reg(map, MAX17047_QRTbl10, config->qrtbl10); - max17042_write_verify_reg(chip->client, MAX17047_QRTbl20, + max17042_write_verify_reg(map, MAX17047_QRTbl20, config->qrtbl20); - max17042_write_verify_reg(chip->client, MAX17047_QRTbl30, + max17042_write_verify_reg(map, MAX17047_QRTbl30, config->qrtbl30); } } @@ -439,58 +411,60 @@ static void max17042_write_custom_regs(struct max17042_chip *chip) static void max17042_update_capacity_regs(struct max17042_chip *chip) { struct max17042_config_data *config = chip->pdata->config_data; + struct regmap *map = chip->regmap; - max17042_write_verify_reg(chip->client, MAX17042_FullCAP, + max17042_write_verify_reg(map, MAX17042_FullCAP, config->fullcap); - max17042_write_reg(chip->client, MAX17042_DesignCap, - config->design_cap); - max17042_write_verify_reg(chip->client, MAX17042_FullCAPNom, + regmap_write(map, MAX17042_DesignCap, config->design_cap); + max17042_write_verify_reg(map, MAX17042_FullCAPNom, config->fullcapnom); } static void max17042_reset_vfsoc0_reg(struct max17042_chip *chip) { - u16 vfSoc; + unsigned int vfSoc; + struct regmap *map = chip->regmap; - vfSoc = max17042_read_reg(chip->client, MAX17042_VFSOC); - max17042_write_reg(chip->client, MAX17042_VFSOC0Enable, VFSOC0_UNLOCK); - max17042_write_verify_reg(chip->client, MAX17042_VFSOC0, vfSoc); - max17042_write_reg(chip->client, MAX17042_VFSOC0Enable, VFSOC0_LOCK); + regmap_read(map, MAX17042_VFSOC, &vfSoc); + regmap_write(map, MAX17042_VFSOC0Enable, VFSOC0_UNLOCK); + max17042_write_verify_reg(map, MAX17042_VFSOC0, vfSoc); + regmap_write(map, MAX17042_VFSOC0Enable, VFSOC0_LOCK); } static void max17042_load_new_capacity_params(struct max17042_chip *chip) { - u16 full_cap0, rep_cap, dq_acc, vfSoc; + u32 full_cap0, rep_cap, dq_acc, vfSoc; u32 rem_cap; struct max17042_config_data *config = chip->pdata->config_data; + struct regmap *map = chip->regmap; - full_cap0 = max17042_read_reg(chip->client, MAX17042_FullCAP0); - vfSoc = max17042_read_reg(chip->client, MAX17042_VFSOC); + regmap_read(map, MAX17042_FullCAP0, &full_cap0); + regmap_read(map, MAX17042_VFSOC, &vfSoc); /* fg_vfSoc needs to shifted by 8 bits to get the * perc in 1% accuracy, to get the right rem_cap multiply * full_cap0, fg_vfSoc and devide by 100 */ rem_cap = ((vfSoc >> 8) * full_cap0) / 100; - max17042_write_verify_reg(chip->client, MAX17042_RemCap, (u16)rem_cap); + max17042_write_verify_reg(map, MAX17042_RemCap, rem_cap); - rep_cap = (u16)rem_cap; - max17042_write_verify_reg(chip->client, MAX17042_RepCap, rep_cap); + rep_cap = rem_cap; + max17042_write_verify_reg(map, MAX17042_RepCap, rep_cap); /* Write dQ_acc to 200% of Capacity and dP_acc to 200% */ dq_acc = config->fullcap / dQ_ACC_DIV; - max17042_write_verify_reg(chip->client, MAX17042_dQacc, dq_acc); - max17042_write_verify_reg(chip->client, MAX17042_dPacc, dP_ACC_200); + max17042_write_verify_reg(map, MAX17042_dQacc, dq_acc); + max17042_write_verify_reg(map, MAX17042_dPacc, dP_ACC_200); - max17042_write_verify_reg(chip->client, MAX17042_FullCAP, + max17042_write_verify_reg(map, MAX17042_FullCAP, config->fullcap); - max17042_write_reg(chip->client, MAX17042_DesignCap, + regmap_write(map, MAX17042_DesignCap, config->design_cap); - max17042_write_verify_reg(chip->client, MAX17042_FullCAPNom, + max17042_write_verify_reg(map, MAX17042_FullCAPNom, config->fullcapnom); /* Update SOC register with new SOC */ - max17042_write_reg(chip->client, MAX17042_RepSOC, vfSoc); + regmap_write(map, MAX17042_RepSOC, vfSoc); } /* @@ -500,59 +474,60 @@ static void max17042_load_new_capacity_params(struct max17042_chip *chip) */ static inline void max17042_override_por_values(struct max17042_chip *chip) { - struct i2c_client *client = chip->client; + struct regmap *map = chip->regmap; struct max17042_config_data *config = chip->pdata->config_data; - max17042_override_por(client, MAX17042_TGAIN, config->tgain); - max17042_override_por(client, MAx17042_TOFF, config->toff); - max17042_override_por(client, MAX17042_CGAIN, config->cgain); - max17042_override_por(client, MAX17042_COFF, config->coff); + max17042_override_por(map, MAX17042_TGAIN, config->tgain); + max17042_override_por(map, MAx17042_TOFF, config->toff); + max17042_override_por(map, MAX17042_CGAIN, config->cgain); + max17042_override_por(map, MAX17042_COFF, config->coff); - max17042_override_por(client, MAX17042_VALRT_Th, config->valrt_thresh); - max17042_override_por(client, MAX17042_TALRT_Th, config->talrt_thresh); - max17042_override_por(client, MAX17042_SALRT_Th, - config->soc_alrt_thresh); - max17042_override_por(client, MAX17042_CONFIG, config->config); - max17042_override_por(client, MAX17042_SHDNTIMER, config->shdntimer); + max17042_override_por(map, MAX17042_VALRT_Th, config->valrt_thresh); + max17042_override_por(map, MAX17042_TALRT_Th, config->talrt_thresh); + max17042_override_por(map, MAX17042_SALRT_Th, + config->soc_alrt_thresh); + max17042_override_por(map, MAX17042_CONFIG, config->config); + max17042_override_por(map, MAX17042_SHDNTIMER, config->shdntimer); - max17042_override_por(client, MAX17042_DesignCap, config->design_cap); - max17042_override_por(client, MAX17042_ICHGTerm, config->ichgt_term); + max17042_override_por(map, MAX17042_DesignCap, config->design_cap); + max17042_override_por(map, MAX17042_ICHGTerm, config->ichgt_term); - max17042_override_por(client, MAX17042_AtRate, config->at_rate); - max17042_override_por(client, MAX17042_LearnCFG, config->learn_cfg); - max17042_override_por(client, MAX17042_FilterCFG, config->filter_cfg); - max17042_override_por(client, MAX17042_RelaxCFG, config->relax_cfg); - max17042_override_por(client, MAX17042_MiscCFG, config->misc_cfg); - max17042_override_por(client, MAX17042_MaskSOC, config->masksoc); + max17042_override_por(map, MAX17042_AtRate, config->at_rate); + max17042_override_por(map, MAX17042_LearnCFG, config->learn_cfg); + max17042_override_por(map, MAX17042_FilterCFG, config->filter_cfg); + max17042_override_por(map, MAX17042_RelaxCFG, config->relax_cfg); + max17042_override_por(map, MAX17042_MiscCFG, config->misc_cfg); + max17042_override_por(map, MAX17042_MaskSOC, config->masksoc); - max17042_override_por(client, MAX17042_FullCAP, config->fullcap); - max17042_override_por(client, MAX17042_FullCAPNom, config->fullcapnom); + max17042_override_por(map, MAX17042_FullCAP, config->fullcap); + max17042_override_por(map, MAX17042_FullCAPNom, config->fullcapnom); if (chip->chip_type == MAX17042) - max17042_override_por(client, MAX17042_SOC_empty, + max17042_override_por(map, MAX17042_SOC_empty, config->socempty); - max17042_override_por(client, MAX17042_LAvg_empty, config->lavg_empty); - max17042_override_por(client, MAX17042_dQacc, config->dqacc); - max17042_override_por(client, MAX17042_dPacc, config->dpacc); + max17042_override_por(map, MAX17042_LAvg_empty, config->lavg_empty); + max17042_override_por(map, MAX17042_dQacc, config->dqacc); + max17042_override_por(map, MAX17042_dPacc, config->dpacc); if (chip->chip_type == MAX17042) - max17042_override_por(client, MAX17042_V_empty, config->vempty); + max17042_override_por(map, MAX17042_V_empty, config->vempty); else - max17042_override_por(client, MAX17047_V_empty, config->vempty); - max17042_override_por(client, MAX17042_TempNom, config->temp_nom); - max17042_override_por(client, MAX17042_TempLim, config->temp_lim); - max17042_override_por(client, MAX17042_FCTC, config->fctc); - max17042_override_por(client, MAX17042_RCOMP0, config->rcomp0); - max17042_override_por(client, MAX17042_TempCo, config->tcompc0); + max17042_override_por(map, MAX17047_V_empty, config->vempty); + max17042_override_por(map, MAX17042_TempNom, config->temp_nom); + max17042_override_por(map, MAX17042_TempLim, config->temp_lim); + max17042_override_por(map, MAX17042_FCTC, config->fctc); + max17042_override_por(map, MAX17042_RCOMP0, config->rcomp0); + max17042_override_por(map, MAX17042_TempCo, config->tcompc0); if (chip->chip_type) { - max17042_override_por(client, MAX17042_EmptyTempCo, - config->empty_tempco); - max17042_override_por(client, MAX17042_K_empty0, - config->kempty0); + max17042_override_por(map, MAX17042_EmptyTempCo, + config->empty_tempco); + max17042_override_por(map, MAX17042_K_empty0, + config->kempty0); } } static int max17042_init_chip(struct max17042_chip *chip) { + struct regmap *map = chip->regmap; int ret; int val; @@ -597,31 +572,32 @@ static int max17042_init_chip(struct max17042_chip *chip) max17042_load_new_capacity_params(chip); /* Init complete, Clear the POR bit */ - val = max17042_read_reg(chip->client, MAX17042_STATUS); - max17042_write_reg(chip->client, MAX17042_STATUS, - val & (~STATUS_POR_BIT)); + regmap_read(map, MAX17042_STATUS, &val); + regmap_write(map, MAX17042_STATUS, val & (~STATUS_POR_BIT)); return 0; } static void max17042_set_soc_threshold(struct max17042_chip *chip, u16 off) { - u16 soc, soc_tr; + struct regmap *map = chip->regmap; + u32 soc, soc_tr; /* program interrupt thesholds such that we should * get interrupt for every 'off' perc change in the soc */ - soc = max17042_read_reg(chip->client, MAX17042_RepSOC) >> 8; + regmap_read(map, MAX17042_RepSOC, &soc); + soc >>= 8; soc_tr = (soc + off) << 8; soc_tr |= (soc - off); - max17042_write_reg(chip->client, MAX17042_SALRT_Th, soc_tr); + regmap_write(map, MAX17042_SALRT_Th, soc_tr); } static irqreturn_t max17042_thread_handler(int id, void *dev) { struct max17042_chip *chip = dev; - u16 val; + u32 val; - val = max17042_read_reg(chip->client, MAX17042_STATUS); + regmap_read(chip->regmap, MAX17042_STATUS, &val); if ((val & STATUS_INTR_SOCMIN_BIT) || (val & STATUS_INTR_SOCMAX_BIT)) { dev_info(&chip->client->dev, "SOC threshold INTR\n"); @@ -682,13 +658,20 @@ max17042_get_pdata(struct device *dev) } #endif +static struct regmap_config max17042_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .val_format_endian = REGMAP_ENDIAN_NATIVE, +}; + static int max17042_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); struct max17042_chip *chip; int ret; - int reg; + int i; + u32 val; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) return -EIO; @@ -698,6 +681,12 @@ static int max17042_probe(struct i2c_client *client, return -ENOMEM; chip->client = client; + chip->regmap = devm_regmap_init_i2c(client, &max17042_regmap_config); + if (IS_ERR(chip->regmap)) { + dev_err(&client->dev, "Failed to initialize regmap\n"); + return -EINVAL; + } + chip->pdata = max17042_get_pdata(&client->dev); if (!chip->pdata) { dev_err(&client->dev, "no platform data provided\n"); @@ -706,15 +695,15 @@ static int max17042_probe(struct i2c_client *client, i2c_set_clientdata(client, chip); - ret = max17042_read_reg(chip->client, MAX17042_DevName); - if (ret == MAX17042_IC_VERSION) { + regmap_read(chip->regmap, MAX17042_DevName, &val); + if (val == MAX17042_IC_VERSION) { dev_dbg(&client->dev, "chip type max17042 detected\n"); chip->chip_type = MAX17042; - } else if (ret == MAX17047_IC_VERSION) { + } else if (val == MAX17047_IC_VERSION) { dev_dbg(&client->dev, "chip type max17047/50 detected\n"); chip->chip_type = MAX17047; } else { - dev_err(&client->dev, "device version mismatch: %x\n", ret); + dev_err(&client->dev, "device version mismatch: %x\n", val); return -EIO; } @@ -733,13 +722,15 @@ static int max17042_probe(struct i2c_client *client, chip->pdata->r_sns = MAX17042_DEFAULT_SNS_RESISTOR; if (chip->pdata->init_data) - max17042_set_reg(client, chip->pdata->init_data, - chip->pdata->num_init_data); + for (i = 0; i < chip->pdata->num_init_data; i++) + regmap_write(chip->regmap, + chip->pdata->init_data[i].addr, + chip->pdata->init_data[i].data); if (!chip->pdata->enable_current_sense) { - max17042_write_reg(client, MAX17042_CGAIN, 0x0000); - max17042_write_reg(client, MAX17042_MiscCFG, 0x0003); - max17042_write_reg(client, MAX17042_LearnCFG, 0x0007); + regmap_write(chip->regmap, MAX17042_CGAIN, 0x0000); + regmap_write(chip->regmap, MAX17042_MiscCFG, 0x0003); + regmap_write(chip->regmap, MAX17042_LearnCFG, 0x0007); } ret = power_supply_register(&client->dev, &chip->battery); @@ -754,9 +745,9 @@ static int max17042_probe(struct i2c_client *client, IRQF_TRIGGER_FALLING, chip->battery.name, chip); if (!ret) { - reg = max17042_read_reg(client, MAX17042_CONFIG); - reg |= CONFIG_ALRT_BIT_ENBL; - max17042_write_reg(client, MAX17042_CONFIG, reg); + regmap_read(chip->regmap, MAX17042_CONFIG, &val); + val |= CONFIG_ALRT_BIT_ENBL; + regmap_write(chip->regmap, MAX17042_CONFIG, val); max17042_set_soc_threshold(chip, 1); } else { client->irq = 0; @@ -765,8 +756,8 @@ static int max17042_probe(struct i2c_client *client, } } - reg = max17042_read_reg(chip->client, MAX17042_STATUS); - if (reg & STATUS_POR_BIT) { + regmap_read(chip->regmap, MAX17042_STATUS, &val); + if (val & STATUS_POR_BIT) { INIT_WORK(&chip->work, max17042_init_worker); schedule_work(&chip->work); } else { From 588bd5918baca940a52802045f3b388358dc6917 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Sun, 27 Oct 2013 22:20:59 -0700 Subject: [PATCH 13/15] power_supply: Fix documentation for TEMP_*ALERT* properties All temperatures should be in tenth degrees Celsius. Reported-by: Jonghwa Lee Cc: Mark A. Greer Signed-off-by: Anton Vorontsov --- Documentation/power/power_supply_class.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Documentation/power/power_supply_class.txt b/Documentation/power/power_supply_class.txt index 3f10b39b0346..89a8816990ff 100644 --- a/Documentation/power/power_supply_class.txt +++ b/Documentation/power/power_supply_class.txt @@ -135,11 +135,11 @@ CAPACITY_LEVEL - capacity level. This corresponds to POWER_SUPPLY_CAPACITY_LEVEL_*. TEMP - temperature of the power supply. -TEMP_ALERT_MIN - minimum battery temperature alert value in milli centigrade. -TEMP_ALERT_MAX - maximum battery temperature alert value in milli centigrade. +TEMP_ALERT_MIN - minimum battery temperature alert. +TEMP_ALERT_MAX - maximum battery temperature alert. TEMP_AMBIENT - ambient temperature. -TEMP_AMBIENT_ALERT_MIN - minimum ambient temperature alert value in milli centigrade. -TEMP_AMBIENT_ALERT_MAX - maximum ambient temperature alert value in milli centigrade. +TEMP_AMBIENT_ALERT_MIN - minimum ambient temperature alert. +TEMP_AMBIENT_ALERT_MAX - maximum ambient temperature alert. TIME_TO_EMPTY - seconds left for battery to be considered empty (i.e. while battery powers a load) From ec0b380245d7b7d4d8e4201facca780a14352cda Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Thu, 31 Oct 2013 17:05:50 +1100 Subject: [PATCH 14/15] twl4030_charger: Add devicetree support This allows the charger to be enabled with devicetree, and allows the parameters for charging the backup battery to be set. Signed-off-by: NeilBrown Acked-by: Kumar Gala Acked-by: Grant Likely Signed-off-by: Anton Vorontsov --- .../devicetree/bindings/power/twl-charger.txt | 20 ++++++++ arch/arm/boot/dts/twl4030.dtsi | 6 +++ drivers/power/twl4030_charger.c | 47 +++++++++++++++++-- 3 files changed, 70 insertions(+), 3 deletions(-) create mode 100644 Documentation/devicetree/bindings/power/twl-charger.txt diff --git a/Documentation/devicetree/bindings/power/twl-charger.txt b/Documentation/devicetree/bindings/power/twl-charger.txt new file mode 100644 index 000000000000..d5c706216df5 --- /dev/null +++ b/Documentation/devicetree/bindings/power/twl-charger.txt @@ -0,0 +1,20 @@ +TWL BCI (Battery Charger Interface) + +Required properties: +- compatible: + - "ti,twl4030-bci" +- interrupts: two interrupt lines from the TWL SIH (secondary + interrupt handler) - interrupts 9 and 2. + +Optional properties: +- ti,bb-uvolt: microvolts for charging the backup battery. +- ti,bb-uamp: microamps for charging the backup battery. + +Examples: + +bci { + compatible = "ti,twl4030-bci"; + interrupts = <9>, <2>; + ti,bb-uvolt = <3200000>; + ti,bb-uamp = <150>; +}; diff --git a/arch/arm/boot/dts/twl4030.dtsi b/arch/arm/boot/dts/twl4030.dtsi index ae6a17aed9ee..5a12540b1d0f 100644 --- a/arch/arm/boot/dts/twl4030.dtsi +++ b/arch/arm/boot/dts/twl4030.dtsi @@ -19,6 +19,12 @@ interrupts = <11>; }; + charger: bci { + compatible = "ti,twl4030-bci"; + interrupts = <9>, <2>; + bci3v1-supply = <&vusb3v1>; + }; + watchdog { compatible = "ti,twl4030-wdt"; }; diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index d98abe911e37..f14108844e1a 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -495,10 +495,38 @@ static enum power_supply_property twl4030_charger_props[] = { POWER_SUPPLY_PROP_CURRENT_NOW, }; +#ifdef CONFIG_OF +static const struct twl4030_bci_platform_data * +twl4030_bci_parse_dt(struct device *dev) +{ + struct device_node *np = dev->of_node; + struct twl4030_bci_platform_data *pdata; + u32 num; + + if (!np) + return NULL; + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return pdata; + + if (of_property_read_u32(np, "ti,bb-uvolt", &num) == 0) + pdata->bb_uvolt = num; + if (of_property_read_u32(np, "ti,bb-uamp", &num) == 0) + pdata->bb_uamp = num; + return pdata; +} +#else +static inline const struct twl4030_bci_platform_data * +twl4030_bci_parse_dt(struct device *dev) +{ + return NULL; +} +#endif + static int __init twl4030_bci_probe(struct platform_device *pdev) { struct twl4030_bci *bci; - struct twl4030_bci_platform_data *pdata = pdev->dev.platform_data; + const struct twl4030_bci_platform_data *pdata = pdev->dev.platform_data; int ret; u32 reg; @@ -506,6 +534,9 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) if (bci == NULL) return -ENOMEM; + if (!pdata) + pdata = twl4030_bci_parse_dt(&pdev->dev); + bci->dev = &pdev->dev; bci->irq_chg = platform_get_irq(pdev, 0); bci->irq_bci = platform_get_irq(pdev, 1); @@ -581,8 +612,11 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) twl4030_charger_enable_ac(true); twl4030_charger_enable_usb(bci, true); - twl4030_charger_enable_backup(pdata->bb_uvolt, - pdata->bb_uamp); + if (pdata) + twl4030_charger_enable_backup(pdata->bb_uvolt, + pdata->bb_uamp); + else + twl4030_charger_enable_backup(0, 0); return 0; @@ -631,10 +665,17 @@ static int __exit twl4030_bci_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id twl_bci_of_match[] = { + {.compatible = "ti,twl4030-bci", }, + { } +}; +MODULE_DEVICE_TABLE(of, twl_bci_of_match); + static struct platform_driver twl4030_bci_driver = { .driver = { .name = "twl4030_bci", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(twl_bci_of_match), }, .remove = __exit_p(twl4030_bci_remove), }; From c8024234c20eaf7b163cc4dbd963cb9cd03a4ff1 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 7 Nov 2013 11:06:17 +0300 Subject: [PATCH 15/15] pm2301-charger: Remove unneeded NULL checks If "pm2" were NULL we would oops printing the error message. Fortunately, that's not possible so I have removed the NULL checks. Signed-off-by: Dan Carpenter Signed-off-by: Anton Vorontsov --- drivers/power/pm2301_charger.c | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/drivers/power/pm2301_charger.c b/drivers/power/pm2301_charger.c index b733c692a17f..62c15af58c9a 100644 --- a/drivers/power/pm2301_charger.c +++ b/drivers/power/pm2301_charger.c @@ -957,37 +957,24 @@ static int pm2xxx_runtime_suspend(struct device *dev) { struct i2c_client *pm2xxx_i2c_client = to_i2c_client(dev); struct pm2xxx_charger *pm2; - int ret = 0; pm2 = (struct pm2xxx_charger *)i2c_get_clientdata(pm2xxx_i2c_client); - if (!pm2) { - dev_err(pm2->dev, "no pm2xxx_charger data supplied\n"); - ret = -EINVAL; - return ret; - } - clear_lpn_pin(pm2); - return ret; + return 0; } static int pm2xxx_runtime_resume(struct device *dev) { struct i2c_client *pm2xxx_i2c_client = to_i2c_client(dev); struct pm2xxx_charger *pm2; - int ret = 0; pm2 = (struct pm2xxx_charger *)i2c_get_clientdata(pm2xxx_i2c_client); - if (!pm2) { - dev_err(pm2->dev, "no pm2xxx_charger data supplied\n"); - ret = -EINVAL; - return ret; - } if (gpio_is_valid(pm2->lpn_pin) && gpio_get_value(pm2->lpn_pin) == 0) set_lpn_pin(pm2); - return ret; + return 0; } #endif