Commit 45352bbf48e95078b4acd20774f49e72676e1e0f

Authored by Linus Torvalds

Merge git://git.infradead.org/battery-2.6

* git://git.infradead.org/battery-2.6:
  power_supply: Makefile cleanup
  bq27x00_battery: Add missing kfree(di->bus) in bq27x00_battery_remove()
  power_supply: Introduce maximum current property
  power_supply: Add types for USB chargers
  ds2782_battery: Fix units
  power_supply: Add driver for TWL4030/TPS65950 BCI charger
  bq20z75: Add support for more power supply properties
  wm831x_power: Add missing kfree(wm831x_power) in wm831x_power_remove()
  jz4740-battery: Add missing kfree(jz_battery) in jz_battery_remove()
  ds2760_battery: Add missing kfree(di) in ds2760_battery_remove()
  olpc_battery: Fix endian neutral breakage for s16 values
  ds2760_battery: Fix W1 and W1_SLAVE_DS2760 dependency
  pcf50633-charger: Add missing sysfs_remove_group()
  power_supply: Add driver for TI BQ20Z75 gas gauge IC
  wm831x_power: Remove duplicate chg mask
  omap: rx51: Add support for USB chargers
  power_supply: Add isp1704 charger detection driver

Showing 15 changed files Side-by-side Diff

arch/arm/mach-omap2/board-rx51-peripherals.c
... ... @@ -107,6 +107,10 @@
107 107 },
108 108 };
109 109  
  110 +static struct platform_device rx51_charger_device = {
  111 + .name = "isp1704_charger",
  112 +};
  113 +
110 114 #if defined(CONFIG_KEYBOARD_GPIO) || defined(CONFIG_KEYBOARD_GPIO_MODULE)
111 115  
112 116 #define RX51_GPIO_CAMERA_LENS_COVER 110
... ... @@ -919,5 +923,6 @@
919 923 spi_register_board_info(rx51_peripherals_spi_board_info,
920 924 ARRAY_SIZE(rx51_peripherals_spi_board_info));
921 925 omap2_hsmmc_init(mmc);
  926 + platform_device_register(&rx51_charger_device);
922 927 }
drivers/power/Kconfig
... ... @@ -64,8 +64,7 @@
64 64  
65 65 config BATTERY_DS2760
66 66 tristate "DS2760 battery driver (HP iPAQ & others)"
67   - select W1
68   - select W1_SLAVE_DS2760
  67 + depends on W1 && W1_SLAVE_DS2760
69 68 help
70 69 Say Y here to enable support for batteries with ds2760 chip.
71 70  
... ... @@ -109,6 +108,13 @@
109 108 help
110 109 Say Y to enable support for battery measured by WM97xx aux port.
111 110  
  111 +config BATTERY_BQ20Z75
  112 + tristate "TI BQ20z75 gas gauge"
  113 + depends on I2C
  114 + help
  115 + Say Y to include support for TI BQ20z75 SBS-compliant
  116 + gas gauge and protection IC.
  117 +
112 118 config BATTERY_BQ27x00
113 119 tristate "BQ27x00 battery driver"
114 120 depends on I2C
... ... @@ -165,6 +171,19 @@
165 171 help
166 172 Say Y here to enable the battery driver on Intel MID
167 173 platforms.
  174 +
  175 +config CHARGER_ISP1704
  176 + tristate "ISP1704 USB Charger Detection"
  177 + depends on USB_OTG_UTILS
  178 + help
  179 + Say Y to enable support for USB Charger Detection with
  180 + ISP1707/ISP1704 USB transceivers.
  181 +
  182 +config CHARGER_TWL4030
  183 + tristate "OMAP TWL4030 BCI charger driver"
  184 + depends on TWL4030_CORE
  185 + help
  186 + Say Y here to enable support for TWL4030 Battery Charge Interface.
168 187  
169 188 endif # POWER_SUPPLY
drivers/power/Makefile
1   -power_supply-objs := power_supply_core.o
  1 +ccflags-$(CONFIG_POWER_SUPPLY_DEBUG) := -DDEBUG
2 2  
3   -ifeq ($(CONFIG_SYSFS),y)
4   -power_supply-objs += power_supply_sysfs.o
5   -endif
  3 +power_supply-y := power_supply_core.o
  4 +power_supply-$(CONFIG_SYSFS) += power_supply_sysfs.o
  5 +power_supply-$(CONFIG_LEDS_TRIGGERS) += power_supply_leds.o
6 6  
7   -ifeq ($(CONFIG_LEDS_TRIGGERS),y)
8   -power_supply-objs += power_supply_leds.o
9   -endif
10   -
11   -ifeq ($(CONFIG_POWER_SUPPLY_DEBUG),y)
12   -EXTRA_CFLAGS += -DDEBUG
13   -endif
14   -
15 7 obj-$(CONFIG_POWER_SUPPLY) += power_supply.o
16 8  
17 9 obj-$(CONFIG_PDA_POWER) += pda_power.o
... ... @@ -29,6 +21,7 @@
29 21 obj-$(CONFIG_BATTERY_TOSA) += tosa_battery.o
30 22 obj-$(CONFIG_BATTERY_COLLIE) += collie_battery.o
31 23 obj-$(CONFIG_BATTERY_WM97XX) += wm97xx_battery.o
  24 +obj-$(CONFIG_BATTERY_BQ20Z75) += bq20z75.o
32 25 obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00_battery.o
33 26 obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o
34 27 obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o
... ... @@ -37,4 +30,6 @@
37 30 obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o
38 31 obj-$(CONFIG_BATTERY_JZ4740) += jz4740-battery.o
39 32 obj-$(CONFIG_BATTERY_INTEL_MID) += intel_mid_battery.o
  33 +obj-$(CONFIG_CHARGER_ISP1704) += isp1704_charger.o
  34 +obj-$(CONFIG_CHARGER_TWL4030) += twl4030_charger.o
drivers/power/bq20z75.c
  1 +/*
  2 + * Gas Gauge driver for TI's BQ20Z75
  3 + *
  4 + * Copyright (c) 2010, NVIDIA Corporation.
  5 + *
  6 + * This program is free software; you can redistribute it and/or modify
  7 + * it under the terms of the GNU General Public License as published by
  8 + * the Free Software Foundation; either version 2 of the License, or
  9 + * (at your option) any later version.
  10 + *
  11 + * This program is distributed in the hope that it will be useful, but WITHOUT
  12 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  14 + * more details.
  15 + *
  16 + * You should have received a copy of the GNU General Public License along
  17 + * with this program; if not, write to the Free Software Foundation, Inc.,
  18 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19 + */
  20 +
  21 +#include <linux/init.h>
  22 +#include <linux/module.h>
  23 +#include <linux/kernel.h>
  24 +#include <linux/err.h>
  25 +#include <linux/power_supply.h>
  26 +#include <linux/i2c.h>
  27 +#include <linux/slab.h>
  28 +
  29 +enum {
  30 + REG_MANUFACTURER_DATA,
  31 + REG_TEMPERATURE,
  32 + REG_VOLTAGE,
  33 + REG_CURRENT,
  34 + REG_CAPACITY,
  35 + REG_TIME_TO_EMPTY,
  36 + REG_TIME_TO_FULL,
  37 + REG_STATUS,
  38 + REG_CYCLE_COUNT,
  39 + REG_SERIAL_NUMBER,
  40 + REG_REMAINING_CAPACITY,
  41 + REG_FULL_CHARGE_CAPACITY,
  42 + REG_DESIGN_CAPACITY,
  43 + REG_DESIGN_VOLTAGE,
  44 +};
  45 +
  46 +/* manufacturer access defines */
  47 +#define MANUFACTURER_ACCESS_STATUS 0x0006
  48 +#define MANUFACTURER_ACCESS_SLEEP 0x0011
  49 +
  50 +/* battery status value bits */
  51 +#define BATTERY_DISCHARGING 0x40
  52 +#define BATTERY_FULL_CHARGED 0x20
  53 +#define BATTERY_FULL_DISCHARGED 0x10
  54 +
  55 +#define BQ20Z75_DATA(_psp, _addr, _min_value, _max_value) { \
  56 + .psp = _psp, \
  57 + .addr = _addr, \
  58 + .min_value = _min_value, \
  59 + .max_value = _max_value, \
  60 +}
  61 +
  62 +static const struct bq20z75_device_data {
  63 + enum power_supply_property psp;
  64 + u8 addr;
  65 + int min_value;
  66 + int max_value;
  67 +} bq20z75_data[] = {
  68 + [REG_MANUFACTURER_DATA] =
  69 + BQ20Z75_DATA(POWER_SUPPLY_PROP_PRESENT, 0x00, 0, 65535),
  70 + [REG_TEMPERATURE] =
  71 + BQ20Z75_DATA(POWER_SUPPLY_PROP_TEMP, 0x08, 0, 65535),
  72 + [REG_VOLTAGE] =
  73 + BQ20Z75_DATA(POWER_SUPPLY_PROP_VOLTAGE_NOW, 0x09, 0, 20000),
  74 + [REG_CURRENT] =
  75 + BQ20Z75_DATA(POWER_SUPPLY_PROP_CURRENT_NOW, 0x0A, -32768,
  76 + 32767),
  77 + [REG_CAPACITY] =
  78 + BQ20Z75_DATA(POWER_SUPPLY_PROP_CAPACITY, 0x0E, 0, 100),
  79 + [REG_REMAINING_CAPACITY] =
  80 + BQ20Z75_DATA(POWER_SUPPLY_PROP_ENERGY_NOW, 0x0F, 0, 65535),
  81 + [REG_FULL_CHARGE_CAPACITY] =
  82 + BQ20Z75_DATA(POWER_SUPPLY_PROP_ENERGY_FULL, 0x10, 0, 65535),
  83 + [REG_TIME_TO_EMPTY] =
  84 + BQ20Z75_DATA(POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, 0x12, 0,
  85 + 65535),
  86 + [REG_TIME_TO_FULL] =
  87 + BQ20Z75_DATA(POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, 0x13, 0,
  88 + 65535),
  89 + [REG_STATUS] =
  90 + BQ20Z75_DATA(POWER_SUPPLY_PROP_STATUS, 0x16, 0, 65535),
  91 + [REG_CYCLE_COUNT] =
  92 + BQ20Z75_DATA(POWER_SUPPLY_PROP_CYCLE_COUNT, 0x17, 0, 65535),
  93 + [REG_DESIGN_CAPACITY] =
  94 + BQ20Z75_DATA(POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, 0x18, 0,
  95 + 65535),
  96 + [REG_DESIGN_VOLTAGE] =
  97 + BQ20Z75_DATA(POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, 0x19, 0,
  98 + 65535),
  99 + [REG_SERIAL_NUMBER] =
  100 + BQ20Z75_DATA(POWER_SUPPLY_PROP_SERIAL_NUMBER, 0x1C, 0, 65535),
  101 +};
  102 +
  103 +static enum power_supply_property bq20z75_properties[] = {
  104 + POWER_SUPPLY_PROP_STATUS,
  105 + POWER_SUPPLY_PROP_HEALTH,
  106 + POWER_SUPPLY_PROP_PRESENT,
  107 + POWER_SUPPLY_PROP_TECHNOLOGY,
  108 + POWER_SUPPLY_PROP_CYCLE_COUNT,
  109 + POWER_SUPPLY_PROP_VOLTAGE_NOW,
  110 + POWER_SUPPLY_PROP_CURRENT_NOW,
  111 + POWER_SUPPLY_PROP_CAPACITY,
  112 + POWER_SUPPLY_PROP_TEMP,
  113 + POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
  114 + POWER_SUPPLY_PROP_TIME_TO_FULL_AVG,
  115 + POWER_SUPPLY_PROP_SERIAL_NUMBER,
  116 + POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
  117 + POWER_SUPPLY_PROP_ENERGY_NOW,
  118 + POWER_SUPPLY_PROP_ENERGY_FULL,
  119 + POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
  120 +};
  121 +
  122 +struct bq20z75_info {
  123 + struct i2c_client *client;
  124 + struct power_supply power_supply;
  125 +};
  126 +
  127 +static int bq20z75_read_word_data(struct i2c_client *client, u8 address)
  128 +{
  129 + s32 ret;
  130 +
  131 + ret = i2c_smbus_read_word_data(client, address);
  132 + if (ret < 0) {
  133 + dev_err(&client->dev,
  134 + "%s: i2c read at address 0x%x failed\n",
  135 + __func__, address);
  136 + return ret;
  137 + }
  138 + return le16_to_cpu(ret);
  139 +}
  140 +
  141 +static int bq20z75_write_word_data(struct i2c_client *client, u8 address,
  142 + u16 value)
  143 +{
  144 + s32 ret;
  145 +
  146 + ret = i2c_smbus_write_word_data(client, address, le16_to_cpu(value));
  147 + if (ret < 0) {
  148 + dev_err(&client->dev,
  149 + "%s: i2c write to address 0x%x failed\n",
  150 + __func__, address);
  151 + return ret;
  152 + }
  153 + return 0;
  154 +}
  155 +
  156 +static int bq20z75_get_battery_presence_and_health(
  157 + struct i2c_client *client, enum power_supply_property psp,
  158 + union power_supply_propval *val)
  159 +{
  160 + s32 ret;
  161 +
  162 + /* Write to ManufacturerAccess with
  163 + * ManufacturerAccess command and then
  164 + * read the status */
  165 + ret = bq20z75_write_word_data(client,
  166 + bq20z75_data[REG_MANUFACTURER_DATA].addr,
  167 + MANUFACTURER_ACCESS_STATUS);
  168 + if (ret < 0)
  169 + return ret;
  170 +
  171 +
  172 + ret = bq20z75_read_word_data(client,
  173 + bq20z75_data[REG_MANUFACTURER_DATA].addr);
  174 + if (ret < 0)
  175 + return ret;
  176 +
  177 + if (ret < bq20z75_data[REG_MANUFACTURER_DATA].min_value ||
  178 + ret > bq20z75_data[REG_MANUFACTURER_DATA].max_value) {
  179 + val->intval = 0;
  180 + return 0;
  181 + }
  182 +
  183 + /* Mask the upper nibble of 2nd byte and
  184 + * lower byte of response then
  185 + * shift the result by 8 to get status*/
  186 + ret &= 0x0F00;
  187 + ret >>= 8;
  188 + if (psp == POWER_SUPPLY_PROP_PRESENT) {
  189 + if (ret == 0x0F)
  190 + /* battery removed */
  191 + val->intval = 0;
  192 + else
  193 + val->intval = 1;
  194 + } else if (psp == POWER_SUPPLY_PROP_HEALTH) {
  195 + if (ret == 0x09)
  196 + val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
  197 + else if (ret == 0x0B)
  198 + val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
  199 + else if (ret == 0x0C)
  200 + val->intval = POWER_SUPPLY_HEALTH_DEAD;
  201 + else
  202 + val->intval = POWER_SUPPLY_HEALTH_GOOD;
  203 + }
  204 +
  205 + return 0;
  206 +}
  207 +
  208 +static int bq20z75_get_battery_property(struct i2c_client *client,
  209 + int reg_offset, enum power_supply_property psp,
  210 + union power_supply_propval *val)
  211 +{
  212 + s32 ret;
  213 +
  214 + ret = bq20z75_read_word_data(client,
  215 + bq20z75_data[reg_offset].addr);
  216 + if (ret < 0)
  217 + return ret;
  218 +
  219 + /* returned values are 16 bit */
  220 + if (bq20z75_data[reg_offset].min_value < 0)
  221 + ret = (s16)ret;
  222 +
  223 + if (ret >= bq20z75_data[reg_offset].min_value &&
  224 + ret <= bq20z75_data[reg_offset].max_value) {
  225 + val->intval = ret;
  226 + if (psp == POWER_SUPPLY_PROP_STATUS) {
  227 + if (ret & BATTERY_FULL_CHARGED)
  228 + val->intval = POWER_SUPPLY_STATUS_FULL;
  229 + else if (ret & BATTERY_FULL_DISCHARGED)
  230 + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
  231 + else if (ret & BATTERY_DISCHARGING)
  232 + val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
  233 + else
  234 + val->intval = POWER_SUPPLY_STATUS_CHARGING;
  235 + }
  236 + } else {
  237 + if (psp == POWER_SUPPLY_PROP_STATUS)
  238 + val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
  239 + else
  240 + val->intval = 0;
  241 + }
  242 +
  243 + return 0;
  244 +}
  245 +
  246 +static void bq20z75_unit_adjustment(struct i2c_client *client,
  247 + enum power_supply_property psp, union power_supply_propval *val)
  248 +{
  249 +#define BASE_UNIT_CONVERSION 1000
  250 +#define BATTERY_MODE_CAP_MULT_WATT (10 * BASE_UNIT_CONVERSION)
  251 +#define TIME_UNIT_CONVERSION 600
  252 +#define TEMP_KELVIN_TO_CELCIUS 2731
  253 + switch (psp) {
  254 + case POWER_SUPPLY_PROP_ENERGY_NOW:
  255 + case POWER_SUPPLY_PROP_ENERGY_FULL:
  256 + case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
  257 + val->intval *= BATTERY_MODE_CAP_MULT_WATT;
  258 + break;
  259 +
  260 + case POWER_SUPPLY_PROP_VOLTAGE_NOW:
  261 + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
  262 + case POWER_SUPPLY_PROP_CURRENT_NOW:
  263 + val->intval *= BASE_UNIT_CONVERSION;
  264 + break;
  265 +
  266 + case POWER_SUPPLY_PROP_TEMP:
  267 + /* bq20z75 provides battery tempreture in 0.1°K
  268 + * so convert it to 0.1°C */
  269 + val->intval -= TEMP_KELVIN_TO_CELCIUS;
  270 + val->intval *= 10;
  271 + break;
  272 +
  273 + case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
  274 + case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG:
  275 + val->intval *= TIME_UNIT_CONVERSION;
  276 + break;
  277 +
  278 + default:
  279 + dev_dbg(&client->dev,
  280 + "%s: no need for unit conversion %d\n", __func__, psp);
  281 + }
  282 +}
  283 +
  284 +static int bq20z75_get_battery_capacity(struct i2c_client *client,
  285 + int reg_offset, enum power_supply_property psp,
  286 + union power_supply_propval *val)
  287 +{
  288 + s32 ret;
  289 +
  290 + ret = bq20z75_read_word_data(client, bq20z75_data[reg_offset].addr);
  291 + if (ret < 0)
  292 + return ret;
  293 +
  294 + if (psp == POWER_SUPPLY_PROP_CAPACITY) {
  295 + /* bq20z75 spec says that this can be >100 %
  296 + * even if max value is 100 % */
  297 + val->intval = min(ret, 100);
  298 + } else
  299 + val->intval = ret;
  300 +
  301 + return 0;
  302 +}
  303 +
  304 +static char bq20z75_serial[5];
  305 +static int bq20z75_get_battery_serial_number(struct i2c_client *client,
  306 + union power_supply_propval *val)
  307 +{
  308 + int ret;
  309 +
  310 + ret = bq20z75_read_word_data(client,
  311 + bq20z75_data[REG_SERIAL_NUMBER].addr);
  312 + if (ret < 0)
  313 + return ret;
  314 +
  315 + ret = sprintf(bq20z75_serial, "%04x", ret);
  316 + val->strval = bq20z75_serial;
  317 +
  318 + return 0;
  319 +}
  320 +
  321 +static int bq20z75_get_property(struct power_supply *psy,
  322 + enum power_supply_property psp,
  323 + union power_supply_propval *val)
  324 +{
  325 + int count;
  326 + int ret;
  327 + struct bq20z75_info *bq20z75_device = container_of(psy,
  328 + struct bq20z75_info, power_supply);
  329 + struct i2c_client *client = bq20z75_device->client;
  330 +
  331 + switch (psp) {
  332 + case POWER_SUPPLY_PROP_PRESENT:
  333 + case POWER_SUPPLY_PROP_HEALTH:
  334 + ret = bq20z75_get_battery_presence_and_health(client, psp, val);
  335 + if (ret)
  336 + return ret;
  337 + break;
  338 +
  339 + case POWER_SUPPLY_PROP_TECHNOLOGY:
  340 + val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
  341 + break;
  342 +
  343 + case POWER_SUPPLY_PROP_ENERGY_NOW:
  344 + case POWER_SUPPLY_PROP_ENERGY_FULL:
  345 + case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
  346 + case POWER_SUPPLY_PROP_CAPACITY:
  347 + for (count = 0; count < ARRAY_SIZE(bq20z75_data); count++) {
  348 + if (psp == bq20z75_data[count].psp)
  349 + break;
  350 + }
  351 +
  352 + ret = bq20z75_get_battery_capacity(client, count, psp, val);
  353 + if (ret)
  354 + return ret;
  355 +
  356 + break;
  357 +
  358 + case POWER_SUPPLY_PROP_SERIAL_NUMBER:
  359 + ret = bq20z75_get_battery_serial_number(client, val);
  360 + if (ret)
  361 + return ret;
  362 + break;
  363 +
  364 + case POWER_SUPPLY_PROP_STATUS:
  365 + case POWER_SUPPLY_PROP_CYCLE_COUNT:
  366 + case POWER_SUPPLY_PROP_VOLTAGE_NOW:
  367 + case POWER_SUPPLY_PROP_CURRENT_NOW:
  368 + case POWER_SUPPLY_PROP_TEMP:
  369 + case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
  370 + case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG:
  371 + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
  372 + for (count = 0; count < ARRAY_SIZE(bq20z75_data); count++) {
  373 + if (psp == bq20z75_data[count].psp)
  374 + break;
  375 + }
  376 +
  377 + ret = bq20z75_get_battery_property(client, count, psp, val);
  378 + if (ret)
  379 + return ret;
  380 +
  381 + break;
  382 +
  383 + default:
  384 + dev_err(&client->dev,
  385 + "%s: INVALID property\n", __func__);
  386 + return -EINVAL;
  387 + }
  388 +
  389 + /* Convert units to match requirements for power supply class */
  390 + bq20z75_unit_adjustment(client, psp, val);
  391 +
  392 + dev_dbg(&client->dev,
  393 + "%s: property = %d, value = %d\n", __func__, psp, val->intval);
  394 +
  395 + return 0;
  396 +}
  397 +
  398 +static int bq20z75_probe(struct i2c_client *client,
  399 + const struct i2c_device_id *id)
  400 +{
  401 + struct bq20z75_info *bq20z75_device;
  402 + int rc;
  403 +
  404 + bq20z75_device = kzalloc(sizeof(struct bq20z75_info), GFP_KERNEL);
  405 + if (!bq20z75_device)
  406 + return -ENOMEM;
  407 +
  408 + bq20z75_device->client = client;
  409 + bq20z75_device->power_supply.name = "battery";
  410 + bq20z75_device->power_supply.type = POWER_SUPPLY_TYPE_BATTERY;
  411 + bq20z75_device->power_supply.properties = bq20z75_properties;
  412 + bq20z75_device->power_supply.num_properties =
  413 + ARRAY_SIZE(bq20z75_properties);
  414 + bq20z75_device->power_supply.get_property = bq20z75_get_property;
  415 +
  416 + i2c_set_clientdata(client, bq20z75_device);
  417 +
  418 + rc = power_supply_register(&client->dev, &bq20z75_device->power_supply);
  419 + if (rc) {
  420 + dev_err(&client->dev,
  421 + "%s: Failed to register power supply\n", __func__);
  422 + kfree(bq20z75_device);
  423 + return rc;
  424 + }
  425 +
  426 + dev_info(&client->dev,
  427 + "%s: battery gas gauge device registered\n", client->name);
  428 +
  429 + return 0;
  430 +}
  431 +
  432 +static int bq20z75_remove(struct i2c_client *client)
  433 +{
  434 + struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client);
  435 +
  436 + power_supply_unregister(&bq20z75_device->power_supply);
  437 + kfree(bq20z75_device);
  438 + bq20z75_device = NULL;
  439 +
  440 + return 0;
  441 +}
  442 +
  443 +#if defined CONFIG_PM
  444 +static int bq20z75_suspend(struct i2c_client *client,
  445 + pm_message_t state)
  446 +{
  447 + s32 ret;
  448 +
  449 + /* write to manufacturer access with sleep command */
  450 + ret = bq20z75_write_word_data(client,
  451 + bq20z75_data[REG_MANUFACTURER_DATA].addr,
  452 + MANUFACTURER_ACCESS_SLEEP);
  453 + if (ret < 0)
  454 + return ret;
  455 +
  456 + return 0;
  457 +}
  458 +#else
  459 +#define bq20z75_suspend NULL
  460 +#endif
  461 +/* any smbus transaction will wake up bq20z75 */
  462 +#define bq20z75_resume NULL
  463 +
  464 +static const struct i2c_device_id bq20z75_id[] = {
  465 + { "bq20z75", 0 },
  466 + {}
  467 +};
  468 +
  469 +static struct i2c_driver bq20z75_battery_driver = {
  470 + .probe = bq20z75_probe,
  471 + .remove = bq20z75_remove,
  472 + .suspend = bq20z75_suspend,
  473 + .resume = bq20z75_resume,
  474 + .id_table = bq20z75_id,
  475 + .driver = {
  476 + .name = "bq20z75-battery",
  477 + },
  478 +};
  479 +
  480 +static int __init bq20z75_battery_init(void)
  481 +{
  482 + return i2c_add_driver(&bq20z75_battery_driver);
  483 +}
  484 +module_init(bq20z75_battery_init);
  485 +
  486 +static void __exit bq20z75_battery_exit(void)
  487 +{
  488 + i2c_del_driver(&bq20z75_battery_driver);
  489 +}
  490 +module_exit(bq20z75_battery_exit);
  491 +
  492 +MODULE_DESCRIPTION("BQ20z75 battery monitor driver");
  493 +MODULE_LICENSE("GPL");
drivers/power/bq27x00_battery.c
... ... @@ -418,6 +418,7 @@
418 418  
419 419 power_supply_unregister(&di->bat);
420 420  
  421 + kfree(di->bus);
421 422 kfree(di->bat.name);
422 423  
423 424 mutex_lock(&battery_mutex);
drivers/power/ds2760_battery.c
... ... @@ -586,6 +586,7 @@
586 586 &di->set_charged_work);
587 587 destroy_workqueue(di->monitor_wqueue);
588 588 power_supply_unregister(&di->bat);
  589 + kfree(di);
589 590  
590 591 return 0;
591 592 }
drivers/power/ds2782_battery.c
... ... @@ -44,8 +44,8 @@
44 44  
45 45 struct ds278x_battery_ops {
46 46 int (*get_battery_current)(struct ds278x_info *info, int *current_uA);
47   - int (*get_battery_voltage)(struct ds278x_info *info, int *voltage_uA);
48   - int (*get_battery_capacity)(struct ds278x_info *info, int *capacity_uA);
  47 + int (*get_battery_voltage)(struct ds278x_info *info, int *voltage_uV);
  48 + int (*get_battery_capacity)(struct ds278x_info *info, int *capacity);
49 49 };
50 50  
51 51 #define to_ds278x_info(x) container_of(x, struct ds278x_info, battery)
... ... @@ -137,7 +137,7 @@
137 137 return 0;
138 138 }
139 139  
140   -static int ds2782_get_voltage(struct ds278x_info *info, int *voltage_uA)
  140 +static int ds2782_get_voltage(struct ds278x_info *info, int *voltage_uV)
141 141 {
142 142 s16 raw;
143 143 int err;
... ... @@ -149,7 +149,7 @@
149 149 err = ds278x_read_reg16(info, DS278x_REG_VOLT_MSB, &raw);
150 150 if (err)
151 151 return err;
152   - *voltage_uA = (raw / 32) * 4800;
  152 + *voltage_uV = (raw / 32) * 4800;
153 153 return 0;
154 154 }
155 155  
... ... @@ -177,7 +177,7 @@
177 177 return 0;
178 178 }
179 179  
180   -static int ds2786_get_voltage(struct ds278x_info *info, int *voltage_uA)
  180 +static int ds2786_get_voltage(struct ds278x_info *info, int *voltage_uV)
181 181 {
182 182 s16 raw;
183 183 int err;
... ... @@ -189,7 +189,7 @@
189 189 err = ds278x_read_reg16(info, DS278x_REG_VOLT_MSB, &raw);
190 190 if (err)
191 191 return err;
192   - *voltage_uA = (raw / 8) * 1220;
  192 + *voltage_uV = (raw / 8) * 1220;
193 193 return 0;
194 194 }
195 195  
drivers/power/isp1704_charger.c
  1 +/*
  2 + * ISP1704 USB Charger Detection driver
  3 + *
  4 + * Copyright (C) 2010 Nokia Corporation
  5 + *
  6 + * This program is free software; you can redistribute it and/or modify
  7 + * it under the terms of the GNU General Public License as published by
  8 + * the Free Software Foundation; either version 2 of the License, or
  9 + * (at your option) any later version.
  10 + *
  11 + * This program is distributed in the hope that it will be useful,
  12 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14 + * GNU General Public License for more details.
  15 + *
  16 + * You should have received a copy of the GNU General Public License
  17 + * along with this program; if not, write to the Free Software
  18 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  19 + */
  20 +
  21 +#include <linux/kernel.h>
  22 +#include <linux/module.h>
  23 +#include <linux/err.h>
  24 +#include <linux/init.h>
  25 +#include <linux/types.h>
  26 +#include <linux/device.h>
  27 +#include <linux/sysfs.h>
  28 +#include <linux/platform_device.h>
  29 +#include <linux/power_supply.h>
  30 +#include <linux/delay.h>
  31 +
  32 +#include <linux/usb/otg.h>
  33 +#include <linux/usb/ulpi.h>
  34 +#include <linux/usb/ch9.h>
  35 +#include <linux/usb/gadget.h>
  36 +
  37 +/* Vendor specific Power Control register */
  38 +#define ISP1704_PWR_CTRL 0x3d
  39 +#define ISP1704_PWR_CTRL_SWCTRL (1 << 0)
  40 +#define ISP1704_PWR_CTRL_DET_COMP (1 << 1)
  41 +#define ISP1704_PWR_CTRL_BVALID_RISE (1 << 2)
  42 +#define ISP1704_PWR_CTRL_BVALID_FALL (1 << 3)
  43 +#define ISP1704_PWR_CTRL_DP_WKPU_EN (1 << 4)
  44 +#define ISP1704_PWR_CTRL_VDAT_DET (1 << 5)
  45 +#define ISP1704_PWR_CTRL_DPVSRC_EN (1 << 6)
  46 +#define ISP1704_PWR_CTRL_HWDETECT (1 << 7)
  47 +
  48 +#define NXP_VENDOR_ID 0x04cc
  49 +
  50 +static u16 isp170x_id[] = {
  51 + 0x1704,
  52 + 0x1707,
  53 +};
  54 +
  55 +struct isp1704_charger {
  56 + struct device *dev;
  57 + struct power_supply psy;
  58 + struct otg_transceiver *otg;
  59 + struct notifier_block nb;
  60 + struct work_struct work;
  61 +
  62 + char model[7];
  63 + unsigned present:1;
  64 +};
  65 +
  66 +/*
  67 + * ISP1704 detects PS/2 adapters as charger. To make sure the detected charger
  68 + * is actually a dedicated charger, the following steps need to be taken.
  69 + */
  70 +static inline int isp1704_charger_verify(struct isp1704_charger *isp)
  71 +{
  72 + int ret = 0;
  73 + u8 r;
  74 +
  75 + /* Reset the transceiver */
  76 + r = otg_io_read(isp->otg, ULPI_FUNC_CTRL);
  77 + r |= ULPI_FUNC_CTRL_RESET;
  78 + otg_io_write(isp->otg, ULPI_FUNC_CTRL, r);
  79 + usleep_range(1000, 2000);
  80 +
  81 + /* Set normal mode */
  82 + r &= ~(ULPI_FUNC_CTRL_RESET | ULPI_FUNC_CTRL_OPMODE_MASK);
  83 + otg_io_write(isp->otg, ULPI_FUNC_CTRL, r);
  84 +
  85 + /* Clear the DP and DM pull-down bits */
  86 + r = ULPI_OTG_CTRL_DP_PULLDOWN | ULPI_OTG_CTRL_DM_PULLDOWN;
  87 + otg_io_write(isp->otg, ULPI_CLR(ULPI_OTG_CTRL), r);
  88 +
  89 + /* Enable strong pull-up on DP (1.5K) and reset */
  90 + r = ULPI_FUNC_CTRL_TERMSELECT | ULPI_FUNC_CTRL_RESET;
  91 + otg_io_write(isp->otg, ULPI_SET(ULPI_FUNC_CTRL), r);
  92 + usleep_range(1000, 2000);
  93 +
  94 + /* Read the line state */
  95 + if (!otg_io_read(isp->otg, ULPI_DEBUG)) {
  96 + /* Disable strong pull-up on DP (1.5K) */
  97 + otg_io_write(isp->otg, ULPI_CLR(ULPI_FUNC_CTRL),
  98 + ULPI_FUNC_CTRL_TERMSELECT);
  99 + return 1;
  100 + }
  101 +
  102 + /* Is it a charger or PS/2 connection */
  103 +
  104 + /* Enable weak pull-up resistor on DP */
  105 + otg_io_write(isp->otg, ULPI_SET(ISP1704_PWR_CTRL),
  106 + ISP1704_PWR_CTRL_DP_WKPU_EN);
  107 +
  108 + /* Disable strong pull-up on DP (1.5K) */
  109 + otg_io_write(isp->otg, ULPI_CLR(ULPI_FUNC_CTRL),
  110 + ULPI_FUNC_CTRL_TERMSELECT);
  111 +
  112 + /* Enable weak pull-down resistor on DM */
  113 + otg_io_write(isp->otg, ULPI_SET(ULPI_OTG_CTRL),
  114 + ULPI_OTG_CTRL_DM_PULLDOWN);
  115 +
  116 + /* It's a charger if the line states are clear */
  117 + if (!(otg_io_read(isp->otg, ULPI_DEBUG)))
  118 + ret = 1;
  119 +
  120 + /* Disable weak pull-up resistor on DP */
  121 + otg_io_write(isp->otg, ULPI_CLR(ISP1704_PWR_CTRL),
  122 + ISP1704_PWR_CTRL_DP_WKPU_EN);
  123 +
  124 + return ret;
  125 +}
  126 +
  127 +static inline int isp1704_charger_detect(struct isp1704_charger *isp)
  128 +{
  129 + unsigned long timeout;
  130 + u8 r;
  131 + int ret = 0;
  132 +
  133 + /* set SW control bit in PWR_CTRL register */
  134 + otg_io_write(isp->otg, ISP1704_PWR_CTRL,
  135 + ISP1704_PWR_CTRL_SWCTRL);
  136 +
  137 + /* enable manual charger detection */
  138 + r = (ISP1704_PWR_CTRL_SWCTRL | ISP1704_PWR_CTRL_DPVSRC_EN);
  139 + otg_io_write(isp->otg, ULPI_SET(ISP1704_PWR_CTRL), r);
  140 + usleep_range(1000, 2000);
  141 +
  142 + timeout = jiffies + msecs_to_jiffies(300);
  143 + do {
  144 + /* Check if there is a charger */
  145 + if (otg_io_read(isp->otg, ISP1704_PWR_CTRL)
  146 + & ISP1704_PWR_CTRL_VDAT_DET) {
  147 + ret = isp1704_charger_verify(isp);
  148 + break;
  149 + }
  150 + } while (!time_after(jiffies, timeout));
  151 +
  152 + return ret;
  153 +}
  154 +
  155 +static void isp1704_charger_work(struct work_struct *data)
  156 +{
  157 + int detect;
  158 + struct isp1704_charger *isp =
  159 + container_of(data, struct isp1704_charger, work);
  160 +
  161 + /*
  162 + * FIXME Only supporting dedicated chargers even though isp1704 can
  163 + * detect HUB and HOST chargers. If the device has already been
  164 + * enumerated, the detection will break the connection.
  165 + */
  166 + if (isp->otg->state != OTG_STATE_B_IDLE)
  167 + return;
  168 +
  169 + /* disable data pullups */
  170 + if (isp->otg->gadget)
  171 + usb_gadget_disconnect(isp->otg->gadget);
  172 +
  173 + /* detect charger */
  174 + detect = isp1704_charger_detect(isp);
  175 + if (detect) {
  176 + isp->present = detect;
  177 + power_supply_changed(&isp->psy);
  178 + }
  179 +
  180 + /* enable data pullups */
  181 + if (isp->otg->gadget)
  182 + usb_gadget_connect(isp->otg->gadget);
  183 +}
  184 +
  185 +static int isp1704_notifier_call(struct notifier_block *nb,
  186 + unsigned long event, void *unused)
  187 +{
  188 + struct isp1704_charger *isp =
  189 + container_of(nb, struct isp1704_charger, nb);
  190 +
  191 + switch (event) {
  192 + case USB_EVENT_VBUS:
  193 + schedule_work(&isp->work);
  194 + break;
  195 + case USB_EVENT_NONE:
  196 + if (isp->present) {
  197 + isp->present = 0;
  198 + power_supply_changed(&isp->psy);
  199 + }
  200 + break;
  201 + default:
  202 + return NOTIFY_DONE;
  203 + }
  204 +
  205 + return NOTIFY_OK;
  206 +}
  207 +
  208 +static int isp1704_charger_get_property(struct power_supply *psy,
  209 + enum power_supply_property psp,
  210 + union power_supply_propval *val)
  211 +{
  212 + struct isp1704_charger *isp =
  213 + container_of(psy, struct isp1704_charger, psy);
  214 +
  215 + switch (psp) {
  216 + case POWER_SUPPLY_PROP_PRESENT:
  217 + val->intval = isp->present;
  218 + break;
  219 + case POWER_SUPPLY_PROP_MODEL_NAME:
  220 + val->strval = isp->model;
  221 + break;
  222 + case POWER_SUPPLY_PROP_MANUFACTURER:
  223 + val->strval = "NXP";
  224 + break;
  225 + default:
  226 + return -EINVAL;
  227 + }
  228 + return 0;
  229 +}
  230 +
  231 +static enum power_supply_property power_props[] = {
  232 + POWER_SUPPLY_PROP_PRESENT,
  233 + POWER_SUPPLY_PROP_MODEL_NAME,
  234 + POWER_SUPPLY_PROP_MANUFACTURER,
  235 +};
  236 +
  237 +static inline int isp1704_test_ulpi(struct isp1704_charger *isp)
  238 +{
  239 + int vendor;
  240 + int product;
  241 + int i;
  242 + int ret = -ENODEV;
  243 +
  244 + /* Test ULPI interface */
  245 + ret = otg_io_write(isp->otg, ULPI_SCRATCH, 0xaa);
  246 + if (ret < 0)
  247 + return ret;
  248 +
  249 + ret = otg_io_read(isp->otg, ULPI_SCRATCH);
  250 + if (ret < 0)
  251 + return ret;
  252 +
  253 + if (ret != 0xaa)
  254 + return -ENODEV;
  255 +
  256 + /* Verify the product and vendor id matches */
  257 + vendor = otg_io_read(isp->otg, ULPI_VENDOR_ID_LOW);
  258 + vendor |= otg_io_read(isp->otg, ULPI_VENDOR_ID_HIGH) << 8;
  259 + if (vendor != NXP_VENDOR_ID)
  260 + return -ENODEV;
  261 +
  262 + product = otg_io_read(isp->otg, ULPI_PRODUCT_ID_LOW);
  263 + product |= otg_io_read(isp->otg, ULPI_PRODUCT_ID_HIGH) << 8;
  264 +
  265 + for (i = 0; i < ARRAY_SIZE(isp170x_id); i++) {
  266 + if (product == isp170x_id[i]) {
  267 + sprintf(isp->model, "isp%x", product);
  268 + return product;
  269 + }
  270 + }
  271 +
  272 + dev_err(isp->dev, "product id %x not matching known ids", product);
  273 +
  274 + return -ENODEV;
  275 +}
  276 +
  277 +static int __devinit isp1704_charger_probe(struct platform_device *pdev)
  278 +{
  279 + struct isp1704_charger *isp;
  280 + int ret = -ENODEV;
  281 +
  282 + isp = kzalloc(sizeof *isp, GFP_KERNEL);
  283 + if (!isp)
  284 + return -ENOMEM;
  285 +
  286 + isp->otg = otg_get_transceiver();
  287 + if (!isp->otg)
  288 + goto fail0;
  289 +
  290 + ret = isp1704_test_ulpi(isp);
  291 + if (ret < 0)
  292 + goto fail1;
  293 +
  294 + isp->dev = &pdev->dev;
  295 + platform_set_drvdata(pdev, isp);
  296 +
  297 + isp->psy.name = "isp1704";
  298 + isp->psy.type = POWER_SUPPLY_TYPE_USB;
  299 + isp->psy.properties = power_props;
  300 + isp->psy.num_properties = ARRAY_SIZE(power_props);
  301 + isp->psy.get_property = isp1704_charger_get_property;
  302 +
  303 + ret = power_supply_register(isp->dev, &isp->psy);
  304 + if (ret)
  305 + goto fail1;
  306 +
  307 + /*
  308 + * REVISIT: using work in order to allow the otg notifications to be
  309 + * made atomically in the future.
  310 + */
  311 + INIT_WORK(&isp->work, isp1704_charger_work);
  312 +
  313 + isp->nb.notifier_call = isp1704_notifier_call;
  314 +
  315 + ret = otg_register_notifier(isp->otg, &isp->nb);
  316 + if (ret)
  317 + goto fail2;
  318 +
  319 + dev_info(isp->dev, "registered with product id %s\n", isp->model);
  320 +
  321 + return 0;
  322 +fail2:
  323 + power_supply_unregister(&isp->psy);
  324 +fail1:
  325 + otg_put_transceiver(isp->otg);
  326 +fail0:
  327 + kfree(isp);
  328 +
  329 + dev_err(&pdev->dev, "failed to register isp1704 with error %d\n", ret);
  330 +
  331 + return ret;
  332 +}
  333 +
  334 +static int __devexit isp1704_charger_remove(struct platform_device *pdev)
  335 +{
  336 + struct isp1704_charger *isp = platform_get_drvdata(pdev);
  337 +
  338 + otg_unregister_notifier(isp->otg, &isp->nb);
  339 + power_supply_unregister(&isp->psy);
  340 + otg_put_transceiver(isp->otg);
  341 + kfree(isp);
  342 +
  343 + return 0;
  344 +}
  345 +
  346 +static struct platform_driver isp1704_charger_driver = {
  347 + .driver = {
  348 + .name = "isp1704_charger",
  349 + },
  350 + .probe = isp1704_charger_probe,
  351 + .remove = __devexit_p(isp1704_charger_remove),
  352 +};
  353 +
  354 +static int __init isp1704_charger_init(void)
  355 +{
  356 + return platform_driver_register(&isp1704_charger_driver);
  357 +}
  358 +module_init(isp1704_charger_init);
  359 +
  360 +static void __exit isp1704_charger_exit(void)
  361 +{
  362 + platform_driver_unregister(&isp1704_charger_driver);
  363 +}
  364 +module_exit(isp1704_charger_exit);
  365 +
  366 +MODULE_ALIAS("platform:isp1704_charger");
  367 +MODULE_AUTHOR("Nokia Corporation");
  368 +MODULE_DESCRIPTION("ISP170x USB Charger driver");
  369 +MODULE_LICENSE("GPL");
drivers/power/jz4740-battery.c
... ... @@ -383,6 +383,7 @@
383 383  
384 384 iounmap(jz_battery->base);
385 385 release_mem_region(jz_battery->mem->start, resource_size(jz_battery->mem));
  386 + kfree(jz_battery);
386 387  
387 388 return 0;
388 389 }
drivers/power/olpc_battery.c
... ... @@ -271,14 +271,14 @@
271 271 if (ret)
272 272 return ret;
273 273  
274   - val->intval = (int)be16_to_cpu(ec_word) * 9760L / 32;
  274 + val->intval = (s16)be16_to_cpu(ec_word) * 9760L / 32;
275 275 break;
276 276 case POWER_SUPPLY_PROP_CURRENT_AVG:
277 277 ret = olpc_ec_cmd(EC_BAT_CURRENT, NULL, 0, (void *)&ec_word, 2);
278 278 if (ret)
279 279 return ret;
280 280  
281   - val->intval = (int)be16_to_cpu(ec_word) * 15625L / 120;
  281 + val->intval = (s16)be16_to_cpu(ec_word) * 15625L / 120;
282 282 break;
283 283 case POWER_SUPPLY_PROP_CAPACITY:
284 284 ret = olpc_ec_cmd(EC_BAT_SOC, NULL, 0, &ec_byte, 1);
... ... @@ -299,7 +299,7 @@
299 299 if (ret)
300 300 return ret;
301 301  
302   - val->intval = (int)be16_to_cpu(ec_word) * 100 / 256;
  302 + val->intval = (s16)be16_to_cpu(ec_word) * 100 / 256;
303 303 break;
304 304 case POWER_SUPPLY_PROP_TEMP_AMBIENT:
305 305 ret = olpc_ec_cmd(EC_AMB_TEMP, NULL, 0, (void *)&ec_word, 2);
... ... @@ -313,7 +313,7 @@
313 313 if (ret)
314 314 return ret;
315 315  
316   - val->intval = (int)be16_to_cpu(ec_word) * 6250 / 15;
  316 + val->intval = (s16)be16_to_cpu(ec_word) * 6250 / 15;
317 317 break;
318 318 case POWER_SUPPLY_PROP_SERIAL_NUMBER:
319 319 ret = olpc_ec_cmd(EC_BAT_SERIAL, NULL, 0, (void *)&ser_buf, 8);
drivers/power/pcf50633-charger.c
... ... @@ -456,6 +456,7 @@
456 456 for (i = 0; i < ARRAY_SIZE(mbc_irq_handlers); i++)
457 457 pcf50633_free_irq(mbc->pcf, mbc_irq_handlers[i]);
458 458  
  459 + sysfs_remove_group(&pdev->dev.kobj, &mbc_attr_group);
459 460 power_supply_unregister(&mbc->usb);
460 461 power_supply_unregister(&mbc->adapter);
461 462 power_supply_unregister(&mbc->ac);
drivers/power/power_supply_sysfs.c
... ... @@ -42,7 +42,8 @@
42 42 struct device_attribute *attr,
43 43 char *buf) {
44 44 static char *type_text[] = {
45   - "Battery", "UPS", "Mains", "USB"
  45 + "Battery", "UPS", "Mains", "USB",
  46 + "USB_DCP", "USB_CDP", "USB_ACA"
46 47 };
47 48 static char *status_text[] = {
48 49 "Unknown", "Charging", "Discharging", "Not charging", "Full"
... ... @@ -138,6 +139,7 @@
138 139 POWER_SUPPLY_ATTR(voltage_min_design),
139 140 POWER_SUPPLY_ATTR(voltage_now),
140 141 POWER_SUPPLY_ATTR(voltage_avg),
  142 + POWER_SUPPLY_ATTR(current_max),
141 143 POWER_SUPPLY_ATTR(current_now),
142 144 POWER_SUPPLY_ATTR(current_avg),
143 145 POWER_SUPPLY_ATTR(power_now),
drivers/power/twl4030_charger.c
  1 +/*
  2 + * TWL4030/TPS65950 BCI (Battery Charger Interface) driver
  3 + *
  4 + * Copyright (C) 2010 Gražvydas Ignotas <notasas@gmail.com>
  5 + *
  6 + * based on twl4030_bci_battery.c by TI
  7 + * Copyright (C) 2008 Texas Instruments, Inc.
  8 + *
  9 + * This program is free software; you can redistribute it and/or modify
  10 + * it under the terms of the GNU General Public License as published by
  11 + * the Free Software Foundation; either version 2 of the License, or
  12 + * (at your option) any later version.
  13 + */
  14 +
  15 +#include <linux/init.h>
  16 +#include <linux/module.h>
  17 +#include <linux/slab.h>
  18 +#include <linux/platform_device.h>
  19 +#include <linux/interrupt.h>
  20 +#include <linux/i2c/twl.h>
  21 +#include <linux/power_supply.h>
  22 +#include <linux/notifier.h>
  23 +#include <linux/usb/otg.h>
  24 +
  25 +#define TWL4030_BCIMSTATEC 0x02
  26 +#define TWL4030_BCIICHG 0x08
  27 +#define TWL4030_BCIVAC 0x0a
  28 +#define TWL4030_BCIVBUS 0x0c
  29 +#define TWL4030_BCIMFSTS4 0x10
  30 +#define TWL4030_BCICTL1 0x23
  31 +
  32 +#define TWL4030_BCIAUTOWEN BIT(5)
  33 +#define TWL4030_CONFIG_DONE BIT(4)
  34 +#define TWL4030_BCIAUTOUSB BIT(1)
  35 +#define TWL4030_BCIAUTOAC BIT(0)
  36 +#define TWL4030_CGAIN BIT(5)
  37 +#define TWL4030_USBFASTMCHG BIT(2)
  38 +#define TWL4030_STS_VBUS BIT(7)
  39 +#define TWL4030_STS_USB_ID BIT(2)
  40 +
  41 +/* BCI interrupts */
  42 +#define TWL4030_WOVF BIT(0) /* Watchdog overflow */
  43 +#define TWL4030_TMOVF BIT(1) /* Timer overflow */
  44 +#define TWL4030_ICHGHIGH BIT(2) /* Battery charge current high */
  45 +#define TWL4030_ICHGLOW BIT(3) /* Battery cc. low / FSM state change */
  46 +#define TWL4030_ICHGEOC BIT(4) /* Battery current end-of-charge */
  47 +#define TWL4030_TBATOR2 BIT(5) /* Battery temperature out of range 2 */
  48 +#define TWL4030_TBATOR1 BIT(6) /* Battery temperature out of range 1 */
  49 +#define TWL4030_BATSTS BIT(7) /* Battery status */
  50 +
  51 +#define TWL4030_VBATLVL BIT(0) /* VBAT level */
  52 +#define TWL4030_VBATOV BIT(1) /* VBAT overvoltage */
  53 +#define TWL4030_VBUSOV BIT(2) /* VBUS overvoltage */
  54 +#define TWL4030_ACCHGOV BIT(3) /* Ac charger overvoltage */
  55 +
  56 +#define TWL4030_MSTATEC_USB BIT(4)
  57 +#define TWL4030_MSTATEC_AC BIT(5)
  58 +#define TWL4030_MSTATEC_MASK 0x0f
  59 +#define TWL4030_MSTATEC_QUICK1 0x02
  60 +#define TWL4030_MSTATEC_QUICK7 0x07
  61 +#define TWL4030_MSTATEC_COMPLETE1 0x0b
  62 +#define TWL4030_MSTATEC_COMPLETE4 0x0e
  63 +
  64 +static bool allow_usb;
  65 +module_param(allow_usb, bool, 1);
  66 +MODULE_PARM_DESC(allow_usb, "Allow USB charge drawing default current");
  67 +
  68 +struct twl4030_bci {
  69 + struct device *dev;
  70 + struct power_supply ac;
  71 + struct power_supply usb;
  72 + struct otg_transceiver *transceiver;
  73 + struct notifier_block otg_nb;
  74 + int irq_chg;
  75 + int irq_bci;
  76 +};
  77 +
  78 +/*
  79 + * clear and set bits on an given register on a given module
  80 + */
  81 +static int twl4030_clear_set(u8 mod_no, u8 clear, u8 set, u8 reg)
  82 +{
  83 + u8 val = 0;
  84 + int ret;
  85 +
  86 + ret = twl_i2c_read_u8(mod_no, &val, reg);
  87 + if (ret)
  88 + return ret;
  89 +
  90 + val &= ~clear;
  91 + val |= set;
  92 +
  93 + return twl_i2c_write_u8(mod_no, val, reg);
  94 +}
  95 +
  96 +static int twl4030_bci_read(u8 reg, u8 *val)
  97 +{
  98 + return twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, val, reg);
  99 +}
  100 +
  101 +static int twl4030_clear_set_boot_bci(u8 clear, u8 set)
  102 +{
  103 + return twl4030_clear_set(TWL4030_MODULE_PM_MASTER, 0,
  104 + TWL4030_CONFIG_DONE | TWL4030_BCIAUTOWEN | set,
  105 + TWL4030_PM_MASTER_BOOT_BCI);
  106 +}
  107 +
  108 +static int twl4030bci_read_adc_val(u8 reg)
  109 +{
  110 + int ret, temp;
  111 + u8 val;
  112 +
  113 + /* read MSB */
  114 + ret = twl4030_bci_read(reg + 1, &val);
  115 + if (ret)
  116 + return ret;
  117 +
  118 + temp = (int)(val & 0x03) << 8;
  119 +
  120 + /* read LSB */
  121 + ret = twl4030_bci_read(reg, &val);
  122 + if (ret)
  123 + return ret;
  124 +
  125 + return temp | val;
  126 +}
  127 +
  128 +/*
  129 + * Check if VBUS power is present
  130 + */
  131 +static int twl4030_bci_have_vbus(struct twl4030_bci *bci)
  132 +{
  133 + int ret;
  134 + u8 hwsts;
  135 +
  136 + ret = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &hwsts,
  137 + TWL4030_PM_MASTER_STS_HW_CONDITIONS);
  138 + if (ret < 0)
  139 + return 0;
  140 +
  141 + dev_dbg(bci->dev, "check_vbus: HW_CONDITIONS %02x\n", hwsts);
  142 +
  143 + /* in case we also have STS_USB_ID, VBUS is driven by TWL itself */
  144 + if ((hwsts & TWL4030_STS_VBUS) && !(hwsts & TWL4030_STS_USB_ID))
  145 + return 1;
  146 +
  147 + return 0;
  148 +}
  149 +
  150 +/*
  151 + * Enable/Disable USB Charge funtionality.
  152 + */
  153 +static int twl4030_charger_enable_usb(struct twl4030_bci *bci, bool enable)
  154 +{
  155 + int ret;
  156 +
  157 + if (enable) {
  158 + /* Check for USB charger conneted */
  159 + if (!twl4030_bci_have_vbus(bci))
  160 + return -ENODEV;
  161 +
  162 + /*
  163 + * Until we can find out what current the device can provide,
  164 + * require a module param to enable USB charging.
  165 + */
  166 + if (!allow_usb) {
  167 + dev_warn(bci->dev, "USB charging is disabled.\n");
  168 + return -EACCES;
  169 + }
  170 +
  171 + /* forcing the field BCIAUTOUSB (BOOT_BCI[1]) to 1 */
  172 + ret = twl4030_clear_set_boot_bci(0, TWL4030_BCIAUTOUSB);
  173 + if (ret < 0)
  174 + return ret;
  175 +
  176 + /* forcing USBFASTMCHG(BCIMFSTS4[2]) to 1 */
  177 + ret = twl4030_clear_set(TWL4030_MODULE_MAIN_CHARGE, 0,
  178 + TWL4030_USBFASTMCHG, TWL4030_BCIMFSTS4);
  179 + } else {
  180 + ret = twl4030_clear_set_boot_bci(TWL4030_BCIAUTOUSB, 0);
  181 + }
  182 +
  183 + return ret;
  184 +}
  185 +
  186 +/*
  187 + * Enable/Disable AC Charge funtionality.
  188 + */
  189 +static int twl4030_charger_enable_ac(bool enable)
  190 +{
  191 + int ret;
  192 +
  193 + if (enable)
  194 + ret = twl4030_clear_set_boot_bci(0, TWL4030_BCIAUTOAC);
  195 + else
  196 + ret = twl4030_clear_set_boot_bci(TWL4030_BCIAUTOAC, 0);
  197 +
  198 + return ret;
  199 +}
  200 +
  201 +/*
  202 + * TWL4030 CHG_PRES (AC charger presence) events
  203 + */
  204 +static irqreturn_t twl4030_charger_interrupt(int irq, void *arg)
  205 +{
  206 + struct twl4030_bci *bci = arg;
  207 +
  208 + dev_dbg(bci->dev, "CHG_PRES irq\n");
  209 + power_supply_changed(&bci->ac);
  210 + power_supply_changed(&bci->usb);
  211 +
  212 + return IRQ_HANDLED;
  213 +}
  214 +
  215 +/*
  216 + * TWL4030 BCI monitoring events
  217 + */
  218 +static irqreturn_t twl4030_bci_interrupt(int irq, void *arg)
  219 +{
  220 + struct twl4030_bci *bci = arg;
  221 + u8 irqs1, irqs2;
  222 + int ret;
  223 +
  224 + ret = twl_i2c_read_u8(TWL4030_MODULE_INTERRUPTS, &irqs1,
  225 + TWL4030_INTERRUPTS_BCIISR1A);
  226 + if (ret < 0)
  227 + return IRQ_HANDLED;
  228 +
  229 + ret = twl_i2c_read_u8(TWL4030_MODULE_INTERRUPTS, &irqs2,
  230 + TWL4030_INTERRUPTS_BCIISR2A);
  231 + if (ret < 0)
  232 + return IRQ_HANDLED;
  233 +
  234 + dev_dbg(bci->dev, "BCI irq %02x %02x\n", irqs2, irqs1);
  235 +
  236 + if (irqs1 & (TWL4030_ICHGLOW | TWL4030_ICHGEOC)) {
  237 + /* charger state change, inform the core */
  238 + power_supply_changed(&bci->ac);
  239 + power_supply_changed(&bci->usb);
  240 + }
  241 +
  242 + /* various monitoring events, for now we just log them here */
  243 + if (irqs1 & (TWL4030_TBATOR2 | TWL4030_TBATOR1))
  244 + dev_warn(bci->dev, "battery temperature out of range\n");
  245 +
  246 + if (irqs1 & TWL4030_BATSTS)
  247 + dev_crit(bci->dev, "battery disconnected\n");
  248 +
  249 + if (irqs2 & TWL4030_VBATOV)
  250 + dev_crit(bci->dev, "VBAT overvoltage\n");
  251 +
  252 + if (irqs2 & TWL4030_VBUSOV)
  253 + dev_crit(bci->dev, "VBUS overvoltage\n");
  254 +
  255 + if (irqs2 & TWL4030_ACCHGOV)
  256 + dev_crit(bci->dev, "Ac charger overvoltage\n");
  257 +
  258 + return IRQ_HANDLED;
  259 +}
  260 +
  261 +static int twl4030_bci_usb_ncb(struct notifier_block *nb, unsigned long val,
  262 + void *priv)
  263 +{
  264 + struct twl4030_bci *bci = container_of(nb, struct twl4030_bci, otg_nb);
  265 +
  266 + dev_dbg(bci->dev, "OTG notify %lu\n", val);
  267 +
  268 + switch (val) {
  269 + case USB_EVENT_VBUS:
  270 + case USB_EVENT_CHARGER:
  271 + twl4030_charger_enable_usb(bci, true);
  272 + break;
  273 + case USB_EVENT_NONE:
  274 + twl4030_charger_enable_usb(bci, false);
  275 + break;
  276 + }
  277 +
  278 + return NOTIFY_OK;
  279 +}
  280 +
  281 +/*
  282 + * TI provided formulas:
  283 + * CGAIN == 0: ICHG = (BCIICHG * 1.7) / (2^10 - 1) - 0.85
  284 + * CGAIN == 1: ICHG = (BCIICHG * 3.4) / (2^10 - 1) - 1.7
  285 + * Here we use integer approximation of:
  286 + * CGAIN == 0: val * 1.6618 - 0.85
  287 + * CGAIN == 1: (val * 1.6618 - 0.85) * 2
  288 + */
  289 +static int twl4030_charger_get_current(void)
  290 +{
  291 + int curr;
  292 + int ret;
  293 + u8 bcictl1;
  294 +
  295 + curr = twl4030bci_read_adc_val(TWL4030_BCIICHG);
  296 + if (curr < 0)
  297 + return curr;
  298 +
  299 + ret = twl4030_bci_read(TWL4030_BCICTL1, &bcictl1);
  300 + if (ret)
  301 + return ret;
  302 +
  303 + ret = (curr * 16618 - 850 * 10000) / 10;
  304 + if (bcictl1 & TWL4030_CGAIN)
  305 + ret *= 2;
  306 +
  307 + return ret;
  308 +}
  309 +
  310 +/*
  311 + * Returns the main charge FSM state
  312 + * Or < 0 on failure.
  313 + */
  314 +static int twl4030bci_state(struct twl4030_bci *bci)
  315 +{
  316 + int ret;
  317 + u8 state;
  318 +
  319 + ret = twl4030_bci_read(TWL4030_BCIMSTATEC, &state);
  320 + if (ret) {
  321 + pr_err("twl4030_bci: error reading BCIMSTATEC\n");
  322 + return ret;
  323 + }
  324 +
  325 + dev_dbg(bci->dev, "state: %02x\n", state);
  326 +
  327 + return state;
  328 +}
  329 +
  330 +static int twl4030_bci_state_to_status(int state)
  331 +{
  332 + state &= TWL4030_MSTATEC_MASK;
  333 + if (TWL4030_MSTATEC_QUICK1 <= state && state <= TWL4030_MSTATEC_QUICK7)
  334 + return POWER_SUPPLY_STATUS_CHARGING;
  335 + else if (TWL4030_MSTATEC_COMPLETE1 <= state &&
  336 + state <= TWL4030_MSTATEC_COMPLETE4)
  337 + return POWER_SUPPLY_STATUS_FULL;
  338 + else
  339 + return POWER_SUPPLY_STATUS_NOT_CHARGING;
  340 +}
  341 +
  342 +static int twl4030_bci_get_property(struct power_supply *psy,
  343 + enum power_supply_property psp,
  344 + union power_supply_propval *val)
  345 +{
  346 + struct twl4030_bci *bci = dev_get_drvdata(psy->dev->parent);
  347 + int is_charging;
  348 + int state;
  349 + int ret;
  350 +
  351 + state = twl4030bci_state(bci);
  352 + if (state < 0)
  353 + return state;
  354 +
  355 + if (psy->type == POWER_SUPPLY_TYPE_USB)
  356 + is_charging = state & TWL4030_MSTATEC_USB;
  357 + else
  358 + is_charging = state & TWL4030_MSTATEC_AC;
  359 +
  360 + switch (psp) {
  361 + case POWER_SUPPLY_PROP_STATUS:
  362 + if (is_charging)
  363 + val->intval = twl4030_bci_state_to_status(state);
  364 + else
  365 + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
  366 + break;
  367 + case POWER_SUPPLY_PROP_VOLTAGE_NOW:
  368 + /* charging must be active for meaningful result */
  369 + if (!is_charging)
  370 + return -ENODATA;
  371 + if (psy->type == POWER_SUPPLY_TYPE_USB) {
  372 + ret = twl4030bci_read_adc_val(TWL4030_BCIVBUS);
  373 + if (ret < 0)
  374 + return ret;
  375 + /* BCIVBUS uses ADCIN8, 7/1023 V/step */
  376 + val->intval = ret * 6843;
  377 + } else {
  378 + ret = twl4030bci_read_adc_val(TWL4030_BCIVAC);
  379 + if (ret < 0)
  380 + return ret;
  381 + /* BCIVAC uses ADCIN11, 10/1023 V/step */
  382 + val->intval = ret * 9775;
  383 + }
  384 + break;
  385 + case POWER_SUPPLY_PROP_CURRENT_NOW:
  386 + if (!is_charging)
  387 + return -ENODATA;
  388 + /* current measurement is shared between AC and USB */
  389 + ret = twl4030_charger_get_current();
  390 + if (ret < 0)
  391 + return ret;
  392 + val->intval = ret;
  393 + break;
  394 + case POWER_SUPPLY_PROP_ONLINE:
  395 + val->intval = is_charging &&
  396 + twl4030_bci_state_to_status(state) !=
  397 + POWER_SUPPLY_STATUS_NOT_CHARGING;
  398 + break;
  399 + default:
  400 + return -EINVAL;
  401 + }
  402 +
  403 + return 0;
  404 +}
  405 +
  406 +static enum power_supply_property twl4030_charger_props[] = {
  407 + POWER_SUPPLY_PROP_STATUS,
  408 + POWER_SUPPLY_PROP_ONLINE,
  409 + POWER_SUPPLY_PROP_VOLTAGE_NOW,
  410 + POWER_SUPPLY_PROP_CURRENT_NOW,
  411 +};
  412 +
  413 +static int __init twl4030_bci_probe(struct platform_device *pdev)
  414 +{
  415 + struct twl4030_bci *bci;
  416 + int ret;
  417 + int reg;
  418 +
  419 + bci = kzalloc(sizeof(*bci), GFP_KERNEL);
  420 + if (bci == NULL)
  421 + return -ENOMEM;
  422 +
  423 + bci->dev = &pdev->dev;
  424 + bci->irq_chg = platform_get_irq(pdev, 0);
  425 + bci->irq_bci = platform_get_irq(pdev, 1);
  426 +
  427 + platform_set_drvdata(pdev, bci);
  428 +
  429 + bci->ac.name = "twl4030_ac";
  430 + bci->ac.type = POWER_SUPPLY_TYPE_MAINS;
  431 + bci->ac.properties = twl4030_charger_props;
  432 + bci->ac.num_properties = ARRAY_SIZE(twl4030_charger_props);
  433 + bci->ac.get_property = twl4030_bci_get_property;
  434 +
  435 + ret = power_supply_register(&pdev->dev, &bci->ac);
  436 + if (ret) {
  437 + dev_err(&pdev->dev, "failed to register ac: %d\n", ret);
  438 + goto fail_register_ac;
  439 + }
  440 +
  441 + bci->usb.name = "twl4030_usb";
  442 + bci->usb.type = POWER_SUPPLY_TYPE_USB;
  443 + bci->usb.properties = twl4030_charger_props;
  444 + bci->usb.num_properties = ARRAY_SIZE(twl4030_charger_props);
  445 + bci->usb.get_property = twl4030_bci_get_property;
  446 +
  447 + ret = power_supply_register(&pdev->dev, &bci->usb);
  448 + if (ret) {
  449 + dev_err(&pdev->dev, "failed to register usb: %d\n", ret);
  450 + goto fail_register_usb;
  451 + }
  452 +
  453 + ret = request_threaded_irq(bci->irq_chg, NULL,
  454 + twl4030_charger_interrupt, 0, pdev->name, bci);
  455 + if (ret < 0) {
  456 + dev_err(&pdev->dev, "could not request irq %d, status %d\n",
  457 + bci->irq_chg, ret);
  458 + goto fail_chg_irq;
  459 + }
  460 +
  461 + ret = request_threaded_irq(bci->irq_bci, NULL,
  462 + twl4030_bci_interrupt, 0, pdev->name, bci);
  463 + if (ret < 0) {
  464 + dev_err(&pdev->dev, "could not request irq %d, status %d\n",
  465 + bci->irq_bci, ret);
  466 + goto fail_bci_irq;
  467 + }
  468 +
  469 + bci->transceiver = otg_get_transceiver();
  470 + if (bci->transceiver != NULL) {
  471 + bci->otg_nb.notifier_call = twl4030_bci_usb_ncb;
  472 + otg_register_notifier(bci->transceiver, &bci->otg_nb);
  473 + }
  474 +
  475 + /* Enable interrupts now. */
  476 + reg = ~(TWL4030_ICHGLOW | TWL4030_ICHGEOC | TWL4030_TBATOR2 |
  477 + TWL4030_TBATOR1 | TWL4030_BATSTS);
  478 + ret = twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, reg,
  479 + TWL4030_INTERRUPTS_BCIIMR1A);
  480 + if (ret < 0) {
  481 + dev_err(&pdev->dev, "failed to unmask interrupts: %d\n", ret);
  482 + goto fail_unmask_interrupts;
  483 + }
  484 +
  485 + reg = ~(TWL4030_VBATOV | TWL4030_VBUSOV | TWL4030_ACCHGOV);
  486 + ret = twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, reg,
  487 + TWL4030_INTERRUPTS_BCIIMR2A);
  488 + if (ret < 0)
  489 + dev_warn(&pdev->dev, "failed to unmask interrupts: %d\n", ret);
  490 +
  491 + twl4030_charger_enable_ac(true);
  492 + twl4030_charger_enable_usb(bci, true);
  493 +
  494 + return 0;
  495 +
  496 +fail_unmask_interrupts:
  497 + if (bci->transceiver != NULL) {
  498 + otg_unregister_notifier(bci->transceiver, &bci->otg_nb);
  499 + otg_put_transceiver(bci->transceiver);
  500 + }
  501 + free_irq(bci->irq_bci, bci);
  502 +fail_bci_irq:
  503 + free_irq(bci->irq_chg, bci);
  504 +fail_chg_irq:
  505 + power_supply_unregister(&bci->usb);
  506 +fail_register_usb:
  507 + power_supply_unregister(&bci->ac);
  508 +fail_register_ac:
  509 + platform_set_drvdata(pdev, NULL);
  510 + kfree(bci);
  511 +
  512 + return ret;
  513 +}
  514 +
  515 +static int __exit twl4030_bci_remove(struct platform_device *pdev)
  516 +{
  517 + struct twl4030_bci *bci = platform_get_drvdata(pdev);
  518 +
  519 + twl4030_charger_enable_ac(false);
  520 + twl4030_charger_enable_usb(bci, false);
  521 +
  522 + /* mask interrupts */
  523 + twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff,
  524 + TWL4030_INTERRUPTS_BCIIMR1A);
  525 + twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff,
  526 + TWL4030_INTERRUPTS_BCIIMR2A);
  527 +
  528 + if (bci->transceiver != NULL) {
  529 + otg_unregister_notifier(bci->transceiver, &bci->otg_nb);
  530 + otg_put_transceiver(bci->transceiver);
  531 + }
  532 + free_irq(bci->irq_bci, bci);
  533 + free_irq(bci->irq_chg, bci);
  534 + power_supply_unregister(&bci->usb);
  535 + power_supply_unregister(&bci->ac);
  536 + platform_set_drvdata(pdev, NULL);
  537 + kfree(bci);
  538 +
  539 + return 0;
  540 +}
  541 +
  542 +static struct platform_driver twl4030_bci_driver = {
  543 + .driver = {
  544 + .name = "twl4030_bci",
  545 + .owner = THIS_MODULE,
  546 + },
  547 + .remove = __exit_p(twl4030_bci_remove),
  548 +};
  549 +
  550 +static int __init twl4030_bci_init(void)
  551 +{
  552 + return platform_driver_probe(&twl4030_bci_driver, twl4030_bci_probe);
  553 +}
  554 +module_init(twl4030_bci_init);
  555 +
  556 +static void __exit twl4030_bci_exit(void)
  557 +{
  558 + platform_driver_unregister(&twl4030_bci_driver);
  559 +}
  560 +module_exit(twl4030_bci_exit);
  561 +
  562 +MODULE_AUTHOR("Gražydas Ignotas");
  563 +MODULE_DESCRIPTION("TWL4030 Battery Charger Interface driver");
  564 +MODULE_LICENSE("GPL");
  565 +MODULE_ALIAS("platform:twl4030_bci");
drivers/power/wm831x_power.c
... ... @@ -267,7 +267,6 @@
267 267 ret = wm831x_set_bits(wm831x, WM831X_CHARGER_CONTROL_1,
268 268 WM831X_CHG_ENA_MASK |
269 269 WM831X_CHG_FAST_MASK |
270   - WM831X_CHG_ITERM_MASK |
271 270 WM831X_CHG_ITERM_MASK,
272 271 reg1);
273 272 if (ret != 0)
... ... @@ -612,6 +611,7 @@
612 611 power_supply_unregister(&wm831x_power->battery);
613 612 power_supply_unregister(&wm831x_power->wall);
614 613 power_supply_unregister(&wm831x_power->usb);
  614 + kfree(wm831x_power);
615 615 return 0;
616 616 }
617 617  
include/linux/power_supply.h
... ... @@ -89,6 +89,7 @@
89 89 POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
90 90 POWER_SUPPLY_PROP_VOLTAGE_NOW,
91 91 POWER_SUPPLY_PROP_VOLTAGE_AVG,
  92 + POWER_SUPPLY_PROP_CURRENT_MAX,
92 93 POWER_SUPPLY_PROP_CURRENT_NOW,
93 94 POWER_SUPPLY_PROP_CURRENT_AVG,
94 95 POWER_SUPPLY_PROP_POWER_NOW,
... ... @@ -125,7 +126,10 @@
125 126 POWER_SUPPLY_TYPE_BATTERY = 0,
126 127 POWER_SUPPLY_TYPE_UPS,
127 128 POWER_SUPPLY_TYPE_MAINS,
128   - POWER_SUPPLY_TYPE_USB,
  129 + POWER_SUPPLY_TYPE_USB, /* Standard Downstream Port */
  130 + POWER_SUPPLY_TYPE_USB_DCP, /* Dedicated Charging Port */
  131 + POWER_SUPPLY_TYPE_USB_CDP, /* Charging Downstream Port */
  132 + POWER_SUPPLY_TYPE_USB_ACA, /* Accessory Charger Adapters */
129 133 };
130 134  
131 135 union power_supply_propval {