diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-10-26 13:14:23 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-10-26 13:14:23 -0400 |
commit | 45352bbf48e95078b4acd20774f49e72676e1e0f (patch) | |
tree | 7c7121dae3f4ecebb1c08cea5f3b3d1c6c531756 /drivers/power | |
parent | da62aa69c181e3bd465a5c868ece166921a81e14 (diff) | |
parent | 57894517139b283a3cf27e36b29b4b4d7c45b303 (diff) |
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
Diffstat (limited to 'drivers/power')
-rw-r--r-- | drivers/power/Kconfig | 23 | ||||
-rw-r--r-- | drivers/power/Makefile | 19 | ||||
-rw-r--r-- | drivers/power/bq20z75.c | 493 | ||||
-rw-r--r-- | drivers/power/bq27x00_battery.c | 1 | ||||
-rw-r--r-- | drivers/power/ds2760_battery.c | 1 | ||||
-rw-r--r-- | drivers/power/ds2782_battery.c | 12 | ||||
-rw-r--r-- | drivers/power/isp1704_charger.c | 369 | ||||
-rw-r--r-- | drivers/power/jz4740-battery.c | 1 | ||||
-rw-r--r-- | drivers/power/olpc_battery.c | 8 | ||||
-rw-r--r-- | drivers/power/pcf50633-charger.c | 1 | ||||
-rw-r--r-- | drivers/power/power_supply_sysfs.c | 4 | ||||
-rw-r--r-- | drivers/power/twl4030_charger.c | 565 | ||||
-rw-r--r-- | drivers/power/wm831x_power.c | 2 |
13 files changed, 1473 insertions, 26 deletions
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 07343568a12e..60d83d983a36 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig | |||
@@ -64,8 +64,7 @@ config TEST_POWER | |||
64 | 64 | ||
65 | config BATTERY_DS2760 | 65 | config BATTERY_DS2760 |
66 | tristate "DS2760 battery driver (HP iPAQ & others)" | 66 | tristate "DS2760 battery driver (HP iPAQ & others)" |
67 | select W1 | 67 | depends on W1 && W1_SLAVE_DS2760 |
68 | select W1_SLAVE_DS2760 | ||
69 | help | 68 | help |
70 | Say Y here to enable support for batteries with ds2760 chip. | 69 | Say Y here to enable support for batteries with ds2760 chip. |
71 | 70 | ||
@@ -109,6 +108,13 @@ config BATTERY_WM97XX | |||
109 | help | 108 | help |
110 | Say Y to enable support for battery measured by WM97xx aux port. | 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 | config BATTERY_BQ27x00 | 118 | config BATTERY_BQ27x00 |
113 | tristate "BQ27x00 battery driver" | 119 | tristate "BQ27x00 battery driver" |
114 | depends on I2C | 120 | depends on I2C |
@@ -166,4 +172,17 @@ config BATTERY_INTEL_MID | |||
166 | Say Y here to enable the battery driver on Intel MID | 172 | Say Y here to enable the battery driver on Intel MID |
167 | platforms. | 173 | platforms. |
168 | 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. | ||
187 | |||
169 | endif # POWER_SUPPLY | 188 | endif # POWER_SUPPLY |
diff --git a/drivers/power/Makefile b/drivers/power/Makefile index 10143aaf4ee3..c75772eb157c 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile | |||
@@ -1,16 +1,8 @@ | |||
1 | power_supply-objs := power_supply_core.o | 1 | ccflags-$(CONFIG_POWER_SUPPLY_DEBUG) := -DDEBUG |
2 | 2 | ||
3 | ifeq ($(CONFIG_SYSFS),y) | 3 | power_supply-y := power_supply_core.o |
4 | power_supply-objs += power_supply_sysfs.o | 4 | power_supply-$(CONFIG_SYSFS) += power_supply_sysfs.o |
5 | endif | 5 | power_supply-$(CONFIG_LEDS_TRIGGERS) += power_supply_leds.o |
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 | 6 | ||
15 | obj-$(CONFIG_POWER_SUPPLY) += power_supply.o | 7 | obj-$(CONFIG_POWER_SUPPLY) += power_supply.o |
16 | 8 | ||
@@ -29,6 +21,7 @@ obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o | |||
29 | obj-$(CONFIG_BATTERY_TOSA) += tosa_battery.o | 21 | obj-$(CONFIG_BATTERY_TOSA) += tosa_battery.o |
30 | obj-$(CONFIG_BATTERY_COLLIE) += collie_battery.o | 22 | obj-$(CONFIG_BATTERY_COLLIE) += collie_battery.o |
31 | obj-$(CONFIG_BATTERY_WM97XX) += wm97xx_battery.o | 23 | obj-$(CONFIG_BATTERY_WM97XX) += wm97xx_battery.o |
24 | obj-$(CONFIG_BATTERY_BQ20Z75) += bq20z75.o | ||
32 | obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00_battery.o | 25 | obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00_battery.o |
33 | obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o | 26 | obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o |
34 | obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o | 27 | obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o |
@@ -37,3 +30,5 @@ obj-$(CONFIG_BATTERY_S3C_ADC) += s3c_adc_battery.o | |||
37 | obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o | 30 | obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o |
38 | obj-$(CONFIG_BATTERY_JZ4740) += jz4740-battery.o | 31 | obj-$(CONFIG_BATTERY_JZ4740) += jz4740-battery.o |
39 | obj-$(CONFIG_BATTERY_INTEL_MID) += intel_mid_battery.o | 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 | ||
diff --git a/drivers/power/bq20z75.c b/drivers/power/bq20z75.c new file mode 100644 index 000000000000..492da27e1a47 --- /dev/null +++ b/drivers/power/bq20z75.c | |||
@@ -0,0 +1,493 @@ | |||
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"); | ||
diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index 3ec9c6a8896b..eff0273d4030 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c | |||
@@ -418,6 +418,7 @@ static int bq27x00_battery_remove(struct i2c_client *client) | |||
418 | 418 | ||
419 | power_supply_unregister(&di->bat); | 419 | power_supply_unregister(&di->bat); |
420 | 420 | ||
421 | kfree(di->bus); | ||
421 | kfree(di->bat.name); | 422 | kfree(di->bat.name); |
422 | 423 | ||
423 | mutex_lock(&battery_mutex); | 424 | mutex_lock(&battery_mutex); |
diff --git a/drivers/power/ds2760_battery.c b/drivers/power/ds2760_battery.c index 4d3b27228a2e..b3c01c16a164 100644 --- a/drivers/power/ds2760_battery.c +++ b/drivers/power/ds2760_battery.c | |||
@@ -586,6 +586,7 @@ static int ds2760_battery_remove(struct platform_device *pdev) | |||
586 | &di->set_charged_work); | 586 | &di->set_charged_work); |
587 | destroy_workqueue(di->monitor_wqueue); | 587 | destroy_workqueue(di->monitor_wqueue); |
588 | power_supply_unregister(&di->bat); | 588 | power_supply_unregister(&di->bat); |
589 | kfree(di); | ||
589 | 590 | ||
590 | return 0; | 591 | return 0; |
591 | } | 592 | } |
diff --git a/drivers/power/ds2782_battery.c b/drivers/power/ds2782_battery.c index 84d3c43cf2bc..6957e8af6449 100644 --- a/drivers/power/ds2782_battery.c +++ b/drivers/power/ds2782_battery.c | |||
@@ -44,8 +44,8 @@ struct ds278x_info; | |||
44 | 44 | ||
45 | struct ds278x_battery_ops { | 45 | struct ds278x_battery_ops { |
46 | int (*get_battery_current)(struct ds278x_info *info, int *current_uA); | 46 | int (*get_battery_current)(struct ds278x_info *info, int *current_uA); |
47 | int (*get_battery_voltage)(struct ds278x_info *info, int *voltage_uA); | 47 | int (*get_battery_voltage)(struct ds278x_info *info, int *voltage_uV); |
48 | int (*get_battery_capacity)(struct ds278x_info *info, int *capacity_uA); | 48 | int (*get_battery_capacity)(struct ds278x_info *info, int *capacity); |
49 | }; | 49 | }; |
50 | 50 | ||
51 | #define to_ds278x_info(x) container_of(x, struct ds278x_info, battery) | 51 | #define to_ds278x_info(x) container_of(x, struct ds278x_info, battery) |
@@ -137,7 +137,7 @@ static int ds2782_get_current(struct ds278x_info *info, int *current_uA) | |||
137 | return 0; | 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 | s16 raw; | 142 | s16 raw; |
143 | int err; | 143 | int err; |
@@ -149,7 +149,7 @@ static int ds2782_get_voltage(struct ds278x_info *info, int *voltage_uA) | |||
149 | err = ds278x_read_reg16(info, DS278x_REG_VOLT_MSB, &raw); | 149 | err = ds278x_read_reg16(info, DS278x_REG_VOLT_MSB, &raw); |
150 | if (err) | 150 | if (err) |
151 | return err; | 151 | return err; |
152 | *voltage_uA = (raw / 32) * 4800; | 152 | *voltage_uV = (raw / 32) * 4800; |
153 | return 0; | 153 | return 0; |
154 | } | 154 | } |
155 | 155 | ||
@@ -177,7 +177,7 @@ static int ds2786_get_current(struct ds278x_info *info, int *current_uA) | |||
177 | return 0; | 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 | s16 raw; | 182 | s16 raw; |
183 | int err; | 183 | int err; |
@@ -189,7 +189,7 @@ static int ds2786_get_voltage(struct ds278x_info *info, int *voltage_uA) | |||
189 | err = ds278x_read_reg16(info, DS278x_REG_VOLT_MSB, &raw); | 189 | err = ds278x_read_reg16(info, DS278x_REG_VOLT_MSB, &raw); |
190 | if (err) | 190 | if (err) |
191 | return err; | 191 | return err; |
192 | *voltage_uA = (raw / 8) * 1220; | 192 | *voltage_uV = (raw / 8) * 1220; |
193 | return 0; | 193 | return 0; |
194 | } | 194 | } |
195 | 195 | ||
diff --git a/drivers/power/isp1704_charger.c b/drivers/power/isp1704_charger.c new file mode 100644 index 000000000000..72512185f3e2 --- /dev/null +++ b/drivers/power/isp1704_charger.c | |||
@@ -0,0 +1,369 @@ | |||
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"); | ||
diff --git a/drivers/power/jz4740-battery.c b/drivers/power/jz4740-battery.c index 20c4b952e9bd..a8108a73593e 100644 --- a/drivers/power/jz4740-battery.c +++ b/drivers/power/jz4740-battery.c | |||
@@ -383,6 +383,7 @@ static int __devexit jz_battery_remove(struct platform_device *pdev) | |||
383 | 383 | ||
384 | iounmap(jz_battery->base); | 384 | iounmap(jz_battery->base); |
385 | release_mem_region(jz_battery->mem->start, resource_size(jz_battery->mem)); | 385 | release_mem_region(jz_battery->mem->start, resource_size(jz_battery->mem)); |
386 | kfree(jz_battery); | ||
386 | 387 | ||
387 | return 0; | 388 | return 0; |
388 | } | 389 | } |
diff --git a/drivers/power/olpc_battery.c b/drivers/power/olpc_battery.c index aafc1c506eda..5bc1dcf7785e 100644 --- a/drivers/power/olpc_battery.c +++ b/drivers/power/olpc_battery.c | |||
@@ -271,14 +271,14 @@ static int olpc_bat_get_property(struct power_supply *psy, | |||
271 | if (ret) | 271 | if (ret) |
272 | return ret; | 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 | break; | 275 | break; |
276 | case POWER_SUPPLY_PROP_CURRENT_AVG: | 276 | case POWER_SUPPLY_PROP_CURRENT_AVG: |
277 | ret = olpc_ec_cmd(EC_BAT_CURRENT, NULL, 0, (void *)&ec_word, 2); | 277 | ret = olpc_ec_cmd(EC_BAT_CURRENT, NULL, 0, (void *)&ec_word, 2); |
278 | if (ret) | 278 | if (ret) |
279 | return ret; | 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 | break; | 282 | break; |
283 | case POWER_SUPPLY_PROP_CAPACITY: | 283 | case POWER_SUPPLY_PROP_CAPACITY: |
284 | ret = olpc_ec_cmd(EC_BAT_SOC, NULL, 0, &ec_byte, 1); | 284 | ret = olpc_ec_cmd(EC_BAT_SOC, NULL, 0, &ec_byte, 1); |
@@ -299,7 +299,7 @@ static int olpc_bat_get_property(struct power_supply *psy, | |||
299 | if (ret) | 299 | if (ret) |
300 | return ret; | 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 | break; | 303 | break; |
304 | case POWER_SUPPLY_PROP_TEMP_AMBIENT: | 304 | case POWER_SUPPLY_PROP_TEMP_AMBIENT: |
305 | ret = olpc_ec_cmd(EC_AMB_TEMP, NULL, 0, (void *)&ec_word, 2); | 305 | ret = olpc_ec_cmd(EC_AMB_TEMP, NULL, 0, (void *)&ec_word, 2); |
@@ -313,7 +313,7 @@ static int olpc_bat_get_property(struct power_supply *psy, | |||
313 | if (ret) | 313 | if (ret) |
314 | return ret; | 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 | break; | 317 | break; |
318 | case POWER_SUPPLY_PROP_SERIAL_NUMBER: | 318 | case POWER_SUPPLY_PROP_SERIAL_NUMBER: |
319 | ret = olpc_ec_cmd(EC_BAT_SERIAL, NULL, 0, (void *)&ser_buf, 8); | 319 | ret = olpc_ec_cmd(EC_BAT_SERIAL, NULL, 0, (void *)&ser_buf, 8); |
diff --git a/drivers/power/pcf50633-charger.c b/drivers/power/pcf50633-charger.c index 066f994e6fe5..4fa52e1781a2 100644 --- a/drivers/power/pcf50633-charger.c +++ b/drivers/power/pcf50633-charger.c | |||
@@ -456,6 +456,7 @@ static int __devexit pcf50633_mbc_remove(struct platform_device *pdev) | |||
456 | for (i = 0; i < ARRAY_SIZE(mbc_irq_handlers); i++) | 456 | for (i = 0; i < ARRAY_SIZE(mbc_irq_handlers); i++) |
457 | pcf50633_free_irq(mbc->pcf, mbc_irq_handlers[i]); | 457 | pcf50633_free_irq(mbc->pcf, mbc_irq_handlers[i]); |
458 | 458 | ||
459 | sysfs_remove_group(&pdev->dev.kobj, &mbc_attr_group); | ||
459 | power_supply_unregister(&mbc->usb); | 460 | power_supply_unregister(&mbc->usb); |
460 | power_supply_unregister(&mbc->adapter); | 461 | power_supply_unregister(&mbc->adapter); |
461 | power_supply_unregister(&mbc->ac); | 462 | power_supply_unregister(&mbc->ac); |
diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index 9d30eeb8c810..cd1f90754a3a 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c | |||
@@ -42,7 +42,8 @@ static ssize_t power_supply_show_property(struct device *dev, | |||
42 | struct device_attribute *attr, | 42 | struct device_attribute *attr, |
43 | char *buf) { | 43 | char *buf) { |
44 | static char *type_text[] = { | 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 | static char *status_text[] = { | 48 | static char *status_text[] = { |
48 | "Unknown", "Charging", "Discharging", "Not charging", "Full" | 49 | "Unknown", "Charging", "Discharging", "Not charging", "Full" |
@@ -138,6 +139,7 @@ static struct device_attribute power_supply_attrs[] = { | |||
138 | POWER_SUPPLY_ATTR(voltage_min_design), | 139 | POWER_SUPPLY_ATTR(voltage_min_design), |
139 | POWER_SUPPLY_ATTR(voltage_now), | 140 | POWER_SUPPLY_ATTR(voltage_now), |
140 | POWER_SUPPLY_ATTR(voltage_avg), | 141 | POWER_SUPPLY_ATTR(voltage_avg), |
142 | POWER_SUPPLY_ATTR(current_max), | ||
141 | POWER_SUPPLY_ATTR(current_now), | 143 | POWER_SUPPLY_ATTR(current_now), |
142 | POWER_SUPPLY_ATTR(current_avg), | 144 | POWER_SUPPLY_ATTR(current_avg), |
143 | POWER_SUPPLY_ATTR(power_now), | 145 | POWER_SUPPLY_ATTR(power_now), |
diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c new file mode 100644 index 000000000000..ff1f42398a2e --- /dev/null +++ b/drivers/power/twl4030_charger.c | |||
@@ -0,0 +1,565 @@ | |||
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"); | ||
diff --git a/drivers/power/wm831x_power.c b/drivers/power/wm831x_power.c index fbcc36dae470..ddf8cf5f3204 100644 --- a/drivers/power/wm831x_power.c +++ b/drivers/power/wm831x_power.c | |||
@@ -267,7 +267,6 @@ static void wm831x_config_battery(struct wm831x *wm831x) | |||
267 | ret = wm831x_set_bits(wm831x, WM831X_CHARGER_CONTROL_1, | 267 | ret = wm831x_set_bits(wm831x, WM831X_CHARGER_CONTROL_1, |
268 | WM831X_CHG_ENA_MASK | | 268 | WM831X_CHG_ENA_MASK | |
269 | WM831X_CHG_FAST_MASK | | 269 | WM831X_CHG_FAST_MASK | |
270 | WM831X_CHG_ITERM_MASK | | ||
271 | WM831X_CHG_ITERM_MASK, | 270 | WM831X_CHG_ITERM_MASK, |
272 | reg1); | 271 | reg1); |
273 | if (ret != 0) | 272 | if (ret != 0) |
@@ -612,6 +611,7 @@ static __devexit int wm831x_power_remove(struct platform_device *pdev) | |||
612 | power_supply_unregister(&wm831x_power->battery); | 611 | power_supply_unregister(&wm831x_power->battery); |
613 | power_supply_unregister(&wm831x_power->wall); | 612 | power_supply_unregister(&wm831x_power->wall); |
614 | power_supply_unregister(&wm831x_power->usb); | 613 | power_supply_unregister(&wm831x_power->usb); |
614 | kfree(wm831x_power); | ||
615 | return 0; | 615 | return 0; |
616 | } | 616 | } |
617 | 617 | ||