diff options
author | Laurentiu Palcu <laurentiu.palcu@intel.com> | 2015-04-16 05:31:16 -0400 |
---|---|---|
committer | Sebastian Reichel <sre@kernel.org> | 2015-05-23 12:54:34 -0400 |
commit | 2219a935963e5eeb19e5abfb99475fcc06bbb804 (patch) | |
tree | d9a38544046f1e143d892fa12ba035743ec6d7af | |
parent | 7f1a57fdd6cb6e7be2ed31878a34655df38e1861 (diff) |
power_supply: Add TI BQ24257 charger driver
Based on the datasheet found here:
http://www.ti.com/lit/ds/symlink/bq24257.pdf
Signed-off-by: Laurentiu Palcu <laurentiu.palcu@intel.com>
Reviewed-by: Krzysztof Kozlowski <k.kozlowski.k@gmail.com>
Signed-off-by: Sebastian Reichel <sre@kernel.org>
-rw-r--r-- | Documentation/devicetree/bindings/power/bq24257.txt | 21 | ||||
-rw-r--r-- | drivers/power/Kconfig | 7 | ||||
-rw-r--r-- | drivers/power/Makefile | 1 | ||||
-rw-r--r-- | drivers/power/bq24257_charger.c | 863 |
4 files changed, 892 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/power/bq24257.txt b/Documentation/devicetree/bindings/power/bq24257.txt new file mode 100644 index 000000000000..5c9d3940d07c --- /dev/null +++ b/Documentation/devicetree/bindings/power/bq24257.txt | |||
@@ -0,0 +1,21 @@ | |||
1 | Binding for TI bq24257 Li-Ion Charger | ||
2 | |||
3 | Required properties: | ||
4 | - compatible: Should contain one of the following: | ||
5 | * "ti,bq24257" | ||
6 | - reg: integer, i2c address of the device. | ||
7 | - ti,battery-regulation-voltage: integer, maximum charging voltage in uV. | ||
8 | - ti,charge-current: integer, maximum charging current in uA. | ||
9 | - ti,termination-current: integer, charge will be terminated when current in | ||
10 | constant-voltage phase drops below this value (in uA). | ||
11 | |||
12 | Example: | ||
13 | |||
14 | bq24257 { | ||
15 | compatible = "ti,bq24257"; | ||
16 | reg = <0x6a>; | ||
17 | |||
18 | ti,battery-regulation-voltage = <4200000>; | ||
19 | ti,charge-current = <1000000>; | ||
20 | ti,termination-current = <50000>; | ||
21 | }; | ||
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 4091fb092d06..20352a7a0081 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig | |||
@@ -388,6 +388,13 @@ config CHARGER_BQ24190 | |||
388 | help | 388 | help |
389 | Say Y to enable support for the TI BQ24190 battery charger. | 389 | Say Y to enable support for the TI BQ24190 battery charger. |
390 | 390 | ||
391 | config CHARGER_BQ24257 | ||
392 | tristate "TI BQ24257 battery charger driver" | ||
393 | depends on I2C && GPIOLIB | ||
394 | depends on REGMAP_I2C | ||
395 | help | ||
396 | Say Y to enable support for the TI BQ24257 battery charger. | ||
397 | |||
391 | config CHARGER_BQ24735 | 398 | config CHARGER_BQ24735 |
392 | tristate "TI BQ24735 battery charger support" | 399 | tristate "TI BQ24735 battery charger support" |
393 | depends on I2C && GPIOLIB | 400 | depends on I2C && GPIOLIB |
diff --git a/drivers/power/Makefile b/drivers/power/Makefile index b7b0181c95e5..19a1f4186d4d 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile | |||
@@ -58,6 +58,7 @@ obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o | |||
58 | obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o | 58 | obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o |
59 | obj-$(CONFIG_CHARGER_BQ2415X) += bq2415x_charger.o | 59 | obj-$(CONFIG_CHARGER_BQ2415X) += bq2415x_charger.o |
60 | obj-$(CONFIG_CHARGER_BQ24190) += bq24190_charger.o | 60 | obj-$(CONFIG_CHARGER_BQ24190) += bq24190_charger.o |
61 | obj-$(CONFIG_CHARGER_BQ24257) += bq24257_charger.o | ||
61 | obj-$(CONFIG_CHARGER_BQ24735) += bq24735-charger.o | 62 | obj-$(CONFIG_CHARGER_BQ24735) += bq24735-charger.o |
62 | obj-$(CONFIG_POWER_AVS) += avs/ | 63 | obj-$(CONFIG_POWER_AVS) += avs/ |
63 | obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o | 64 | obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o |
diff --git a/drivers/power/bq24257_charger.c b/drivers/power/bq24257_charger.c new file mode 100644 index 000000000000..ce7f5bbfd5e3 --- /dev/null +++ b/drivers/power/bq24257_charger.c | |||
@@ -0,0 +1,863 @@ | |||
1 | /* | ||
2 | * TI BQ24257 charger driver | ||
3 | * | ||
4 | * Copyright (C) 2015 Intel Corporation | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | */ | ||
17 | |||
18 | #include <linux/module.h> | ||
19 | #include <linux/i2c.h> | ||
20 | #include <linux/power_supply.h> | ||
21 | #include <linux/regmap.h> | ||
22 | #include <linux/types.h> | ||
23 | #include <linux/gpio/consumer.h> | ||
24 | #include <linux/interrupt.h> | ||
25 | #include <linux/delay.h> | ||
26 | |||
27 | #include <linux/acpi.h> | ||
28 | #include <linux/of.h> | ||
29 | |||
30 | #define BQ24257_REG_1 0x00 | ||
31 | #define BQ24257_REG_2 0x01 | ||
32 | #define BQ24257_REG_3 0x02 | ||
33 | #define BQ24257_REG_4 0x03 | ||
34 | #define BQ24257_REG_5 0x04 | ||
35 | #define BQ24257_REG_6 0x05 | ||
36 | #define BQ24257_REG_7 0x06 | ||
37 | |||
38 | #define BQ24257_MANUFACTURER "Texas Instruments" | ||
39 | #define BQ24257_STAT_IRQ "stat" | ||
40 | #define BQ24257_PG_GPIO "pg" | ||
41 | |||
42 | #define BQ24257_ILIM_SET_DELAY 1000 /* msec */ | ||
43 | |||
44 | enum bq24257_fields { | ||
45 | F_WD_FAULT, F_WD_EN, F_STAT, F_FAULT, /* REG 1 */ | ||
46 | F_RESET, F_IILIMIT, F_EN_STAT, F_EN_TERM, F_CE, F_HZ_MODE, /* REG 2 */ | ||
47 | F_VBAT, F_USB_DET, /* REG 3 */ | ||
48 | F_ICHG, F_ITERM, /* REG 4 */ | ||
49 | F_LOOP_STATUS, F_LOW_CHG, F_DPDM_EN, F_CE_STATUS, F_VINDPM, /* REG 5 */ | ||
50 | F_X2_TMR_EN, F_TMR, F_SYSOFF, F_TS_STAT, /* REG 6 */ | ||
51 | F_VOVP, F_CLR_VDP, F_FORCE_BATDET, F_FORCE_PTM, /* REG 7 */ | ||
52 | |||
53 | F_MAX_FIELDS | ||
54 | }; | ||
55 | |||
56 | /* initial field values, converted from uV/uA */ | ||
57 | struct bq24257_init_data { | ||
58 | u8 ichg; /* charge current */ | ||
59 | u8 vbat; /* regulation voltage */ | ||
60 | u8 iterm; /* termination current */ | ||
61 | }; | ||
62 | |||
63 | struct bq24257_state { | ||
64 | u8 status; | ||
65 | u8 fault; | ||
66 | bool power_good; | ||
67 | }; | ||
68 | |||
69 | struct bq24257_device { | ||
70 | struct i2c_client *client; | ||
71 | struct device *dev; | ||
72 | struct power_supply *charger; | ||
73 | |||
74 | struct regmap *rmap; | ||
75 | struct regmap_field *rmap_fields[F_MAX_FIELDS]; | ||
76 | |||
77 | struct gpio_desc *pg; | ||
78 | |||
79 | struct delayed_work iilimit_setup_work; | ||
80 | |||
81 | struct bq24257_init_data init_data; | ||
82 | struct bq24257_state state; | ||
83 | |||
84 | struct mutex lock; /* protect state data */ | ||
85 | }; | ||
86 | |||
87 | static bool bq24257_is_volatile_reg(struct device *dev, unsigned int reg) | ||
88 | { | ||
89 | switch (reg) { | ||
90 | case BQ24257_REG_2: | ||
91 | case BQ24257_REG_4: | ||
92 | return false; | ||
93 | |||
94 | default: | ||
95 | return true; | ||
96 | } | ||
97 | } | ||
98 | |||
99 | static const struct regmap_config bq24257_regmap_config = { | ||
100 | .reg_bits = 8, | ||
101 | .val_bits = 8, | ||
102 | |||
103 | .max_register = BQ24257_REG_7, | ||
104 | .cache_type = REGCACHE_RBTREE, | ||
105 | |||
106 | .volatile_reg = bq24257_is_volatile_reg, | ||
107 | }; | ||
108 | |||
109 | static const struct reg_field bq24257_reg_fields[] = { | ||
110 | /* REG 1 */ | ||
111 | [F_WD_FAULT] = REG_FIELD(BQ24257_REG_1, 7, 7), | ||
112 | [F_WD_EN] = REG_FIELD(BQ24257_REG_1, 6, 6), | ||
113 | [F_STAT] = REG_FIELD(BQ24257_REG_1, 4, 5), | ||
114 | [F_FAULT] = REG_FIELD(BQ24257_REG_1, 0, 3), | ||
115 | /* REG 2 */ | ||
116 | [F_RESET] = REG_FIELD(BQ24257_REG_2, 7, 7), | ||
117 | [F_IILIMIT] = REG_FIELD(BQ24257_REG_2, 4, 6), | ||
118 | [F_EN_STAT] = REG_FIELD(BQ24257_REG_2, 3, 3), | ||
119 | [F_EN_TERM] = REG_FIELD(BQ24257_REG_2, 2, 2), | ||
120 | [F_CE] = REG_FIELD(BQ24257_REG_2, 1, 1), | ||
121 | [F_HZ_MODE] = REG_FIELD(BQ24257_REG_2, 0, 0), | ||
122 | /* REG 3 */ | ||
123 | [F_VBAT] = REG_FIELD(BQ24257_REG_3, 2, 7), | ||
124 | [F_USB_DET] = REG_FIELD(BQ24257_REG_3, 0, 1), | ||
125 | /* REG 4 */ | ||
126 | [F_ICHG] = REG_FIELD(BQ24257_REG_4, 3, 7), | ||
127 | [F_ITERM] = REG_FIELD(BQ24257_REG_4, 0, 2), | ||
128 | /* REG 5 */ | ||
129 | [F_LOOP_STATUS] = REG_FIELD(BQ24257_REG_5, 6, 7), | ||
130 | [F_LOW_CHG] = REG_FIELD(BQ24257_REG_5, 5, 5), | ||
131 | [F_DPDM_EN] = REG_FIELD(BQ24257_REG_5, 4, 4), | ||
132 | [F_CE_STATUS] = REG_FIELD(BQ24257_REG_5, 3, 3), | ||
133 | [F_VINDPM] = REG_FIELD(BQ24257_REG_5, 0, 2), | ||
134 | /* REG 6 */ | ||
135 | [F_X2_TMR_EN] = REG_FIELD(BQ24257_REG_6, 7, 7), | ||
136 | [F_TMR] = REG_FIELD(BQ24257_REG_6, 5, 6), | ||
137 | [F_SYSOFF] = REG_FIELD(BQ24257_REG_6, 4, 4), | ||
138 | [F_TS_STAT] = REG_FIELD(BQ24257_REG_6, 0, 2), | ||
139 | /* REG 7 */ | ||
140 | [F_VOVP] = REG_FIELD(BQ24257_REG_7, 5, 7), | ||
141 | [F_CLR_VDP] = REG_FIELD(BQ24257_REG_7, 4, 4), | ||
142 | [F_FORCE_BATDET] = REG_FIELD(BQ24257_REG_7, 3, 3), | ||
143 | [F_FORCE_PTM] = REG_FIELD(BQ24257_REG_7, 2, 2) | ||
144 | }; | ||
145 | |||
146 | static const u32 bq24257_vbat_map[] = { | ||
147 | 3500000, 3520000, 3540000, 3560000, 3580000, 3600000, 3620000, 3640000, | ||
148 | 3660000, 3680000, 3700000, 3720000, 3740000, 3760000, 3780000, 3800000, | ||
149 | 3820000, 3840000, 3860000, 3880000, 3900000, 3920000, 3940000, 3960000, | ||
150 | 3980000, 4000000, 4020000, 4040000, 4060000, 4080000, 4100000, 4120000, | ||
151 | 4140000, 4160000, 4180000, 4200000, 4220000, 4240000, 4260000, 4280000, | ||
152 | 4300000, 4320000, 4340000, 4360000, 4380000, 4400000, 4420000, 4440000 | ||
153 | }; | ||
154 | |||
155 | #define BQ24257_VBAT_MAP_SIZE ARRAY_SIZE(bq24257_vbat_map) | ||
156 | |||
157 | static const u32 bq24257_ichg_map[] = { | ||
158 | 500000, 550000, 600000, 650000, 700000, 750000, 800000, 850000, 900000, | ||
159 | 950000, 1000000, 1050000, 1100000, 1150000, 1200000, 1250000, 1300000, | ||
160 | 1350000, 1400000, 1450000, 1500000, 1550000, 1600000, 1650000, 1700000, | ||
161 | 1750000, 1800000, 1850000, 1900000, 1950000, 2000000 | ||
162 | }; | ||
163 | |||
164 | #define BQ24257_ICHG_MAP_SIZE ARRAY_SIZE(bq24257_ichg_map) | ||
165 | |||
166 | static const u32 bq24257_iterm_map[] = { | ||
167 | 50000, 75000, 100000, 125000, 150000, 175000, 200000, 225000 | ||
168 | }; | ||
169 | |||
170 | #define BQ24257_ITERM_MAP_SIZE ARRAY_SIZE(bq24257_iterm_map) | ||
171 | |||
172 | static int bq24257_field_read(struct bq24257_device *bq, | ||
173 | enum bq24257_fields field_id) | ||
174 | { | ||
175 | int ret; | ||
176 | int val; | ||
177 | |||
178 | ret = regmap_field_read(bq->rmap_fields[field_id], &val); | ||
179 | if (ret < 0) | ||
180 | return ret; | ||
181 | |||
182 | return val; | ||
183 | } | ||
184 | |||
185 | static int bq24257_field_write(struct bq24257_device *bq, | ||
186 | enum bq24257_fields field_id, u8 val) | ||
187 | { | ||
188 | return regmap_field_write(bq->rmap_fields[field_id], val); | ||
189 | } | ||
190 | |||
191 | static u8 bq24257_find_idx(u32 value, const u32 *map, u8 map_size) | ||
192 | { | ||
193 | u8 idx; | ||
194 | |||
195 | for (idx = 1; idx < map_size; idx++) | ||
196 | if (value < map[idx]) | ||
197 | break; | ||
198 | |||
199 | return idx - 1; | ||
200 | } | ||
201 | |||
202 | enum bq24257_status { | ||
203 | STATUS_READY, | ||
204 | STATUS_CHARGE_IN_PROGRESS, | ||
205 | STATUS_CHARGE_DONE, | ||
206 | STATUS_FAULT, | ||
207 | }; | ||
208 | |||
209 | enum bq24257_fault { | ||
210 | FAULT_NORMAL, | ||
211 | FAULT_INPUT_OVP, | ||
212 | FAULT_INPUT_UVLO, | ||
213 | FAULT_SLEEP, | ||
214 | FAULT_BAT_TS, | ||
215 | FAULT_BAT_OVP, | ||
216 | FAULT_TS, | ||
217 | FAULT_TIMER, | ||
218 | FAULT_NO_BAT, | ||
219 | FAULT_ISET, | ||
220 | FAULT_INPUT_LDO_LOW, | ||
221 | }; | ||
222 | |||
223 | static int bq24257_power_supply_get_property(struct power_supply *psy, | ||
224 | enum power_supply_property psp, | ||
225 | union power_supply_propval *val) | ||
226 | { | ||
227 | struct bq24257_device *bq = power_supply_get_drvdata(psy); | ||
228 | struct bq24257_state state; | ||
229 | |||
230 | mutex_lock(&bq->lock); | ||
231 | state = bq->state; | ||
232 | mutex_unlock(&bq->lock); | ||
233 | |||
234 | switch (psp) { | ||
235 | case POWER_SUPPLY_PROP_STATUS: | ||
236 | if (!state.power_good) | ||
237 | val->intval = POWER_SUPPLY_STATUS_DISCHARGING; | ||
238 | else if (state.status == STATUS_READY) | ||
239 | val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; | ||
240 | else if (state.status == STATUS_CHARGE_IN_PROGRESS) | ||
241 | val->intval = POWER_SUPPLY_STATUS_CHARGING; | ||
242 | else if (state.status == STATUS_CHARGE_DONE) | ||
243 | val->intval = POWER_SUPPLY_STATUS_FULL; | ||
244 | else | ||
245 | val->intval = POWER_SUPPLY_STATUS_UNKNOWN; | ||
246 | break; | ||
247 | |||
248 | case POWER_SUPPLY_PROP_MANUFACTURER: | ||
249 | val->strval = BQ24257_MANUFACTURER; | ||
250 | break; | ||
251 | |||
252 | case POWER_SUPPLY_PROP_ONLINE: | ||
253 | val->intval = state.power_good; | ||
254 | break; | ||
255 | |||
256 | case POWER_SUPPLY_PROP_HEALTH: | ||
257 | switch (state.fault) { | ||
258 | case FAULT_NORMAL: | ||
259 | val->intval = POWER_SUPPLY_HEALTH_GOOD; | ||
260 | break; | ||
261 | |||
262 | case FAULT_INPUT_OVP: | ||
263 | case FAULT_BAT_OVP: | ||
264 | val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE; | ||
265 | break; | ||
266 | |||
267 | case FAULT_TS: | ||
268 | case FAULT_BAT_TS: | ||
269 | val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; | ||
270 | break; | ||
271 | |||
272 | case FAULT_TIMER: | ||
273 | val->intval = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE; | ||
274 | break; | ||
275 | |||
276 | default: | ||
277 | val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; | ||
278 | break; | ||
279 | } | ||
280 | |||
281 | break; | ||
282 | |||
283 | case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: | ||
284 | val->intval = bq24257_ichg_map[bq->init_data.ichg]; | ||
285 | break; | ||
286 | |||
287 | case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: | ||
288 | val->intval = bq24257_ichg_map[BQ24257_ICHG_MAP_SIZE - 1]; | ||
289 | break; | ||
290 | |||
291 | case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: | ||
292 | val->intval = bq24257_vbat_map[bq->init_data.vbat]; | ||
293 | break; | ||
294 | |||
295 | case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: | ||
296 | val->intval = bq24257_vbat_map[BQ24257_VBAT_MAP_SIZE - 1]; | ||
297 | break; | ||
298 | |||
299 | case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT: | ||
300 | val->intval = bq24257_iterm_map[bq->init_data.iterm]; | ||
301 | break; | ||
302 | |||
303 | default: | ||
304 | return -EINVAL; | ||
305 | } | ||
306 | |||
307 | return 0; | ||
308 | } | ||
309 | |||
310 | static int bq24257_get_chip_state(struct bq24257_device *bq, | ||
311 | struct bq24257_state *state) | ||
312 | { | ||
313 | int ret; | ||
314 | |||
315 | ret = bq24257_field_read(bq, F_STAT); | ||
316 | if (ret < 0) | ||
317 | return ret; | ||
318 | |||
319 | state->status = ret; | ||
320 | |||
321 | ret = bq24257_field_read(bq, F_FAULT); | ||
322 | if (ret < 0) | ||
323 | return ret; | ||
324 | |||
325 | state->fault = ret; | ||
326 | |||
327 | state->power_good = !gpiod_get_value_cansleep(bq->pg); | ||
328 | |||
329 | return 0; | ||
330 | } | ||
331 | |||
332 | static bool bq24257_state_changed(struct bq24257_device *bq, | ||
333 | struct bq24257_state *new_state) | ||
334 | { | ||
335 | int ret; | ||
336 | |||
337 | mutex_lock(&bq->lock); | ||
338 | ret = (bq->state.status != new_state->status || | ||
339 | bq->state.fault != new_state->fault || | ||
340 | bq->state.power_good != new_state->power_good); | ||
341 | mutex_unlock(&bq->lock); | ||
342 | |||
343 | return ret; | ||
344 | } | ||
345 | |||
346 | enum bq24257_loop_status { | ||
347 | LOOP_STATUS_NONE, | ||
348 | LOOP_STATUS_IN_DPM, | ||
349 | LOOP_STATUS_IN_CURRENT_LIMIT, | ||
350 | LOOP_STATUS_THERMAL, | ||
351 | }; | ||
352 | |||
353 | enum bq24257_in_ilimit { | ||
354 | IILIMIT_100, | ||
355 | IILIMIT_150, | ||
356 | IILIMIT_500, | ||
357 | IILIMIT_900, | ||
358 | IILIMIT_1500, | ||
359 | IILIMIT_2000, | ||
360 | IILIMIT_EXT, | ||
361 | IILIMIT_NONE, | ||
362 | }; | ||
363 | |||
364 | enum bq24257_port_type { | ||
365 | PORT_TYPE_DCP, /* Dedicated Charging Port */ | ||
366 | PORT_TYPE_CDP, /* Charging Downstream Port */ | ||
367 | PORT_TYPE_SDP, /* Standard Downstream Port */ | ||
368 | PORT_TYPE_NON_STANDARD, | ||
369 | }; | ||
370 | |||
371 | enum bq24257_safety_timer { | ||
372 | SAFETY_TIMER_45, | ||
373 | SAFETY_TIMER_360, | ||
374 | SAFETY_TIMER_540, | ||
375 | SAFETY_TIMER_NONE, | ||
376 | }; | ||
377 | |||
378 | static int bq24257_iilimit_autoset(struct bq24257_device *bq) | ||
379 | { | ||
380 | int loop_status; | ||
381 | int iilimit; | ||
382 | int port_type; | ||
383 | int ret; | ||
384 | const u8 new_iilimit[] = { | ||
385 | [PORT_TYPE_DCP] = IILIMIT_2000, | ||
386 | [PORT_TYPE_CDP] = IILIMIT_2000, | ||
387 | [PORT_TYPE_SDP] = IILIMIT_500, | ||
388 | [PORT_TYPE_NON_STANDARD] = IILIMIT_500 | ||
389 | }; | ||
390 | |||
391 | ret = bq24257_field_read(bq, F_LOOP_STATUS); | ||
392 | if (ret < 0) | ||
393 | goto error; | ||
394 | |||
395 | loop_status = ret; | ||
396 | |||
397 | ret = bq24257_field_read(bq, F_IILIMIT); | ||
398 | if (ret < 0) | ||
399 | goto error; | ||
400 | |||
401 | iilimit = ret; | ||
402 | |||
403 | /* | ||
404 | * All USB ports should be able to handle 500mA. If not, DPM will lower | ||
405 | * the charging current to accommodate the power source. No need to set | ||
406 | * a lower IILIMIT value. | ||
407 | */ | ||
408 | if (loop_status == LOOP_STATUS_IN_DPM && iilimit == IILIMIT_500) | ||
409 | return 0; | ||
410 | |||
411 | ret = bq24257_field_read(bq, F_USB_DET); | ||
412 | if (ret < 0) | ||
413 | goto error; | ||
414 | |||
415 | port_type = ret; | ||
416 | |||
417 | ret = bq24257_field_write(bq, F_IILIMIT, new_iilimit[port_type]); | ||
418 | if (ret < 0) | ||
419 | goto error; | ||
420 | |||
421 | ret = bq24257_field_write(bq, F_TMR, SAFETY_TIMER_360); | ||
422 | if (ret < 0) | ||
423 | goto error; | ||
424 | |||
425 | ret = bq24257_field_write(bq, F_CLR_VDP, 1); | ||
426 | if (ret < 0) | ||
427 | goto error; | ||
428 | |||
429 | dev_dbg(bq->dev, "port/loop = %d/%d -> iilimit = %d\n", | ||
430 | port_type, loop_status, new_iilimit[port_type]); | ||
431 | |||
432 | return 0; | ||
433 | |||
434 | error: | ||
435 | dev_err(bq->dev, "%s: Error communicating with the chip.\n", __func__); | ||
436 | return ret; | ||
437 | } | ||
438 | |||
439 | static void bq24257_iilimit_setup_work(struct work_struct *work) | ||
440 | { | ||
441 | struct bq24257_device *bq = container_of(work, struct bq24257_device, | ||
442 | iilimit_setup_work.work); | ||
443 | |||
444 | bq24257_iilimit_autoset(bq); | ||
445 | } | ||
446 | |||
447 | static void bq24257_handle_state_change(struct bq24257_device *bq, | ||
448 | struct bq24257_state *new_state) | ||
449 | { | ||
450 | int ret; | ||
451 | struct bq24257_state old_state; | ||
452 | bool reset_iilimit = false; | ||
453 | bool config_iilimit = false; | ||
454 | |||
455 | mutex_lock(&bq->lock); | ||
456 | old_state = bq->state; | ||
457 | mutex_unlock(&bq->lock); | ||
458 | |||
459 | if (!new_state->power_good) { /* power removed */ | ||
460 | cancel_delayed_work_sync(&bq->iilimit_setup_work); | ||
461 | |||
462 | /* activate D+/D- port detection algorithm */ | ||
463 | ret = bq24257_field_write(bq, F_DPDM_EN, 1); | ||
464 | if (ret < 0) | ||
465 | goto error; | ||
466 | |||
467 | reset_iilimit = true; | ||
468 | } else if (!old_state.power_good) { /* power inserted */ | ||
469 | config_iilimit = true; | ||
470 | } else if (new_state->fault == FAULT_NO_BAT) { /* battery removed */ | ||
471 | cancel_delayed_work_sync(&bq->iilimit_setup_work); | ||
472 | |||
473 | reset_iilimit = true; | ||
474 | } else if (old_state.fault == FAULT_NO_BAT) { /* battery connected */ | ||
475 | config_iilimit = true; | ||
476 | } else if (new_state->fault == FAULT_TIMER) { /* safety timer expired */ | ||
477 | dev_err(bq->dev, "Safety timer expired! Battery dead?\n"); | ||
478 | } | ||
479 | |||
480 | if (reset_iilimit) { | ||
481 | ret = bq24257_field_write(bq, F_IILIMIT, IILIMIT_500); | ||
482 | if (ret < 0) | ||
483 | goto error; | ||
484 | } else if (config_iilimit) { | ||
485 | schedule_delayed_work(&bq->iilimit_setup_work, | ||
486 | msecs_to_jiffies(BQ24257_ILIM_SET_DELAY)); | ||
487 | } | ||
488 | |||
489 | return; | ||
490 | |||
491 | error: | ||
492 | dev_err(bq->dev, "%s: Error communicating with the chip.\n", __func__); | ||
493 | } | ||
494 | |||
495 | static irqreturn_t bq24257_irq_handler_thread(int irq, void *private) | ||
496 | { | ||
497 | int ret; | ||
498 | struct bq24257_device *bq = private; | ||
499 | struct bq24257_state state; | ||
500 | |||
501 | ret = bq24257_get_chip_state(bq, &state); | ||
502 | if (ret < 0) | ||
503 | return IRQ_HANDLED; | ||
504 | |||
505 | if (!bq24257_state_changed(bq, &state)) | ||
506 | return IRQ_HANDLED; | ||
507 | |||
508 | dev_dbg(bq->dev, "irq(state changed): status/fault/pg = %d/%d/%d\n", | ||
509 | state.status, state.fault, state.power_good); | ||
510 | |||
511 | bq24257_handle_state_change(bq, &state); | ||
512 | |||
513 | mutex_lock(&bq->lock); | ||
514 | bq->state = state; | ||
515 | mutex_unlock(&bq->lock); | ||
516 | |||
517 | power_supply_changed(bq->charger); | ||
518 | |||
519 | return IRQ_HANDLED; | ||
520 | } | ||
521 | |||
522 | static int bq24257_hw_init(struct bq24257_device *bq) | ||
523 | { | ||
524 | int ret; | ||
525 | int i; | ||
526 | struct bq24257_state state; | ||
527 | |||
528 | const struct { | ||
529 | int field; | ||
530 | u32 value; | ||
531 | } init_data[] = { | ||
532 | {F_ICHG, bq->init_data.ichg}, | ||
533 | {F_VBAT, bq->init_data.vbat}, | ||
534 | {F_ITERM, bq->init_data.iterm} | ||
535 | }; | ||
536 | |||
537 | /* | ||
538 | * Disable the watchdog timer to prevent the IC from going back to | ||
539 | * default settings after 50 seconds of I2C inactivity. | ||
540 | */ | ||
541 | ret = bq24257_field_write(bq, F_WD_EN, 0); | ||
542 | if (ret < 0) | ||
543 | return ret; | ||
544 | |||
545 | /* configure the charge currents and voltages */ | ||
546 | for (i = 0; i < ARRAY_SIZE(init_data); i++) { | ||
547 | ret = bq24257_field_write(bq, init_data[i].field, | ||
548 | init_data[i].value); | ||
549 | if (ret < 0) | ||
550 | return ret; | ||
551 | } | ||
552 | |||
553 | ret = bq24257_get_chip_state(bq, &state); | ||
554 | if (ret < 0) | ||
555 | return ret; | ||
556 | |||
557 | mutex_lock(&bq->lock); | ||
558 | bq->state = state; | ||
559 | mutex_unlock(&bq->lock); | ||
560 | |||
561 | if (!state.power_good) | ||
562 | /* activate D+/D- detection algorithm */ | ||
563 | ret = bq24257_field_write(bq, F_DPDM_EN, 1); | ||
564 | else if (state.fault != FAULT_NO_BAT) | ||
565 | ret = bq24257_iilimit_autoset(bq); | ||
566 | |||
567 | return ret; | ||
568 | } | ||
569 | |||
570 | static enum power_supply_property bq24257_power_supply_props[] = { | ||
571 | POWER_SUPPLY_PROP_MANUFACTURER, | ||
572 | POWER_SUPPLY_PROP_STATUS, | ||
573 | POWER_SUPPLY_PROP_ONLINE, | ||
574 | POWER_SUPPLY_PROP_HEALTH, | ||
575 | POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, | ||
576 | POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, | ||
577 | POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, | ||
578 | POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, | ||
579 | POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT, | ||
580 | }; | ||
581 | |||
582 | static char *bq24257_charger_supplied_to[] = { | ||
583 | "main-battery", | ||
584 | }; | ||
585 | |||
586 | static const struct power_supply_desc bq24257_power_supply_desc = { | ||
587 | .name = "bq24257-charger", | ||
588 | .type = POWER_SUPPLY_TYPE_USB, | ||
589 | .properties = bq24257_power_supply_props, | ||
590 | .num_properties = ARRAY_SIZE(bq24257_power_supply_props), | ||
591 | .get_property = bq24257_power_supply_get_property, | ||
592 | }; | ||
593 | |||
594 | static int bq24257_power_supply_init(struct bq24257_device *bq) | ||
595 | { | ||
596 | struct power_supply_config psy_cfg = { .drv_data = bq, }; | ||
597 | |||
598 | psy_cfg.supplied_to = bq24257_charger_supplied_to; | ||
599 | psy_cfg.num_supplicants = ARRAY_SIZE(bq24257_charger_supplied_to); | ||
600 | |||
601 | bq->charger = power_supply_register(bq->dev, &bq24257_power_supply_desc, | ||
602 | &psy_cfg); | ||
603 | if (IS_ERR(bq->charger)) | ||
604 | return PTR_ERR(bq->charger); | ||
605 | |||
606 | return 0; | ||
607 | } | ||
608 | |||
609 | static int bq24257_irq_probe(struct bq24257_device *bq) | ||
610 | { | ||
611 | int ret; | ||
612 | struct gpio_desc *stat_irq; | ||
613 | |||
614 | stat_irq = devm_gpiod_get_index(bq->dev, BQ24257_STAT_IRQ, 0); | ||
615 | if (IS_ERR(stat_irq)) { | ||
616 | dev_err(bq->dev, "could not probe stat_irq pin\n"); | ||
617 | return PTR_ERR(stat_irq); | ||
618 | } | ||
619 | |||
620 | ret = gpiod_direction_input(stat_irq); | ||
621 | if (ret < 0) | ||
622 | return ret; | ||
623 | |||
624 | return gpiod_to_irq(stat_irq); | ||
625 | } | ||
626 | |||
627 | static int bq24257_pg_gpio_probe(struct bq24257_device *bq) | ||
628 | { | ||
629 | bq->pg = devm_gpiod_get_index(bq->dev, BQ24257_PG_GPIO, 0); | ||
630 | if (IS_ERR(bq->pg)) { | ||
631 | dev_err(bq->dev, "could not probe PG pin\n"); | ||
632 | return PTR_ERR(bq->pg); | ||
633 | } | ||
634 | |||
635 | return gpiod_direction_input(bq->pg); | ||
636 | } | ||
637 | |||
638 | static int bq24257_fw_probe(struct bq24257_device *bq) | ||
639 | { | ||
640 | int ret; | ||
641 | u32 property; | ||
642 | |||
643 | ret = device_property_read_u32(bq->dev, "ti,charge-current", &property); | ||
644 | if (ret < 0) | ||
645 | return ret; | ||
646 | |||
647 | bq->init_data.ichg = bq24257_find_idx(property, bq24257_ichg_map, | ||
648 | BQ24257_ICHG_MAP_SIZE); | ||
649 | |||
650 | ret = device_property_read_u32(bq->dev, "ti,battery-regulation-voltage", | ||
651 | &property); | ||
652 | if (ret < 0) | ||
653 | return ret; | ||
654 | |||
655 | bq->init_data.vbat = bq24257_find_idx(property, bq24257_vbat_map, | ||
656 | BQ24257_VBAT_MAP_SIZE); | ||
657 | |||
658 | ret = device_property_read_u32(bq->dev, "ti,termination-current", | ||
659 | &property); | ||
660 | if (ret < 0) | ||
661 | return ret; | ||
662 | |||
663 | bq->init_data.iterm = bq24257_find_idx(property, bq24257_iterm_map, | ||
664 | BQ24257_ITERM_MAP_SIZE); | ||
665 | |||
666 | return 0; | ||
667 | } | ||
668 | |||
669 | static int bq24257_probe(struct i2c_client *client, | ||
670 | const struct i2c_device_id *id) | ||
671 | { | ||
672 | struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); | ||
673 | struct device *dev = &client->dev; | ||
674 | struct bq24257_device *bq; | ||
675 | int ret; | ||
676 | int i; | ||
677 | |||
678 | if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { | ||
679 | dev_err(dev, "No support for SMBUS_BYTE_DATA\n"); | ||
680 | return -ENODEV; | ||
681 | } | ||
682 | |||
683 | bq = devm_kzalloc(dev, sizeof(*bq), GFP_KERNEL); | ||
684 | if (!bq) | ||
685 | return -ENOMEM; | ||
686 | |||
687 | bq->client = client; | ||
688 | bq->dev = dev; | ||
689 | |||
690 | mutex_init(&bq->lock); | ||
691 | |||
692 | bq->rmap = devm_regmap_init_i2c(client, &bq24257_regmap_config); | ||
693 | if (IS_ERR(bq->rmap)) { | ||
694 | dev_err(dev, "failed to allocate register map\n"); | ||
695 | return PTR_ERR(bq->rmap); | ||
696 | } | ||
697 | |||
698 | for (i = 0; i < ARRAY_SIZE(bq24257_reg_fields); i++) { | ||
699 | const struct reg_field *reg_fields = bq24257_reg_fields; | ||
700 | |||
701 | bq->rmap_fields[i] = devm_regmap_field_alloc(dev, bq->rmap, | ||
702 | reg_fields[i]); | ||
703 | if (IS_ERR(bq->rmap_fields[i])) { | ||
704 | dev_err(dev, "cannot allocate regmap field\n"); | ||
705 | return PTR_ERR(bq->rmap_fields[i]); | ||
706 | } | ||
707 | } | ||
708 | |||
709 | i2c_set_clientdata(client, bq); | ||
710 | |||
711 | INIT_DELAYED_WORK(&bq->iilimit_setup_work, bq24257_iilimit_setup_work); | ||
712 | |||
713 | if (!dev->platform_data) { | ||
714 | ret = bq24257_fw_probe(bq); | ||
715 | if (ret < 0) { | ||
716 | dev_err(dev, "Cannot read device properties.\n"); | ||
717 | return ret; | ||
718 | } | ||
719 | } else { | ||
720 | return -ENODEV; | ||
721 | } | ||
722 | |||
723 | /* we can only check Power Good status by probing the PG pin */ | ||
724 | ret = bq24257_pg_gpio_probe(bq); | ||
725 | if (ret < 0) | ||
726 | return ret; | ||
727 | |||
728 | /* reset all registers to defaults */ | ||
729 | ret = bq24257_field_write(bq, F_RESET, 1); | ||
730 | if (ret < 0) | ||
731 | return ret; | ||
732 | |||
733 | /* | ||
734 | * Put the RESET bit back to 0, in cache. For some reason the HW always | ||
735 | * returns 1 on this bit, so this is the only way to avoid resetting the | ||
736 | * chip every time we update another field in this register. | ||
737 | */ | ||
738 | ret = bq24257_field_write(bq, F_RESET, 0); | ||
739 | if (ret < 0) | ||
740 | return ret; | ||
741 | |||
742 | ret = bq24257_hw_init(bq); | ||
743 | if (ret < 0) { | ||
744 | dev_err(dev, "Cannot initialize the chip.\n"); | ||
745 | return ret; | ||
746 | } | ||
747 | |||
748 | if (client->irq <= 0) | ||
749 | client->irq = bq24257_irq_probe(bq); | ||
750 | |||
751 | if (client->irq < 0) { | ||
752 | dev_err(dev, "no irq resource found\n"); | ||
753 | return client->irq; | ||
754 | } | ||
755 | |||
756 | ret = devm_request_threaded_irq(dev, client->irq, NULL, | ||
757 | bq24257_irq_handler_thread, | ||
758 | IRQF_TRIGGER_FALLING | | ||
759 | IRQF_TRIGGER_RISING | IRQF_ONESHOT, | ||
760 | BQ24257_STAT_IRQ, bq); | ||
761 | if (ret) | ||
762 | return ret; | ||
763 | |||
764 | ret = bq24257_power_supply_init(bq); | ||
765 | if (ret < 0) | ||
766 | dev_err(dev, "Failed to register power supply\n"); | ||
767 | |||
768 | return ret; | ||
769 | } | ||
770 | |||
771 | static int bq24257_remove(struct i2c_client *client) | ||
772 | { | ||
773 | struct bq24257_device *bq = i2c_get_clientdata(client); | ||
774 | |||
775 | cancel_delayed_work_sync(&bq->iilimit_setup_work); | ||
776 | |||
777 | power_supply_unregister(bq->charger); | ||
778 | |||
779 | bq24257_field_write(bq, F_RESET, 1); /* reset to defaults */ | ||
780 | |||
781 | return 0; | ||
782 | } | ||
783 | |||
784 | #ifdef CONFIG_PM_SLEEP | ||
785 | static int bq24257_suspend(struct device *dev) | ||
786 | { | ||
787 | struct bq24257_device *bq = dev_get_drvdata(dev); | ||
788 | int ret = 0; | ||
789 | |||
790 | cancel_delayed_work_sync(&bq->iilimit_setup_work); | ||
791 | |||
792 | /* reset all registers to default (and activate standalone mode) */ | ||
793 | ret = bq24257_field_write(bq, F_RESET, 1); | ||
794 | if (ret < 0) | ||
795 | dev_err(bq->dev, "Cannot reset chip to standalone mode.\n"); | ||
796 | |||
797 | return ret; | ||
798 | } | ||
799 | |||
800 | static int bq24257_resume(struct device *dev) | ||
801 | { | ||
802 | int ret; | ||
803 | struct bq24257_device *bq = dev_get_drvdata(dev); | ||
804 | |||
805 | ret = regcache_drop_region(bq->rmap, BQ24257_REG_1, BQ24257_REG_7); | ||
806 | if (ret < 0) | ||
807 | return ret; | ||
808 | |||
809 | ret = bq24257_field_write(bq, F_RESET, 0); | ||
810 | if (ret < 0) | ||
811 | return ret; | ||
812 | |||
813 | ret = bq24257_hw_init(bq); | ||
814 | if (ret < 0) { | ||
815 | dev_err(bq->dev, "Cannot init chip after resume.\n"); | ||
816 | return ret; | ||
817 | } | ||
818 | |||
819 | /* signal userspace, maybe state changed while suspended */ | ||
820 | power_supply_changed(bq->charger); | ||
821 | |||
822 | return 0; | ||
823 | } | ||
824 | #endif | ||
825 | |||
826 | static const struct dev_pm_ops bq24257_pm = { | ||
827 | SET_SYSTEM_SLEEP_PM_OPS(bq24257_suspend, bq24257_resume) | ||
828 | }; | ||
829 | |||
830 | static const struct i2c_device_id bq24257_i2c_ids[] = { | ||
831 | { "bq24257", 0 }, | ||
832 | {}, | ||
833 | }; | ||
834 | MODULE_DEVICE_TABLE(i2c, bq24257_i2c_ids); | ||
835 | |||
836 | static const struct of_device_id bq24257_of_match[] = { | ||
837 | { .compatible = "ti,bq24257", }, | ||
838 | { }, | ||
839 | }; | ||
840 | MODULE_DEVICE_TABLE(of, bq24257_of_match); | ||
841 | |||
842 | static const struct acpi_device_id bq24257_acpi_match[] = { | ||
843 | {"BQ242570", 0}, | ||
844 | {}, | ||
845 | }; | ||
846 | MODULE_DEVICE_TABLE(acpi, bq24257_acpi_match); | ||
847 | |||
848 | static struct i2c_driver bq24257_driver = { | ||
849 | .driver = { | ||
850 | .name = "bq24257-charger", | ||
851 | .of_match_table = of_match_ptr(bq24257_of_match), | ||
852 | .acpi_match_table = ACPI_PTR(bq24257_acpi_match), | ||
853 | .pm = &bq24257_pm, | ||
854 | }, | ||
855 | .probe = bq24257_probe, | ||
856 | .remove = bq24257_remove, | ||
857 | .id_table = bq24257_i2c_ids, | ||
858 | }; | ||
859 | module_i2c_driver(bq24257_driver); | ||
860 | |||
861 | MODULE_AUTHOR("Laurentiu Palcu <laurentiu.palcu@intel.com>"); | ||
862 | MODULE_DESCRIPTION("bq24257 charger driver"); | ||
863 | MODULE_LICENSE("GPL"); | ||