diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mfd/88pm860x-core.c | 22 | ||||
-rw-r--r-- | drivers/power/88pm860x_battery.c | 1041 | ||||
-rw-r--r-- | drivers/power/88pm860x_charger.c | 746 | ||||
-rw-r--r-- | drivers/power/Kconfig | 20 | ||||
-rw-r--r-- | drivers/power/Makefile | 3 | ||||
-rw-r--r-- | drivers/power/ab8500_btemp.c | 1 | ||||
-rw-r--r-- | drivers/power/ab8500_charger.c | 1 | ||||
-rw-r--r-- | drivers/power/ab8500_fg.c | 1 | ||||
-rw-r--r-- | drivers/power/bq27x00_battery.c | 3 | ||||
-rw-r--r-- | drivers/power/charger-manager.c | 434 | ||||
-rw-r--r-- | drivers/power/da9030_battery.c | 4 | ||||
-rw-r--r-- | drivers/power/da9052-battery.c | 11 | ||||
-rw-r--r-- | drivers/power/ds2781_battery.c | 28 | ||||
-rw-r--r-- | drivers/power/lp8727_charger.c | 360 | ||||
-rw-r--r-- | drivers/power/lp8788-charger.c | 795 | ||||
-rw-r--r-- | drivers/power/pda_power.c | 13 | ||||
-rw-r--r-- | drivers/power/power_supply_sysfs.c | 3 | ||||
-rw-r--r-- | drivers/power/sbs-battery.c | 10 | ||||
-rw-r--r-- | drivers/power/smb347-charger.c | 97 | ||||
-rw-r--r-- | drivers/power/twl4030_charger.c | 25 | ||||
-rw-r--r-- | drivers/power/wm97xx_battery.c | 4 |
21 files changed, 3355 insertions, 267 deletions
diff --git a/drivers/mfd/88pm860x-core.c b/drivers/mfd/88pm860x-core.c index 59d117e9fa31..8fa86edf70d4 100644 --- a/drivers/mfd/88pm860x-core.c +++ b/drivers/mfd/88pm860x-core.c | |||
@@ -24,6 +24,7 @@ | |||
24 | #include <linux/mfd/core.h> | 24 | #include <linux/mfd/core.h> |
25 | #include <linux/mfd/88pm860x.h> | 25 | #include <linux/mfd/88pm860x.h> |
26 | #include <linux/regulator/machine.h> | 26 | #include <linux/regulator/machine.h> |
27 | #include <linux/power/charger-manager.h> | ||
27 | 28 | ||
28 | #define INT_STATUS_NUM 3 | 29 | #define INT_STATUS_NUM 3 |
29 | 30 | ||
@@ -150,7 +151,8 @@ static struct resource battery_resources[] __devinitdata = { | |||
150 | static struct resource charger_resources[] __devinitdata = { | 151 | static struct resource charger_resources[] __devinitdata = { |
151 | {PM8607_IRQ_CHG, PM8607_IRQ_CHG, "charger detect", IORESOURCE_IRQ,}, | 152 | {PM8607_IRQ_CHG, PM8607_IRQ_CHG, "charger detect", IORESOURCE_IRQ,}, |
152 | {PM8607_IRQ_CHG_DONE, PM8607_IRQ_CHG_DONE, "charging done", IORESOURCE_IRQ,}, | 153 | {PM8607_IRQ_CHG_DONE, PM8607_IRQ_CHG_DONE, "charging done", IORESOURCE_IRQ,}, |
153 | {PM8607_IRQ_CHG_FAULT, PM8607_IRQ_CHG_FAULT, "charging timeout", IORESOURCE_IRQ,}, | 154 | {PM8607_IRQ_CHG_FAIL, PM8607_IRQ_CHG_FAIL, "charging timeout", IORESOURCE_IRQ,}, |
155 | {PM8607_IRQ_CHG_FAULT, PM8607_IRQ_CHG_FAULT, "charging fault", IORESOURCE_IRQ,}, | ||
154 | {PM8607_IRQ_GPADC1, PM8607_IRQ_GPADC1, "battery temperature", IORESOURCE_IRQ,}, | 156 | {PM8607_IRQ_GPADC1, PM8607_IRQ_GPADC1, "battery temperature", IORESOURCE_IRQ,}, |
155 | {PM8607_IRQ_VBAT, PM8607_IRQ_VBAT, "battery voltage", IORESOURCE_IRQ,}, | 157 | {PM8607_IRQ_VBAT, PM8607_IRQ_VBAT, "battery voltage", IORESOURCE_IRQ,}, |
156 | {PM8607_IRQ_VCHG, PM8607_IRQ_VCHG, "vchg voltage", IORESOURCE_IRQ,}, | 158 | {PM8607_IRQ_VCHG, PM8607_IRQ_VCHG, "vchg voltage", IORESOURCE_IRQ,}, |
@@ -318,10 +320,15 @@ static struct regulator_init_data preg_init_data = { | |||
318 | .consumer_supplies = &preg_supply[0], | 320 | .consumer_supplies = &preg_supply[0], |
319 | }; | 321 | }; |
320 | 322 | ||
323 | static struct charger_regulator chg_desc_regulator_data[] = { | ||
324 | { .regulator_name = "preg", }, | ||
325 | }; | ||
326 | |||
321 | static struct mfd_cell power_devs[] = { | 327 | static struct mfd_cell power_devs[] = { |
322 | {"88pm860x-battery", -1,}, | 328 | {"88pm860x-battery", -1,}, |
323 | {"88pm860x-charger", -1,}, | 329 | {"88pm860x-charger", -1,}, |
324 | {"88pm860x-preg", -1,}, | 330 | {"88pm860x-preg", -1,}, |
331 | {"charger-manager", -1,}, | ||
325 | }; | 332 | }; |
326 | 333 | ||
327 | static struct mfd_cell rtc_devs[] = { | 334 | static struct mfd_cell rtc_devs[] = { |
@@ -929,6 +936,19 @@ static void __devinit device_power_init(struct pm860x_chip *chip, | |||
929 | NULL, chip->irq_base, NULL); | 936 | NULL, chip->irq_base, NULL); |
930 | if (ret < 0) | 937 | if (ret < 0) |
931 | dev_err(chip->dev, "Failed to add preg subdev\n"); | 938 | dev_err(chip->dev, "Failed to add preg subdev\n"); |
939 | |||
940 | if (pdata->chg_desc) { | ||
941 | pdata->chg_desc->charger_regulators = | ||
942 | &chg_desc_regulator_data[0]; | ||
943 | pdata->chg_desc->num_charger_regulators = | ||
944 | ARRAY_SIZE(chg_desc_regulator_data), | ||
945 | power_devs[3].platform_data = pdata->chg_desc; | ||
946 | power_devs[3].pdata_size = sizeof(*pdata->chg_desc); | ||
947 | ret = mfd_add_devices(chip->dev, 0, &power_devs[3], 1, | ||
948 | NULL, chip->irq_base, NULL); | ||
949 | if (ret < 0) | ||
950 | dev_err(chip->dev, "Failed to add chg-manager subdev\n"); | ||
951 | } | ||
932 | } | 952 | } |
933 | 953 | ||
934 | static void __devinit device_onkey_init(struct pm860x_chip *chip, | 954 | static void __devinit device_onkey_init(struct pm860x_chip *chip, |
diff --git a/drivers/power/88pm860x_battery.c b/drivers/power/88pm860x_battery.c new file mode 100644 index 000000000000..beed5ecf75e1 --- /dev/null +++ b/drivers/power/88pm860x_battery.c | |||
@@ -0,0 +1,1041 @@ | |||
1 | /* | ||
2 | * Battery driver for Marvell 88PM860x PMIC | ||
3 | * | ||
4 | * Copyright (c) 2012 Marvell International Ltd. | ||
5 | * Author: Jett Zhou <jtzhou@marvell.com> | ||
6 | * Haojian Zhuang <haojian.zhuang@marvell.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/platform_device.h> | ||
16 | #include <linux/slab.h> | ||
17 | #include <linux/mutex.h> | ||
18 | #include <linux/string.h> | ||
19 | #include <linux/power_supply.h> | ||
20 | #include <linux/mfd/88pm860x.h> | ||
21 | #include <linux/delay.h> | ||
22 | |||
23 | /* bit definitions of Status Query Interface 2 */ | ||
24 | #define STATUS2_CHG (1 << 2) | ||
25 | #define STATUS2_BAT (1 << 3) | ||
26 | #define STATUS2_VBUS (1 << 4) | ||
27 | |||
28 | /* bit definitions of Measurement Enable 1 Register */ | ||
29 | #define MEAS1_TINT (1 << 3) | ||
30 | #define MEAS1_GP1 (1 << 5) | ||
31 | |||
32 | /* bit definitions of Measurement Enable 3 Register */ | ||
33 | #define MEAS3_IBAT (1 << 0) | ||
34 | #define MEAS3_BAT_DET (1 << 1) | ||
35 | #define MEAS3_CC (1 << 2) | ||
36 | |||
37 | /* bit definitions of Measurement Off Time Register */ | ||
38 | #define MEAS_OFF_SLEEP_EN (1 << 1) | ||
39 | |||
40 | /* bit definitions of GPADC Bias Current 2 Register */ | ||
41 | #define GPBIAS2_GPADC1_SET (2 << 4) | ||
42 | /* GPADC1 Bias Current value in uA unit */ | ||
43 | #define GPBIAS2_GPADC1_UA ((GPBIAS2_GPADC1_SET >> 4) * 5 + 1) | ||
44 | |||
45 | /* bit definitions of GPADC Misc 1 Register */ | ||
46 | #define GPMISC1_GPADC_EN (1 << 0) | ||
47 | |||
48 | /* bit definitions of Charger Control 6 Register */ | ||
49 | #define CC6_BAT_DET_GPADC1 1 | ||
50 | |||
51 | /* bit definitions of Coulomb Counter Reading Register */ | ||
52 | #define CCNT_AVG_SEL (4 << 3) | ||
53 | |||
54 | /* bit definitions of RTC miscellaneous Register1 */ | ||
55 | #define RTC_SOC_5LSB (0x1F << 3) | ||
56 | |||
57 | /* bit definitions of RTC Register1 */ | ||
58 | #define RTC_SOC_3MSB (0x7) | ||
59 | |||
60 | /* bit definitions of Power up Log register */ | ||
61 | #define BAT_WU_LOG (1<<6) | ||
62 | |||
63 | /* coulomb counter index */ | ||
64 | #define CCNT_POS1 0 | ||
65 | #define CCNT_POS2 1 | ||
66 | #define CCNT_NEG1 2 | ||
67 | #define CCNT_NEG2 3 | ||
68 | #define CCNT_SPOS 4 | ||
69 | #define CCNT_SNEG 5 | ||
70 | |||
71 | /* OCV -- Open Circuit Voltage */ | ||
72 | #define OCV_MODE_ACTIVE 0 | ||
73 | #define OCV_MODE_SLEEP 1 | ||
74 | |||
75 | /* Vbat range of CC for measuring Rbat */ | ||
76 | #define LOW_BAT_THRESHOLD 3600 | ||
77 | #define VBATT_RESISTOR_MIN 3800 | ||
78 | #define VBATT_RESISTOR_MAX 4100 | ||
79 | |||
80 | /* TBAT for batt, TINT for chip itself */ | ||
81 | #define PM860X_TEMP_TINT (0) | ||
82 | #define PM860X_TEMP_TBAT (1) | ||
83 | |||
84 | /* | ||
85 | * Battery temperature based on NTC resistor, defined | ||
86 | * corresponding resistor value -- Ohm / C degeree. | ||
87 | */ | ||
88 | #define TBAT_NEG_25D 127773 /* -25 */ | ||
89 | #define TBAT_NEG_10D 54564 /* -10 */ | ||
90 | #define TBAT_0D 32330 /* 0 */ | ||
91 | #define TBAT_10D 19785 /* 10 */ | ||
92 | #define TBAT_20D 12468 /* 20 */ | ||
93 | #define TBAT_30D 8072 /* 30 */ | ||
94 | #define TBAT_40D 5356 /* 40 */ | ||
95 | |||
96 | struct pm860x_battery_info { | ||
97 | struct pm860x_chip *chip; | ||
98 | struct i2c_client *i2c; | ||
99 | struct device *dev; | ||
100 | |||
101 | struct power_supply battery; | ||
102 | struct mutex lock; | ||
103 | int status; | ||
104 | int irq_cc; | ||
105 | int irq_batt; | ||
106 | int max_capacity; | ||
107 | int resistor; /* Battery Internal Resistor */ | ||
108 | int last_capacity; | ||
109 | int start_soc; | ||
110 | unsigned present:1; | ||
111 | unsigned temp_type:1; /* TINT or TBAT */ | ||
112 | }; | ||
113 | |||
114 | struct ccnt { | ||
115 | unsigned long long int pos; | ||
116 | unsigned long long int neg; | ||
117 | unsigned int spos; | ||
118 | unsigned int sneg; | ||
119 | |||
120 | int total_chg; /* mAh(3.6C) */ | ||
121 | int total_dischg; /* mAh(3.6C) */ | ||
122 | }; | ||
123 | |||
124 | /* | ||
125 | * State of Charge. | ||
126 | * The first number is mAh(=3.6C), and the second number is percent point. | ||
127 | */ | ||
128 | static int array_soc[][2] = { | ||
129 | {4170, 100}, {4154, 99}, {4136, 98}, {4122, 97}, {4107, 96}, | ||
130 | {4102, 95}, {4088, 94}, {4081, 93}, {4070, 92}, {4060, 91}, | ||
131 | {4053, 90}, {4044, 89}, {4035, 88}, {4028, 87}, {4019, 86}, | ||
132 | {4013, 85}, {4006, 84}, {3995, 83}, {3987, 82}, {3982, 81}, | ||
133 | {3976, 80}, {3968, 79}, {3962, 78}, {3954, 77}, {3946, 76}, | ||
134 | {3941, 75}, {3934, 74}, {3929, 73}, {3922, 72}, {3916, 71}, | ||
135 | {3910, 70}, {3904, 69}, {3898, 68}, {3892, 67}, {3887, 66}, | ||
136 | {3880, 65}, {3874, 64}, {3868, 63}, {3862, 62}, {3854, 61}, | ||
137 | {3849, 60}, {3843, 59}, {3840, 58}, {3833, 57}, {3829, 56}, | ||
138 | {3824, 55}, {3818, 54}, {3815, 53}, {3810, 52}, {3808, 51}, | ||
139 | {3804, 50}, {3801, 49}, {3798, 48}, {3796, 47}, {3792, 46}, | ||
140 | {3789, 45}, {3785, 44}, {3784, 43}, {3782, 42}, {3780, 41}, | ||
141 | {3777, 40}, {3776, 39}, {3774, 38}, {3772, 37}, {3771, 36}, | ||
142 | {3769, 35}, {3768, 34}, {3764, 33}, {3763, 32}, {3760, 31}, | ||
143 | {3760, 30}, {3754, 29}, {3750, 28}, {3749, 27}, {3744, 26}, | ||
144 | {3740, 25}, {3734, 24}, {3732, 23}, {3728, 22}, {3726, 21}, | ||
145 | {3720, 20}, {3716, 19}, {3709, 18}, {3703, 17}, {3698, 16}, | ||
146 | {3692, 15}, {3683, 14}, {3675, 13}, {3670, 12}, {3665, 11}, | ||
147 | {3661, 10}, {3649, 9}, {3637, 8}, {3622, 7}, {3609, 6}, | ||
148 | {3580, 5}, {3558, 4}, {3540, 3}, {3510, 2}, {3429, 1}, | ||
149 | }; | ||
150 | |||
151 | static struct ccnt ccnt_data; | ||
152 | |||
153 | /* | ||
154 | * register 1 bit[7:0] -- bit[11:4] of measured value of voltage | ||
155 | * register 0 bit[3:0] -- bit[3:0] of measured value of voltage | ||
156 | */ | ||
157 | static int measure_12bit_voltage(struct pm860x_battery_info *info, | ||
158 | int offset, int *data) | ||
159 | { | ||
160 | unsigned char buf[2]; | ||
161 | int ret; | ||
162 | |||
163 | ret = pm860x_bulk_read(info->i2c, offset, 2, buf); | ||
164 | if (ret < 0) | ||
165 | return ret; | ||
166 | |||
167 | *data = ((buf[0] & 0xff) << 4) | (buf[1] & 0x0f); | ||
168 | /* V_MEAS(mV) = data * 1.8 * 1000 / (2^12) */ | ||
169 | *data = ((*data & 0xfff) * 9 * 25) >> 9; | ||
170 | return 0; | ||
171 | } | ||
172 | |||
173 | static int measure_vbatt(struct pm860x_battery_info *info, int state, | ||
174 | int *data) | ||
175 | { | ||
176 | unsigned char buf[5]; | ||
177 | int ret; | ||
178 | |||
179 | switch (state) { | ||
180 | case OCV_MODE_ACTIVE: | ||
181 | ret = measure_12bit_voltage(info, PM8607_VBAT_MEAS1, data); | ||
182 | if (ret) | ||
183 | return ret; | ||
184 | /* V_BATT_MEAS(mV) = value * 3 * 1.8 * 1000 / (2^12) */ | ||
185 | *data *= 3; | ||
186 | break; | ||
187 | case OCV_MODE_SLEEP: | ||
188 | /* | ||
189 | * voltage value of VBATT in sleep mode is saved in different | ||
190 | * registers. | ||
191 | * bit[11:10] -- bit[7:6] of LDO9(0x18) | ||
192 | * bit[9:8] -- bit[7:6] of LDO8(0x17) | ||
193 | * bit[7:6] -- bit[7:6] of LDO7(0x16) | ||
194 | * bit[5:4] -- bit[7:6] of LDO6(0x15) | ||
195 | * bit[3:0] -- bit[7:4] of LDO5(0x14) | ||
196 | */ | ||
197 | ret = pm860x_bulk_read(info->i2c, PM8607_LDO5, 5, buf); | ||
198 | if (ret < 0) | ||
199 | return ret; | ||
200 | ret = ((buf[4] >> 6) << 10) | ((buf[3] >> 6) << 8) | ||
201 | | ((buf[2] >> 6) << 6) | ((buf[1] >> 6) << 4) | ||
202 | | (buf[0] >> 4); | ||
203 | /* V_BATT_MEAS(mV) = data * 3 * 1.8 * 1000 / (2^12) */ | ||
204 | *data = ((*data & 0xff) * 27 * 25) >> 9; | ||
205 | break; | ||
206 | default: | ||
207 | return -EINVAL; | ||
208 | } | ||
209 | return 0; | ||
210 | } | ||
211 | |||
212 | /* | ||
213 | * Return value is signed data. | ||
214 | * Negative value means discharging, and positive value means charging. | ||
215 | */ | ||
216 | static int measure_current(struct pm860x_battery_info *info, int *data) | ||
217 | { | ||
218 | unsigned char buf[2]; | ||
219 | short s; | ||
220 | int ret; | ||
221 | |||
222 | ret = pm860x_bulk_read(info->i2c, PM8607_IBAT_MEAS1, 2, buf); | ||
223 | if (ret < 0) | ||
224 | return ret; | ||
225 | |||
226 | s = ((buf[0] & 0xff) << 8) | (buf[1] & 0xff); | ||
227 | /* current(mA) = value * 0.125 */ | ||
228 | *data = s >> 3; | ||
229 | return 0; | ||
230 | } | ||
231 | |||
232 | static int set_charger_current(struct pm860x_battery_info *info, int data, | ||
233 | int *old) | ||
234 | { | ||
235 | int ret; | ||
236 | |||
237 | if (data < 50 || data > 1600 || !old) | ||
238 | return -EINVAL; | ||
239 | |||
240 | data = ((data - 50) / 50) & 0x1f; | ||
241 | *old = pm860x_reg_read(info->i2c, PM8607_CHG_CTRL2); | ||
242 | *old = (*old & 0x1f) * 50 + 50; | ||
243 | ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL2, 0x1f, data); | ||
244 | if (ret < 0) | ||
245 | return ret; | ||
246 | return 0; | ||
247 | } | ||
248 | |||
249 | static int read_ccnt(struct pm860x_battery_info *info, int offset, | ||
250 | int *ccnt) | ||
251 | { | ||
252 | unsigned char buf[2]; | ||
253 | int ret; | ||
254 | |||
255 | ret = pm860x_set_bits(info->i2c, PM8607_CCNT, 7, offset & 7); | ||
256 | if (ret < 0) | ||
257 | goto out; | ||
258 | ret = pm860x_bulk_read(info->i2c, PM8607_CCNT_MEAS1, 2, buf); | ||
259 | if (ret < 0) | ||
260 | goto out; | ||
261 | *ccnt = ((buf[0] & 0xff) << 8) | (buf[1] & 0xff); | ||
262 | return 0; | ||
263 | out: | ||
264 | return ret; | ||
265 | } | ||
266 | |||
267 | static int calc_ccnt(struct pm860x_battery_info *info, struct ccnt *ccnt) | ||
268 | { | ||
269 | unsigned int sum; | ||
270 | int ret; | ||
271 | int data; | ||
272 | |||
273 | ret = read_ccnt(info, CCNT_POS1, &data); | ||
274 | if (ret) | ||
275 | goto out; | ||
276 | sum = data & 0xffff; | ||
277 | ret = read_ccnt(info, CCNT_POS2, &data); | ||
278 | if (ret) | ||
279 | goto out; | ||
280 | sum |= (data & 0xffff) << 16; | ||
281 | ccnt->pos += sum; | ||
282 | |||
283 | ret = read_ccnt(info, CCNT_NEG1, &data); | ||
284 | if (ret) | ||
285 | goto out; | ||
286 | sum = data & 0xffff; | ||
287 | ret = read_ccnt(info, CCNT_NEG2, &data); | ||
288 | if (ret) | ||
289 | goto out; | ||
290 | sum |= (data & 0xffff) << 16; | ||
291 | sum = ~sum + 1; /* since it's negative */ | ||
292 | ccnt->neg += sum; | ||
293 | |||
294 | ret = read_ccnt(info, CCNT_SPOS, &data); | ||
295 | if (ret) | ||
296 | goto out; | ||
297 | ccnt->spos += data; | ||
298 | ret = read_ccnt(info, CCNT_SNEG, &data); | ||
299 | if (ret) | ||
300 | goto out; | ||
301 | |||
302 | /* | ||
303 | * charge(mAh) = count * 1.6984 * 1e(-8) | ||
304 | * = count * 16984 * 1.024 * 1.024 * 1.024 / (2 ^ 40) | ||
305 | * = count * 18236 / (2 ^ 40) | ||
306 | */ | ||
307 | ccnt->total_chg = (int) ((ccnt->pos * 18236) >> 40); | ||
308 | ccnt->total_dischg = (int) ((ccnt->neg * 18236) >> 40); | ||
309 | return 0; | ||
310 | out: | ||
311 | return ret; | ||
312 | } | ||
313 | |||
314 | static int clear_ccnt(struct pm860x_battery_info *info, struct ccnt *ccnt) | ||
315 | { | ||
316 | int data; | ||
317 | |||
318 | memset(ccnt, 0, sizeof(*ccnt)); | ||
319 | /* read to clear ccnt */ | ||
320 | read_ccnt(info, CCNT_POS1, &data); | ||
321 | read_ccnt(info, CCNT_POS2, &data); | ||
322 | read_ccnt(info, CCNT_NEG1, &data); | ||
323 | read_ccnt(info, CCNT_NEG2, &data); | ||
324 | read_ccnt(info, CCNT_SPOS, &data); | ||
325 | read_ccnt(info, CCNT_SNEG, &data); | ||
326 | return 0; | ||
327 | } | ||
328 | |||
329 | /* Calculate Open Circuit Voltage */ | ||
330 | static int calc_ocv(struct pm860x_battery_info *info, int *ocv) | ||
331 | { | ||
332 | int ret; | ||
333 | int i; | ||
334 | int data; | ||
335 | int vbatt_avg; | ||
336 | int vbatt_sum; | ||
337 | int ibatt_avg; | ||
338 | int ibatt_sum; | ||
339 | |||
340 | if (!ocv) | ||
341 | return -EINVAL; | ||
342 | |||
343 | for (i = 0, ibatt_sum = 0, vbatt_sum = 0; i < 10; i++) { | ||
344 | ret = measure_vbatt(info, OCV_MODE_ACTIVE, &data); | ||
345 | if (ret) | ||
346 | goto out; | ||
347 | vbatt_sum += data; | ||
348 | ret = measure_current(info, &data); | ||
349 | if (ret) | ||
350 | goto out; | ||
351 | ibatt_sum += data; | ||
352 | } | ||
353 | vbatt_avg = vbatt_sum / 10; | ||
354 | ibatt_avg = ibatt_sum / 10; | ||
355 | |||
356 | mutex_lock(&info->lock); | ||
357 | if (info->present) | ||
358 | *ocv = vbatt_avg - ibatt_avg * info->resistor / 1000; | ||
359 | else | ||
360 | *ocv = vbatt_avg; | ||
361 | mutex_unlock(&info->lock); | ||
362 | dev_dbg(info->dev, "VBAT average:%d, OCV:%d\n", vbatt_avg, *ocv); | ||
363 | return 0; | ||
364 | out: | ||
365 | return ret; | ||
366 | } | ||
367 | |||
368 | /* Calculate State of Charge (percent points) */ | ||
369 | static int calc_soc(struct pm860x_battery_info *info, int state, int *soc) | ||
370 | { | ||
371 | int i; | ||
372 | int ocv; | ||
373 | int count; | ||
374 | int ret = -EINVAL; | ||
375 | |||
376 | if (!soc) | ||
377 | return -EINVAL; | ||
378 | |||
379 | switch (state) { | ||
380 | case OCV_MODE_ACTIVE: | ||
381 | ret = calc_ocv(info, &ocv); | ||
382 | break; | ||
383 | case OCV_MODE_SLEEP: | ||
384 | ret = measure_vbatt(info, OCV_MODE_SLEEP, &ocv); | ||
385 | break; | ||
386 | } | ||
387 | if (ret) | ||
388 | return ret; | ||
389 | |||
390 | count = ARRAY_SIZE(array_soc); | ||
391 | if (ocv < array_soc[count - 1][0]) { | ||
392 | *soc = 0; | ||
393 | return 0; | ||
394 | } | ||
395 | |||
396 | for (i = 0; i < count; i++) { | ||
397 | if (ocv >= array_soc[i][0]) { | ||
398 | *soc = array_soc[i][1]; | ||
399 | break; | ||
400 | } | ||
401 | } | ||
402 | return 0; | ||
403 | } | ||
404 | |||
405 | static irqreturn_t pm860x_coulomb_handler(int irq, void *data) | ||
406 | { | ||
407 | struct pm860x_battery_info *info = data; | ||
408 | |||
409 | calc_ccnt(info, &ccnt_data); | ||
410 | return IRQ_HANDLED; | ||
411 | } | ||
412 | |||
413 | static irqreturn_t pm860x_batt_handler(int irq, void *data) | ||
414 | { | ||
415 | struct pm860x_battery_info *info = data; | ||
416 | int ret; | ||
417 | |||
418 | mutex_lock(&info->lock); | ||
419 | ret = pm860x_reg_read(info->i2c, PM8607_STATUS_2); | ||
420 | if (ret & STATUS2_BAT) { | ||
421 | info->present = 1; | ||
422 | info->temp_type = PM860X_TEMP_TBAT; | ||
423 | } else { | ||
424 | info->present = 0; | ||
425 | info->temp_type = PM860X_TEMP_TINT; | ||
426 | } | ||
427 | mutex_unlock(&info->lock); | ||
428 | /* clear ccnt since battery is attached or dettached */ | ||
429 | clear_ccnt(info, &ccnt_data); | ||
430 | return IRQ_HANDLED; | ||
431 | } | ||
432 | |||
433 | static void pm860x_init_battery(struct pm860x_battery_info *info) | ||
434 | { | ||
435 | unsigned char buf[2]; | ||
436 | int ret; | ||
437 | int data; | ||
438 | int bat_remove; | ||
439 | int soc; | ||
440 | |||
441 | /* measure enable on GPADC1 */ | ||
442 | data = MEAS1_GP1; | ||
443 | if (info->temp_type == PM860X_TEMP_TINT) | ||
444 | data |= MEAS1_TINT; | ||
445 | ret = pm860x_set_bits(info->i2c, PM8607_MEAS_EN1, data, data); | ||
446 | if (ret) | ||
447 | goto out; | ||
448 | |||
449 | /* measure enable on IBAT, BAT_DET, CC. IBAT is depend on CC. */ | ||
450 | data = MEAS3_IBAT | MEAS3_BAT_DET | MEAS3_CC; | ||
451 | ret = pm860x_set_bits(info->i2c, PM8607_MEAS_EN3, data, data); | ||
452 | if (ret) | ||
453 | goto out; | ||
454 | |||
455 | /* measure disable CC in sleep time */ | ||
456 | ret = pm860x_reg_write(info->i2c, PM8607_MEAS_OFF_TIME1, 0x82); | ||
457 | if (ret) | ||
458 | goto out; | ||
459 | ret = pm860x_reg_write(info->i2c, PM8607_MEAS_OFF_TIME2, 0x6c); | ||
460 | if (ret) | ||
461 | goto out; | ||
462 | |||
463 | /* enable GPADC */ | ||
464 | ret = pm860x_set_bits(info->i2c, PM8607_GPADC_MISC1, | ||
465 | GPMISC1_GPADC_EN, GPMISC1_GPADC_EN); | ||
466 | if (ret < 0) | ||
467 | goto out; | ||
468 | |||
469 | /* detect battery via GPADC1 */ | ||
470 | ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL6, | ||
471 | CC6_BAT_DET_GPADC1, CC6_BAT_DET_GPADC1); | ||
472 | if (ret < 0) | ||
473 | goto out; | ||
474 | |||
475 | ret = pm860x_set_bits(info->i2c, PM8607_CCNT, 7 << 3, | ||
476 | CCNT_AVG_SEL); | ||
477 | if (ret < 0) | ||
478 | goto out; | ||
479 | |||
480 | /* set GPADC1 bias */ | ||
481 | ret = pm860x_set_bits(info->i2c, PM8607_GP_BIAS2, 0xF << 4, | ||
482 | GPBIAS2_GPADC1_SET); | ||
483 | if (ret < 0) | ||
484 | goto out; | ||
485 | |||
486 | /* check whether battery present) */ | ||
487 | mutex_lock(&info->lock); | ||
488 | ret = pm860x_reg_read(info->i2c, PM8607_STATUS_2); | ||
489 | if (ret < 0) { | ||
490 | mutex_unlock(&info->lock); | ||
491 | goto out; | ||
492 | } | ||
493 | if (ret & STATUS2_BAT) { | ||
494 | info->present = 1; | ||
495 | info->temp_type = PM860X_TEMP_TBAT; | ||
496 | } else { | ||
497 | info->present = 0; | ||
498 | info->temp_type = PM860X_TEMP_TINT; | ||
499 | } | ||
500 | mutex_unlock(&info->lock); | ||
501 | |||
502 | calc_soc(info, OCV_MODE_ACTIVE, &soc); | ||
503 | |||
504 | data = pm860x_reg_read(info->i2c, PM8607_POWER_UP_LOG); | ||
505 | bat_remove = data & BAT_WU_LOG; | ||
506 | |||
507 | dev_dbg(info->dev, "battery wake up? %s\n", | ||
508 | bat_remove != 0 ? "yes" : "no"); | ||
509 | |||
510 | /* restore SOC from RTC domain register */ | ||
511 | if (bat_remove == 0) { | ||
512 | buf[0] = pm860x_reg_read(info->i2c, PM8607_RTC_MISC2); | ||
513 | buf[1] = pm860x_reg_read(info->i2c, PM8607_RTC1); | ||
514 | data = ((buf[1] & 0x3) << 5) | ((buf[0] >> 3) & 0x1F); | ||
515 | if (data > soc + 15) | ||
516 | info->start_soc = soc; | ||
517 | else if (data < soc - 15) | ||
518 | info->start_soc = soc; | ||
519 | else | ||
520 | info->start_soc = data; | ||
521 | dev_dbg(info->dev, "soc_rtc %d, soc_ocv :%d\n", data, soc); | ||
522 | } else { | ||
523 | pm860x_set_bits(info->i2c, PM8607_POWER_UP_LOG, | ||
524 | BAT_WU_LOG, BAT_WU_LOG); | ||
525 | info->start_soc = soc; | ||
526 | } | ||
527 | info->last_capacity = info->start_soc; | ||
528 | dev_dbg(info->dev, "init soc : %d\n", info->last_capacity); | ||
529 | out: | ||
530 | return; | ||
531 | } | ||
532 | |||
533 | static void set_temp_threshold(struct pm860x_battery_info *info, | ||
534 | int min, int max) | ||
535 | { | ||
536 | int data; | ||
537 | |||
538 | /* (tmp << 8) / 1800 */ | ||
539 | if (min <= 0) | ||
540 | data = 0; | ||
541 | else | ||
542 | data = (min << 8) / 1800; | ||
543 | pm860x_reg_write(info->i2c, PM8607_GPADC1_HIGHTH, data); | ||
544 | dev_dbg(info->dev, "TEMP_HIGHTH : min: %d, 0x%x\n", min, data); | ||
545 | |||
546 | if (max <= 0) | ||
547 | data = 0xff; | ||
548 | else | ||
549 | data = (max << 8) / 1800; | ||
550 | pm860x_reg_write(info->i2c, PM8607_GPADC1_LOWTH, data); | ||
551 | dev_dbg(info->dev, "TEMP_LOWTH:max : %d, 0x%x\n", max, data); | ||
552 | } | ||
553 | |||
554 | static int measure_temp(struct pm860x_battery_info *info, int *data) | ||
555 | { | ||
556 | int ret; | ||
557 | int temp; | ||
558 | int min; | ||
559 | int max; | ||
560 | |||
561 | if (info->temp_type == PM860X_TEMP_TINT) { | ||
562 | ret = measure_12bit_voltage(info, PM8607_TINT_MEAS1, data); | ||
563 | if (ret) | ||
564 | return ret; | ||
565 | *data = (*data - 884) * 1000 / 3611; | ||
566 | } else { | ||
567 | ret = measure_12bit_voltage(info, PM8607_GPADC1_MEAS1, data); | ||
568 | if (ret) | ||
569 | return ret; | ||
570 | /* meausered Vtbat(mV) / Ibias_current(11uA)*/ | ||
571 | *data = (*data * 1000) / GPBIAS2_GPADC1_UA; | ||
572 | |||
573 | if (*data > TBAT_NEG_25D) { | ||
574 | temp = -30; /* over cold , suppose -30 roughly */ | ||
575 | max = TBAT_NEG_10D * GPBIAS2_GPADC1_UA / 1000; | ||
576 | set_temp_threshold(info, 0, max); | ||
577 | } else if (*data > TBAT_NEG_10D) { | ||
578 | temp = -15; /* -15 degree, code */ | ||
579 | max = TBAT_NEG_10D * GPBIAS2_GPADC1_UA / 1000; | ||
580 | set_temp_threshold(info, 0, max); | ||
581 | } else if (*data > TBAT_0D) { | ||
582 | temp = -5; /* -5 degree */ | ||
583 | min = TBAT_NEG_10D * GPBIAS2_GPADC1_UA / 1000; | ||
584 | max = TBAT_40D * GPBIAS2_GPADC1_UA / 1000; | ||
585 | set_temp_threshold(info, min, max); | ||
586 | } else if (*data > TBAT_10D) { | ||
587 | temp = 5; /* in range of (0, 10) */ | ||
588 | min = TBAT_NEG_10D * GPBIAS2_GPADC1_UA / 1000; | ||
589 | max = TBAT_40D * GPBIAS2_GPADC1_UA / 1000; | ||
590 | set_temp_threshold(info, min, max); | ||
591 | } else if (*data > TBAT_20D) { | ||
592 | temp = 15; /* in range of (10, 20) */ | ||
593 | min = TBAT_NEG_10D * GPBIAS2_GPADC1_UA / 1000; | ||
594 | max = TBAT_40D * GPBIAS2_GPADC1_UA / 1000; | ||
595 | set_temp_threshold(info, min, max); | ||
596 | } else if (*data > TBAT_30D) { | ||
597 | temp = 25; /* in range of (20, 30) */ | ||
598 | min = TBAT_NEG_10D * GPBIAS2_GPADC1_UA / 1000; | ||
599 | max = TBAT_40D * GPBIAS2_GPADC1_UA / 1000; | ||
600 | set_temp_threshold(info, min, max); | ||
601 | } else if (*data > TBAT_40D) { | ||
602 | temp = 35; /* in range of (30, 40) */ | ||
603 | min = TBAT_NEG_10D * GPBIAS2_GPADC1_UA / 1000; | ||
604 | max = TBAT_40D * GPBIAS2_GPADC1_UA / 1000; | ||
605 | set_temp_threshold(info, min, max); | ||
606 | } else { | ||
607 | min = TBAT_40D * GPBIAS2_GPADC1_UA / 1000; | ||
608 | set_temp_threshold(info, min, 0); | ||
609 | temp = 45; /* over heat ,suppose 45 roughly */ | ||
610 | } | ||
611 | |||
612 | dev_dbg(info->dev, "temp_C:%d C,temp_mv:%d mv\n", temp, *data); | ||
613 | *data = temp; | ||
614 | } | ||
615 | return 0; | ||
616 | } | ||
617 | |||
618 | static int calc_resistor(struct pm860x_battery_info *info) | ||
619 | { | ||
620 | int vbatt_sum1; | ||
621 | int vbatt_sum2; | ||
622 | int chg_current; | ||
623 | int ibatt_sum1; | ||
624 | int ibatt_sum2; | ||
625 | int data; | ||
626 | int ret; | ||
627 | int i; | ||
628 | |||
629 | ret = measure_current(info, &data); | ||
630 | /* make sure that charging is launched by data > 0 */ | ||
631 | if (ret || data < 0) | ||
632 | goto out; | ||
633 | |||
634 | ret = measure_vbatt(info, OCV_MODE_ACTIVE, &data); | ||
635 | if (ret) | ||
636 | goto out; | ||
637 | /* calculate resistor only in CC charge mode */ | ||
638 | if (data < VBATT_RESISTOR_MIN || data > VBATT_RESISTOR_MAX) | ||
639 | goto out; | ||
640 | |||
641 | /* current is saved */ | ||
642 | if (set_charger_current(info, 500, &chg_current)) | ||
643 | goto out; | ||
644 | |||
645 | /* | ||
646 | * set charge current as 500mA, wait about 500ms till charging | ||
647 | * process is launched and stable with the newer charging current. | ||
648 | */ | ||
649 | msleep(500); | ||
650 | |||
651 | for (i = 0, vbatt_sum1 = 0, ibatt_sum1 = 0; i < 10; i++) { | ||
652 | ret = measure_vbatt(info, OCV_MODE_ACTIVE, &data); | ||
653 | if (ret) | ||
654 | goto out_meas; | ||
655 | vbatt_sum1 += data; | ||
656 | ret = measure_current(info, &data); | ||
657 | if (ret) | ||
658 | goto out_meas; | ||
659 | |||
660 | if (data < 0) | ||
661 | ibatt_sum1 = ibatt_sum1 - data; /* discharging */ | ||
662 | else | ||
663 | ibatt_sum1 = ibatt_sum1 + data; /* charging */ | ||
664 | } | ||
665 | |||
666 | if (set_charger_current(info, 100, &ret)) | ||
667 | goto out_meas; | ||
668 | /* | ||
669 | * set charge current as 100mA, wait about 500ms till charging | ||
670 | * process is launched and stable with the newer charging current. | ||
671 | */ | ||
672 | msleep(500); | ||
673 | |||
674 | for (i = 0, vbatt_sum2 = 0, ibatt_sum2 = 0; i < 10; i++) { | ||
675 | ret = measure_vbatt(info, OCV_MODE_ACTIVE, &data); | ||
676 | if (ret) | ||
677 | goto out_meas; | ||
678 | vbatt_sum2 += data; | ||
679 | ret = measure_current(info, &data); | ||
680 | if (ret) | ||
681 | goto out_meas; | ||
682 | |||
683 | if (data < 0) | ||
684 | ibatt_sum2 = ibatt_sum2 - data; /* discharging */ | ||
685 | else | ||
686 | ibatt_sum2 = ibatt_sum2 + data; /* charging */ | ||
687 | } | ||
688 | |||
689 | /* restore current setting */ | ||
690 | if (set_charger_current(info, chg_current, &ret)) | ||
691 | goto out_meas; | ||
692 | |||
693 | if ((vbatt_sum1 > vbatt_sum2) && (ibatt_sum1 > ibatt_sum2) && | ||
694 | (ibatt_sum2 > 0)) { | ||
695 | /* calculate resistor in discharging case */ | ||
696 | data = 1000 * (vbatt_sum1 - vbatt_sum2) | ||
697 | / (ibatt_sum1 - ibatt_sum2); | ||
698 | if ((data - info->resistor > 0) && | ||
699 | (data - info->resistor < info->resistor)) | ||
700 | info->resistor = data; | ||
701 | if ((info->resistor - data > 0) && | ||
702 | (info->resistor - data < data)) | ||
703 | info->resistor = data; | ||
704 | } | ||
705 | return 0; | ||
706 | |||
707 | out_meas: | ||
708 | set_charger_current(info, chg_current, &ret); | ||
709 | out: | ||
710 | return -EINVAL; | ||
711 | } | ||
712 | |||
713 | static int calc_capacity(struct pm860x_battery_info *info, int *cap) | ||
714 | { | ||
715 | int ret; | ||
716 | int data; | ||
717 | int ibat; | ||
718 | int cap_ocv = 0; | ||
719 | int cap_cc = 0; | ||
720 | |||
721 | ret = calc_ccnt(info, &ccnt_data); | ||
722 | if (ret) | ||
723 | goto out; | ||
724 | soc: | ||
725 | data = info->max_capacity * info->start_soc / 100; | ||
726 | if (ccnt_data.total_dischg - ccnt_data.total_chg <= data) { | ||
727 | cap_cc = | ||
728 | data + ccnt_data.total_chg - ccnt_data.total_dischg; | ||
729 | } else { | ||
730 | clear_ccnt(info, &ccnt_data); | ||
731 | calc_soc(info, OCV_MODE_ACTIVE, &info->start_soc); | ||
732 | dev_dbg(info->dev, "restart soc = %d !\n", | ||
733 | info->start_soc); | ||
734 | goto soc; | ||
735 | } | ||
736 | |||
737 | cap_cc = cap_cc * 100 / info->max_capacity; | ||
738 | if (cap_cc < 0) | ||
739 | cap_cc = 0; | ||
740 | else if (cap_cc > 100) | ||
741 | cap_cc = 100; | ||
742 | |||
743 | dev_dbg(info->dev, "%s, last cap : %d", __func__, | ||
744 | info->last_capacity); | ||
745 | |||
746 | ret = measure_current(info, &ibat); | ||
747 | if (ret) | ||
748 | goto out; | ||
749 | /* Calculate the capacity when discharging(ibat < 0) */ | ||
750 | if (ibat < 0) { | ||
751 | ret = calc_soc(info, OCV_MODE_ACTIVE, &cap_ocv); | ||
752 | if (ret) | ||
753 | cap_ocv = info->last_capacity; | ||
754 | ret = measure_vbatt(info, OCV_MODE_ACTIVE, &data); | ||
755 | if (ret) | ||
756 | goto out; | ||
757 | if (data <= LOW_BAT_THRESHOLD) { | ||
758 | /* choose the lower capacity value to report | ||
759 | * between vbat and CC when vbat < 3.6v; | ||
760 | * than 3.6v; | ||
761 | */ | ||
762 | *cap = min(cap_ocv, cap_cc); | ||
763 | } else { | ||
764 | /* when detect vbat > 3.6v, but cap_cc < 15,and | ||
765 | * cap_ocv is 10% larger than cap_cc, we can think | ||
766 | * CC have some accumulation error, switch to OCV | ||
767 | * to estimate capacity; | ||
768 | * */ | ||
769 | if (cap_cc < 15 && cap_ocv - cap_cc > 10) | ||
770 | *cap = cap_ocv; | ||
771 | else | ||
772 | *cap = cap_cc; | ||
773 | } | ||
774 | /* when discharging, make sure current capacity | ||
775 | * is lower than last*/ | ||
776 | if (*cap > info->last_capacity) | ||
777 | *cap = info->last_capacity; | ||
778 | } else { | ||
779 | *cap = cap_cc; | ||
780 | } | ||
781 | info->last_capacity = *cap; | ||
782 | |||
783 | dev_dbg(info->dev, "%s, cap_ocv:%d cap_cc:%d, cap:%d\n", | ||
784 | (ibat < 0) ? "discharging" : "charging", | ||
785 | cap_ocv, cap_cc, *cap); | ||
786 | /* | ||
787 | * store the current capacity to RTC domain register, | ||
788 | * after next power up , it will be restored. | ||
789 | */ | ||
790 | pm860x_set_bits(info->i2c, PM8607_RTC_MISC2, RTC_SOC_5LSB, | ||
791 | (*cap & 0x1F) << 3); | ||
792 | pm860x_set_bits(info->i2c, PM8607_RTC1, RTC_SOC_3MSB, | ||
793 | ((*cap >> 5) & 0x3)); | ||
794 | return 0; | ||
795 | out: | ||
796 | return ret; | ||
797 | } | ||
798 | |||
799 | static void pm860x_external_power_changed(struct power_supply *psy) | ||
800 | { | ||
801 | struct pm860x_battery_info *info; | ||
802 | |||
803 | info = container_of(psy, struct pm860x_battery_info, battery); | ||
804 | calc_resistor(info); | ||
805 | } | ||
806 | |||
807 | static int pm860x_batt_get_prop(struct power_supply *psy, | ||
808 | enum power_supply_property psp, | ||
809 | union power_supply_propval *val) | ||
810 | { | ||
811 | struct pm860x_battery_info *info = dev_get_drvdata(psy->dev->parent); | ||
812 | int data; | ||
813 | int ret; | ||
814 | |||
815 | switch (psp) { | ||
816 | case POWER_SUPPLY_PROP_PRESENT: | ||
817 | val->intval = info->present; | ||
818 | break; | ||
819 | case POWER_SUPPLY_PROP_CAPACITY: | ||
820 | ret = calc_capacity(info, &data); | ||
821 | if (ret) | ||
822 | return ret; | ||
823 | if (data < 0) | ||
824 | data = 0; | ||
825 | else if (data > 100) | ||
826 | data = 100; | ||
827 | /* return 100 if battery is not attached */ | ||
828 | if (!info->present) | ||
829 | data = 100; | ||
830 | val->intval = data; | ||
831 | break; | ||
832 | case POWER_SUPPLY_PROP_TECHNOLOGY: | ||
833 | val->intval = POWER_SUPPLY_TECHNOLOGY_LION; | ||
834 | break; | ||
835 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: | ||
836 | /* return real vbatt Voltage */ | ||
837 | ret = measure_vbatt(info, OCV_MODE_ACTIVE, &data); | ||
838 | if (ret) | ||
839 | return ret; | ||
840 | val->intval = data * 1000; | ||
841 | break; | ||
842 | case POWER_SUPPLY_PROP_VOLTAGE_AVG: | ||
843 | /* return Open Circuit Voltage (not measured voltage) */ | ||
844 | ret = calc_ocv(info, &data); | ||
845 | if (ret) | ||
846 | return ret; | ||
847 | val->intval = data * 1000; | ||
848 | break; | ||
849 | case POWER_SUPPLY_PROP_CURRENT_NOW: | ||
850 | ret = measure_current(info, &data); | ||
851 | if (ret) | ||
852 | return ret; | ||
853 | val->intval = data; | ||
854 | break; | ||
855 | case POWER_SUPPLY_PROP_TEMP: | ||
856 | if (info->present) { | ||
857 | ret = measure_temp(info, &data); | ||
858 | if (ret) | ||
859 | return ret; | ||
860 | data *= 10; | ||
861 | } else { | ||
862 | /* Fake Temp 25C Without Battery */ | ||
863 | data = 250; | ||
864 | } | ||
865 | val->intval = data; | ||
866 | break; | ||
867 | default: | ||
868 | return -ENODEV; | ||
869 | } | ||
870 | return 0; | ||
871 | } | ||
872 | |||
873 | static int pm860x_batt_set_prop(struct power_supply *psy, | ||
874 | enum power_supply_property psp, | ||
875 | const union power_supply_propval *val) | ||
876 | { | ||
877 | struct pm860x_battery_info *info = dev_get_drvdata(psy->dev->parent); | ||
878 | |||
879 | switch (psp) { | ||
880 | case POWER_SUPPLY_PROP_CHARGE_FULL: | ||
881 | clear_ccnt(info, &ccnt_data); | ||
882 | info->start_soc = 100; | ||
883 | dev_dbg(info->dev, "chg done, update soc = %d\n", | ||
884 | info->start_soc); | ||
885 | break; | ||
886 | default: | ||
887 | return -EPERM; | ||
888 | } | ||
889 | |||
890 | return 0; | ||
891 | } | ||
892 | |||
893 | |||
894 | static enum power_supply_property pm860x_batt_props[] = { | ||
895 | POWER_SUPPLY_PROP_PRESENT, | ||
896 | POWER_SUPPLY_PROP_CAPACITY, | ||
897 | POWER_SUPPLY_PROP_TECHNOLOGY, | ||
898 | POWER_SUPPLY_PROP_VOLTAGE_NOW, | ||
899 | POWER_SUPPLY_PROP_VOLTAGE_AVG, | ||
900 | POWER_SUPPLY_PROP_CURRENT_NOW, | ||
901 | POWER_SUPPLY_PROP_TEMP, | ||
902 | }; | ||
903 | |||
904 | static __devinit int pm860x_battery_probe(struct platform_device *pdev) | ||
905 | { | ||
906 | struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent); | ||
907 | struct pm860x_battery_info *info; | ||
908 | struct pm860x_power_pdata *pdata; | ||
909 | int ret; | ||
910 | |||
911 | info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); | ||
912 | if (!info) | ||
913 | return -ENOMEM; | ||
914 | |||
915 | info->irq_cc = platform_get_irq(pdev, 0); | ||
916 | if (info->irq_cc <= 0) { | ||
917 | dev_err(&pdev->dev, "No IRQ resource!\n"); | ||
918 | ret = -EINVAL; | ||
919 | goto out; | ||
920 | } | ||
921 | |||
922 | info->irq_batt = platform_get_irq(pdev, 1); | ||
923 | if (info->irq_batt <= 0) { | ||
924 | dev_err(&pdev->dev, "No IRQ resource!\n"); | ||
925 | ret = -EINVAL; | ||
926 | goto out; | ||
927 | } | ||
928 | |||
929 | info->chip = chip; | ||
930 | info->i2c = | ||
931 | (chip->id == CHIP_PM8607) ? chip->client : chip->companion; | ||
932 | info->dev = &pdev->dev; | ||
933 | info->status = POWER_SUPPLY_STATUS_UNKNOWN; | ||
934 | pdata = pdev->dev.platform_data; | ||
935 | |||
936 | mutex_init(&info->lock); | ||
937 | platform_set_drvdata(pdev, info); | ||
938 | |||
939 | pm860x_init_battery(info); | ||
940 | |||
941 | info->battery.name = "battery-monitor"; | ||
942 | info->battery.type = POWER_SUPPLY_TYPE_BATTERY; | ||
943 | info->battery.properties = pm860x_batt_props; | ||
944 | info->battery.num_properties = ARRAY_SIZE(pm860x_batt_props); | ||
945 | info->battery.get_property = pm860x_batt_get_prop; | ||
946 | info->battery.set_property = pm860x_batt_set_prop; | ||
947 | info->battery.external_power_changed = pm860x_external_power_changed; | ||
948 | |||
949 | if (pdata && pdata->max_capacity) | ||
950 | info->max_capacity = pdata->max_capacity; | ||
951 | else | ||
952 | info->max_capacity = 1500; /* set default capacity */ | ||
953 | if (pdata && pdata->resistor) | ||
954 | info->resistor = pdata->resistor; | ||
955 | else | ||
956 | info->resistor = 300; /* set default internal resistor */ | ||
957 | |||
958 | ret = power_supply_register(&pdev->dev, &info->battery); | ||
959 | if (ret) | ||
960 | goto out; | ||
961 | info->battery.dev->parent = &pdev->dev; | ||
962 | |||
963 | ret = request_threaded_irq(info->irq_cc, NULL, | ||
964 | pm860x_coulomb_handler, IRQF_ONESHOT, | ||
965 | "coulomb", info); | ||
966 | if (ret < 0) { | ||
967 | dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n", | ||
968 | info->irq_cc, ret); | ||
969 | goto out_reg; | ||
970 | } | ||
971 | |||
972 | ret = request_threaded_irq(info->irq_batt, NULL, pm860x_batt_handler, | ||
973 | IRQF_ONESHOT, "battery", info); | ||
974 | if (ret < 0) { | ||
975 | dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n", | ||
976 | info->irq_batt, ret); | ||
977 | goto out_coulomb; | ||
978 | } | ||
979 | |||
980 | |||
981 | return 0; | ||
982 | |||
983 | out_coulomb: | ||
984 | free_irq(info->irq_cc, info); | ||
985 | out_reg: | ||
986 | power_supply_unregister(&info->battery); | ||
987 | out: | ||
988 | kfree(info); | ||
989 | return ret; | ||
990 | } | ||
991 | |||
992 | static int __devexit pm860x_battery_remove(struct platform_device *pdev) | ||
993 | { | ||
994 | struct pm860x_battery_info *info = platform_get_drvdata(pdev); | ||
995 | |||
996 | power_supply_unregister(&info->battery); | ||
997 | free_irq(info->irq_batt, info); | ||
998 | free_irq(info->irq_cc, info); | ||
999 | kfree(info); | ||
1000 | platform_set_drvdata(pdev, NULL); | ||
1001 | return 0; | ||
1002 | } | ||
1003 | |||
1004 | #ifdef CONFIG_PM_SLEEP | ||
1005 | static int pm860x_battery_suspend(struct device *dev) | ||
1006 | { | ||
1007 | struct platform_device *pdev = to_platform_device(dev); | ||
1008 | struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent); | ||
1009 | |||
1010 | if (device_may_wakeup(dev)) | ||
1011 | chip->wakeup_flag |= 1 << PM8607_IRQ_CC; | ||
1012 | return 0; | ||
1013 | } | ||
1014 | |||
1015 | static int pm860x_battery_resume(struct device *dev) | ||
1016 | { | ||
1017 | struct platform_device *pdev = to_platform_device(dev); | ||
1018 | struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent); | ||
1019 | |||
1020 | if (device_may_wakeup(dev)) | ||
1021 | chip->wakeup_flag &= ~(1 << PM8607_IRQ_CC); | ||
1022 | return 0; | ||
1023 | } | ||
1024 | #endif | ||
1025 | |||
1026 | static SIMPLE_DEV_PM_OPS(pm860x_battery_pm_ops, | ||
1027 | pm860x_battery_suspend, pm860x_battery_resume); | ||
1028 | |||
1029 | static struct platform_driver pm860x_battery_driver = { | ||
1030 | .driver = { | ||
1031 | .name = "88pm860x-battery", | ||
1032 | .owner = THIS_MODULE, | ||
1033 | .pm = &pm860x_battery_pm_ops, | ||
1034 | }, | ||
1035 | .probe = pm860x_battery_probe, | ||
1036 | .remove = __devexit_p(pm860x_battery_remove), | ||
1037 | }; | ||
1038 | module_platform_driver(pm860x_battery_driver); | ||
1039 | |||
1040 | MODULE_DESCRIPTION("Marvell 88PM860x Battery driver"); | ||
1041 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/power/88pm860x_charger.c b/drivers/power/88pm860x_charger.c new file mode 100644 index 000000000000..2dbeb1460901 --- /dev/null +++ b/drivers/power/88pm860x_charger.c | |||
@@ -0,0 +1,746 @@ | |||
1 | /* | ||
2 | * Battery driver for Marvell 88PM860x PMIC | ||
3 | * | ||
4 | * Copyright (c) 2012 Marvell International Ltd. | ||
5 | * Author: Jett Zhou <jtzhou@marvell.com> | ||
6 | * Haojian Zhuang <haojian.zhuang@marvell.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/platform_device.h> | ||
16 | #include <linux/slab.h> | ||
17 | #include <linux/power_supply.h> | ||
18 | #include <linux/mfd/88pm860x.h> | ||
19 | #include <linux/delay.h> | ||
20 | #include <linux/uaccess.h> | ||
21 | #include <asm/div64.h> | ||
22 | |||
23 | /* bit definitions of Status Query Interface 2 */ | ||
24 | #define STATUS2_CHG (1 << 2) | ||
25 | |||
26 | /* bit definitions of Reset Out Register */ | ||
27 | #define RESET_SW_PD (1 << 7) | ||
28 | |||
29 | /* bit definitions of PreReg 1 */ | ||
30 | #define PREREG1_90MA (0x0) | ||
31 | #define PREREG1_180MA (0x1) | ||
32 | #define PREREG1_450MA (0x4) | ||
33 | #define PREREG1_540MA (0x5) | ||
34 | #define PREREG1_1350MA (0xE) | ||
35 | #define PREREG1_VSYS_4_5V (3 << 4) | ||
36 | |||
37 | /* bit definitions of Charger Control 1 Register */ | ||
38 | #define CC1_MODE_OFF (0) | ||
39 | #define CC1_MODE_PRECHARGE (1) | ||
40 | #define CC1_MODE_FASTCHARGE (2) | ||
41 | #define CC1_MODE_PULSECHARGE (3) | ||
42 | #define CC1_ITERM_20MA (0 << 2) | ||
43 | #define CC1_ITERM_60MA (2 << 2) | ||
44 | #define CC1_VFCHG_4_2V (9 << 4) | ||
45 | |||
46 | /* bit definitions of Charger Control 2 Register */ | ||
47 | #define CC2_ICHG_100MA (0x1) | ||
48 | #define CC2_ICHG_500MA (0x9) | ||
49 | #define CC2_ICHG_1000MA (0x13) | ||
50 | |||
51 | /* bit definitions of Charger Control 3 Register */ | ||
52 | #define CC3_180MIN_TIMEOUT (0x6 << 4) | ||
53 | #define CC3_270MIN_TIMEOUT (0x7 << 4) | ||
54 | #define CC3_360MIN_TIMEOUT (0xA << 4) | ||
55 | #define CC3_DISABLE_TIMEOUT (0xF << 4) | ||
56 | |||
57 | /* bit definitions of Charger Control 4 Register */ | ||
58 | #define CC4_IPRE_40MA (7) | ||
59 | #define CC4_VPCHG_3_2V (3 << 4) | ||
60 | #define CC4_IFCHG_MON_EN (1 << 6) | ||
61 | #define CC4_BTEMP_MON_EN (1 << 7) | ||
62 | |||
63 | /* bit definitions of Charger Control 6 Register */ | ||
64 | #define CC6_BAT_OV_EN (1 << 2) | ||
65 | #define CC6_BAT_UV_EN (1 << 3) | ||
66 | #define CC6_UV_VBAT_SET (0x3 << 6) /* 2.8v */ | ||
67 | |||
68 | /* bit definitions of Charger Control 7 Register */ | ||
69 | #define CC7_BAT_REM_EN (1 << 3) | ||
70 | #define CC7_IFSM_EN (1 << 7) | ||
71 | |||
72 | /* bit definitions of Measurement Enable 1 Register */ | ||
73 | #define MEAS1_VBAT (1 << 0) | ||
74 | |||
75 | /* bit definitions of Measurement Enable 3 Register */ | ||
76 | #define MEAS3_IBAT_EN (1 << 0) | ||
77 | #define MEAS3_CC_EN (1 << 2) | ||
78 | |||
79 | #define FSM_INIT 0 | ||
80 | #define FSM_DISCHARGE 1 | ||
81 | #define FSM_PRECHARGE 2 | ||
82 | #define FSM_FASTCHARGE 3 | ||
83 | |||
84 | #define PRECHARGE_THRESHOLD 3100 | ||
85 | #define POWEROFF_THRESHOLD 3400 | ||
86 | #define CHARGE_THRESHOLD 4000 | ||
87 | #define DISCHARGE_THRESHOLD 4180 | ||
88 | |||
89 | /* over-temperature on PM8606 setting */ | ||
90 | #define OVER_TEMP_FLAG (1 << 6) | ||
91 | #define OVTEMP_AUTORECOVER (1 << 3) | ||
92 | |||
93 | /* over-voltage protect on vchg setting mv */ | ||
94 | #define VCHG_NORMAL_LOW 4200 | ||
95 | #define VCHG_NORMAL_CHECK 5800 | ||
96 | #define VCHG_NORMAL_HIGH 6000 | ||
97 | #define VCHG_OVP_LOW 5500 | ||
98 | |||
99 | struct pm860x_charger_info { | ||
100 | struct pm860x_chip *chip; | ||
101 | struct i2c_client *i2c; | ||
102 | struct i2c_client *i2c_8606; | ||
103 | struct device *dev; | ||
104 | |||
105 | struct power_supply usb; | ||
106 | struct mutex lock; | ||
107 | int irq_nums; | ||
108 | int irq[7]; | ||
109 | unsigned state:3; /* fsm state */ | ||
110 | unsigned online:1; /* usb charger */ | ||
111 | unsigned present:1; /* battery present */ | ||
112 | unsigned allowed:1; | ||
113 | }; | ||
114 | |||
115 | static char *pm860x_supplied_to[] = { | ||
116 | "battery-monitor", | ||
117 | }; | ||
118 | |||
119 | static int measure_vchg(struct pm860x_charger_info *info, int *data) | ||
120 | { | ||
121 | unsigned char buf[2]; | ||
122 | int ret = 0; | ||
123 | |||
124 | ret = pm860x_bulk_read(info->i2c, PM8607_VCHG_MEAS1, 2, buf); | ||
125 | if (ret < 0) | ||
126 | return ret; | ||
127 | |||
128 | *data = ((buf[0] & 0xff) << 4) | (buf[1] & 0x0f); | ||
129 | /* V_BATT_MEAS(mV) = value * 5 * 1.8 * 1000 / (2^12) */ | ||
130 | *data = ((*data & 0xfff) * 9 * 125) >> 9; | ||
131 | |||
132 | dev_dbg(info->dev, "%s, vchg: %d mv\n", __func__, *data); | ||
133 | |||
134 | return ret; | ||
135 | } | ||
136 | |||
137 | static void set_vchg_threshold(struct pm860x_charger_info *info, | ||
138 | int min, int max) | ||
139 | { | ||
140 | int data; | ||
141 | |||
142 | /* (tmp << 8) * / 5 / 1800 */ | ||
143 | if (min <= 0) | ||
144 | data = 0; | ||
145 | else | ||
146 | data = (min << 5) / 1125; | ||
147 | pm860x_reg_write(info->i2c, PM8607_VCHG_LOWTH, data); | ||
148 | dev_dbg(info->dev, "VCHG_LOWTH:%dmv, 0x%x\n", min, data); | ||
149 | |||
150 | if (max <= 0) | ||
151 | data = 0xff; | ||
152 | else | ||
153 | data = (max << 5) / 1125; | ||
154 | pm860x_reg_write(info->i2c, PM8607_VCHG_HIGHTH, data); | ||
155 | dev_dbg(info->dev, "VCHG_HIGHTH:%dmv, 0x%x\n", max, data); | ||
156 | |||
157 | } | ||
158 | |||
159 | static void set_vbatt_threshold(struct pm860x_charger_info *info, | ||
160 | int min, int max) | ||
161 | { | ||
162 | int data; | ||
163 | |||
164 | /* (tmp << 8) * 3 / 1800 */ | ||
165 | if (min <= 0) | ||
166 | data = 0; | ||
167 | else | ||
168 | data = (min << 5) / 675; | ||
169 | pm860x_reg_write(info->i2c, PM8607_VBAT_LOWTH, data); | ||
170 | dev_dbg(info->dev, "VBAT Min:%dmv, LOWTH:0x%x\n", min, data); | ||
171 | |||
172 | if (max <= 0) | ||
173 | data = 0xff; | ||
174 | else | ||
175 | data = (max << 5) / 675; | ||
176 | pm860x_reg_write(info->i2c, PM8607_VBAT_HIGHTH, data); | ||
177 | dev_dbg(info->dev, "VBAT Max:%dmv, HIGHTH:0x%x\n", max, data); | ||
178 | |||
179 | return; | ||
180 | } | ||
181 | |||
182 | static int start_precharge(struct pm860x_charger_info *info) | ||
183 | { | ||
184 | int ret; | ||
185 | |||
186 | dev_dbg(info->dev, "Start Pre-charging!\n"); | ||
187 | set_vbatt_threshold(info, 0, 0); | ||
188 | |||
189 | ret = pm860x_reg_write(info->i2c_8606, PM8606_PREREGULATORA, | ||
190 | PREREG1_1350MA | PREREG1_VSYS_4_5V); | ||
191 | if (ret < 0) | ||
192 | goto out; | ||
193 | /* stop charging */ | ||
194 | ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL1, 3, | ||
195 | CC1_MODE_OFF); | ||
196 | if (ret < 0) | ||
197 | goto out; | ||
198 | /* set 270 minutes timeout */ | ||
199 | ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL3, (0xf << 4), | ||
200 | CC3_270MIN_TIMEOUT); | ||
201 | if (ret < 0) | ||
202 | goto out; | ||
203 | /* set precharge current, termination voltage, IBAT & TBAT monitor */ | ||
204 | ret = pm860x_reg_write(info->i2c, PM8607_CHG_CTRL4, | ||
205 | CC4_IPRE_40MA | CC4_VPCHG_3_2V | | ||
206 | CC4_IFCHG_MON_EN | CC4_BTEMP_MON_EN); | ||
207 | if (ret < 0) | ||
208 | goto out; | ||
209 | ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL7, | ||
210 | CC7_BAT_REM_EN | CC7_IFSM_EN, | ||
211 | CC7_BAT_REM_EN | CC7_IFSM_EN); | ||
212 | if (ret < 0) | ||
213 | goto out; | ||
214 | /* trigger precharge */ | ||
215 | ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL1, 3, | ||
216 | CC1_MODE_PRECHARGE); | ||
217 | out: | ||
218 | return ret; | ||
219 | } | ||
220 | |||
221 | static int start_fastcharge(struct pm860x_charger_info *info) | ||
222 | { | ||
223 | int ret; | ||
224 | |||
225 | dev_dbg(info->dev, "Start Fast-charging!\n"); | ||
226 | |||
227 | /* set fastcharge termination current & voltage, disable charging */ | ||
228 | ret = pm860x_reg_write(info->i2c, PM8607_CHG_CTRL1, | ||
229 | CC1_MODE_OFF | CC1_ITERM_60MA | | ||
230 | CC1_VFCHG_4_2V); | ||
231 | if (ret < 0) | ||
232 | goto out; | ||
233 | ret = pm860x_reg_write(info->i2c_8606, PM8606_PREREGULATORA, | ||
234 | PREREG1_540MA | PREREG1_VSYS_4_5V); | ||
235 | if (ret < 0) | ||
236 | goto out; | ||
237 | ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL2, 0x1f, | ||
238 | CC2_ICHG_500MA); | ||
239 | if (ret < 0) | ||
240 | goto out; | ||
241 | /* set 270 minutes timeout */ | ||
242 | ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL3, (0xf << 4), | ||
243 | CC3_270MIN_TIMEOUT); | ||
244 | if (ret < 0) | ||
245 | goto out; | ||
246 | /* set IBAT & TBAT monitor */ | ||
247 | ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL4, | ||
248 | CC4_IFCHG_MON_EN | CC4_BTEMP_MON_EN, | ||
249 | CC4_IFCHG_MON_EN | CC4_BTEMP_MON_EN); | ||
250 | if (ret < 0) | ||
251 | goto out; | ||
252 | ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL6, | ||
253 | CC6_BAT_OV_EN | CC6_BAT_UV_EN | | ||
254 | CC6_UV_VBAT_SET, | ||
255 | CC6_BAT_OV_EN | CC6_BAT_UV_EN | | ||
256 | CC6_UV_VBAT_SET); | ||
257 | if (ret < 0) | ||
258 | goto out; | ||
259 | ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL7, | ||
260 | CC7_BAT_REM_EN | CC7_IFSM_EN, | ||
261 | CC7_BAT_REM_EN | CC7_IFSM_EN); | ||
262 | if (ret < 0) | ||
263 | goto out; | ||
264 | /* launch fast-charge */ | ||
265 | ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL1, 3, | ||
266 | CC1_MODE_FASTCHARGE); | ||
267 | /* vchg threshold setting */ | ||
268 | set_vchg_threshold(info, VCHG_NORMAL_LOW, VCHG_NORMAL_HIGH); | ||
269 | out: | ||
270 | return ret; | ||
271 | } | ||
272 | |||
273 | static void stop_charge(struct pm860x_charger_info *info, int vbatt) | ||
274 | { | ||
275 | dev_dbg(info->dev, "Stop charging!\n"); | ||
276 | pm860x_set_bits(info->i2c, PM8607_CHG_CTRL1, 3, CC1_MODE_OFF); | ||
277 | if (vbatt > CHARGE_THRESHOLD && info->online) | ||
278 | set_vbatt_threshold(info, CHARGE_THRESHOLD, 0); | ||
279 | } | ||
280 | |||
281 | static void power_off_notification(struct pm860x_charger_info *info) | ||
282 | { | ||
283 | dev_dbg(info->dev, "Power-off notification!\n"); | ||
284 | } | ||
285 | |||
286 | static int set_charging_fsm(struct pm860x_charger_info *info) | ||
287 | { | ||
288 | struct power_supply *psy; | ||
289 | union power_supply_propval data; | ||
290 | unsigned char fsm_state[][16] = { "init", "discharge", "precharge", | ||
291 | "fastcharge", | ||
292 | }; | ||
293 | int ret; | ||
294 | int vbatt; | ||
295 | |||
296 | psy = power_supply_get_by_name(pm860x_supplied_to[0]); | ||
297 | if (!psy) | ||
298 | return -EINVAL; | ||
299 | ret = psy->get_property(psy, POWER_SUPPLY_PROP_VOLTAGE_NOW, &data); | ||
300 | if (ret) | ||
301 | return ret; | ||
302 | vbatt = data.intval / 1000; | ||
303 | |||
304 | ret = psy->get_property(psy, POWER_SUPPLY_PROP_PRESENT, &data); | ||
305 | if (ret) | ||
306 | return ret; | ||
307 | |||
308 | mutex_lock(&info->lock); | ||
309 | info->present = data.intval; | ||
310 | |||
311 | dev_dbg(info->dev, "Entering FSM:%s, Charger:%s, Battery:%s, " | ||
312 | "Allowed:%d\n", | ||
313 | &fsm_state[info->state][0], | ||
314 | (info->online) ? "online" : "N/A", | ||
315 | (info->present) ? "present" : "N/A", info->allowed); | ||
316 | dev_dbg(info->dev, "set_charging_fsm:vbatt:%d(mV)\n", vbatt); | ||
317 | |||
318 | switch (info->state) { | ||
319 | case FSM_INIT: | ||
320 | if (info->online && info->present && info->allowed) { | ||
321 | if (vbatt < PRECHARGE_THRESHOLD) { | ||
322 | info->state = FSM_PRECHARGE; | ||
323 | start_precharge(info); | ||
324 | } else if (vbatt > DISCHARGE_THRESHOLD) { | ||
325 | info->state = FSM_DISCHARGE; | ||
326 | stop_charge(info, vbatt); | ||
327 | } else if (vbatt < DISCHARGE_THRESHOLD) { | ||
328 | info->state = FSM_FASTCHARGE; | ||
329 | start_fastcharge(info); | ||
330 | } | ||
331 | } else { | ||
332 | if (vbatt < POWEROFF_THRESHOLD) { | ||
333 | power_off_notification(info); | ||
334 | } else { | ||
335 | info->state = FSM_DISCHARGE; | ||
336 | stop_charge(info, vbatt); | ||
337 | } | ||
338 | } | ||
339 | break; | ||
340 | case FSM_PRECHARGE: | ||
341 | if (info->online && info->present && info->allowed) { | ||
342 | if (vbatt > PRECHARGE_THRESHOLD) { | ||
343 | info->state = FSM_FASTCHARGE; | ||
344 | start_fastcharge(info); | ||
345 | } | ||
346 | } else { | ||
347 | info->state = FSM_DISCHARGE; | ||
348 | stop_charge(info, vbatt); | ||
349 | } | ||
350 | break; | ||
351 | case FSM_FASTCHARGE: | ||
352 | if (info->online && info->present && info->allowed) { | ||
353 | if (vbatt < PRECHARGE_THRESHOLD) { | ||
354 | info->state = FSM_PRECHARGE; | ||
355 | start_precharge(info); | ||
356 | } | ||
357 | } else { | ||
358 | info->state = FSM_DISCHARGE; | ||
359 | stop_charge(info, vbatt); | ||
360 | } | ||
361 | break; | ||
362 | case FSM_DISCHARGE: | ||
363 | if (info->online && info->present && info->allowed) { | ||
364 | if (vbatt < PRECHARGE_THRESHOLD) { | ||
365 | info->state = FSM_PRECHARGE; | ||
366 | start_precharge(info); | ||
367 | } else if (vbatt < DISCHARGE_THRESHOLD) { | ||
368 | info->state = FSM_FASTCHARGE; | ||
369 | start_fastcharge(info); | ||
370 | } | ||
371 | } else { | ||
372 | if (vbatt < POWEROFF_THRESHOLD) | ||
373 | power_off_notification(info); | ||
374 | else if (vbatt > CHARGE_THRESHOLD && info->online) | ||
375 | set_vbatt_threshold(info, CHARGE_THRESHOLD, 0); | ||
376 | } | ||
377 | break; | ||
378 | default: | ||
379 | dev_warn(info->dev, "FSM meets wrong state:%d\n", | ||
380 | info->state); | ||
381 | break; | ||
382 | } | ||
383 | dev_dbg(info->dev, | ||
384 | "Out FSM:%s, Charger:%s, Battery:%s, Allowed:%d\n", | ||
385 | &fsm_state[info->state][0], | ||
386 | (info->online) ? "online" : "N/A", | ||
387 | (info->present) ? "present" : "N/A", info->allowed); | ||
388 | mutex_unlock(&info->lock); | ||
389 | |||
390 | return 0; | ||
391 | } | ||
392 | |||
393 | static irqreturn_t pm860x_charger_handler(int irq, void *data) | ||
394 | { | ||
395 | struct pm860x_charger_info *info = data; | ||
396 | int ret; | ||
397 | |||
398 | mutex_lock(&info->lock); | ||
399 | ret = pm860x_reg_read(info->i2c, PM8607_STATUS_2); | ||
400 | if (ret < 0) { | ||
401 | mutex_unlock(&info->lock); | ||
402 | goto out; | ||
403 | } | ||
404 | if (ret & STATUS2_CHG) { | ||
405 | info->online = 1; | ||
406 | info->allowed = 1; | ||
407 | } else { | ||
408 | info->online = 0; | ||
409 | info->allowed = 0; | ||
410 | } | ||
411 | mutex_unlock(&info->lock); | ||
412 | dev_dbg(info->dev, "%s, Charger:%s, Allowed:%d\n", __func__, | ||
413 | (info->online) ? "online" : "N/A", info->allowed); | ||
414 | |||
415 | set_charging_fsm(info); | ||
416 | |||
417 | power_supply_changed(&info->usb); | ||
418 | out: | ||
419 | return IRQ_HANDLED; | ||
420 | } | ||
421 | |||
422 | static irqreturn_t pm860x_temp_handler(int irq, void *data) | ||
423 | { | ||
424 | struct power_supply *psy; | ||
425 | struct pm860x_charger_info *info = data; | ||
426 | union power_supply_propval temp; | ||
427 | int value; | ||
428 | int ret; | ||
429 | |||
430 | psy = power_supply_get_by_name(pm860x_supplied_to[0]); | ||
431 | if (!psy) | ||
432 | goto out; | ||
433 | ret = psy->get_property(psy, POWER_SUPPLY_PROP_TEMP, &temp); | ||
434 | if (ret) | ||
435 | goto out; | ||
436 | value = temp.intval / 10; | ||
437 | |||
438 | mutex_lock(&info->lock); | ||
439 | /* Temperature < -10 C or >40 C, Will not allow charge */ | ||
440 | if (value < -10 || value > 40) | ||
441 | info->allowed = 0; | ||
442 | else | ||
443 | info->allowed = 1; | ||
444 | dev_dbg(info->dev, "%s, Allowed: %d\n", __func__, info->allowed); | ||
445 | mutex_unlock(&info->lock); | ||
446 | |||
447 | set_charging_fsm(info); | ||
448 | out: | ||
449 | return IRQ_HANDLED; | ||
450 | } | ||
451 | |||
452 | static irqreturn_t pm860x_exception_handler(int irq, void *data) | ||
453 | { | ||
454 | struct pm860x_charger_info *info = data; | ||
455 | |||
456 | mutex_lock(&info->lock); | ||
457 | info->allowed = 0; | ||
458 | mutex_unlock(&info->lock); | ||
459 | dev_dbg(info->dev, "%s, irq: %d\n", __func__, irq); | ||
460 | |||
461 | set_charging_fsm(info); | ||
462 | return IRQ_HANDLED; | ||
463 | } | ||
464 | |||
465 | static irqreturn_t pm860x_done_handler(int irq, void *data) | ||
466 | { | ||
467 | struct pm860x_charger_info *info = data; | ||
468 | struct power_supply *psy; | ||
469 | union power_supply_propval val; | ||
470 | int ret; | ||
471 | int vbatt; | ||
472 | |||
473 | mutex_lock(&info->lock); | ||
474 | /* pre-charge done, will transimit to fast-charge stage */ | ||
475 | if (info->state == FSM_PRECHARGE) { | ||
476 | info->allowed = 1; | ||
477 | goto out; | ||
478 | } | ||
479 | /* | ||
480 | * Fast charge done, delay to read | ||
481 | * the correct status of CHG_DET. | ||
482 | */ | ||
483 | mdelay(5); | ||
484 | info->allowed = 0; | ||
485 | psy = power_supply_get_by_name(pm860x_supplied_to[0]); | ||
486 | if (!psy) | ||
487 | goto out; | ||
488 | ret = psy->get_property(psy, POWER_SUPPLY_PROP_VOLTAGE_NOW, &val); | ||
489 | if (ret) | ||
490 | goto out; | ||
491 | vbatt = val.intval / 1000; | ||
492 | /* | ||
493 | * CHG_DONE interrupt is faster than CHG_DET interrupt when | ||
494 | * plug in/out usb, So we can not rely on info->online, we | ||
495 | * need check pm8607 status register to check usb is online | ||
496 | * or not, then we can decide it is real charge done | ||
497 | * automatically or it is triggered by usb plug out; | ||
498 | */ | ||
499 | ret = pm860x_reg_read(info->i2c, PM8607_STATUS_2); | ||
500 | if (ret < 0) | ||
501 | goto out; | ||
502 | if (vbatt > CHARGE_THRESHOLD && ret & STATUS2_CHG) | ||
503 | psy->set_property(psy, POWER_SUPPLY_PROP_CHARGE_FULL, &val); | ||
504 | |||
505 | out: | ||
506 | mutex_unlock(&info->lock); | ||
507 | dev_dbg(info->dev, "%s, Allowed: %d\n", __func__, info->allowed); | ||
508 | set_charging_fsm(info); | ||
509 | |||
510 | return IRQ_HANDLED; | ||
511 | } | ||
512 | |||
513 | static irqreturn_t pm860x_vbattery_handler(int irq, void *data) | ||
514 | { | ||
515 | struct pm860x_charger_info *info = data; | ||
516 | |||
517 | mutex_lock(&info->lock); | ||
518 | |||
519 | set_vbatt_threshold(info, 0, 0); | ||
520 | |||
521 | if (info->present && info->online) | ||
522 | info->allowed = 1; | ||
523 | else | ||
524 | info->allowed = 0; | ||
525 | mutex_unlock(&info->lock); | ||
526 | dev_dbg(info->dev, "%s, Allowed: %d\n", __func__, info->allowed); | ||
527 | |||
528 | set_charging_fsm(info); | ||
529 | |||
530 | return IRQ_HANDLED; | ||
531 | } | ||
532 | |||
533 | static irqreturn_t pm860x_vchg_handler(int irq, void *data) | ||
534 | { | ||
535 | struct pm860x_charger_info *info = data; | ||
536 | int vchg = 0; | ||
537 | |||
538 | if (info->present) | ||
539 | goto out; | ||
540 | |||
541 | measure_vchg(info, &vchg); | ||
542 | |||
543 | mutex_lock(&info->lock); | ||
544 | if (!info->online) { | ||
545 | int status; | ||
546 | /* check if over-temp on pm8606 or not */ | ||
547 | status = pm860x_reg_read(info->i2c_8606, PM8606_FLAGS); | ||
548 | if (status & OVER_TEMP_FLAG) { | ||
549 | /* clear over temp flag and set auto recover */ | ||
550 | pm860x_set_bits(info->i2c_8606, PM8606_FLAGS, | ||
551 | OVER_TEMP_FLAG, OVER_TEMP_FLAG); | ||
552 | pm860x_set_bits(info->i2c_8606, | ||
553 | PM8606_VSYS, | ||
554 | OVTEMP_AUTORECOVER, | ||
555 | OVTEMP_AUTORECOVER); | ||
556 | dev_dbg(info->dev, | ||
557 | "%s, pm8606 over-temp occure\n", __func__); | ||
558 | } | ||
559 | } | ||
560 | |||
561 | if (vchg > VCHG_NORMAL_CHECK) { | ||
562 | set_vchg_threshold(info, VCHG_OVP_LOW, 0); | ||
563 | info->allowed = 0; | ||
564 | dev_dbg(info->dev, | ||
565 | "%s,pm8607 over-vchg occure,vchg = %dmv\n", | ||
566 | __func__, vchg); | ||
567 | } else if (vchg < VCHG_OVP_LOW) { | ||
568 | set_vchg_threshold(info, VCHG_NORMAL_LOW, | ||
569 | VCHG_NORMAL_HIGH); | ||
570 | info->allowed = 1; | ||
571 | dev_dbg(info->dev, | ||
572 | "%s,pm8607 over-vchg recover,vchg = %dmv\n", | ||
573 | __func__, vchg); | ||
574 | } | ||
575 | mutex_unlock(&info->lock); | ||
576 | |||
577 | dev_dbg(info->dev, "%s, Allowed: %d\n", __func__, info->allowed); | ||
578 | set_charging_fsm(info); | ||
579 | out: | ||
580 | return IRQ_HANDLED; | ||
581 | } | ||
582 | |||
583 | static int pm860x_usb_get_prop(struct power_supply *psy, | ||
584 | enum power_supply_property psp, | ||
585 | union power_supply_propval *val) | ||
586 | { | ||
587 | struct pm860x_charger_info *info = | ||
588 | dev_get_drvdata(psy->dev->parent); | ||
589 | |||
590 | switch (psp) { | ||
591 | case POWER_SUPPLY_PROP_STATUS: | ||
592 | if (info->state == FSM_FASTCHARGE || | ||
593 | info->state == FSM_PRECHARGE) | ||
594 | val->intval = POWER_SUPPLY_STATUS_CHARGING; | ||
595 | else | ||
596 | val->intval = POWER_SUPPLY_STATUS_DISCHARGING; | ||
597 | break; | ||
598 | case POWER_SUPPLY_PROP_ONLINE: | ||
599 | val->intval = info->online; | ||
600 | break; | ||
601 | default: | ||
602 | return -ENODEV; | ||
603 | } | ||
604 | return 0; | ||
605 | } | ||
606 | |||
607 | static enum power_supply_property pm860x_usb_props[] = { | ||
608 | POWER_SUPPLY_PROP_STATUS, | ||
609 | POWER_SUPPLY_PROP_ONLINE, | ||
610 | }; | ||
611 | |||
612 | static int pm860x_init_charger(struct pm860x_charger_info *info) | ||
613 | { | ||
614 | int ret; | ||
615 | |||
616 | ret = pm860x_reg_read(info->i2c, PM8607_STATUS_2); | ||
617 | if (ret < 0) | ||
618 | return ret; | ||
619 | |||
620 | mutex_lock(&info->lock); | ||
621 | info->state = FSM_INIT; | ||
622 | if (ret & STATUS2_CHG) { | ||
623 | info->online = 1; | ||
624 | info->allowed = 1; | ||
625 | } else { | ||
626 | info->online = 0; | ||
627 | info->allowed = 0; | ||
628 | } | ||
629 | mutex_unlock(&info->lock); | ||
630 | |||
631 | set_charging_fsm(info); | ||
632 | return 0; | ||
633 | } | ||
634 | |||
635 | static struct pm860x_irq_desc { | ||
636 | const char *name; | ||
637 | irqreturn_t (*handler)(int irq, void *data); | ||
638 | } pm860x_irq_descs[] = { | ||
639 | { "usb supply detect", pm860x_charger_handler }, | ||
640 | { "charge done", pm860x_done_handler }, | ||
641 | { "charge timeout", pm860x_exception_handler }, | ||
642 | { "charge fault", pm860x_exception_handler }, | ||
643 | { "temperature", pm860x_temp_handler }, | ||
644 | { "vbatt", pm860x_vbattery_handler }, | ||
645 | { "vchg", pm860x_vchg_handler }, | ||
646 | }; | ||
647 | |||
648 | static __devinit int pm860x_charger_probe(struct platform_device *pdev) | ||
649 | { | ||
650 | struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent); | ||
651 | struct pm860x_charger_info *info; | ||
652 | int ret; | ||
653 | int count; | ||
654 | int i; | ||
655 | int j; | ||
656 | |||
657 | info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); | ||
658 | if (!info) | ||
659 | return -ENOMEM; | ||
660 | |||
661 | count = pdev->num_resources; | ||
662 | for (i = 0, j = 0; i < count; i++) { | ||
663 | info->irq[j] = platform_get_irq(pdev, i); | ||
664 | if (info->irq[j] < 0) | ||
665 | continue; | ||
666 | j++; | ||
667 | } | ||
668 | info->irq_nums = j; | ||
669 | |||
670 | info->chip = chip; | ||
671 | info->i2c = | ||
672 | (chip->id == CHIP_PM8607) ? chip->client : chip->companion; | ||
673 | info->i2c_8606 = | ||
674 | (chip->id == CHIP_PM8607) ? chip->companion : chip->client; | ||
675 | if (!info->i2c_8606) { | ||
676 | dev_err(&pdev->dev, "Missed I2C address of 88PM8606!\n"); | ||
677 | ret = -EINVAL; | ||
678 | goto out; | ||
679 | } | ||
680 | info->dev = &pdev->dev; | ||
681 | |||
682 | /* set init value for the case we are not using battery */ | ||
683 | set_vchg_threshold(info, VCHG_NORMAL_LOW, VCHG_OVP_LOW); | ||
684 | |||
685 | mutex_init(&info->lock); | ||
686 | platform_set_drvdata(pdev, info); | ||
687 | |||
688 | info->usb.name = "usb"; | ||
689 | info->usb.type = POWER_SUPPLY_TYPE_USB; | ||
690 | info->usb.supplied_to = pm860x_supplied_to; | ||
691 | info->usb.num_supplicants = ARRAY_SIZE(pm860x_supplied_to); | ||
692 | info->usb.properties = pm860x_usb_props; | ||
693 | info->usb.num_properties = ARRAY_SIZE(pm860x_usb_props); | ||
694 | info->usb.get_property = pm860x_usb_get_prop; | ||
695 | ret = power_supply_register(&pdev->dev, &info->usb); | ||
696 | if (ret) | ||
697 | goto out; | ||
698 | |||
699 | pm860x_init_charger(info); | ||
700 | |||
701 | for (i = 0; i < ARRAY_SIZE(info->irq); i++) { | ||
702 | ret = request_threaded_irq(info->irq[i], NULL, | ||
703 | pm860x_irq_descs[i].handler, | ||
704 | IRQF_ONESHOT, pm860x_irq_descs[i].name, info); | ||
705 | if (ret < 0) { | ||
706 | dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n", | ||
707 | info->irq[i], ret); | ||
708 | goto out_irq; | ||
709 | } | ||
710 | } | ||
711 | return 0; | ||
712 | |||
713 | out_irq: | ||
714 | while (--i >= 0) | ||
715 | free_irq(info->irq[i], info); | ||
716 | out: | ||
717 | kfree(info); | ||
718 | return ret; | ||
719 | } | ||
720 | |||
721 | static int __devexit pm860x_charger_remove(struct platform_device *pdev) | ||
722 | { | ||
723 | struct pm860x_charger_info *info = platform_get_drvdata(pdev); | ||
724 | int i; | ||
725 | |||
726 | platform_set_drvdata(pdev, NULL); | ||
727 | power_supply_unregister(&info->usb); | ||
728 | free_irq(info->irq[0], info); | ||
729 | for (i = 0; i < info->irq_nums; i++) | ||
730 | free_irq(info->irq[i], info); | ||
731 | kfree(info); | ||
732 | return 0; | ||
733 | } | ||
734 | |||
735 | static struct platform_driver pm860x_charger_driver = { | ||
736 | .driver = { | ||
737 | .name = "88pm860x-charger", | ||
738 | .owner = THIS_MODULE, | ||
739 | }, | ||
740 | .probe = pm860x_charger_probe, | ||
741 | .remove = __devexit_p(pm860x_charger_remove), | ||
742 | }; | ||
743 | module_platform_driver(pm860x_charger_driver); | ||
744 | |||
745 | MODULE_DESCRIPTION("Marvell 88PM860x Charger driver"); | ||
746 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 80978196aae8..49a893972318 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig | |||
@@ -69,6 +69,12 @@ config TEST_POWER | |||
69 | help | 69 | help |
70 | This driver is used for testing. It's safe to say M here. | 70 | This driver is used for testing. It's safe to say M here. |
71 | 71 | ||
72 | config BATTERY_88PM860X | ||
73 | tristate "Marvell 88PM860x battery driver" | ||
74 | depends on MFD_88PM860X | ||
75 | help | ||
76 | Say Y here to enable battery monitor for Marvell 88PM860x chip. | ||
77 | |||
72 | config BATTERY_DS2760 | 78 | config BATTERY_DS2760 |
73 | tristate "DS2760 battery driver (HP iPAQ & others)" | 79 | tristate "DS2760 battery driver (HP iPAQ & others)" |
74 | depends on W1 && W1_SLAVE_DS2760 | 80 | depends on W1 && W1_SLAVE_DS2760 |
@@ -174,7 +180,6 @@ config BATTERY_DA9030 | |||
174 | config BATTERY_DA9052 | 180 | config BATTERY_DA9052 |
175 | tristate "Dialog DA9052 Battery" | 181 | tristate "Dialog DA9052 Battery" |
176 | depends on PMIC_DA9052 | 182 | depends on PMIC_DA9052 |
177 | depends on BROKEN | ||
178 | help | 183 | help |
179 | Say Y here to enable support for batteries charger integrated into | 184 | Say Y here to enable support for batteries charger integrated into |
180 | DA9052 PMIC. | 185 | DA9052 PMIC. |
@@ -210,6 +215,12 @@ config BATTERY_S3C_ADC | |||
210 | help | 215 | help |
211 | Say Y here to enable support for iPAQ h1930/h1940/rx1950 battery | 216 | Say Y here to enable support for iPAQ h1930/h1940/rx1950 battery |
212 | 217 | ||
218 | config CHARGER_88PM860X | ||
219 | tristate "Marvell 88PM860x Charger driver" | ||
220 | depends on MFD_88PM860X && BATTERY_88PM860X | ||
221 | help | ||
222 | Say Y here to enable charger for Marvell 88PM860x chip. | ||
223 | |||
213 | config CHARGER_PCF50633 | 224 | config CHARGER_PCF50633 |
214 | tristate "NXP PCF50633 MBC" | 225 | tristate "NXP PCF50633 MBC" |
215 | depends on MFD_PCF50633 | 226 | depends on MFD_PCF50633 |
@@ -262,6 +273,13 @@ config CHARGER_LP8727 | |||
262 | help | 273 | help |
263 | Say Y here to enable support for LP8727 Charger Driver. | 274 | Say Y here to enable support for LP8727 Charger Driver. |
264 | 275 | ||
276 | config CHARGER_LP8788 | ||
277 | tristate "TI LP8788 charger driver" | ||
278 | depends on MFD_LP8788 | ||
279 | depends on LP8788_ADC | ||
280 | help | ||
281 | Say Y to enable support for the LP8788 linear charger. | ||
282 | |||
265 | config CHARGER_GPIO | 283 | config CHARGER_GPIO |
266 | tristate "GPIO charger" | 284 | tristate "GPIO charger" |
267 | depends on GPIOLIB | 285 | depends on GPIOLIB |
diff --git a/drivers/power/Makefile b/drivers/power/Makefile index e0b4d4284e1d..b949cf85590c 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile | |||
@@ -15,6 +15,7 @@ obj-$(CONFIG_WM831X_POWER) += wm831x_power.o | |||
15 | obj-$(CONFIG_WM8350_POWER) += wm8350_power.o | 15 | obj-$(CONFIG_WM8350_POWER) += wm8350_power.o |
16 | obj-$(CONFIG_TEST_POWER) += test_power.o | 16 | obj-$(CONFIG_TEST_POWER) += test_power.o |
17 | 17 | ||
18 | obj-$(CONFIG_BATTERY_88PM860X) += 88pm860x_battery.o | ||
18 | obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o | 19 | obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o |
19 | obj-$(CONFIG_BATTERY_DS2780) += ds2780_battery.o | 20 | obj-$(CONFIG_BATTERY_DS2780) += ds2780_battery.o |
20 | obj-$(CONFIG_BATTERY_DS2781) += ds2781_battery.o | 21 | obj-$(CONFIG_BATTERY_DS2781) += ds2781_battery.o |
@@ -32,6 +33,7 @@ obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o | |||
32 | obj-$(CONFIG_BATTERY_MAX17042) += max17042_battery.o | 33 | obj-$(CONFIG_BATTERY_MAX17042) += max17042_battery.o |
33 | obj-$(CONFIG_BATTERY_Z2) += z2_battery.o | 34 | obj-$(CONFIG_BATTERY_Z2) += z2_battery.o |
34 | obj-$(CONFIG_BATTERY_S3C_ADC) += s3c_adc_battery.o | 35 | obj-$(CONFIG_BATTERY_S3C_ADC) += s3c_adc_battery.o |
36 | obj-$(CONFIG_CHARGER_88PM860X) += 88pm860x_charger.o | ||
35 | obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o | 37 | obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o |
36 | obj-$(CONFIG_BATTERY_JZ4740) += jz4740-battery.o | 38 | obj-$(CONFIG_BATTERY_JZ4740) += jz4740-battery.o |
37 | obj-$(CONFIG_BATTERY_INTEL_MID) += intel_mid_battery.o | 39 | obj-$(CONFIG_BATTERY_INTEL_MID) += intel_mid_battery.o |
@@ -40,6 +42,7 @@ obj-$(CONFIG_CHARGER_ISP1704) += isp1704_charger.o | |||
40 | obj-$(CONFIG_CHARGER_MAX8903) += max8903_charger.o | 42 | obj-$(CONFIG_CHARGER_MAX8903) += max8903_charger.o |
41 | obj-$(CONFIG_CHARGER_TWL4030) += twl4030_charger.o | 43 | obj-$(CONFIG_CHARGER_TWL4030) += twl4030_charger.o |
42 | obj-$(CONFIG_CHARGER_LP8727) += lp8727_charger.o | 44 | obj-$(CONFIG_CHARGER_LP8727) += lp8727_charger.o |
45 | obj-$(CONFIG_CHARGER_LP8788) += lp8788-charger.o | ||
43 | obj-$(CONFIG_CHARGER_GPIO) += gpio-charger.o | 46 | obj-$(CONFIG_CHARGER_GPIO) += gpio-charger.o |
44 | obj-$(CONFIG_CHARGER_MANAGER) += charger-manager.o | 47 | obj-$(CONFIG_CHARGER_MANAGER) += charger-manager.o |
45 | obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o | 48 | obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o |
diff --git a/drivers/power/ab8500_btemp.c b/drivers/power/ab8500_btemp.c index 3041514f4d3f..e3b6395b20dd 100644 --- a/drivers/power/ab8500_btemp.c +++ b/drivers/power/ab8500_btemp.c | |||
@@ -1014,6 +1014,7 @@ static int __devinit ab8500_btemp_probe(struct platform_device *pdev) | |||
1014 | create_singlethread_workqueue("ab8500_btemp_wq"); | 1014 | create_singlethread_workqueue("ab8500_btemp_wq"); |
1015 | if (di->btemp_wq == NULL) { | 1015 | if (di->btemp_wq == NULL) { |
1016 | dev_err(di->dev, "failed to create work queue\n"); | 1016 | dev_err(di->dev, "failed to create work queue\n"); |
1017 | ret = -ENOMEM; | ||
1017 | goto free_device_info; | 1018 | goto free_device_info; |
1018 | } | 1019 | } |
1019 | 1020 | ||
diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c index 0701dbc2b7e1..26ff759e2220 100644 --- a/drivers/power/ab8500_charger.c +++ b/drivers/power/ab8500_charger.c | |||
@@ -2614,6 +2614,7 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev) | |||
2614 | create_singlethread_workqueue("ab8500_charger_wq"); | 2614 | create_singlethread_workqueue("ab8500_charger_wq"); |
2615 | if (di->charger_wq == NULL) { | 2615 | if (di->charger_wq == NULL) { |
2616 | dev_err(di->dev, "failed to create work queue\n"); | 2616 | dev_err(di->dev, "failed to create work queue\n"); |
2617 | ret = -ENOMEM; | ||
2617 | goto free_device_info; | 2618 | goto free_device_info; |
2618 | } | 2619 | } |
2619 | 2620 | ||
diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c index 5c9e7c263c38..2db8cc254399 100644 --- a/drivers/power/ab8500_fg.c +++ b/drivers/power/ab8500_fg.c | |||
@@ -2506,6 +2506,7 @@ static int __devinit ab8500_fg_probe(struct platform_device *pdev) | |||
2506 | di->fg_wq = create_singlethread_workqueue("ab8500_fg_wq"); | 2506 | di->fg_wq = create_singlethread_workqueue("ab8500_fg_wq"); |
2507 | if (di->fg_wq == NULL) { | 2507 | if (di->fg_wq == NULL) { |
2508 | dev_err(di->dev, "failed to create work queue\n"); | 2508 | dev_err(di->dev, "failed to create work queue\n"); |
2509 | ret = -ENOMEM; | ||
2509 | goto free_device_info; | 2510 | goto free_device_info; |
2510 | } | 2511 | } |
2511 | 2512 | ||
diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index 181ddece5181..5860d4dfbe9c 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c | |||
@@ -814,7 +814,8 @@ static int bq27x00_battery_probe(struct i2c_client *client, | |||
814 | di->bat.name = name; | 814 | di->bat.name = name; |
815 | di->bus.read = &bq27x00_read_i2c; | 815 | di->bus.read = &bq27x00_read_i2c; |
816 | 816 | ||
817 | if (bq27x00_powersupply_init(di)) | 817 | retval = bq27x00_powersupply_init(di); |
818 | if (retval) | ||
818 | goto batt_failed_3; | 819 | goto batt_failed_3; |
819 | 820 | ||
820 | i2c_set_clientdata(client, di); | 821 | i2c_set_clientdata(client, di); |
diff --git a/drivers/power/charger-manager.c b/drivers/power/charger-manager.c index 7ff83cf43c8c..8a0aca6364c7 100644 --- a/drivers/power/charger-manager.c +++ b/drivers/power/charger-manager.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include <linux/platform_device.h> | 22 | #include <linux/platform_device.h> |
23 | #include <linux/power/charger-manager.h> | 23 | #include <linux/power/charger-manager.h> |
24 | #include <linux/regulator/consumer.h> | 24 | #include <linux/regulator/consumer.h> |
25 | #include <linux/sysfs.h> | ||
25 | 26 | ||
26 | static const char * const default_event_names[] = { | 27 | static const char * const default_event_names[] = { |
27 | [CM_EVENT_UNKNOWN] = "Unknown", | 28 | [CM_EVENT_UNKNOWN] = "Unknown", |
@@ -227,6 +228,58 @@ static bool is_charging(struct charger_manager *cm) | |||
227 | } | 228 | } |
228 | 229 | ||
229 | /** | 230 | /** |
231 | * is_full_charged - Returns true if the battery is fully charged. | ||
232 | * @cm: the Charger Manager representing the battery. | ||
233 | */ | ||
234 | static bool is_full_charged(struct charger_manager *cm) | ||
235 | { | ||
236 | struct charger_desc *desc = cm->desc; | ||
237 | union power_supply_propval val; | ||
238 | int ret = 0; | ||
239 | int uV; | ||
240 | |||
241 | /* If there is no battery, it cannot be charged */ | ||
242 | if (!is_batt_present(cm)) { | ||
243 | val.intval = 0; | ||
244 | goto out; | ||
245 | } | ||
246 | |||
247 | if (cm->fuel_gauge && desc->fullbatt_full_capacity > 0) { | ||
248 | /* Not full if capacity of fuel gauge isn't full */ | ||
249 | ret = cm->fuel_gauge->get_property(cm->fuel_gauge, | ||
250 | POWER_SUPPLY_PROP_CHARGE_FULL, &val); | ||
251 | if (!ret && val.intval > desc->fullbatt_full_capacity) { | ||
252 | val.intval = 1; | ||
253 | goto out; | ||
254 | } | ||
255 | } | ||
256 | |||
257 | /* Full, if it's over the fullbatt voltage */ | ||
258 | if (desc->fullbatt_uV > 0) { | ||
259 | ret = get_batt_uV(cm, &uV); | ||
260 | if (!ret && uV >= desc->fullbatt_uV) { | ||
261 | val.intval = 1; | ||
262 | goto out; | ||
263 | } | ||
264 | } | ||
265 | |||
266 | /* Full, if the capacity is more than fullbatt_soc */ | ||
267 | if (cm->fuel_gauge && desc->fullbatt_soc > 0) { | ||
268 | ret = cm->fuel_gauge->get_property(cm->fuel_gauge, | ||
269 | POWER_SUPPLY_PROP_CAPACITY, &val); | ||
270 | if (!ret && val.intval >= desc->fullbatt_soc) { | ||
271 | val.intval = 1; | ||
272 | goto out; | ||
273 | } | ||
274 | } | ||
275 | |||
276 | val.intval = 0; | ||
277 | |||
278 | out: | ||
279 | return val.intval ? true : false; | ||
280 | } | ||
281 | |||
282 | /** | ||
230 | * is_polling_required - Return true if need to continue polling for this CM. | 283 | * is_polling_required - Return true if need to continue polling for this CM. |
231 | * @cm: the Charger Manager representing the battery. | 284 | * @cm: the Charger Manager representing the battery. |
232 | */ | 285 | */ |
@@ -271,10 +324,46 @@ static int try_charger_enable(struct charger_manager *cm, bool enable) | |||
271 | if (enable) { | 324 | if (enable) { |
272 | if (cm->emergency_stop) | 325 | if (cm->emergency_stop) |
273 | return -EAGAIN; | 326 | return -EAGAIN; |
274 | for (i = 0 ; i < desc->num_charger_regulators ; i++) | 327 | |
275 | regulator_enable(desc->charger_regulators[i].consumer); | 328 | /* |
329 | * Save start time of charging to limit | ||
330 | * maximum possible charging time. | ||
331 | */ | ||
332 | cm->charging_start_time = ktime_to_ms(ktime_get()); | ||
333 | cm->charging_end_time = 0; | ||
334 | |||
335 | for (i = 0 ; i < desc->num_charger_regulators ; i++) { | ||
336 | if (desc->charger_regulators[i].externally_control) | ||
337 | continue; | ||
338 | |||
339 | err = regulator_enable(desc->charger_regulators[i].consumer); | ||
340 | if (err < 0) { | ||
341 | dev_warn(cm->dev, | ||
342 | "Cannot enable %s regulator\n", | ||
343 | desc->charger_regulators[i].regulator_name); | ||
344 | } | ||
345 | } | ||
276 | } else { | 346 | } else { |
277 | /* | 347 | /* |
348 | * Save end time of charging to maintain fully charged state | ||
349 | * of battery after full-batt. | ||
350 | */ | ||
351 | cm->charging_start_time = 0; | ||
352 | cm->charging_end_time = ktime_to_ms(ktime_get()); | ||
353 | |||
354 | for (i = 0 ; i < desc->num_charger_regulators ; i++) { | ||
355 | if (desc->charger_regulators[i].externally_control) | ||
356 | continue; | ||
357 | |||
358 | err = regulator_disable(desc->charger_regulators[i].consumer); | ||
359 | if (err < 0) { | ||
360 | dev_warn(cm->dev, | ||
361 | "Cannot disable %s regulator\n", | ||
362 | desc->charger_regulators[i].regulator_name); | ||
363 | } | ||
364 | } | ||
365 | |||
366 | /* | ||
278 | * Abnormal battery state - Stop charging forcibly, | 367 | * Abnormal battery state - Stop charging forcibly, |
279 | * even if charger was enabled at the other places | 368 | * even if charger was enabled at the other places |
280 | */ | 369 | */ |
@@ -400,15 +489,62 @@ static void fullbatt_vchk(struct work_struct *work) | |||
400 | return; | 489 | return; |
401 | } | 490 | } |
402 | 491 | ||
403 | diff = cm->fullbatt_vchk_uV; | 492 | diff = desc->fullbatt_uV; |
404 | diff -= batt_uV; | 493 | diff -= batt_uV; |
405 | 494 | ||
406 | dev_dbg(cm->dev, "VBATT dropped %duV after full-batt.\n", diff); | 495 | dev_info(cm->dev, "VBATT dropped %duV after full-batt.\n", diff); |
407 | 496 | ||
408 | if (diff > desc->fullbatt_vchkdrop_uV) { | 497 | if (diff > desc->fullbatt_vchkdrop_uV) { |
409 | try_charger_restart(cm); | 498 | try_charger_restart(cm); |
410 | uevent_notify(cm, "Recharge"); | 499 | uevent_notify(cm, "Recharging"); |
500 | } | ||
501 | } | ||
502 | |||
503 | /** | ||
504 | * check_charging_duration - Monitor charging/discharging duration | ||
505 | * @cm: the Charger Manager representing the battery. | ||
506 | * | ||
507 | * If whole charging duration exceed 'charging_max_duration_ms', | ||
508 | * cm stop charging to prevent overcharge/overheat. If discharging | ||
509 | * duration exceed 'discharging _max_duration_ms', charger cable is | ||
510 | * attached, after full-batt, cm start charging to maintain fully | ||
511 | * charged state for battery. | ||
512 | */ | ||
513 | static int check_charging_duration(struct charger_manager *cm) | ||
514 | { | ||
515 | struct charger_desc *desc = cm->desc; | ||
516 | u64 curr = ktime_to_ms(ktime_get()); | ||
517 | u64 duration; | ||
518 | int ret = false; | ||
519 | |||
520 | if (!desc->charging_max_duration_ms && | ||
521 | !desc->discharging_max_duration_ms) | ||
522 | return ret; | ||
523 | |||
524 | if (cm->charger_enabled) { | ||
525 | duration = curr - cm->charging_start_time; | ||
526 | |||
527 | if (duration > desc->charging_max_duration_ms) { | ||
528 | dev_info(cm->dev, "Charging duration exceed %lldms", | ||
529 | desc->charging_max_duration_ms); | ||
530 | uevent_notify(cm, "Discharging"); | ||
531 | try_charger_enable(cm, false); | ||
532 | ret = true; | ||
533 | } | ||
534 | } else if (is_ext_pwr_online(cm) && !cm->charger_enabled) { | ||
535 | duration = curr - cm->charging_end_time; | ||
536 | |||
537 | if (duration > desc->charging_max_duration_ms && | ||
538 | is_ext_pwr_online(cm)) { | ||
539 | dev_info(cm->dev, "DisCharging duration exceed %lldms", | ||
540 | desc->discharging_max_duration_ms); | ||
541 | uevent_notify(cm, "Recharing"); | ||
542 | try_charger_enable(cm, true); | ||
543 | ret = true; | ||
544 | } | ||
411 | } | 545 | } |
546 | |||
547 | return ret; | ||
412 | } | 548 | } |
413 | 549 | ||
414 | /** | 550 | /** |
@@ -426,10 +562,14 @@ static bool _cm_monitor(struct charger_manager *cm) | |||
426 | dev_dbg(cm->dev, "monitoring (%2.2d.%3.3dC)\n", | 562 | dev_dbg(cm->dev, "monitoring (%2.2d.%3.3dC)\n", |
427 | cm->last_temp_mC / 1000, cm->last_temp_mC % 1000); | 563 | cm->last_temp_mC / 1000, cm->last_temp_mC % 1000); |
428 | 564 | ||
429 | /* It has been stopped or charging already */ | 565 | /* It has been stopped already */ |
430 | if (!!temp == !!cm->emergency_stop) | 566 | if (temp && cm->emergency_stop) |
431 | return false; | 567 | return false; |
432 | 568 | ||
569 | /* | ||
570 | * Check temperature whether overheat or cold. | ||
571 | * If temperature is out of range normal state, stop charging. | ||
572 | */ | ||
433 | if (temp) { | 573 | if (temp) { |
434 | cm->emergency_stop = temp; | 574 | cm->emergency_stop = temp; |
435 | if (!try_charger_enable(cm, false)) { | 575 | if (!try_charger_enable(cm, false)) { |
@@ -438,10 +578,41 @@ static bool _cm_monitor(struct charger_manager *cm) | |||
438 | else | 578 | else |
439 | uevent_notify(cm, "COLD"); | 579 | uevent_notify(cm, "COLD"); |
440 | } | 580 | } |
581 | |||
582 | /* | ||
583 | * Check whole charging duration and discharing duration | ||
584 | * after full-batt. | ||
585 | */ | ||
586 | } else if (!cm->emergency_stop && check_charging_duration(cm)) { | ||
587 | dev_dbg(cm->dev, | ||
588 | "Charging/Discharging duration is out of range"); | ||
589 | /* | ||
590 | * Check dropped voltage of battery. If battery voltage is more | ||
591 | * dropped than fullbatt_vchkdrop_uV after fully charged state, | ||
592 | * charger-manager have to recharge battery. | ||
593 | */ | ||
594 | } else if (!cm->emergency_stop && is_ext_pwr_online(cm) && | ||
595 | !cm->charger_enabled) { | ||
596 | fullbatt_vchk(&cm->fullbatt_vchk_work.work); | ||
597 | |||
598 | /* | ||
599 | * Check whether fully charged state to protect overcharge | ||
600 | * if charger-manager is charging for battery. | ||
601 | */ | ||
602 | } else if (!cm->emergency_stop && is_full_charged(cm) && | ||
603 | cm->charger_enabled) { | ||
604 | dev_info(cm->dev, "EVENT_HANDLE: Battery Fully Charged.\n"); | ||
605 | uevent_notify(cm, default_event_names[CM_EVENT_BATT_FULL]); | ||
606 | |||
607 | try_charger_enable(cm, false); | ||
608 | |||
609 | fullbatt_vchk(&cm->fullbatt_vchk_work.work); | ||
441 | } else { | 610 | } else { |
442 | cm->emergency_stop = 0; | 611 | cm->emergency_stop = 0; |
443 | if (!try_charger_enable(cm, true)) | 612 | if (is_ext_pwr_online(cm)) { |
444 | uevent_notify(cm, "CHARGING"); | 613 | if (!try_charger_enable(cm, true)) |
614 | uevent_notify(cm, "CHARGING"); | ||
615 | } | ||
445 | } | 616 | } |
446 | 617 | ||
447 | return true; | 618 | return true; |
@@ -701,47 +872,10 @@ static int charger_get_property(struct power_supply *psy, | |||
701 | val->intval = 0; | 872 | val->intval = 0; |
702 | break; | 873 | break; |
703 | case POWER_SUPPLY_PROP_CHARGE_FULL: | 874 | case POWER_SUPPLY_PROP_CHARGE_FULL: |
704 | if (cm->fuel_gauge) { | 875 | if (is_full_charged(cm)) |
705 | if (cm->fuel_gauge->get_property(cm->fuel_gauge, | ||
706 | POWER_SUPPLY_PROP_CHARGE_FULL, val) == 0) | ||
707 | break; | ||
708 | } | ||
709 | |||
710 | if (is_ext_pwr_online(cm)) { | ||
711 | /* Not full if it's charging. */ | ||
712 | if (is_charging(cm)) { | ||
713 | val->intval = 0; | ||
714 | break; | ||
715 | } | ||
716 | /* | ||
717 | * Full if it's powered but not charging andi | ||
718 | * not forced stop by emergency | ||
719 | */ | ||
720 | if (!cm->emergency_stop) { | ||
721 | val->intval = 1; | ||
722 | break; | ||
723 | } | ||
724 | } | ||
725 | |||
726 | /* Full if it's over the fullbatt voltage */ | ||
727 | ret = get_batt_uV(cm, &uV); | ||
728 | if (!ret && desc->fullbatt_uV > 0 && uV >= desc->fullbatt_uV && | ||
729 | !is_charging(cm)) { | ||
730 | val->intval = 1; | 876 | val->intval = 1; |
731 | break; | 877 | else |
732 | } | 878 | val->intval = 0; |
733 | |||
734 | /* Full if the cap is 100 */ | ||
735 | if (cm->fuel_gauge) { | ||
736 | ret = cm->fuel_gauge->get_property(cm->fuel_gauge, | ||
737 | POWER_SUPPLY_PROP_CAPACITY, val); | ||
738 | if (!ret && val->intval >= 100 && !is_charging(cm)) { | ||
739 | val->intval = 1; | ||
740 | break; | ||
741 | } | ||
742 | } | ||
743 | |||
744 | val->intval = 0; | ||
745 | ret = 0; | 879 | ret = 0; |
746 | break; | 880 | break; |
747 | case POWER_SUPPLY_PROP_CHARGE_NOW: | 881 | case POWER_SUPPLY_PROP_CHARGE_NOW: |
@@ -1031,7 +1165,26 @@ static int charger_extcon_notifier(struct notifier_block *self, | |||
1031 | struct charger_cable *cable = | 1165 | struct charger_cable *cable = |
1032 | container_of(self, struct charger_cable, nb); | 1166 | container_of(self, struct charger_cable, nb); |
1033 | 1167 | ||
1168 | /* | ||
1169 | * The newly state of charger cable. | ||
1170 | * If cable is attached, cable->attached is true. | ||
1171 | */ | ||
1034 | cable->attached = event; | 1172 | cable->attached = event; |
1173 | |||
1174 | /* | ||
1175 | * Setup monitoring to check battery state | ||
1176 | * when charger cable is attached. | ||
1177 | */ | ||
1178 | if (cable->attached && is_polling_required(cable->cm)) { | ||
1179 | if (work_pending(&setup_polling)) | ||
1180 | cancel_work_sync(&setup_polling); | ||
1181 | schedule_work(&setup_polling); | ||
1182 | } | ||
1183 | |||
1184 | /* | ||
1185 | * Setup work for controlling charger(regulator) | ||
1186 | * according to charger cable. | ||
1187 | */ | ||
1035 | schedule_work(&cable->wq); | 1188 | schedule_work(&cable->wq); |
1036 | 1189 | ||
1037 | return NOTIFY_DONE; | 1190 | return NOTIFY_DONE; |
@@ -1068,12 +1221,101 @@ static int charger_extcon_init(struct charger_manager *cm, | |||
1068 | return ret; | 1221 | return ret; |
1069 | } | 1222 | } |
1070 | 1223 | ||
1224 | /* help function of sysfs node to control charger(regulator) */ | ||
1225 | static ssize_t charger_name_show(struct device *dev, | ||
1226 | struct device_attribute *attr, char *buf) | ||
1227 | { | ||
1228 | struct charger_regulator *charger | ||
1229 | = container_of(attr, struct charger_regulator, attr_name); | ||
1230 | |||
1231 | return sprintf(buf, "%s\n", charger->regulator_name); | ||
1232 | } | ||
1233 | |||
1234 | static ssize_t charger_state_show(struct device *dev, | ||
1235 | struct device_attribute *attr, char *buf) | ||
1236 | { | ||
1237 | struct charger_regulator *charger | ||
1238 | = container_of(attr, struct charger_regulator, attr_state); | ||
1239 | int state = 0; | ||
1240 | |||
1241 | if (!charger->externally_control) | ||
1242 | state = regulator_is_enabled(charger->consumer); | ||
1243 | |||
1244 | return sprintf(buf, "%s\n", state ? "enabled" : "disabled"); | ||
1245 | } | ||
1246 | |||
1247 | static ssize_t charger_externally_control_show(struct device *dev, | ||
1248 | struct device_attribute *attr, char *buf) | ||
1249 | { | ||
1250 | struct charger_regulator *charger = container_of(attr, | ||
1251 | struct charger_regulator, attr_externally_control); | ||
1252 | |||
1253 | return sprintf(buf, "%d\n", charger->externally_control); | ||
1254 | } | ||
1255 | |||
1256 | static ssize_t charger_externally_control_store(struct device *dev, | ||
1257 | struct device_attribute *attr, const char *buf, | ||
1258 | size_t count) | ||
1259 | { | ||
1260 | struct charger_regulator *charger | ||
1261 | = container_of(attr, struct charger_regulator, | ||
1262 | attr_externally_control); | ||
1263 | struct charger_manager *cm = charger->cm; | ||
1264 | struct charger_desc *desc = cm->desc; | ||
1265 | int i; | ||
1266 | int ret; | ||
1267 | int externally_control; | ||
1268 | int chargers_externally_control = 1; | ||
1269 | |||
1270 | ret = sscanf(buf, "%d", &externally_control); | ||
1271 | if (ret == 0) { | ||
1272 | ret = -EINVAL; | ||
1273 | return ret; | ||
1274 | } | ||
1275 | |||
1276 | if (!externally_control) { | ||
1277 | charger->externally_control = 0; | ||
1278 | return count; | ||
1279 | } | ||
1280 | |||
1281 | for (i = 0; i < desc->num_charger_regulators; i++) { | ||
1282 | if (&desc->charger_regulators[i] != charger && | ||
1283 | !desc->charger_regulators[i].externally_control) { | ||
1284 | /* | ||
1285 | * At least, one charger is controlled by | ||
1286 | * charger-manager | ||
1287 | */ | ||
1288 | chargers_externally_control = 0; | ||
1289 | break; | ||
1290 | } | ||
1291 | } | ||
1292 | |||
1293 | if (!chargers_externally_control) { | ||
1294 | if (cm->charger_enabled) { | ||
1295 | try_charger_enable(charger->cm, false); | ||
1296 | charger->externally_control = externally_control; | ||
1297 | try_charger_enable(charger->cm, true); | ||
1298 | } else { | ||
1299 | charger->externally_control = externally_control; | ||
1300 | } | ||
1301 | } else { | ||
1302 | dev_warn(cm->dev, | ||
1303 | "'%s' regulator should be controlled " | ||
1304 | "in charger-manager because charger-manager " | ||
1305 | "must need at least one charger for charging\n", | ||
1306 | charger->regulator_name); | ||
1307 | } | ||
1308 | |||
1309 | return count; | ||
1310 | } | ||
1311 | |||
1071 | static int charger_manager_probe(struct platform_device *pdev) | 1312 | static int charger_manager_probe(struct platform_device *pdev) |
1072 | { | 1313 | { |
1073 | struct charger_desc *desc = dev_get_platdata(&pdev->dev); | 1314 | struct charger_desc *desc = dev_get_platdata(&pdev->dev); |
1074 | struct charger_manager *cm; | 1315 | struct charger_manager *cm; |
1075 | int ret = 0, i = 0; | 1316 | int ret = 0, i = 0; |
1076 | int j = 0; | 1317 | int j = 0; |
1318 | int chargers_externally_control = 1; | ||
1077 | union power_supply_propval val; | 1319 | union power_supply_propval val; |
1078 | 1320 | ||
1079 | if (g_desc && !rtc_dev && g_desc->rtc_name) { | 1321 | if (g_desc && !rtc_dev && g_desc->rtc_name) { |
@@ -1125,6 +1367,15 @@ static int charger_manager_probe(struct platform_device *pdev) | |||
1125 | desc->fullbatt_vchkdrop_ms = 0; | 1367 | desc->fullbatt_vchkdrop_ms = 0; |
1126 | desc->fullbatt_vchkdrop_uV = 0; | 1368 | desc->fullbatt_vchkdrop_uV = 0; |
1127 | } | 1369 | } |
1370 | if (desc->fullbatt_soc == 0) { | ||
1371 | dev_info(&pdev->dev, "Ignoring full-battery soc(state of" | ||
1372 | " charge) threshold as it is not" | ||
1373 | " supplied."); | ||
1374 | } | ||
1375 | if (desc->fullbatt_full_capacity == 0) { | ||
1376 | dev_info(&pdev->dev, "Ignoring full-battery full capacity" | ||
1377 | " threshold as it is not supplied."); | ||
1378 | } | ||
1128 | 1379 | ||
1129 | if (!desc->charger_regulators || desc->num_charger_regulators < 1) { | 1380 | if (!desc->charger_regulators || desc->num_charger_regulators < 1) { |
1130 | ret = -EINVAL; | 1381 | ret = -EINVAL; |
@@ -1182,6 +1433,15 @@ static int charger_manager_probe(struct platform_device *pdev) | |||
1182 | goto err_chg_stat; | 1433 | goto err_chg_stat; |
1183 | } | 1434 | } |
1184 | 1435 | ||
1436 | if (!desc->charging_max_duration_ms || | ||
1437 | !desc->discharging_max_duration_ms) { | ||
1438 | dev_info(&pdev->dev, "Cannot limit charging duration " | ||
1439 | "checking mechanism to prevent overcharge/overheat " | ||
1440 | "and control discharging duration"); | ||
1441 | desc->charging_max_duration_ms = 0; | ||
1442 | desc->discharging_max_duration_ms = 0; | ||
1443 | } | ||
1444 | |||
1185 | platform_set_drvdata(pdev, cm); | 1445 | platform_set_drvdata(pdev, cm); |
1186 | 1446 | ||
1187 | memcpy(&cm->charger_psy, &psy_default, sizeof(psy_default)); | 1447 | memcpy(&cm->charger_psy, &psy_default, sizeof(psy_default)); |
@@ -1245,6 +1505,8 @@ static int charger_manager_probe(struct platform_device *pdev) | |||
1245 | for (i = 0 ; i < desc->num_charger_regulators ; i++) { | 1505 | for (i = 0 ; i < desc->num_charger_regulators ; i++) { |
1246 | struct charger_regulator *charger | 1506 | struct charger_regulator *charger |
1247 | = &desc->charger_regulators[i]; | 1507 | = &desc->charger_regulators[i]; |
1508 | char buf[11]; | ||
1509 | char *str; | ||
1248 | 1510 | ||
1249 | charger->consumer = regulator_get(&pdev->dev, | 1511 | charger->consumer = regulator_get(&pdev->dev, |
1250 | charger->regulator_name); | 1512 | charger->regulator_name); |
@@ -1254,6 +1516,7 @@ static int charger_manager_probe(struct platform_device *pdev) | |||
1254 | ret = -EINVAL; | 1516 | ret = -EINVAL; |
1255 | goto err_chg_get; | 1517 | goto err_chg_get; |
1256 | } | 1518 | } |
1519 | charger->cm = cm; | ||
1257 | 1520 | ||
1258 | for (j = 0 ; j < charger->num_cables ; j++) { | 1521 | for (j = 0 ; j < charger->num_cables ; j++) { |
1259 | struct charger_cable *cable = &charger->cables[j]; | 1522 | struct charger_cable *cable = &charger->cables[j]; |
@@ -1267,6 +1530,71 @@ static int charger_manager_probe(struct platform_device *pdev) | |||
1267 | cable->charger = charger; | 1530 | cable->charger = charger; |
1268 | cable->cm = cm; | 1531 | cable->cm = cm; |
1269 | } | 1532 | } |
1533 | |||
1534 | /* Create sysfs entry to control charger(regulator) */ | ||
1535 | snprintf(buf, 10, "charger.%d", i); | ||
1536 | str = kzalloc(sizeof(char) * (strlen(buf) + 1), GFP_KERNEL); | ||
1537 | if (!str) { | ||
1538 | for (i--; i >= 0; i--) { | ||
1539 | charger = &desc->charger_regulators[i]; | ||
1540 | kfree(charger->attr_g.name); | ||
1541 | } | ||
1542 | ret = -ENOMEM; | ||
1543 | |||
1544 | goto err_extcon; | ||
1545 | } | ||
1546 | strcpy(str, buf); | ||
1547 | |||
1548 | charger->attrs[0] = &charger->attr_name.attr; | ||
1549 | charger->attrs[1] = &charger->attr_state.attr; | ||
1550 | charger->attrs[2] = &charger->attr_externally_control.attr; | ||
1551 | charger->attrs[3] = NULL; | ||
1552 | charger->attr_g.name = str; | ||
1553 | charger->attr_g.attrs = charger->attrs; | ||
1554 | |||
1555 | sysfs_attr_init(&charger->attr_name.attr); | ||
1556 | charger->attr_name.attr.name = "name"; | ||
1557 | charger->attr_name.attr.mode = 0444; | ||
1558 | charger->attr_name.show = charger_name_show; | ||
1559 | |||
1560 | sysfs_attr_init(&charger->attr_state.attr); | ||
1561 | charger->attr_state.attr.name = "state"; | ||
1562 | charger->attr_state.attr.mode = 0444; | ||
1563 | charger->attr_state.show = charger_state_show; | ||
1564 | |||
1565 | sysfs_attr_init(&charger->attr_externally_control.attr); | ||
1566 | charger->attr_externally_control.attr.name | ||
1567 | = "externally_control"; | ||
1568 | charger->attr_externally_control.attr.mode = 0644; | ||
1569 | charger->attr_externally_control.show | ||
1570 | = charger_externally_control_show; | ||
1571 | charger->attr_externally_control.store | ||
1572 | = charger_externally_control_store; | ||
1573 | |||
1574 | if (!desc->charger_regulators[i].externally_control || | ||
1575 | !chargers_externally_control) { | ||
1576 | chargers_externally_control = 0; | ||
1577 | } | ||
1578 | dev_info(&pdev->dev, "'%s' regulator's externally_control" | ||
1579 | "is %d\n", charger->regulator_name, | ||
1580 | charger->externally_control); | ||
1581 | |||
1582 | ret = sysfs_create_group(&cm->charger_psy.dev->kobj, | ||
1583 | &charger->attr_g); | ||
1584 | if (ret < 0) { | ||
1585 | dev_info(&pdev->dev, "Cannot create sysfs entry" | ||
1586 | "of %s regulator\n", | ||
1587 | charger->regulator_name); | ||
1588 | } | ||
1589 | } | ||
1590 | |||
1591 | if (chargers_externally_control) { | ||
1592 | dev_err(&pdev->dev, "Cannot register regulator because " | ||
1593 | "charger-manager must need at least " | ||
1594 | "one charger for charging battery\n"); | ||
1595 | |||
1596 | ret = -EINVAL; | ||
1597 | goto err_chg_enable; | ||
1270 | } | 1598 | } |
1271 | 1599 | ||
1272 | ret = try_charger_enable(cm, true); | 1600 | ret = try_charger_enable(cm, true); |
@@ -1292,6 +1620,14 @@ static int charger_manager_probe(struct platform_device *pdev) | |||
1292 | return 0; | 1620 | return 0; |
1293 | 1621 | ||
1294 | err_chg_enable: | 1622 | err_chg_enable: |
1623 | for (i = 0; i < desc->num_charger_regulators; i++) { | ||
1624 | struct charger_regulator *charger; | ||
1625 | |||
1626 | charger = &desc->charger_regulators[i]; | ||
1627 | sysfs_remove_group(&cm->charger_psy.dev->kobj, | ||
1628 | &charger->attr_g); | ||
1629 | kfree(charger->attr_g.name); | ||
1630 | } | ||
1295 | err_extcon: | 1631 | err_extcon: |
1296 | for (i = 0 ; i < desc->num_charger_regulators ; i++) { | 1632 | for (i = 0 ; i < desc->num_charger_regulators ; i++) { |
1297 | struct charger_regulator *charger | 1633 | struct charger_regulator *charger |
diff --git a/drivers/power/da9030_battery.c b/drivers/power/da9030_battery.c index 3fd3e95d2b85..94762e67e22b 100644 --- a/drivers/power/da9030_battery.c +++ b/drivers/power/da9030_battery.c | |||
@@ -187,8 +187,8 @@ static const struct file_operations bat_debug_fops = { | |||
187 | 187 | ||
188 | static struct dentry *da9030_bat_create_debugfs(struct da9030_charger *charger) | 188 | static struct dentry *da9030_bat_create_debugfs(struct da9030_charger *charger) |
189 | { | 189 | { |
190 | charger->debug_file = debugfs_create_file("charger", 0666, 0, charger, | 190 | charger->debug_file = debugfs_create_file("charger", 0666, NULL, |
191 | &bat_debug_fops); | 191 | charger, &bat_debug_fops); |
192 | return charger->debug_file; | 192 | return charger->debug_file; |
193 | } | 193 | } |
194 | 194 | ||
diff --git a/drivers/power/da9052-battery.c b/drivers/power/da9052-battery.c index a5f6a0ec1572..d9d034d7496f 100644 --- a/drivers/power/da9052-battery.c +++ b/drivers/power/da9052-battery.c | |||
@@ -327,7 +327,7 @@ static int da9052_bat_interpolate(int vbat_lower, int vbat_upper, | |||
327 | return tmp; | 327 | return tmp; |
328 | } | 328 | } |
329 | 329 | ||
330 | unsigned char da9052_determine_vc_tbl_index(unsigned char adc_temp) | 330 | static unsigned char da9052_determine_vc_tbl_index(unsigned char adc_temp) |
331 | { | 331 | { |
332 | int i; | 332 | int i; |
333 | 333 | ||
@@ -345,6 +345,13 @@ unsigned char da9052_determine_vc_tbl_index(unsigned char adc_temp) | |||
345 | && (adc_temp <= vc_tbl_ref[i])) | 345 | && (adc_temp <= vc_tbl_ref[i])) |
346 | return i + 1; | 346 | return i + 1; |
347 | } | 347 | } |
348 | /* | ||
349 | * For some reason authors of the driver didn't presume that we can | ||
350 | * end up here. It might be OK, but might be not, no one knows for | ||
351 | * sure. Go check your battery, is it on fire? | ||
352 | */ | ||
353 | WARN_ON(1); | ||
354 | return 0; | ||
348 | } | 355 | } |
349 | 356 | ||
350 | static int da9052_bat_read_capacity(struct da9052_battery *bat, int *capacity) | 357 | static int da9052_bat_read_capacity(struct da9052_battery *bat, int *capacity) |
@@ -616,7 +623,7 @@ static s32 __devinit da9052_bat_probe(struct platform_device *pdev) | |||
616 | return 0; | 623 | return 0; |
617 | 624 | ||
618 | err: | 625 | err: |
619 | for (; i >= 0; i--) { | 626 | while (--i >= 0) { |
620 | irq = platform_get_irq_byname(pdev, da9052_bat_irqs[i]); | 627 | irq = platform_get_irq_byname(pdev, da9052_bat_irqs[i]); |
621 | free_irq(bat->da9052->irq_base + irq, bat); | 628 | free_irq(bat->da9052->irq_base + irq, bat); |
622 | } | 629 | } |
diff --git a/drivers/power/ds2781_battery.c b/drivers/power/ds2781_battery.c index 7a1ff4e4cf9a..22b3c8c93552 100644 --- a/drivers/power/ds2781_battery.c +++ b/drivers/power/ds2781_battery.c | |||
@@ -755,11 +755,9 @@ static int __devinit ds2781_battery_probe(struct platform_device *pdev) | |||
755 | int ret = 0; | 755 | int ret = 0; |
756 | struct ds2781_device_info *dev_info; | 756 | struct ds2781_device_info *dev_info; |
757 | 757 | ||
758 | dev_info = kzalloc(sizeof(*dev_info), GFP_KERNEL); | 758 | dev_info = devm_kzalloc(&pdev->dev, sizeof(*dev_info), GFP_KERNEL); |
759 | if (!dev_info) { | 759 | if (!dev_info) |
760 | ret = -ENOMEM; | 760 | return -ENOMEM; |
761 | goto fail; | ||
762 | } | ||
763 | 761 | ||
764 | platform_set_drvdata(pdev, dev_info); | 762 | platform_set_drvdata(pdev, dev_info); |
765 | 763 | ||
@@ -774,7 +772,7 @@ static int __devinit ds2781_battery_probe(struct platform_device *pdev) | |||
774 | ret = power_supply_register(&pdev->dev, &dev_info->bat); | 772 | ret = power_supply_register(&pdev->dev, &dev_info->bat); |
775 | if (ret) { | 773 | if (ret) { |
776 | dev_err(dev_info->dev, "failed to register battery\n"); | 774 | dev_err(dev_info->dev, "failed to register battery\n"); |
777 | goto fail_free_info; | 775 | goto fail; |
778 | } | 776 | } |
779 | 777 | ||
780 | ret = sysfs_create_group(&dev_info->bat.dev->kobj, &ds2781_attr_group); | 778 | ret = sysfs_create_group(&dev_info->bat.dev->kobj, &ds2781_attr_group); |
@@ -808,8 +806,6 @@ fail_remove_group: | |||
808 | sysfs_remove_group(&dev_info->bat.dev->kobj, &ds2781_attr_group); | 806 | sysfs_remove_group(&dev_info->bat.dev->kobj, &ds2781_attr_group); |
809 | fail_unregister: | 807 | fail_unregister: |
810 | power_supply_unregister(&dev_info->bat); | 808 | power_supply_unregister(&dev_info->bat); |
811 | fail_free_info: | ||
812 | kfree(dev_info); | ||
813 | fail: | 809 | fail: |
814 | return ret; | 810 | return ret; |
815 | } | 811 | } |
@@ -823,7 +819,6 @@ static int __devexit ds2781_battery_remove(struct platform_device *pdev) | |||
823 | 819 | ||
824 | power_supply_unregister(&dev_info->bat); | 820 | power_supply_unregister(&dev_info->bat); |
825 | 821 | ||
826 | kfree(dev_info); | ||
827 | return 0; | 822 | return 0; |
828 | } | 823 | } |
829 | 824 | ||
@@ -834,20 +829,7 @@ static struct platform_driver ds2781_battery_driver = { | |||
834 | .probe = ds2781_battery_probe, | 829 | .probe = ds2781_battery_probe, |
835 | .remove = __devexit_p(ds2781_battery_remove), | 830 | .remove = __devexit_p(ds2781_battery_remove), |
836 | }; | 831 | }; |
837 | 832 | module_platform_driver(ds2781_battery_driver); | |
838 | static int __init ds2781_battery_init(void) | ||
839 | { | ||
840 | return platform_driver_register(&ds2781_battery_driver); | ||
841 | } | ||
842 | |||
843 | static void __exit ds2781_battery_exit(void) | ||
844 | { | ||
845 | platform_driver_unregister(&ds2781_battery_driver); | ||
846 | } | ||
847 | |||
848 | module_init(ds2781_battery_init); | ||
849 | module_exit(ds2781_battery_exit); | ||
850 | |||
851 | 833 | ||
852 | MODULE_LICENSE("GPL"); | 834 | MODULE_LICENSE("GPL"); |
853 | MODULE_AUTHOR("Renata Sayakhova <renata@oktetlabs.ru>"); | 835 | MODULE_AUTHOR("Renata Sayakhova <renata@oktetlabs.ru>"); |
diff --git a/drivers/power/lp8727_charger.c b/drivers/power/lp8727_charger.c index 6a364f4798f7..c628224b7f58 100644 --- a/drivers/power/lp8727_charger.c +++ b/drivers/power/lp8727_charger.c | |||
@@ -17,61 +17,65 @@ | |||
17 | #include <linux/power_supply.h> | 17 | #include <linux/power_supply.h> |
18 | #include <linux/platform_data/lp8727.h> | 18 | #include <linux/platform_data/lp8727.h> |
19 | 19 | ||
20 | #define DEBOUNCE_MSEC 270 | 20 | #define LP8788_NUM_INTREGS 2 |
21 | #define DEFAULT_DEBOUNCE_MSEC 270 | ||
21 | 22 | ||
22 | /* Registers */ | 23 | /* Registers */ |
23 | #define CTRL1 0x1 | 24 | #define LP8727_CTRL1 0x1 |
24 | #define CTRL2 0x2 | 25 | #define LP8727_CTRL2 0x2 |
25 | #define SWCTRL 0x3 | 26 | #define LP8727_SWCTRL 0x3 |
26 | #define INT1 0x4 | 27 | #define LP8727_INT1 0x4 |
27 | #define INT2 0x5 | 28 | #define LP8727_INT2 0x5 |
28 | #define STATUS1 0x6 | 29 | #define LP8727_STATUS1 0x6 |
29 | #define STATUS2 0x7 | 30 | #define LP8727_STATUS2 0x7 |
30 | #define CHGCTRL2 0x9 | 31 | #define LP8727_CHGCTRL2 0x9 |
31 | 32 | ||
32 | /* CTRL1 register */ | 33 | /* CTRL1 register */ |
33 | #define CP_EN (1 << 0) | 34 | #define LP8727_CP_EN BIT(0) |
34 | #define ADC_EN (1 << 1) | 35 | #define LP8727_ADC_EN BIT(1) |
35 | #define ID200_EN (1 << 4) | 36 | #define LP8727_ID200_EN BIT(4) |
36 | 37 | ||
37 | /* CTRL2 register */ | 38 | /* CTRL2 register */ |
38 | #define CHGDET_EN (1 << 1) | 39 | #define LP8727_CHGDET_EN BIT(1) |
39 | #define INT_EN (1 << 6) | 40 | #define LP8727_INT_EN BIT(6) |
40 | 41 | ||
41 | /* SWCTRL register */ | 42 | /* SWCTRL register */ |
42 | #define SW_DM1_DM (0x0 << 0) | 43 | #define LP8727_SW_DM1_DM (0x0 << 0) |
43 | #define SW_DM1_U1 (0x1 << 0) | 44 | #define LP8727_SW_DM1_HiZ (0x7 << 0) |
44 | #define SW_DM1_HiZ (0x7 << 0) | 45 | #define LP8727_SW_DP2_DP (0x0 << 3) |
45 | #define SW_DP2_DP (0x0 << 3) | 46 | #define LP8727_SW_DP2_HiZ (0x7 << 3) |
46 | #define SW_DP2_U2 (0x1 << 3) | ||
47 | #define SW_DP2_HiZ (0x7 << 3) | ||
48 | 47 | ||
49 | /* INT1 register */ | 48 | /* INT1 register */ |
50 | #define IDNO (0xF << 0) | 49 | #define LP8727_IDNO (0xF << 0) |
51 | #define VBUS (1 << 4) | 50 | #define LP8727_VBUS BIT(4) |
52 | 51 | ||
53 | /* STATUS1 register */ | 52 | /* STATUS1 register */ |
54 | #define CHGSTAT (3 << 4) | 53 | #define LP8727_CHGSTAT (3 << 4) |
55 | #define CHPORT (1 << 6) | 54 | #define LP8727_CHPORT BIT(6) |
56 | #define DCPORT (1 << 7) | 55 | #define LP8727_DCPORT BIT(7) |
56 | #define LP8727_STAT_EOC 0x30 | ||
57 | 57 | ||
58 | /* STATUS2 register */ | 58 | /* STATUS2 register */ |
59 | #define TEMP_STAT (3 << 5) | 59 | #define LP8727_TEMP_STAT (3 << 5) |
60 | #define LP8727_TEMP_SHIFT 5 | ||
61 | |||
62 | /* CHGCTRL2 register */ | ||
63 | #define LP8727_ICHG_SHIFT 4 | ||
60 | 64 | ||
61 | enum lp8727_dev_id { | 65 | enum lp8727_dev_id { |
62 | ID_NONE, | 66 | LP8727_ID_NONE, |
63 | ID_TA, | 67 | LP8727_ID_TA, |
64 | ID_DEDICATED_CHG, | 68 | LP8727_ID_DEDICATED_CHG, |
65 | ID_USB_CHG, | 69 | LP8727_ID_USB_CHG, |
66 | ID_USB_DS, | 70 | LP8727_ID_USB_DS, |
67 | ID_MAX, | 71 | LP8727_ID_MAX, |
68 | }; | 72 | }; |
69 | 73 | ||
70 | enum lp8727_chg_stat { | 74 | enum lp8727_die_temp { |
71 | PRECHG, | 75 | LP8788_TEMP_75C, |
72 | CC, | 76 | LP8788_TEMP_95C, |
73 | CV, | 77 | LP8788_TEMP_115C, |
74 | EOC, | 78 | LP8788_TEMP_135C, |
75 | }; | 79 | }; |
76 | 80 | ||
77 | struct lp8727_psy { | 81 | struct lp8727_psy { |
@@ -84,12 +88,17 @@ struct lp8727_chg { | |||
84 | struct device *dev; | 88 | struct device *dev; |
85 | struct i2c_client *client; | 89 | struct i2c_client *client; |
86 | struct mutex xfer_lock; | 90 | struct mutex xfer_lock; |
87 | struct delayed_work work; | ||
88 | struct workqueue_struct *irqthread; | ||
89 | struct lp8727_platform_data *pdata; | ||
90 | struct lp8727_psy *psy; | 91 | struct lp8727_psy *psy; |
91 | struct lp8727_chg_param *chg_parm; | 92 | struct lp8727_platform_data *pdata; |
93 | |||
94 | /* Charger Data */ | ||
92 | enum lp8727_dev_id devid; | 95 | enum lp8727_dev_id devid; |
96 | struct lp8727_chg_param *chg_param; | ||
97 | |||
98 | /* Interrupt Handling */ | ||
99 | int irq; | ||
100 | struct delayed_work work; | ||
101 | unsigned long debounce_jiffies; | ||
93 | }; | 102 | }; |
94 | 103 | ||
95 | static int lp8727_read_bytes(struct lp8727_chg *pchg, u8 reg, u8 *data, u8 len) | 104 | static int lp8727_read_bytes(struct lp8727_chg *pchg, u8 reg, u8 *data, u8 len) |
@@ -119,81 +128,84 @@ static int lp8727_write_byte(struct lp8727_chg *pchg, u8 reg, u8 data) | |||
119 | return ret; | 128 | return ret; |
120 | } | 129 | } |
121 | 130 | ||
122 | static int lp8727_is_charger_attached(const char *name, int id) | 131 | static bool lp8727_is_charger_attached(const char *name, int id) |
123 | { | 132 | { |
124 | if (name) { | 133 | if (!strcmp(name, "ac")) |
125 | if (!strcmp(name, "ac")) | 134 | return id == LP8727_ID_TA || id == LP8727_ID_DEDICATED_CHG; |
126 | return (id == ID_TA || id == ID_DEDICATED_CHG) ? 1 : 0; | 135 | else if (!strcmp(name, "usb")) |
127 | else if (!strcmp(name, "usb")) | 136 | return id == LP8727_ID_USB_CHG; |
128 | return (id == ID_USB_CHG) ? 1 : 0; | ||
129 | } | ||
130 | 137 | ||
131 | return (id >= ID_TA && id <= ID_USB_CHG) ? 1 : 0; | 138 | return id >= LP8727_ID_TA && id <= LP8727_ID_USB_CHG; |
132 | } | 139 | } |
133 | 140 | ||
134 | static int lp8727_init_device(struct lp8727_chg *pchg) | 141 | static int lp8727_init_device(struct lp8727_chg *pchg) |
135 | { | 142 | { |
136 | u8 val; | 143 | u8 val; |
137 | int ret; | 144 | int ret; |
145 | u8 intstat[LP8788_NUM_INTREGS]; | ||
138 | 146 | ||
139 | val = ID200_EN | ADC_EN | CP_EN; | 147 | /* clear interrupts */ |
140 | ret = lp8727_write_byte(pchg, CTRL1, val); | 148 | ret = lp8727_read_bytes(pchg, LP8727_INT1, intstat, LP8788_NUM_INTREGS); |
141 | if (ret) | 149 | if (ret) |
142 | return ret; | 150 | return ret; |
143 | 151 | ||
144 | val = INT_EN | CHGDET_EN; | 152 | val = LP8727_ID200_EN | LP8727_ADC_EN | LP8727_CP_EN; |
145 | ret = lp8727_write_byte(pchg, CTRL2, val); | 153 | ret = lp8727_write_byte(pchg, LP8727_CTRL1, val); |
146 | if (ret) | 154 | if (ret) |
147 | return ret; | 155 | return ret; |
148 | 156 | ||
149 | return 0; | 157 | val = LP8727_INT_EN | LP8727_CHGDET_EN; |
158 | return lp8727_write_byte(pchg, LP8727_CTRL2, val); | ||
150 | } | 159 | } |
151 | 160 | ||
152 | static int lp8727_is_dedicated_charger(struct lp8727_chg *pchg) | 161 | static int lp8727_is_dedicated_charger(struct lp8727_chg *pchg) |
153 | { | 162 | { |
154 | u8 val; | 163 | u8 val; |
155 | lp8727_read_byte(pchg, STATUS1, &val); | 164 | |
156 | return val & DCPORT; | 165 | lp8727_read_byte(pchg, LP8727_STATUS1, &val); |
166 | return val & LP8727_DCPORT; | ||
157 | } | 167 | } |
158 | 168 | ||
159 | static int lp8727_is_usb_charger(struct lp8727_chg *pchg) | 169 | static int lp8727_is_usb_charger(struct lp8727_chg *pchg) |
160 | { | 170 | { |
161 | u8 val; | 171 | u8 val; |
162 | lp8727_read_byte(pchg, STATUS1, &val); | 172 | |
163 | return val & CHPORT; | 173 | lp8727_read_byte(pchg, LP8727_STATUS1, &val); |
174 | return val & LP8727_CHPORT; | ||
164 | } | 175 | } |
165 | 176 | ||
166 | static void lp8727_ctrl_switch(struct lp8727_chg *pchg, u8 sw) | 177 | static inline void lp8727_ctrl_switch(struct lp8727_chg *pchg, u8 sw) |
167 | { | 178 | { |
168 | lp8727_write_byte(pchg, SWCTRL, sw); | 179 | lp8727_write_byte(pchg, LP8727_SWCTRL, sw); |
169 | } | 180 | } |
170 | 181 | ||
171 | static void lp8727_id_detection(struct lp8727_chg *pchg, u8 id, int vbusin) | 182 | static void lp8727_id_detection(struct lp8727_chg *pchg, u8 id, int vbusin) |
172 | { | 183 | { |
173 | u8 devid = ID_NONE; | 184 | struct lp8727_platform_data *pdata = pchg->pdata; |
174 | u8 swctrl = SW_DM1_HiZ | SW_DP2_HiZ; | 185 | u8 devid = LP8727_ID_NONE; |
186 | u8 swctrl = LP8727_SW_DM1_HiZ | LP8727_SW_DP2_HiZ; | ||
175 | 187 | ||
176 | switch (id) { | 188 | switch (id) { |
177 | case 0x5: | 189 | case 0x5: |
178 | devid = ID_TA; | 190 | devid = LP8727_ID_TA; |
179 | pchg->chg_parm = &pchg->pdata->ac; | 191 | pchg->chg_param = pdata ? pdata->ac : NULL; |
180 | break; | 192 | break; |
181 | case 0xB: | 193 | case 0xB: |
182 | if (lp8727_is_dedicated_charger(pchg)) { | 194 | if (lp8727_is_dedicated_charger(pchg)) { |
183 | pchg->chg_parm = &pchg->pdata->ac; | 195 | pchg->chg_param = pdata ? pdata->ac : NULL; |
184 | devid = ID_DEDICATED_CHG; | 196 | devid = LP8727_ID_DEDICATED_CHG; |
185 | } else if (lp8727_is_usb_charger(pchg)) { | 197 | } else if (lp8727_is_usb_charger(pchg)) { |
186 | pchg->chg_parm = &pchg->pdata->usb; | 198 | pchg->chg_param = pdata ? pdata->usb : NULL; |
187 | devid = ID_USB_CHG; | 199 | devid = LP8727_ID_USB_CHG; |
188 | swctrl = SW_DM1_DM | SW_DP2_DP; | 200 | swctrl = LP8727_SW_DM1_DM | LP8727_SW_DP2_DP; |
189 | } else if (vbusin) { | 201 | } else if (vbusin) { |
190 | devid = ID_USB_DS; | 202 | devid = LP8727_ID_USB_DS; |
191 | swctrl = SW_DM1_DM | SW_DP2_DP; | 203 | swctrl = LP8727_SW_DM1_DM | LP8727_SW_DP2_DP; |
192 | } | 204 | } |
193 | break; | 205 | break; |
194 | default: | 206 | default: |
195 | devid = ID_NONE; | 207 | devid = LP8727_ID_NONE; |
196 | pchg->chg_parm = NULL; | 208 | pchg->chg_param = NULL; |
197 | break; | 209 | break; |
198 | } | 210 | } |
199 | 211 | ||
@@ -205,24 +217,26 @@ static void lp8727_enable_chgdet(struct lp8727_chg *pchg) | |||
205 | { | 217 | { |
206 | u8 val; | 218 | u8 val; |
207 | 219 | ||
208 | lp8727_read_byte(pchg, CTRL2, &val); | 220 | lp8727_read_byte(pchg, LP8727_CTRL2, &val); |
209 | val |= CHGDET_EN; | 221 | val |= LP8727_CHGDET_EN; |
210 | lp8727_write_byte(pchg, CTRL2, val); | 222 | lp8727_write_byte(pchg, LP8727_CTRL2, val); |
211 | } | 223 | } |
212 | 224 | ||
213 | static void lp8727_delayed_func(struct work_struct *_work) | 225 | static void lp8727_delayed_func(struct work_struct *_work) |
214 | { | 226 | { |
215 | u8 intstat[2], idno, vbus; | 227 | struct lp8727_chg *pchg = container_of(_work, struct lp8727_chg, |
216 | struct lp8727_chg *pchg = | 228 | work.work); |
217 | container_of(_work, struct lp8727_chg, work.work); | 229 | u8 intstat[LP8788_NUM_INTREGS]; |
230 | u8 idno; | ||
231 | u8 vbus; | ||
218 | 232 | ||
219 | if (lp8727_read_bytes(pchg, INT1, intstat, 2)) { | 233 | if (lp8727_read_bytes(pchg, LP8727_INT1, intstat, LP8788_NUM_INTREGS)) { |
220 | dev_err(pchg->dev, "can not read INT registers\n"); | 234 | dev_err(pchg->dev, "can not read INT registers\n"); |
221 | return; | 235 | return; |
222 | } | 236 | } |
223 | 237 | ||
224 | idno = intstat[0] & IDNO; | 238 | idno = intstat[0] & LP8727_IDNO; |
225 | vbus = intstat[0] & VBUS; | 239 | vbus = intstat[0] & LP8727_VBUS; |
226 | 240 | ||
227 | lp8727_id_detection(pchg, idno, vbus); | 241 | lp8727_id_detection(pchg, idno, vbus); |
228 | lp8727_enable_chgdet(pchg); | 242 | lp8727_enable_chgdet(pchg); |
@@ -235,29 +249,44 @@ static void lp8727_delayed_func(struct work_struct *_work) | |||
235 | static irqreturn_t lp8727_isr_func(int irq, void *ptr) | 249 | static irqreturn_t lp8727_isr_func(int irq, void *ptr) |
236 | { | 250 | { |
237 | struct lp8727_chg *pchg = ptr; | 251 | struct lp8727_chg *pchg = ptr; |
238 | unsigned long delay = msecs_to_jiffies(DEBOUNCE_MSEC); | ||
239 | |||
240 | queue_delayed_work(pchg->irqthread, &pchg->work, delay); | ||
241 | 252 | ||
253 | schedule_delayed_work(&pchg->work, pchg->debounce_jiffies); | ||
242 | return IRQ_HANDLED; | 254 | return IRQ_HANDLED; |
243 | } | 255 | } |
244 | 256 | ||
245 | static int lp8727_intr_config(struct lp8727_chg *pchg) | 257 | static int lp8727_setup_irq(struct lp8727_chg *pchg) |
246 | { | 258 | { |
259 | int ret; | ||
260 | int irq = pchg->client->irq; | ||
261 | unsigned delay_msec = pchg->pdata ? pchg->pdata->debounce_msec : | ||
262 | DEFAULT_DEBOUNCE_MSEC; | ||
263 | |||
247 | INIT_DELAYED_WORK(&pchg->work, lp8727_delayed_func); | 264 | INIT_DELAYED_WORK(&pchg->work, lp8727_delayed_func); |
248 | 265 | ||
249 | pchg->irqthread = create_singlethread_workqueue("lp8727-irqthd"); | 266 | if (irq <= 0) { |
250 | if (!pchg->irqthread) { | 267 | dev_warn(pchg->dev, "invalid irq number: %d\n", irq); |
251 | dev_err(pchg->dev, "can not create thread for lp8727\n"); | 268 | return 0; |
252 | return -ENOMEM; | ||
253 | } | 269 | } |
254 | 270 | ||
255 | return request_threaded_irq(pchg->client->irq, | 271 | ret = request_threaded_irq(irq, NULL, lp8727_isr_func, |
256 | NULL, | 272 | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, |
257 | lp8727_isr_func, | 273 | "lp8727_irq", pchg); |
258 | IRQF_TRIGGER_FALLING, | 274 | |
259 | "lp8727_irq", | 275 | if (ret) |
260 | pchg); | 276 | return ret; |
277 | |||
278 | pchg->irq = irq; | ||
279 | pchg->debounce_jiffies = msecs_to_jiffies(delay_msec); | ||
280 | |||
281 | return 0; | ||
282 | } | ||
283 | |||
284 | static void lp8727_release_irq(struct lp8727_chg *pchg) | ||
285 | { | ||
286 | cancel_delayed_work_sync(&pchg->work); | ||
287 | |||
288 | if (pchg->irq) | ||
289 | free_irq(pchg->irq, pchg); | ||
261 | } | 290 | } |
262 | 291 | ||
263 | static enum power_supply_property lp8727_charger_prop[] = { | 292 | static enum power_supply_property lp8727_charger_prop[] = { |
@@ -283,54 +312,82 @@ static int lp8727_charger_get_property(struct power_supply *psy, | |||
283 | { | 312 | { |
284 | struct lp8727_chg *pchg = dev_get_drvdata(psy->dev->parent); | 313 | struct lp8727_chg *pchg = dev_get_drvdata(psy->dev->parent); |
285 | 314 | ||
286 | if (psp == POWER_SUPPLY_PROP_ONLINE) | 315 | if (psp != POWER_SUPPLY_PROP_ONLINE) |
287 | val->intval = lp8727_is_charger_attached(psy->name, | 316 | return -EINVAL; |
288 | pchg->devid); | 317 | |
318 | val->intval = lp8727_is_charger_attached(psy->name, pchg->devid); | ||
289 | 319 | ||
290 | return 0; | 320 | return 0; |
291 | } | 321 | } |
292 | 322 | ||
323 | static bool lp8727_is_high_temperature(enum lp8727_die_temp temp) | ||
324 | { | ||
325 | switch (temp) { | ||
326 | case LP8788_TEMP_95C: | ||
327 | case LP8788_TEMP_115C: | ||
328 | case LP8788_TEMP_135C: | ||
329 | return true; | ||
330 | default: | ||
331 | return false; | ||
332 | } | ||
333 | } | ||
334 | |||
293 | static int lp8727_battery_get_property(struct power_supply *psy, | 335 | static int lp8727_battery_get_property(struct power_supply *psy, |
294 | enum power_supply_property psp, | 336 | enum power_supply_property psp, |
295 | union power_supply_propval *val) | 337 | union power_supply_propval *val) |
296 | { | 338 | { |
297 | struct lp8727_chg *pchg = dev_get_drvdata(psy->dev->parent); | 339 | struct lp8727_chg *pchg = dev_get_drvdata(psy->dev->parent); |
340 | struct lp8727_platform_data *pdata = pchg->pdata; | ||
341 | enum lp8727_die_temp temp; | ||
298 | u8 read; | 342 | u8 read; |
299 | 343 | ||
300 | switch (psp) { | 344 | switch (psp) { |
301 | case POWER_SUPPLY_PROP_STATUS: | 345 | case POWER_SUPPLY_PROP_STATUS: |
302 | if (lp8727_is_charger_attached(psy->name, pchg->devid)) { | 346 | if (!lp8727_is_charger_attached(psy->name, pchg->devid)) { |
303 | lp8727_read_byte(pchg, STATUS1, &read); | ||
304 | if (((read & CHGSTAT) >> 4) == EOC) | ||
305 | val->intval = POWER_SUPPLY_STATUS_FULL; | ||
306 | else | ||
307 | val->intval = POWER_SUPPLY_STATUS_CHARGING; | ||
308 | } else { | ||
309 | val->intval = POWER_SUPPLY_STATUS_DISCHARGING; | 347 | val->intval = POWER_SUPPLY_STATUS_DISCHARGING; |
348 | return 0; | ||
310 | } | 349 | } |
350 | |||
351 | lp8727_read_byte(pchg, LP8727_STATUS1, &read); | ||
352 | |||
353 | val->intval = (read & LP8727_CHGSTAT) == LP8727_STAT_EOC ? | ||
354 | POWER_SUPPLY_STATUS_FULL : | ||
355 | POWER_SUPPLY_STATUS_CHARGING; | ||
311 | break; | 356 | break; |
312 | case POWER_SUPPLY_PROP_HEALTH: | 357 | case POWER_SUPPLY_PROP_HEALTH: |
313 | lp8727_read_byte(pchg, STATUS2, &read); | 358 | lp8727_read_byte(pchg, LP8727_STATUS2, &read); |
314 | read = (read & TEMP_STAT) >> 5; | 359 | temp = (read & LP8727_TEMP_STAT) >> LP8727_TEMP_SHIFT; |
315 | if (read >= 0x1 && read <= 0x3) | 360 | |
316 | val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; | 361 | val->intval = lp8727_is_high_temperature(temp) ? |
317 | else | 362 | POWER_SUPPLY_HEALTH_OVERHEAT : |
318 | val->intval = POWER_SUPPLY_HEALTH_GOOD; | 363 | POWER_SUPPLY_HEALTH_GOOD; |
319 | break; | 364 | break; |
320 | case POWER_SUPPLY_PROP_PRESENT: | 365 | case POWER_SUPPLY_PROP_PRESENT: |
321 | if (pchg->pdata->get_batt_present) | 366 | if (!pdata) |
367 | return -EINVAL; | ||
368 | |||
369 | if (pdata->get_batt_present) | ||
322 | val->intval = pchg->pdata->get_batt_present(); | 370 | val->intval = pchg->pdata->get_batt_present(); |
323 | break; | 371 | break; |
324 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: | 372 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: |
325 | if (pchg->pdata->get_batt_level) | 373 | if (!pdata) |
374 | return -EINVAL; | ||
375 | |||
376 | if (pdata->get_batt_level) | ||
326 | val->intval = pchg->pdata->get_batt_level(); | 377 | val->intval = pchg->pdata->get_batt_level(); |
327 | break; | 378 | break; |
328 | case POWER_SUPPLY_PROP_CAPACITY: | 379 | case POWER_SUPPLY_PROP_CAPACITY: |
329 | if (pchg->pdata->get_batt_capacity) | 380 | if (!pdata) |
381 | return -EINVAL; | ||
382 | |||
383 | if (pdata->get_batt_capacity) | ||
330 | val->intval = pchg->pdata->get_batt_capacity(); | 384 | val->intval = pchg->pdata->get_batt_capacity(); |
331 | break; | 385 | break; |
332 | case POWER_SUPPLY_PROP_TEMP: | 386 | case POWER_SUPPLY_PROP_TEMP: |
333 | if (pchg->pdata->get_batt_temp) | 387 | if (!pdata) |
388 | return -EINVAL; | ||
389 | |||
390 | if (pdata->get_batt_temp) | ||
334 | val->intval = pchg->pdata->get_batt_temp(); | 391 | val->intval = pchg->pdata->get_batt_temp(); |
335 | break; | 392 | break; |
336 | default: | 393 | default: |
@@ -343,16 +400,20 @@ static int lp8727_battery_get_property(struct power_supply *psy, | |||
343 | static void lp8727_charger_changed(struct power_supply *psy) | 400 | static void lp8727_charger_changed(struct power_supply *psy) |
344 | { | 401 | { |
345 | struct lp8727_chg *pchg = dev_get_drvdata(psy->dev->parent); | 402 | struct lp8727_chg *pchg = dev_get_drvdata(psy->dev->parent); |
403 | u8 eoc_level; | ||
404 | u8 ichg; | ||
346 | u8 val; | 405 | u8 val; |
347 | u8 eoc_level, ichg; | 406 | |
348 | 407 | /* skip if no charger exists */ | |
349 | if (lp8727_is_charger_attached(psy->name, pchg->devid)) { | 408 | if (!lp8727_is_charger_attached(psy->name, pchg->devid)) |
350 | if (pchg->chg_parm) { | 409 | return; |
351 | eoc_level = pchg->chg_parm->eoc_level; | 410 | |
352 | ichg = pchg->chg_parm->ichg; | 411 | /* update charging parameters */ |
353 | val = (ichg << 4) | eoc_level; | 412 | if (pchg->chg_param) { |
354 | lp8727_write_byte(pchg, CHGCTRL2, val); | 413 | eoc_level = pchg->chg_param->eoc_level; |
355 | } | 414 | ichg = pchg->chg_param->ichg; |
415 | val = (ichg << LP8727_ICHG_SHIFT) | eoc_level; | ||
416 | lp8727_write_byte(pchg, LP8727_CHGCTRL2, val); | ||
356 | } | 417 | } |
357 | } | 418 | } |
358 | 419 | ||
@@ -360,9 +421,9 @@ static int lp8727_register_psy(struct lp8727_chg *pchg) | |||
360 | { | 421 | { |
361 | struct lp8727_psy *psy; | 422 | struct lp8727_psy *psy; |
362 | 423 | ||
363 | psy = kzalloc(sizeof(*psy), GFP_KERNEL); | 424 | psy = devm_kzalloc(pchg->dev, sizeof(*psy), GFP_KERNEL); |
364 | if (!psy) | 425 | if (!psy) |
365 | goto err_mem; | 426 | return -ENOMEM; |
366 | 427 | ||
367 | pchg->psy = psy; | 428 | pchg->psy = psy; |
368 | 429 | ||
@@ -375,7 +436,7 @@ static int lp8727_register_psy(struct lp8727_chg *pchg) | |||
375 | psy->ac.num_supplicants = ARRAY_SIZE(battery_supplied_to); | 436 | psy->ac.num_supplicants = ARRAY_SIZE(battery_supplied_to); |
376 | 437 | ||
377 | if (power_supply_register(pchg->dev, &psy->ac)) | 438 | if (power_supply_register(pchg->dev, &psy->ac)) |
378 | goto err_psy; | 439 | goto err_psy_ac; |
379 | 440 | ||
380 | psy->usb.name = "usb"; | 441 | psy->usb.name = "usb"; |
381 | psy->usb.type = POWER_SUPPLY_TYPE_USB; | 442 | psy->usb.type = POWER_SUPPLY_TYPE_USB; |
@@ -386,7 +447,7 @@ static int lp8727_register_psy(struct lp8727_chg *pchg) | |||
386 | psy->usb.num_supplicants = ARRAY_SIZE(battery_supplied_to); | 447 | psy->usb.num_supplicants = ARRAY_SIZE(battery_supplied_to); |
387 | 448 | ||
388 | if (power_supply_register(pchg->dev, &psy->usb)) | 449 | if (power_supply_register(pchg->dev, &psy->usb)) |
389 | goto err_psy; | 450 | goto err_psy_usb; |
390 | 451 | ||
391 | psy->batt.name = "main_batt"; | 452 | psy->batt.name = "main_batt"; |
392 | psy->batt.type = POWER_SUPPLY_TYPE_BATTERY; | 453 | psy->batt.type = POWER_SUPPLY_TYPE_BATTERY; |
@@ -396,14 +457,15 @@ static int lp8727_register_psy(struct lp8727_chg *pchg) | |||
396 | psy->batt.external_power_changed = lp8727_charger_changed; | 457 | psy->batt.external_power_changed = lp8727_charger_changed; |
397 | 458 | ||
398 | if (power_supply_register(pchg->dev, &psy->batt)) | 459 | if (power_supply_register(pchg->dev, &psy->batt)) |
399 | goto err_psy; | 460 | goto err_psy_batt; |
400 | 461 | ||
401 | return 0; | 462 | return 0; |
402 | 463 | ||
403 | err_mem: | 464 | err_psy_batt: |
404 | return -ENOMEM; | 465 | power_supply_unregister(&psy->usb); |
405 | err_psy: | 466 | err_psy_usb: |
406 | kfree(psy); | 467 | power_supply_unregister(&psy->ac); |
468 | err_psy_ac: | ||
407 | return -EPERM; | 469 | return -EPERM; |
408 | } | 470 | } |
409 | 471 | ||
@@ -417,7 +479,6 @@ static void lp8727_unregister_psy(struct lp8727_chg *pchg) | |||
417 | power_supply_unregister(&psy->ac); | 479 | power_supply_unregister(&psy->ac); |
418 | power_supply_unregister(&psy->usb); | 480 | power_supply_unregister(&psy->usb); |
419 | power_supply_unregister(&psy->batt); | 481 | power_supply_unregister(&psy->batt); |
420 | kfree(psy); | ||
421 | } | 482 | } |
422 | 483 | ||
423 | static int lp8727_probe(struct i2c_client *cl, const struct i2c_device_id *id) | 484 | static int lp8727_probe(struct i2c_client *cl, const struct i2c_device_id *id) |
@@ -428,7 +489,7 @@ static int lp8727_probe(struct i2c_client *cl, const struct i2c_device_id *id) | |||
428 | if (!i2c_check_functionality(cl->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) | 489 | if (!i2c_check_functionality(cl->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) |
429 | return -EIO; | 490 | return -EIO; |
430 | 491 | ||
431 | pchg = kzalloc(sizeof(*pchg), GFP_KERNEL); | 492 | pchg = devm_kzalloc(&cl->dev, sizeof(*pchg), GFP_KERNEL); |
432 | if (!pchg) | 493 | if (!pchg) |
433 | return -ENOMEM; | 494 | return -ENOMEM; |
434 | 495 | ||
@@ -442,37 +503,31 @@ static int lp8727_probe(struct i2c_client *cl, const struct i2c_device_id *id) | |||
442 | ret = lp8727_init_device(pchg); | 503 | ret = lp8727_init_device(pchg); |
443 | if (ret) { | 504 | if (ret) { |
444 | dev_err(pchg->dev, "i2c communication err: %d", ret); | 505 | dev_err(pchg->dev, "i2c communication err: %d", ret); |
445 | goto error; | 506 | return ret; |
446 | } | 507 | } |
447 | 508 | ||
448 | ret = lp8727_intr_config(pchg); | 509 | ret = lp8727_register_psy(pchg); |
449 | if (ret) { | 510 | if (ret) { |
450 | dev_err(pchg->dev, "irq handler err: %d", ret); | 511 | dev_err(pchg->dev, "power supplies register err: %d", ret); |
451 | goto error; | 512 | return ret; |
452 | } | 513 | } |
453 | 514 | ||
454 | ret = lp8727_register_psy(pchg); | 515 | ret = lp8727_setup_irq(pchg); |
455 | if (ret) { | 516 | if (ret) { |
456 | dev_err(pchg->dev, "power supplies register err: %d", ret); | 517 | dev_err(pchg->dev, "irq handler err: %d", ret); |
457 | goto error; | 518 | lp8727_unregister_psy(pchg); |
519 | return ret; | ||
458 | } | 520 | } |
459 | 521 | ||
460 | return 0; | 522 | return 0; |
461 | |||
462 | error: | ||
463 | kfree(pchg); | ||
464 | return ret; | ||
465 | } | 523 | } |
466 | 524 | ||
467 | static int __devexit lp8727_remove(struct i2c_client *cl) | 525 | static int __devexit lp8727_remove(struct i2c_client *cl) |
468 | { | 526 | { |
469 | struct lp8727_chg *pchg = i2c_get_clientdata(cl); | 527 | struct lp8727_chg *pchg = i2c_get_clientdata(cl); |
470 | 528 | ||
529 | lp8727_release_irq(pchg); | ||
471 | lp8727_unregister_psy(pchg); | 530 | lp8727_unregister_psy(pchg); |
472 | free_irq(pchg->client->irq, pchg); | ||
473 | flush_workqueue(pchg->irqthread); | ||
474 | destroy_workqueue(pchg->irqthread); | ||
475 | kfree(pchg); | ||
476 | return 0; | 531 | return 0; |
477 | } | 532 | } |
478 | 533 | ||
@@ -493,6 +548,5 @@ static struct i2c_driver lp8727_driver = { | |||
493 | module_i2c_driver(lp8727_driver); | 548 | module_i2c_driver(lp8727_driver); |
494 | 549 | ||
495 | MODULE_DESCRIPTION("TI/National Semiconductor LP8727 charger driver"); | 550 | MODULE_DESCRIPTION("TI/National Semiconductor LP8727 charger driver"); |
496 | MODULE_AUTHOR("Woogyom Kim <milo.kim@ti.com>, " | 551 | MODULE_AUTHOR("Milo Kim <milo.kim@ti.com>, Daniel Jeong <daniel.jeong@ti.com>"); |
497 | "Daniel Jeong <daniel.jeong@ti.com>"); | ||
498 | MODULE_LICENSE("GPL"); | 552 | MODULE_LICENSE("GPL"); |
diff --git a/drivers/power/lp8788-charger.c b/drivers/power/lp8788-charger.c new file mode 100644 index 000000000000..e852d12cd077 --- /dev/null +++ b/drivers/power/lp8788-charger.c | |||
@@ -0,0 +1,795 @@ | |||
1 | /* | ||
2 | * TI LP8788 MFD - battery charger driver | ||
3 | * | ||
4 | * Copyright 2012 Texas Instruments | ||
5 | * | ||
6 | * Author: Milo(Woogyom) Kim <milo.kim@ti.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | * | ||
12 | */ | ||
13 | |||
14 | #include <linux/err.h> | ||
15 | #include <linux/iio/consumer.h> | ||
16 | #include <linux/interrupt.h> | ||
17 | #include <linux/irqdomain.h> | ||
18 | #include <linux/mfd/lp8788.h> | ||
19 | #include <linux/module.h> | ||
20 | #include <linux/platform_device.h> | ||
21 | #include <linux/power_supply.h> | ||
22 | #include <linux/slab.h> | ||
23 | #include <linux/workqueue.h> | ||
24 | |||
25 | /* register address */ | ||
26 | #define LP8788_CHG_STATUS 0x07 | ||
27 | #define LP8788_CHG_IDCIN 0x13 | ||
28 | #define LP8788_CHG_IBATT 0x14 | ||
29 | #define LP8788_CHG_VTERM 0x15 | ||
30 | #define LP8788_CHG_EOC 0x16 | ||
31 | |||
32 | /* mask/shift bits */ | ||
33 | #define LP8788_CHG_INPUT_STATE_M 0x03 /* Addr 07h */ | ||
34 | #define LP8788_CHG_STATE_M 0x3C | ||
35 | #define LP8788_CHG_STATE_S 2 | ||
36 | #define LP8788_NO_BATT_M BIT(6) | ||
37 | #define LP8788_BAD_BATT_M BIT(7) | ||
38 | #define LP8788_CHG_IBATT_M 0x1F /* Addr 14h */ | ||
39 | #define LP8788_CHG_VTERM_M 0x0F /* Addr 15h */ | ||
40 | #define LP8788_CHG_EOC_LEVEL_M 0x30 /* Addr 16h */ | ||
41 | #define LP8788_CHG_EOC_LEVEL_S 4 | ||
42 | #define LP8788_CHG_EOC_TIME_M 0x0E | ||
43 | #define LP8788_CHG_EOC_TIME_S 1 | ||
44 | #define LP8788_CHG_EOC_MODE_M BIT(0) | ||
45 | |||
46 | #define LP8788_CHARGER_NAME "charger" | ||
47 | #define LP8788_BATTERY_NAME "main_batt" | ||
48 | |||
49 | #define LP8788_CHG_START 0x11 | ||
50 | #define LP8788_CHG_END 0x1C | ||
51 | |||
52 | #define LP8788_BUF_SIZE 40 | ||
53 | #define LP8788_ISEL_MAX 23 | ||
54 | #define LP8788_ISEL_STEP 50 | ||
55 | #define LP8788_VTERM_MIN 4100 | ||
56 | #define LP8788_VTERM_STEP 25 | ||
57 | #define LP8788_MAX_BATT_CAPACITY 100 | ||
58 | #define LP8788_MAX_CHG_IRQS 11 | ||
59 | |||
60 | enum lp8788_charging_state { | ||
61 | LP8788_OFF, | ||
62 | LP8788_WARM_UP, | ||
63 | LP8788_LOW_INPUT = 0x3, | ||
64 | LP8788_PRECHARGE, | ||
65 | LP8788_CC, | ||
66 | LP8788_CV, | ||
67 | LP8788_MAINTENANCE, | ||
68 | LP8788_BATTERY_FAULT, | ||
69 | LP8788_SYSTEM_SUPPORT = 0xC, | ||
70 | LP8788_HIGH_CURRENT = 0xF, | ||
71 | LP8788_MAX_CHG_STATE, | ||
72 | }; | ||
73 | |||
74 | enum lp8788_charger_adc_sel { | ||
75 | LP8788_VBATT, | ||
76 | LP8788_BATT_TEMP, | ||
77 | LP8788_NUM_CHG_ADC, | ||
78 | }; | ||
79 | |||
80 | enum lp8788_charger_input_state { | ||
81 | LP8788_SYSTEM_SUPPLY = 1, | ||
82 | LP8788_FULL_FUNCTION, | ||
83 | }; | ||
84 | |||
85 | /* | ||
86 | * struct lp8788_chg_irq | ||
87 | * @which : lp8788 interrupt id | ||
88 | * @virq : Linux IRQ number from irq_domain | ||
89 | */ | ||
90 | struct lp8788_chg_irq { | ||
91 | enum lp8788_int_id which; | ||
92 | int virq; | ||
93 | }; | ||
94 | |||
95 | /* | ||
96 | * struct lp8788_charger | ||
97 | * @lp : used for accessing the registers of mfd lp8788 device | ||
98 | * @charger : power supply driver for the battery charger | ||
99 | * @battery : power supply driver for the battery | ||
100 | * @charger_work : work queue for charger input interrupts | ||
101 | * @chan : iio channels for getting adc values | ||
102 | * eg) battery voltage, capacity and temperature | ||
103 | * @irqs : charger dedicated interrupts | ||
104 | * @num_irqs : total numbers of charger interrupts | ||
105 | * @pdata : charger platform specific data | ||
106 | */ | ||
107 | struct lp8788_charger { | ||
108 | struct lp8788 *lp; | ||
109 | struct power_supply charger; | ||
110 | struct power_supply battery; | ||
111 | struct work_struct charger_work; | ||
112 | struct iio_channel *chan[LP8788_NUM_CHG_ADC]; | ||
113 | struct lp8788_chg_irq irqs[LP8788_MAX_CHG_IRQS]; | ||
114 | int num_irqs; | ||
115 | struct lp8788_charger_platform_data *pdata; | ||
116 | }; | ||
117 | |||
118 | static char *battery_supplied_to[] = { | ||
119 | LP8788_BATTERY_NAME, | ||
120 | }; | ||
121 | |||
122 | static enum power_supply_property lp8788_charger_prop[] = { | ||
123 | POWER_SUPPLY_PROP_ONLINE, | ||
124 | POWER_SUPPLY_PROP_CURRENT_MAX, | ||
125 | }; | ||
126 | |||
127 | static enum power_supply_property lp8788_battery_prop[] = { | ||
128 | POWER_SUPPLY_PROP_STATUS, | ||
129 | POWER_SUPPLY_PROP_HEALTH, | ||
130 | POWER_SUPPLY_PROP_PRESENT, | ||
131 | POWER_SUPPLY_PROP_VOLTAGE_NOW, | ||
132 | POWER_SUPPLY_PROP_CAPACITY, | ||
133 | POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, | ||
134 | POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, | ||
135 | POWER_SUPPLY_PROP_TEMP, | ||
136 | }; | ||
137 | |||
138 | static bool lp8788_is_charger_detected(struct lp8788_charger *pchg) | ||
139 | { | ||
140 | u8 data; | ||
141 | |||
142 | lp8788_read_byte(pchg->lp, LP8788_CHG_STATUS, &data); | ||
143 | data &= LP8788_CHG_INPUT_STATE_M; | ||
144 | |||
145 | return data == LP8788_SYSTEM_SUPPLY || data == LP8788_FULL_FUNCTION; | ||
146 | } | ||
147 | |||
148 | static int lp8788_charger_get_property(struct power_supply *psy, | ||
149 | enum power_supply_property psp, | ||
150 | union power_supply_propval *val) | ||
151 | { | ||
152 | struct lp8788_charger *pchg = dev_get_drvdata(psy->dev->parent); | ||
153 | u8 read; | ||
154 | |||
155 | switch (psp) { | ||
156 | case POWER_SUPPLY_PROP_ONLINE: | ||
157 | val->intval = lp8788_is_charger_detected(pchg); | ||
158 | break; | ||
159 | case POWER_SUPPLY_PROP_CURRENT_MAX: | ||
160 | lp8788_read_byte(pchg->lp, LP8788_CHG_IDCIN, &read); | ||
161 | val->intval = LP8788_ISEL_STEP * | ||
162 | (min_t(int, read, LP8788_ISEL_MAX) + 1); | ||
163 | break; | ||
164 | default: | ||
165 | return -EINVAL; | ||
166 | } | ||
167 | |||
168 | return 0; | ||
169 | } | ||
170 | |||
171 | static int lp8788_get_battery_status(struct lp8788_charger *pchg, | ||
172 | union power_supply_propval *val) | ||
173 | { | ||
174 | enum lp8788_charging_state state; | ||
175 | u8 data; | ||
176 | int ret; | ||
177 | |||
178 | ret = lp8788_read_byte(pchg->lp, LP8788_CHG_STATUS, &data); | ||
179 | if (ret) | ||
180 | return ret; | ||
181 | |||
182 | state = (data & LP8788_CHG_STATE_M) >> LP8788_CHG_STATE_S; | ||
183 | switch (state) { | ||
184 | case LP8788_OFF: | ||
185 | val->intval = POWER_SUPPLY_STATUS_DISCHARGING; | ||
186 | break; | ||
187 | case LP8788_PRECHARGE: | ||
188 | case LP8788_CC: | ||
189 | case LP8788_CV: | ||
190 | case LP8788_HIGH_CURRENT: | ||
191 | val->intval = POWER_SUPPLY_STATUS_CHARGING; | ||
192 | break; | ||
193 | case LP8788_MAINTENANCE: | ||
194 | val->intval = POWER_SUPPLY_STATUS_FULL; | ||
195 | break; | ||
196 | default: | ||
197 | val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; | ||
198 | break; | ||
199 | } | ||
200 | |||
201 | return 0; | ||
202 | } | ||
203 | |||
204 | static int lp8788_get_battery_health(struct lp8788_charger *pchg, | ||
205 | union power_supply_propval *val) | ||
206 | { | ||
207 | u8 data; | ||
208 | int ret; | ||
209 | |||
210 | ret = lp8788_read_byte(pchg->lp, LP8788_CHG_STATUS, &data); | ||
211 | if (ret) | ||
212 | return ret; | ||
213 | |||
214 | if (data & LP8788_NO_BATT_M) | ||
215 | val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; | ||
216 | else if (data & LP8788_BAD_BATT_M) | ||
217 | val->intval = POWER_SUPPLY_HEALTH_DEAD; | ||
218 | else | ||
219 | val->intval = POWER_SUPPLY_HEALTH_GOOD; | ||
220 | |||
221 | return 0; | ||
222 | } | ||
223 | |||
224 | static int lp8788_get_battery_present(struct lp8788_charger *pchg, | ||
225 | union power_supply_propval *val) | ||
226 | { | ||
227 | u8 data; | ||
228 | int ret; | ||
229 | |||
230 | ret = lp8788_read_byte(pchg->lp, LP8788_CHG_STATUS, &data); | ||
231 | if (ret) | ||
232 | return ret; | ||
233 | |||
234 | val->intval = !(data & LP8788_NO_BATT_M); | ||
235 | return 0; | ||
236 | } | ||
237 | |||
238 | static int lp8788_get_vbatt_adc(struct lp8788_charger *pchg, | ||
239 | unsigned int *result) | ||
240 | { | ||
241 | struct iio_channel *channel = pchg->chan[LP8788_VBATT]; | ||
242 | int scaleint; | ||
243 | int scalepart; | ||
244 | int ret; | ||
245 | |||
246 | if (!channel) | ||
247 | return -EINVAL; | ||
248 | |||
249 | ret = iio_read_channel_scale(channel, &scaleint, &scalepart); | ||
250 | if (ret != IIO_VAL_INT_PLUS_MICRO) | ||
251 | return -EINVAL; | ||
252 | |||
253 | /* unit: mV */ | ||
254 | *result = (scaleint + scalepart * 1000000) / 1000; | ||
255 | |||
256 | return 0; | ||
257 | } | ||
258 | |||
259 | static int lp8788_get_battery_voltage(struct lp8788_charger *pchg, | ||
260 | union power_supply_propval *val) | ||
261 | { | ||
262 | return lp8788_get_vbatt_adc(pchg, &val->intval); | ||
263 | } | ||
264 | |||
265 | static int lp8788_get_battery_capacity(struct lp8788_charger *pchg, | ||
266 | union power_supply_propval *val) | ||
267 | { | ||
268 | struct lp8788 *lp = pchg->lp; | ||
269 | struct lp8788_charger_platform_data *pdata = pchg->pdata; | ||
270 | unsigned int max_vbatt; | ||
271 | unsigned int vbatt; | ||
272 | enum lp8788_charging_state state; | ||
273 | u8 data; | ||
274 | int ret; | ||
275 | |||
276 | if (!pdata) | ||
277 | return -EINVAL; | ||
278 | |||
279 | max_vbatt = pdata->max_vbatt_mv; | ||
280 | if (max_vbatt == 0) | ||
281 | return -EINVAL; | ||
282 | |||
283 | ret = lp8788_read_byte(lp, LP8788_CHG_STATUS, &data); | ||
284 | if (ret) | ||
285 | return ret; | ||
286 | |||
287 | state = (data & LP8788_CHG_STATE_M) >> LP8788_CHG_STATE_S; | ||
288 | |||
289 | if (state == LP8788_MAINTENANCE) { | ||
290 | val->intval = LP8788_MAX_BATT_CAPACITY; | ||
291 | } else { | ||
292 | ret = lp8788_get_vbatt_adc(pchg, &vbatt); | ||
293 | if (ret) | ||
294 | return ret; | ||
295 | |||
296 | val->intval = (vbatt * LP8788_MAX_BATT_CAPACITY) / max_vbatt; | ||
297 | val->intval = min(val->intval, LP8788_MAX_BATT_CAPACITY); | ||
298 | } | ||
299 | |||
300 | return 0; | ||
301 | } | ||
302 | |||
303 | static int lp8788_get_battery_temperature(struct lp8788_charger *pchg, | ||
304 | union power_supply_propval *val) | ||
305 | { | ||
306 | struct iio_channel *channel = pchg->chan[LP8788_BATT_TEMP]; | ||
307 | int scaleint; | ||
308 | int scalepart; | ||
309 | int ret; | ||
310 | |||
311 | if (!channel) | ||
312 | return -EINVAL; | ||
313 | |||
314 | ret = iio_read_channel_scale(channel, &scaleint, &scalepart); | ||
315 | if (ret != IIO_VAL_INT_PLUS_MICRO) | ||
316 | return -EINVAL; | ||
317 | |||
318 | /* unit: 0.1 'C */ | ||
319 | val->intval = (scaleint + scalepart * 1000000) / 100; | ||
320 | |||
321 | return 0; | ||
322 | } | ||
323 | |||
324 | static int lp8788_get_battery_charging_current(struct lp8788_charger *pchg, | ||
325 | union power_supply_propval *val) | ||
326 | { | ||
327 | u8 read; | ||
328 | |||
329 | lp8788_read_byte(pchg->lp, LP8788_CHG_IBATT, &read); | ||
330 | read &= LP8788_CHG_IBATT_M; | ||
331 | val->intval = LP8788_ISEL_STEP * | ||
332 | (min_t(int, read, LP8788_ISEL_MAX) + 1); | ||
333 | |||
334 | return 0; | ||
335 | } | ||
336 | |||
337 | static int lp8788_get_charging_termination_voltage(struct lp8788_charger *pchg, | ||
338 | union power_supply_propval *val) | ||
339 | { | ||
340 | u8 read; | ||
341 | |||
342 | lp8788_read_byte(pchg->lp, LP8788_CHG_VTERM, &read); | ||
343 | read &= LP8788_CHG_VTERM_M; | ||
344 | val->intval = LP8788_VTERM_MIN + LP8788_VTERM_STEP * read; | ||
345 | |||
346 | return 0; | ||
347 | } | ||
348 | |||
349 | static int lp8788_battery_get_property(struct power_supply *psy, | ||
350 | enum power_supply_property psp, | ||
351 | union power_supply_propval *val) | ||
352 | { | ||
353 | struct lp8788_charger *pchg = dev_get_drvdata(psy->dev->parent); | ||
354 | |||
355 | switch (psp) { | ||
356 | case POWER_SUPPLY_PROP_STATUS: | ||
357 | return lp8788_get_battery_status(pchg, val); | ||
358 | case POWER_SUPPLY_PROP_HEALTH: | ||
359 | return lp8788_get_battery_health(pchg, val); | ||
360 | case POWER_SUPPLY_PROP_PRESENT: | ||
361 | return lp8788_get_battery_present(pchg, val); | ||
362 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: | ||
363 | return lp8788_get_battery_voltage(pchg, val); | ||
364 | case POWER_SUPPLY_PROP_CAPACITY: | ||
365 | return lp8788_get_battery_capacity(pchg, val); | ||
366 | case POWER_SUPPLY_PROP_TEMP: | ||
367 | return lp8788_get_battery_temperature(pchg, val); | ||
368 | case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: | ||
369 | return lp8788_get_battery_charging_current(pchg, val); | ||
370 | case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: | ||
371 | return lp8788_get_charging_termination_voltage(pchg, val); | ||
372 | default: | ||
373 | return -EINVAL; | ||
374 | } | ||
375 | } | ||
376 | |||
377 | static inline bool lp8788_is_valid_charger_register(u8 addr) | ||
378 | { | ||
379 | return addr >= LP8788_CHG_START && addr <= LP8788_CHG_END; | ||
380 | } | ||
381 | |||
382 | static int lp8788_update_charger_params(struct lp8788_charger *pchg) | ||
383 | { | ||
384 | struct lp8788 *lp = pchg->lp; | ||
385 | struct lp8788_charger_platform_data *pdata = pchg->pdata; | ||
386 | struct lp8788_chg_param *param; | ||
387 | int i; | ||
388 | int ret; | ||
389 | |||
390 | if (!pdata || !pdata->chg_params) { | ||
391 | dev_info(lp->dev, "skip updating charger parameters\n"); | ||
392 | return 0; | ||
393 | } | ||
394 | |||
395 | /* settting charging parameters */ | ||
396 | for (i = 0; i < pdata->num_chg_params; i++) { | ||
397 | param = pdata->chg_params + i; | ||
398 | |||
399 | if (!param) | ||
400 | continue; | ||
401 | |||
402 | if (lp8788_is_valid_charger_register(param->addr)) { | ||
403 | ret = lp8788_write_byte(lp, param->addr, param->val); | ||
404 | if (ret) | ||
405 | return ret; | ||
406 | } | ||
407 | } | ||
408 | |||
409 | return 0; | ||
410 | } | ||
411 | |||
412 | static int lp8788_psy_register(struct platform_device *pdev, | ||
413 | struct lp8788_charger *pchg) | ||
414 | { | ||
415 | pchg->charger.name = LP8788_CHARGER_NAME; | ||
416 | pchg->charger.type = POWER_SUPPLY_TYPE_MAINS; | ||
417 | pchg->charger.properties = lp8788_charger_prop; | ||
418 | pchg->charger.num_properties = ARRAY_SIZE(lp8788_charger_prop); | ||
419 | pchg->charger.get_property = lp8788_charger_get_property; | ||
420 | pchg->charger.supplied_to = battery_supplied_to; | ||
421 | pchg->charger.num_supplicants = ARRAY_SIZE(battery_supplied_to); | ||
422 | |||
423 | if (power_supply_register(&pdev->dev, &pchg->charger)) | ||
424 | return -EPERM; | ||
425 | |||
426 | pchg->battery.name = LP8788_BATTERY_NAME; | ||
427 | pchg->battery.type = POWER_SUPPLY_TYPE_BATTERY; | ||
428 | pchg->battery.properties = lp8788_battery_prop; | ||
429 | pchg->battery.num_properties = ARRAY_SIZE(lp8788_battery_prop); | ||
430 | pchg->battery.get_property = lp8788_battery_get_property; | ||
431 | |||
432 | if (power_supply_register(&pdev->dev, &pchg->battery)) | ||
433 | return -EPERM; | ||
434 | |||
435 | return 0; | ||
436 | } | ||
437 | |||
438 | static void lp8788_psy_unregister(struct lp8788_charger *pchg) | ||
439 | { | ||
440 | power_supply_unregister(&pchg->battery); | ||
441 | power_supply_unregister(&pchg->charger); | ||
442 | } | ||
443 | |||
444 | static void lp8788_charger_event(struct work_struct *work) | ||
445 | { | ||
446 | struct lp8788_charger *pchg = | ||
447 | container_of(work, struct lp8788_charger, charger_work); | ||
448 | struct lp8788_charger_platform_data *pdata = pchg->pdata; | ||
449 | enum lp8788_charger_event event = lp8788_is_charger_detected(pchg); | ||
450 | |||
451 | pdata->charger_event(pchg->lp, event); | ||
452 | } | ||
453 | |||
454 | static bool lp8788_find_irq_id(struct lp8788_charger *pchg, int virq, int *id) | ||
455 | { | ||
456 | bool found; | ||
457 | int i; | ||
458 | |||
459 | for (i = 0; i < pchg->num_irqs; i++) { | ||
460 | if (pchg->irqs[i].virq == virq) { | ||
461 | *id = pchg->irqs[i].which; | ||
462 | found = true; | ||
463 | break; | ||
464 | } | ||
465 | } | ||
466 | |||
467 | return found; | ||
468 | } | ||
469 | |||
470 | static irqreturn_t lp8788_charger_irq_thread(int virq, void *ptr) | ||
471 | { | ||
472 | struct lp8788_charger *pchg = ptr; | ||
473 | struct lp8788_charger_platform_data *pdata = pchg->pdata; | ||
474 | int id = -1; | ||
475 | |||
476 | if (!lp8788_find_irq_id(pchg, virq, &id)) | ||
477 | return IRQ_NONE; | ||
478 | |||
479 | switch (id) { | ||
480 | case LP8788_INT_CHG_INPUT_STATE: | ||
481 | case LP8788_INT_CHG_STATE: | ||
482 | case LP8788_INT_EOC: | ||
483 | case LP8788_INT_BATT_LOW: | ||
484 | case LP8788_INT_NO_BATT: | ||
485 | power_supply_changed(&pchg->charger); | ||
486 | power_supply_changed(&pchg->battery); | ||
487 | break; | ||
488 | default: | ||
489 | break; | ||
490 | } | ||
491 | |||
492 | /* report charger dectection event if used */ | ||
493 | if (!pdata) | ||
494 | goto irq_handled; | ||
495 | |||
496 | if (pdata->charger_event && id == LP8788_INT_CHG_INPUT_STATE) | ||
497 | schedule_work(&pchg->charger_work); | ||
498 | |||
499 | irq_handled: | ||
500 | return IRQ_HANDLED; | ||
501 | } | ||
502 | |||
503 | static int lp8788_set_irqs(struct platform_device *pdev, | ||
504 | struct lp8788_charger *pchg, const char *name) | ||
505 | { | ||
506 | struct resource *r; | ||
507 | struct irq_domain *irqdm = pchg->lp->irqdm; | ||
508 | int irq_start; | ||
509 | int irq_end; | ||
510 | int virq; | ||
511 | int nr_irq; | ||
512 | int i; | ||
513 | int ret; | ||
514 | |||
515 | /* no error even if no irq resource */ | ||
516 | r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, name); | ||
517 | if (!r) | ||
518 | return 0; | ||
519 | |||
520 | irq_start = r->start; | ||
521 | irq_end = r->end; | ||
522 | |||
523 | for (i = irq_start; i <= irq_end; i++) { | ||
524 | nr_irq = pchg->num_irqs; | ||
525 | |||
526 | virq = irq_create_mapping(irqdm, i); | ||
527 | pchg->irqs[nr_irq].virq = virq; | ||
528 | pchg->irqs[nr_irq].which = i; | ||
529 | pchg->num_irqs++; | ||
530 | |||
531 | ret = request_threaded_irq(virq, NULL, | ||
532 | lp8788_charger_irq_thread, | ||
533 | 0, name, pchg); | ||
534 | if (ret) | ||
535 | break; | ||
536 | } | ||
537 | |||
538 | if (i <= irq_end) | ||
539 | goto err_free_irq; | ||
540 | |||
541 | return 0; | ||
542 | |||
543 | err_free_irq: | ||
544 | for (i = 0; i < pchg->num_irqs; i++) | ||
545 | free_irq(pchg->irqs[i].virq, pchg); | ||
546 | return ret; | ||
547 | } | ||
548 | |||
549 | static int lp8788_irq_register(struct platform_device *pdev, | ||
550 | struct lp8788_charger *pchg) | ||
551 | { | ||
552 | struct lp8788 *lp = pchg->lp; | ||
553 | const char *name[] = { | ||
554 | LP8788_CHG_IRQ, LP8788_PRSW_IRQ, LP8788_BATT_IRQ | ||
555 | }; | ||
556 | int i; | ||
557 | int ret; | ||
558 | |||
559 | INIT_WORK(&pchg->charger_work, lp8788_charger_event); | ||
560 | pchg->num_irqs = 0; | ||
561 | |||
562 | for (i = 0; i < ARRAY_SIZE(name); i++) { | ||
563 | ret = lp8788_set_irqs(pdev, pchg, name[i]); | ||
564 | if (ret) { | ||
565 | dev_warn(lp->dev, "irq setup failed: %s\n", name[i]); | ||
566 | return ret; | ||
567 | } | ||
568 | } | ||
569 | |||
570 | if (pchg->num_irqs > LP8788_MAX_CHG_IRQS) { | ||
571 | dev_err(lp->dev, "invalid total number of irqs: %d\n", | ||
572 | pchg->num_irqs); | ||
573 | return -EINVAL; | ||
574 | } | ||
575 | |||
576 | |||
577 | return 0; | ||
578 | } | ||
579 | |||
580 | static void lp8788_irq_unregister(struct platform_device *pdev, | ||
581 | struct lp8788_charger *pchg) | ||
582 | { | ||
583 | int i; | ||
584 | int irq; | ||
585 | |||
586 | for (i = 0; i < pchg->num_irqs; i++) { | ||
587 | irq = pchg->irqs[i].virq; | ||
588 | if (!irq) | ||
589 | continue; | ||
590 | |||
591 | free_irq(irq, pchg); | ||
592 | } | ||
593 | } | ||
594 | |||
595 | static void lp8788_setup_adc_channel(struct lp8788_charger *pchg) | ||
596 | { | ||
597 | struct lp8788_charger_platform_data *pdata = pchg->pdata; | ||
598 | struct device *dev = pchg->lp->dev; | ||
599 | struct iio_channel *chan; | ||
600 | enum lp8788_adc_id id; | ||
601 | const char *chan_name[LPADC_MAX] = { | ||
602 | [LPADC_VBATT_5P5] = "vbatt-5p5", | ||
603 | [LPADC_VBATT_6P0] = "vbatt-6p0", | ||
604 | [LPADC_VBATT_5P0] = "vbatt-5p0", | ||
605 | [LPADC_ADC1] = "adc1", | ||
606 | [LPADC_ADC2] = "adc2", | ||
607 | [LPADC_ADC3] = "adc3", | ||
608 | [LPADC_ADC4] = "adc4", | ||
609 | }; | ||
610 | |||
611 | if (!pdata) | ||
612 | return; | ||
613 | |||
614 | id = pdata->vbatt_adc; | ||
615 | switch (id) { | ||
616 | case LPADC_VBATT_5P5: | ||
617 | case LPADC_VBATT_6P0: | ||
618 | case LPADC_VBATT_5P0: | ||
619 | chan = iio_channel_get(NULL, chan_name[id]); | ||
620 | pchg->chan[LP8788_VBATT] = IS_ERR(chan) ? NULL : chan; | ||
621 | break; | ||
622 | default: | ||
623 | dev_err(dev, "invalid ADC id for VBATT: %d\n", id); | ||
624 | pchg->chan[LP8788_VBATT] = NULL; | ||
625 | break; | ||
626 | } | ||
627 | |||
628 | id = pdata->batt_temp_adc; | ||
629 | switch (id) { | ||
630 | case LPADC_ADC1: | ||
631 | case LPADC_ADC2: | ||
632 | case LPADC_ADC3: | ||
633 | case LPADC_ADC4: | ||
634 | chan = iio_channel_get(NULL, chan_name[id]); | ||
635 | pchg->chan[LP8788_BATT_TEMP] = IS_ERR(chan) ? NULL : chan; | ||
636 | break; | ||
637 | default: | ||
638 | dev_err(dev, "invalid ADC id for BATT_TEMP : %d\n", id); | ||
639 | pchg->chan[LP8788_BATT_TEMP] = NULL; | ||
640 | break; | ||
641 | } | ||
642 | } | ||
643 | |||
644 | static void lp8788_release_adc_channel(struct lp8788_charger *pchg) | ||
645 | { | ||
646 | int i; | ||
647 | |||
648 | for (i = 0; i < LP8788_NUM_CHG_ADC; i++) { | ||
649 | if (!pchg->chan[i]) | ||
650 | continue; | ||
651 | |||
652 | iio_channel_release(pchg->chan[i]); | ||
653 | pchg->chan[i] = NULL; | ||
654 | } | ||
655 | } | ||
656 | |||
657 | static ssize_t lp8788_show_charger_status(struct device *dev, | ||
658 | struct device_attribute *attr, char *buf) | ||
659 | { | ||
660 | struct lp8788_charger *pchg = dev_get_drvdata(dev); | ||
661 | enum lp8788_charging_state state; | ||
662 | char *desc[LP8788_MAX_CHG_STATE] = { | ||
663 | [LP8788_OFF] = "CHARGER OFF", | ||
664 | [LP8788_WARM_UP] = "WARM UP", | ||
665 | [LP8788_LOW_INPUT] = "LOW INPUT STATE", | ||
666 | [LP8788_PRECHARGE] = "CHARGING - PRECHARGE", | ||
667 | [LP8788_CC] = "CHARGING - CC", | ||
668 | [LP8788_CV] = "CHARGING - CV", | ||
669 | [LP8788_MAINTENANCE] = "NO CHARGING - MAINTENANCE", | ||
670 | [LP8788_BATTERY_FAULT] = "BATTERY FAULT", | ||
671 | [LP8788_SYSTEM_SUPPORT] = "SYSTEM SUPPORT", | ||
672 | [LP8788_HIGH_CURRENT] = "HIGH CURRENT", | ||
673 | }; | ||
674 | u8 data; | ||
675 | |||
676 | lp8788_read_byte(pchg->lp, LP8788_CHG_STATUS, &data); | ||
677 | state = (data & LP8788_CHG_STATE_M) >> LP8788_CHG_STATE_S; | ||
678 | |||
679 | return scnprintf(buf, LP8788_BUF_SIZE, "%s\n", desc[state]); | ||
680 | } | ||
681 | |||
682 | static ssize_t lp8788_show_eoc_time(struct device *dev, | ||
683 | struct device_attribute *attr, char *buf) | ||
684 | { | ||
685 | struct lp8788_charger *pchg = dev_get_drvdata(dev); | ||
686 | char *stime[] = { "400ms", "5min", "10min", "15min", | ||
687 | "20min", "25min", "30min" "No timeout" }; | ||
688 | u8 val; | ||
689 | |||
690 | lp8788_read_byte(pchg->lp, LP8788_CHG_EOC, &val); | ||
691 | val = (val & LP8788_CHG_EOC_TIME_M) >> LP8788_CHG_EOC_TIME_S; | ||
692 | |||
693 | return scnprintf(buf, LP8788_BUF_SIZE, "End Of Charge Time: %s\n", | ||
694 | stime[val]); | ||
695 | } | ||
696 | |||
697 | static ssize_t lp8788_show_eoc_level(struct device *dev, | ||
698 | struct device_attribute *attr, char *buf) | ||
699 | { | ||
700 | struct lp8788_charger *pchg = dev_get_drvdata(dev); | ||
701 | char *abs_level[] = { "25mA", "49mA", "75mA", "98mA" }; | ||
702 | char *relative_level[] = { "5%", "10%", "15%", "20%" }; | ||
703 | char *level; | ||
704 | u8 val; | ||
705 | u8 mode; | ||
706 | |||
707 | lp8788_read_byte(pchg->lp, LP8788_CHG_EOC, &val); | ||
708 | |||
709 | mode = val & LP8788_CHG_EOC_MODE_M; | ||
710 | val = (val & LP8788_CHG_EOC_LEVEL_M) >> LP8788_CHG_EOC_LEVEL_S; | ||
711 | level = mode ? abs_level[val] : relative_level[val]; | ||
712 | |||
713 | return scnprintf(buf, LP8788_BUF_SIZE, "End Of Charge Level: %s\n", | ||
714 | level); | ||
715 | } | ||
716 | |||
717 | static DEVICE_ATTR(charger_status, S_IRUSR, lp8788_show_charger_status, NULL); | ||
718 | static DEVICE_ATTR(eoc_time, S_IRUSR, lp8788_show_eoc_time, NULL); | ||
719 | static DEVICE_ATTR(eoc_level, S_IRUSR, lp8788_show_eoc_level, NULL); | ||
720 | |||
721 | static struct attribute *lp8788_charger_attr[] = { | ||
722 | &dev_attr_charger_status.attr, | ||
723 | &dev_attr_eoc_time.attr, | ||
724 | &dev_attr_eoc_level.attr, | ||
725 | NULL, | ||
726 | }; | ||
727 | |||
728 | static const struct attribute_group lp8788_attr_group = { | ||
729 | .attrs = lp8788_charger_attr, | ||
730 | }; | ||
731 | |||
732 | static __devinit int lp8788_charger_probe(struct platform_device *pdev) | ||
733 | { | ||
734 | struct lp8788 *lp = dev_get_drvdata(pdev->dev.parent); | ||
735 | struct lp8788_charger *pchg; | ||
736 | int ret; | ||
737 | |||
738 | pchg = devm_kzalloc(lp->dev, sizeof(struct lp8788_charger), GFP_KERNEL); | ||
739 | if (!pchg) | ||
740 | return -ENOMEM; | ||
741 | |||
742 | pchg->lp = lp; | ||
743 | pchg->pdata = lp->pdata ? lp->pdata->chg_pdata : NULL; | ||
744 | platform_set_drvdata(pdev, pchg); | ||
745 | |||
746 | ret = lp8788_update_charger_params(pchg); | ||
747 | if (ret) | ||
748 | return ret; | ||
749 | |||
750 | lp8788_setup_adc_channel(pchg); | ||
751 | |||
752 | ret = lp8788_psy_register(pdev, pchg); | ||
753 | if (ret) | ||
754 | return ret; | ||
755 | |||
756 | ret = sysfs_create_group(&pdev->dev.kobj, &lp8788_attr_group); | ||
757 | if (ret) { | ||
758 | lp8788_psy_unregister(pchg); | ||
759 | return ret; | ||
760 | } | ||
761 | |||
762 | ret = lp8788_irq_register(pdev, pchg); | ||
763 | if (ret) | ||
764 | dev_warn(lp->dev, "failed to register charger irq: %d\n", ret); | ||
765 | |||
766 | return 0; | ||
767 | } | ||
768 | |||
769 | static int __devexit lp8788_charger_remove(struct platform_device *pdev) | ||
770 | { | ||
771 | struct lp8788_charger *pchg = platform_get_drvdata(pdev); | ||
772 | |||
773 | flush_work(&pchg->charger_work); | ||
774 | lp8788_irq_unregister(pdev, pchg); | ||
775 | sysfs_remove_group(&pdev->dev.kobj, &lp8788_attr_group); | ||
776 | lp8788_psy_unregister(pchg); | ||
777 | lp8788_release_adc_channel(pchg); | ||
778 | |||
779 | return 0; | ||
780 | } | ||
781 | |||
782 | static struct platform_driver lp8788_charger_driver = { | ||
783 | .probe = lp8788_charger_probe, | ||
784 | .remove = __devexit_p(lp8788_charger_remove), | ||
785 | .driver = { | ||
786 | .name = LP8788_DEV_CHARGER, | ||
787 | .owner = THIS_MODULE, | ||
788 | }, | ||
789 | }; | ||
790 | module_platform_driver(lp8788_charger_driver); | ||
791 | |||
792 | MODULE_DESCRIPTION("TI LP8788 Charger Driver"); | ||
793 | MODULE_AUTHOR("Milo Kim"); | ||
794 | MODULE_LICENSE("GPL"); | ||
795 | MODULE_ALIAS("platform:lp8788-charger"); | ||
diff --git a/drivers/power/pda_power.c b/drivers/power/pda_power.c index 7312f2651647..7df7c5facc10 100644 --- a/drivers/power/pda_power.c +++ b/drivers/power/pda_power.c | |||
@@ -281,6 +281,12 @@ static int pda_power_probe(struct platform_device *pdev) | |||
281 | goto init_failed; | 281 | goto init_failed; |
282 | } | 282 | } |
283 | 283 | ||
284 | ac_draw = regulator_get(dev, "ac_draw"); | ||
285 | if (IS_ERR(ac_draw)) { | ||
286 | dev_dbg(dev, "couldn't get ac_draw regulator\n"); | ||
287 | ac_draw = NULL; | ||
288 | } | ||
289 | |||
284 | update_status(); | 290 | update_status(); |
285 | update_charger(); | 291 | update_charger(); |
286 | 292 | ||
@@ -309,13 +315,6 @@ static int pda_power_probe(struct platform_device *pdev) | |||
309 | pda_psy_usb.num_supplicants = pdata->num_supplicants; | 315 | pda_psy_usb.num_supplicants = pdata->num_supplicants; |
310 | } | 316 | } |
311 | 317 | ||
312 | ac_draw = regulator_get(dev, "ac_draw"); | ||
313 | if (IS_ERR(ac_draw)) { | ||
314 | dev_dbg(dev, "couldn't get ac_draw regulator\n"); | ||
315 | ac_draw = NULL; | ||
316 | ret = PTR_ERR(ac_draw); | ||
317 | } | ||
318 | |||
319 | #ifdef CONFIG_USB_OTG_UTILS | 318 | #ifdef CONFIG_USB_OTG_UTILS |
320 | transceiver = usb_get_phy(USB_PHY_TYPE_USB2); | 319 | transceiver = usb_get_phy(USB_PHY_TYPE_USB2); |
321 | if (!IS_ERR_OR_NULL(transceiver)) { | 320 | if (!IS_ERR_OR_NULL(transceiver)) { |
diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index 1d96614a17a4..395c2cfa16c0 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c | |||
@@ -138,6 +138,7 @@ static struct device_attribute power_supply_attrs[] = { | |||
138 | POWER_SUPPLY_ATTR(health), | 138 | POWER_SUPPLY_ATTR(health), |
139 | POWER_SUPPLY_ATTR(present), | 139 | POWER_SUPPLY_ATTR(present), |
140 | POWER_SUPPLY_ATTR(online), | 140 | POWER_SUPPLY_ATTR(online), |
141 | POWER_SUPPLY_ATTR(authentic), | ||
141 | POWER_SUPPLY_ATTR(technology), | 142 | POWER_SUPPLY_ATTR(technology), |
142 | POWER_SUPPLY_ATTR(cycle_count), | 143 | POWER_SUPPLY_ATTR(cycle_count), |
143 | POWER_SUPPLY_ATTR(voltage_max), | 144 | POWER_SUPPLY_ATTR(voltage_max), |
@@ -160,7 +161,9 @@ static struct device_attribute power_supply_attrs[] = { | |||
160 | POWER_SUPPLY_ATTR(charge_avg), | 161 | POWER_SUPPLY_ATTR(charge_avg), |
161 | POWER_SUPPLY_ATTR(charge_counter), | 162 | POWER_SUPPLY_ATTR(charge_counter), |
162 | POWER_SUPPLY_ATTR(constant_charge_current), | 163 | POWER_SUPPLY_ATTR(constant_charge_current), |
164 | POWER_SUPPLY_ATTR(constant_charge_current_max), | ||
163 | POWER_SUPPLY_ATTR(constant_charge_voltage), | 165 | POWER_SUPPLY_ATTR(constant_charge_voltage), |
166 | POWER_SUPPLY_ATTR(constant_charge_voltage_max), | ||
164 | POWER_SUPPLY_ATTR(energy_full_design), | 167 | POWER_SUPPLY_ATTR(energy_full_design), |
165 | POWER_SUPPLY_ATTR(energy_empty_design), | 168 | POWER_SUPPLY_ATTR(energy_empty_design), |
166 | POWER_SUPPLY_ATTR(energy_full), | 169 | POWER_SUPPLY_ATTR(energy_full), |
diff --git a/drivers/power/sbs-battery.c b/drivers/power/sbs-battery.c index a65e8f54157e..4146596d254b 100644 --- a/drivers/power/sbs-battery.c +++ b/drivers/power/sbs-battery.c | |||
@@ -759,6 +759,16 @@ static int __devinit sbs_probe(struct i2c_client *client, | |||
759 | chip->irq = irq; | 759 | chip->irq = irq; |
760 | 760 | ||
761 | skip_gpio: | 761 | skip_gpio: |
762 | /* | ||
763 | * Before we register, we need to make sure we can actually talk | ||
764 | * to the battery. | ||
765 | */ | ||
766 | rc = sbs_read_word_data(client, sbs_data[REG_STATUS].addr); | ||
767 | if (rc < 0) { | ||
768 | dev_err(&client->dev, "%s: Failed to get device status\n", | ||
769 | __func__); | ||
770 | goto exit_psupply; | ||
771 | } | ||
762 | 772 | ||
763 | rc = power_supply_register(&client->dev, &chip->power_supply); | 773 | rc = power_supply_register(&client->dev, &chip->power_supply); |
764 | if (rc) { | 774 | if (rc) { |
diff --git a/drivers/power/smb347-charger.c b/drivers/power/smb347-charger.c index 332dd0110bda..a9707c11fbed 100644 --- a/drivers/power/smb347-charger.c +++ b/drivers/power/smb347-charger.c | |||
@@ -80,6 +80,7 @@ | |||
80 | #define CFG_FAULT_IRQ_DCIN_UV BIT(2) | 80 | #define CFG_FAULT_IRQ_DCIN_UV BIT(2) |
81 | #define CFG_STATUS_IRQ 0x0d | 81 | #define CFG_STATUS_IRQ 0x0d |
82 | #define CFG_STATUS_IRQ_TERMINATION_OR_TAPER BIT(4) | 82 | #define CFG_STATUS_IRQ_TERMINATION_OR_TAPER BIT(4) |
83 | #define CFG_STATUS_IRQ_CHARGE_TIMEOUT BIT(7) | ||
83 | #define CFG_ADDRESS 0x0e | 84 | #define CFG_ADDRESS 0x0e |
84 | 85 | ||
85 | /* Command registers */ | 86 | /* Command registers */ |
@@ -96,6 +97,9 @@ | |||
96 | #define IRQSTAT_C_TERMINATION_STAT BIT(0) | 97 | #define IRQSTAT_C_TERMINATION_STAT BIT(0) |
97 | #define IRQSTAT_C_TERMINATION_IRQ BIT(1) | 98 | #define IRQSTAT_C_TERMINATION_IRQ BIT(1) |
98 | #define IRQSTAT_C_TAPER_IRQ BIT(3) | 99 | #define IRQSTAT_C_TAPER_IRQ BIT(3) |
100 | #define IRQSTAT_D 0x38 | ||
101 | #define IRQSTAT_D_CHARGE_TIMEOUT_STAT BIT(2) | ||
102 | #define IRQSTAT_D_CHARGE_TIMEOUT_IRQ BIT(3) | ||
99 | #define IRQSTAT_E 0x39 | 103 | #define IRQSTAT_E 0x39 |
100 | #define IRQSTAT_E_USBIN_UV_STAT BIT(0) | 104 | #define IRQSTAT_E_USBIN_UV_STAT BIT(0) |
101 | #define IRQSTAT_E_USBIN_UV_IRQ BIT(1) | 105 | #define IRQSTAT_E_USBIN_UV_IRQ BIT(1) |
@@ -109,8 +113,10 @@ | |||
109 | #define STAT_B 0x3c | 113 | #define STAT_B 0x3c |
110 | #define STAT_C 0x3d | 114 | #define STAT_C 0x3d |
111 | #define STAT_C_CHG_ENABLED BIT(0) | 115 | #define STAT_C_CHG_ENABLED BIT(0) |
116 | #define STAT_C_HOLDOFF_STAT BIT(3) | ||
112 | #define STAT_C_CHG_MASK 0x06 | 117 | #define STAT_C_CHG_MASK 0x06 |
113 | #define STAT_C_CHG_SHIFT 1 | 118 | #define STAT_C_CHG_SHIFT 1 |
119 | #define STAT_C_CHG_TERM BIT(5) | ||
114 | #define STAT_C_CHARGER_ERROR BIT(6) | 120 | #define STAT_C_CHARGER_ERROR BIT(6) |
115 | #define STAT_E 0x3f | 121 | #define STAT_E 0x3f |
116 | 122 | ||
@@ -701,7 +707,7 @@ fail: | |||
701 | static irqreturn_t smb347_interrupt(int irq, void *data) | 707 | static irqreturn_t smb347_interrupt(int irq, void *data) |
702 | { | 708 | { |
703 | struct smb347_charger *smb = data; | 709 | struct smb347_charger *smb = data; |
704 | unsigned int stat_c, irqstat_e, irqstat_c; | 710 | unsigned int stat_c, irqstat_c, irqstat_d, irqstat_e; |
705 | bool handled = false; | 711 | bool handled = false; |
706 | int ret; | 712 | int ret; |
707 | 713 | ||
@@ -717,6 +723,12 @@ static irqreturn_t smb347_interrupt(int irq, void *data) | |||
717 | return IRQ_NONE; | 723 | return IRQ_NONE; |
718 | } | 724 | } |
719 | 725 | ||
726 | ret = regmap_read(smb->regmap, IRQSTAT_D, &irqstat_d); | ||
727 | if (ret < 0) { | ||
728 | dev_warn(smb->dev, "reading IRQSTAT_D failed\n"); | ||
729 | return IRQ_NONE; | ||
730 | } | ||
731 | |||
720 | ret = regmap_read(smb->regmap, IRQSTAT_E, &irqstat_e); | 732 | ret = regmap_read(smb->regmap, IRQSTAT_E, &irqstat_e); |
721 | if (ret < 0) { | 733 | if (ret < 0) { |
722 | dev_warn(smb->dev, "reading IRQSTAT_E failed\n"); | 734 | dev_warn(smb->dev, "reading IRQSTAT_E failed\n"); |
@@ -724,13 +736,11 @@ static irqreturn_t smb347_interrupt(int irq, void *data) | |||
724 | } | 736 | } |
725 | 737 | ||
726 | /* | 738 | /* |
727 | * If we get charger error we report the error back to user and | 739 | * If we get charger error we report the error back to user. |
728 | * disable charging. | 740 | * If the error is recovered charging will resume again. |
729 | */ | 741 | */ |
730 | if (stat_c & STAT_C_CHARGER_ERROR) { | 742 | if (stat_c & STAT_C_CHARGER_ERROR) { |
731 | dev_err(smb->dev, "error in charger, disabling charging\n"); | 743 | dev_err(smb->dev, "charging stopped due to charger error\n"); |
732 | |||
733 | smb347_charging_disable(smb); | ||
734 | power_supply_changed(&smb->battery); | 744 | power_supply_changed(&smb->battery); |
735 | handled = true; | 745 | handled = true; |
736 | } | 746 | } |
@@ -743,6 +753,20 @@ static irqreturn_t smb347_interrupt(int irq, void *data) | |||
743 | if (irqstat_c & (IRQSTAT_C_TERMINATION_IRQ | IRQSTAT_C_TAPER_IRQ)) { | 753 | if (irqstat_c & (IRQSTAT_C_TERMINATION_IRQ | IRQSTAT_C_TAPER_IRQ)) { |
744 | if (irqstat_c & IRQSTAT_C_TERMINATION_STAT) | 754 | if (irqstat_c & IRQSTAT_C_TERMINATION_STAT) |
745 | power_supply_changed(&smb->battery); | 755 | power_supply_changed(&smb->battery); |
756 | dev_dbg(smb->dev, "going to HW maintenance mode\n"); | ||
757 | handled = true; | ||
758 | } | ||
759 | |||
760 | /* | ||
761 | * If we got a charger timeout INT that means the charge | ||
762 | * full is not detected with in charge timeout value. | ||
763 | */ | ||
764 | if (irqstat_d & IRQSTAT_D_CHARGE_TIMEOUT_IRQ) { | ||
765 | dev_dbg(smb->dev, "total Charge Timeout INT received\n"); | ||
766 | |||
767 | if (irqstat_d & IRQSTAT_D_CHARGE_TIMEOUT_STAT) | ||
768 | dev_warn(smb->dev, "charging stopped due to timeout\n"); | ||
769 | power_supply_changed(&smb->battery); | ||
746 | handled = true; | 770 | handled = true; |
747 | } | 771 | } |
748 | 772 | ||
@@ -776,6 +800,7 @@ static int smb347_irq_set(struct smb347_charger *smb, bool enable) | |||
776 | * Enable/disable interrupts for: | 800 | * Enable/disable interrupts for: |
777 | * - under voltage | 801 | * - under voltage |
778 | * - termination current reached | 802 | * - termination current reached |
803 | * - charger timeout | ||
779 | * - charger error | 804 | * - charger error |
780 | */ | 805 | */ |
781 | ret = regmap_update_bits(smb->regmap, CFG_FAULT_IRQ, 0xff, | 806 | ret = regmap_update_bits(smb->regmap, CFG_FAULT_IRQ, 0xff, |
@@ -784,7 +809,8 @@ static int smb347_irq_set(struct smb347_charger *smb, bool enable) | |||
784 | goto fail; | 809 | goto fail; |
785 | 810 | ||
786 | ret = regmap_update_bits(smb->regmap, CFG_STATUS_IRQ, 0xff, | 811 | ret = regmap_update_bits(smb->regmap, CFG_STATUS_IRQ, 0xff, |
787 | enable ? CFG_STATUS_IRQ_TERMINATION_OR_TAPER : 0); | 812 | enable ? (CFG_STATUS_IRQ_TERMINATION_OR_TAPER | |
813 | CFG_STATUS_IRQ_CHARGE_TIMEOUT) : 0); | ||
788 | if (ret < 0) | 814 | if (ret < 0) |
789 | goto fail; | 815 | goto fail; |
790 | 816 | ||
@@ -988,6 +1014,51 @@ static enum power_supply_property smb347_usb_properties[] = { | |||
988 | POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, | 1014 | POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, |
989 | }; | 1015 | }; |
990 | 1016 | ||
1017 | static int smb347_get_charging_status(struct smb347_charger *smb) | ||
1018 | { | ||
1019 | int ret, status; | ||
1020 | unsigned int val; | ||
1021 | |||
1022 | if (!smb347_is_ps_online(smb)) | ||
1023 | return POWER_SUPPLY_STATUS_DISCHARGING; | ||
1024 | |||
1025 | ret = regmap_read(smb->regmap, STAT_C, &val); | ||
1026 | if (ret < 0) | ||
1027 | return ret; | ||
1028 | |||
1029 | if ((val & STAT_C_CHARGER_ERROR) || | ||
1030 | (val & STAT_C_HOLDOFF_STAT)) { | ||
1031 | /* | ||
1032 | * set to NOT CHARGING upon charger error | ||
1033 | * or charging has stopped. | ||
1034 | */ | ||
1035 | status = POWER_SUPPLY_STATUS_NOT_CHARGING; | ||
1036 | } else { | ||
1037 | if ((val & STAT_C_CHG_MASK) >> STAT_C_CHG_SHIFT) { | ||
1038 | /* | ||
1039 | * set to charging if battery is in pre-charge, | ||
1040 | * fast charge or taper charging mode. | ||
1041 | */ | ||
1042 | status = POWER_SUPPLY_STATUS_CHARGING; | ||
1043 | } else if (val & STAT_C_CHG_TERM) { | ||
1044 | /* | ||
1045 | * set the status to FULL if battery is not in pre | ||
1046 | * charge, fast charge or taper charging mode AND | ||
1047 | * charging is terminated at least once. | ||
1048 | */ | ||
1049 | status = POWER_SUPPLY_STATUS_FULL; | ||
1050 | } else { | ||
1051 | /* | ||
1052 | * in this case no charger error or termination | ||
1053 | * occured but charging is not in progress!!! | ||
1054 | */ | ||
1055 | status = POWER_SUPPLY_STATUS_NOT_CHARGING; | ||
1056 | } | ||
1057 | } | ||
1058 | |||
1059 | return status; | ||
1060 | } | ||
1061 | |||
991 | static int smb347_battery_get_property(struct power_supply *psy, | 1062 | static int smb347_battery_get_property(struct power_supply *psy, |
992 | enum power_supply_property prop, | 1063 | enum power_supply_property prop, |
993 | union power_supply_propval *val) | 1064 | union power_supply_propval *val) |
@@ -1003,14 +1074,10 @@ static int smb347_battery_get_property(struct power_supply *psy, | |||
1003 | 1074 | ||
1004 | switch (prop) { | 1075 | switch (prop) { |
1005 | case POWER_SUPPLY_PROP_STATUS: | 1076 | case POWER_SUPPLY_PROP_STATUS: |
1006 | if (!smb347_is_ps_online(smb)) { | 1077 | ret = smb347_get_charging_status(smb); |
1007 | val->intval = POWER_SUPPLY_STATUS_DISCHARGING; | 1078 | if (ret < 0) |
1008 | break; | 1079 | return ret; |
1009 | } | 1080 | val->intval = ret; |
1010 | if (smb347_charging_status(smb)) | ||
1011 | val->intval = POWER_SUPPLY_STATUS_CHARGING; | ||
1012 | else | ||
1013 | val->intval = POWER_SUPPLY_STATUS_FULL; | ||
1014 | break; | 1081 | break; |
1015 | 1082 | ||
1016 | case POWER_SUPPLY_PROP_CHARGE_TYPE: | 1083 | case POWER_SUPPLY_PROP_CHARGE_TYPE: |
diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index 15f4d5d8611b..f9e70cf08199 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c | |||
@@ -41,16 +41,16 @@ | |||
41 | #define TWL4030_STS_VBUS BIT(7) | 41 | #define TWL4030_STS_VBUS BIT(7) |
42 | #define TWL4030_STS_USB_ID BIT(2) | 42 | #define TWL4030_STS_USB_ID BIT(2) |
43 | #define TWL4030_BBCHEN BIT(4) | 43 | #define TWL4030_BBCHEN BIT(4) |
44 | #define TWL4030_BBSEL_MASK 0b1100 | 44 | #define TWL4030_BBSEL_MASK 0x0c |
45 | #define TWL4030_BBSEL_2V5 0b0000 | 45 | #define TWL4030_BBSEL_2V5 0x00 |
46 | #define TWL4030_BBSEL_3V0 0b0100 | 46 | #define TWL4030_BBSEL_3V0 0x04 |
47 | #define TWL4030_BBSEL_3V1 0b1000 | 47 | #define TWL4030_BBSEL_3V1 0x08 |
48 | #define TWL4030_BBSEL_3V2 0b1100 | 48 | #define TWL4030_BBSEL_3V2 0x0c |
49 | #define TWL4030_BBISEL_MASK 0b11 | 49 | #define TWL4030_BBISEL_MASK 0x03 |
50 | #define TWL4030_BBISEL_25uA 0b00 | 50 | #define TWL4030_BBISEL_25uA 0x00 |
51 | #define TWL4030_BBISEL_150uA 0b01 | 51 | #define TWL4030_BBISEL_150uA 0x01 |
52 | #define TWL4030_BBISEL_500uA 0b10 | 52 | #define TWL4030_BBISEL_500uA 0x02 |
53 | #define TWL4030_BBISEL_1000uA 0b11 | 53 | #define TWL4030_BBISEL_1000uA 0x03 |
54 | 54 | ||
55 | /* BCI interrupts */ | 55 | /* BCI interrupts */ |
56 | #define TWL4030_WOVF BIT(0) /* Watchdog overflow */ | 56 | #define TWL4030_WOVF BIT(0) /* Watchdog overflow */ |
@@ -534,7 +534,8 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) | |||
534 | } | 534 | } |
535 | 535 | ||
536 | ret = request_threaded_irq(bci->irq_chg, NULL, | 536 | ret = request_threaded_irq(bci->irq_chg, NULL, |
537 | twl4030_charger_interrupt, 0, pdev->name, bci); | 537 | twl4030_charger_interrupt, IRQF_ONESHOT, pdev->name, |
538 | bci); | ||
538 | if (ret < 0) { | 539 | if (ret < 0) { |
539 | dev_err(&pdev->dev, "could not request irq %d, status %d\n", | 540 | dev_err(&pdev->dev, "could not request irq %d, status %d\n", |
540 | bci->irq_chg, ret); | 541 | bci->irq_chg, ret); |
@@ -542,7 +543,7 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) | |||
542 | } | 543 | } |
543 | 544 | ||
544 | ret = request_threaded_irq(bci->irq_bci, NULL, | 545 | ret = request_threaded_irq(bci->irq_bci, NULL, |
545 | twl4030_bci_interrupt, 0, pdev->name, bci); | 546 | twl4030_bci_interrupt, IRQF_ONESHOT, pdev->name, bci); |
546 | if (ret < 0) { | 547 | if (ret < 0) { |
547 | dev_err(&pdev->dev, "could not request irq %d, status %d\n", | 548 | dev_err(&pdev->dev, "could not request irq %d, status %d\n", |
548 | bci->irq_bci, ret); | 549 | bci->irq_bci, ret); |
diff --git a/drivers/power/wm97xx_battery.c b/drivers/power/wm97xx_battery.c index 1245fe1f48c3..e128a813dc24 100644 --- a/drivers/power/wm97xx_battery.c +++ b/drivers/power/wm97xx_battery.c | |||
@@ -212,8 +212,10 @@ static int __devinit wm97xx_bat_probe(struct platform_device *dev) | |||
212 | props++; /* POWER_SUPPLY_PROP_VOLTAGE_MIN */ | 212 | props++; /* POWER_SUPPLY_PROP_VOLTAGE_MIN */ |
213 | 213 | ||
214 | prop = kzalloc(props * sizeof(*prop), GFP_KERNEL); | 214 | prop = kzalloc(props * sizeof(*prop), GFP_KERNEL); |
215 | if (!prop) | 215 | if (!prop) { |
216 | ret = -ENOMEM; | ||
216 | goto err3; | 217 | goto err3; |
218 | } | ||
217 | 219 | ||
218 | prop[i++] = POWER_SUPPLY_PROP_PRESENT; | 220 | prop[i++] = POWER_SUPPLY_PROP_PRESENT; |
219 | if (pdata->charge_gpio >= 0) | 221 | if (pdata->charge_gpio >= 0) |