aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2013-11-18 15:35:09 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2013-11-18 15:35:09 -0800
commita709bd585f291b9a6d0a3691ab4a0b9d3300a7a1 (patch)
treee35ef74281f1aefd23bcdb712832685ddb6f0668
parent3ea369eea07eb64adf36a6fb7fddb5d082c84143 (diff)
parentc8024234c20eaf7b163cc4dbd963cb9cd03a4ff1 (diff)
Merge tag 'for-v3.13' of git://git.infradead.org/battery-2.6
Pull battery updates from Anton Vorontsov: "Highlights: - A new driver for TI BQ24735 Battery Chargers, courtesy of NVidia. - Device tree bindings for TWL4030 chips. - Random fixes and cleanups" * tag 'for-v3.13' of git://git.infradead.org/battery-2.6: pm2301-charger: Remove unneeded NULL checks twl4030_charger: Add devicetree support power_supply: Fix documentation for TEMP_*ALERT* properties max17042_battery: Support regmap to access device's registers max17042_battery: Use SIMPLE_DEV_PM_OPS charger-manager : Replace kzalloc to devm_kzalloc and remove uneccessary code bq2415x_charger: Fix max battery regulation voltage tps65090-charger: Use "IS_ENABLED(CONFIG_OF)" for DT code tps65090-charger: Drop devm_free_irq of devm_ allocated irq power_supply: Add support for bq24735 charger pm2301-charger: Staticize pm2xxx_charger_die_therm_mngt pm2301-charger: Check return value of regulator_enable ab8500-charger: Remove redundant break ab8500-charger: Check return value of regulator_enable isp1704_charger: Fix driver to work with changes introduced in v3.5
-rw-r--r--Documentation/devicetree/bindings/power/twl-charger.txt20
-rw-r--r--Documentation/devicetree/bindings/power_supply/ti,bq24735.txt32
-rw-r--r--Documentation/power/power_supply_class.txt8
-rw-r--r--arch/arm/boot/dts/twl4030.dtsi6
-rw-r--r--drivers/power/Kconfig6
-rw-r--r--drivers/power/Makefile1
-rw-r--r--drivers/power/ab8500_charger.c17
-rw-r--r--drivers/power/bq2415x_charger.c6
-rw-r--r--drivers/power/bq24735-charger.c419
-rw-r--r--drivers/power/charger-manager.c85
-rw-r--r--drivers/power/isp1704_charger.c91
-rw-r--r--drivers/power/max17042_battery.c373
-rw-r--r--drivers/power/pm2301_charger.c27
-rw-r--r--drivers/power/tps65090-charger.c30
-rw-r--r--drivers/power/twl4030_charger.c47
-rw-r--r--include/linux/power/bq24735-charger.h39
16 files changed, 852 insertions, 355 deletions
diff --git a/Documentation/devicetree/bindings/power/twl-charger.txt b/Documentation/devicetree/bindings/power/twl-charger.txt
new file mode 100644
index 00000000000..d5c706216df
--- /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/Documentation/devicetree/bindings/power_supply/ti,bq24735.txt b/Documentation/devicetree/bindings/power_supply/ti,bq24735.txt
new file mode 100644
index 00000000000..4f6a550184d
--- /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/Documentation/power/power_supply_class.txt b/Documentation/power/power_supply_class.txt
index 3f10b39b034..89a8816990f 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)
diff --git a/arch/arm/boot/dts/twl4030.dtsi b/arch/arm/boot/dts/twl4030.dtsi
index fb1b2ec8eaa..4217096ee67 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/Kconfig b/drivers/power/Kconfig
index e6f92b45091..5e2054afe84 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 a4b74177706..372b4e8ab59 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/ab8500_charger.c b/drivers/power/ab8500_charger.c
index a4c4a10b3a4..19110aa613a 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:
@@ -1387,8 +1386,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 +1559,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 */
diff --git a/drivers/power/bq2415x_charger.c b/drivers/power/bq2415x_charger.c
index 0727f925613..df893dd1447 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,
diff --git a/drivers/power/bq24735-charger.c b/drivers/power/bq24735-charger.c
new file mode 100644
index 00000000000..d022b823305
--- /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 <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/power_supply.h>
+#include <linux/slab.h>
+
+#include <linux/power/bq24735-charger.h>
+
+#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 <dsriharsha@nvidia.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/power/charger-manager.c b/drivers/power/charger-manager.c
index e30e847600b..7287c0efd6b 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;
}
diff --git a/drivers/power/isp1704_charger.c b/drivers/power/isp1704_charger.c
index fc04d191579..1bb3a91b1ac 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 <pali.rohar@gmail.com>
*
* 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 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;
+ }
- /* detect charger */
- detect = isp1704_charger_detect(isp);
-
- if (detect) {
- isp->present = detect;
- isp->psy.type = isp1704_charger_type(isp);
+ /* enable data pullups */
+ if (isp->phy->otg->gadget)
+ usb_gadget_connect(isp->phy->otg->gadget);
}
- switch (isp->psy.type) {
- case POWER_SUPPLY_TYPE_USB_DCP:
- isp->current_max = 1800;
- break;
- case POWER_SUPPLY_TYPE_USB_CDP:
+ if (isp->psy.type != POWER_SUPPLY_TYPE_USB_DCP) {
/*
* 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->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-