Commit | Line | Data |
---|---|---|
d7bf353f MG |
1 | /* |
2 | * Driver for the TI bq24190 battery charger. | |
3 | * | |
4 | * Author: Mark A. Greer <mgreer@animalcreek.com> | |
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 version 2 as | |
8 | * published by the Free Software Foundation. | |
9 | */ | |
10 | ||
11 | #include <linux/module.h> | |
12 | #include <linux/interrupt.h> | |
13 | #include <linux/delay.h> | |
14 | #include <linux/of_irq.h> | |
15 | #include <linux/of_device.h> | |
16 | #include <linux/pm_runtime.h> | |
17 | #include <linux/power_supply.h> | |
18 | #include <linux/gpio.h> | |
19 | #include <linux/i2c.h> | |
20 | ||
21 | #include <linux/power/bq24190_charger.h> | |
22 | ||
23 | ||
24 | #define BQ24190_MANUFACTURER "Texas Instruments" | |
25 | ||
26 | #define BQ24190_REG_ISC 0x00 /* Input Source Control */ | |
27 | #define BQ24190_REG_ISC_EN_HIZ_MASK BIT(7) | |
28 | #define BQ24190_REG_ISC_EN_HIZ_SHIFT 7 | |
29 | #define BQ24190_REG_ISC_VINDPM_MASK (BIT(6) | BIT(5) | BIT(4) | \ | |
30 | BIT(3)) | |
31 | #define BQ24190_REG_ISC_VINDPM_SHIFT 3 | |
32 | #define BQ24190_REG_ISC_IINLIM_MASK (BIT(2) | BIT(1) | BIT(0)) | |
33 | #define BQ24190_REG_ISC_IINLIM_SHIFT 0 | |
34 | ||
35 | #define BQ24190_REG_POC 0x01 /* Power-On Configuration */ | |
36 | #define BQ24190_REG_POC_RESET_MASK BIT(7) | |
37 | #define BQ24190_REG_POC_RESET_SHIFT 7 | |
38 | #define BQ24190_REG_POC_WDT_RESET_MASK BIT(6) | |
39 | #define BQ24190_REG_POC_WDT_RESET_SHIFT 6 | |
40 | #define BQ24190_REG_POC_CHG_CONFIG_MASK (BIT(5) | BIT(4)) | |
41 | #define BQ24190_REG_POC_CHG_CONFIG_SHIFT 4 | |
42 | #define BQ24190_REG_POC_SYS_MIN_MASK (BIT(3) | BIT(2) | BIT(1)) | |
43 | #define BQ24190_REG_POC_SYS_MIN_SHIFT 1 | |
44 | #define BQ24190_REG_POC_BOOST_LIM_MASK BIT(0) | |
45 | #define BQ24190_REG_POC_BOOST_LIM_SHIFT 0 | |
46 | ||
47 | #define BQ24190_REG_CCC 0x02 /* Charge Current Control */ | |
48 | #define BQ24190_REG_CCC_ICHG_MASK (BIT(7) | BIT(6) | BIT(5) | \ | |
49 | BIT(4) | BIT(3) | BIT(2)) | |
50 | #define BQ24190_REG_CCC_ICHG_SHIFT 2 | |
51 | #define BQ24190_REG_CCC_FORCE_20PCT_MASK BIT(0) | |
52 | #define BQ24190_REG_CCC_FORCE_20PCT_SHIFT 0 | |
53 | ||
54 | #define BQ24190_REG_PCTCC 0x03 /* Pre-charge/Termination Current Cntl */ | |
55 | #define BQ24190_REG_PCTCC_IPRECHG_MASK (BIT(7) | BIT(6) | BIT(5) | \ | |
56 | BIT(4)) | |
57 | #define BQ24190_REG_PCTCC_IPRECHG_SHIFT 4 | |
58 | #define BQ24190_REG_PCTCC_ITERM_MASK (BIT(3) | BIT(2) | BIT(1) | \ | |
59 | BIT(0)) | |
60 | #define BQ24190_REG_PCTCC_ITERM_SHIFT 0 | |
61 | ||
62 | #define BQ24190_REG_CVC 0x04 /* Charge Voltage Control */ | |
63 | #define BQ24190_REG_CVC_VREG_MASK (BIT(7) | BIT(6) | BIT(5) | \ | |
64 | BIT(4) | BIT(3) | BIT(2)) | |
65 | #define BQ24190_REG_CVC_VREG_SHIFT 2 | |
66 | #define BQ24190_REG_CVC_BATLOWV_MASK BIT(1) | |
67 | #define BQ24190_REG_CVC_BATLOWV_SHIFT 1 | |
68 | #define BQ24190_REG_CVC_VRECHG_MASK BIT(0) | |
69 | #define BQ24190_REG_CVC_VRECHG_SHIFT 0 | |
70 | ||
71 | #define BQ24190_REG_CTTC 0x05 /* Charge Term/Timer Control */ | |
72 | #define BQ24190_REG_CTTC_EN_TERM_MASK BIT(7) | |
73 | #define BQ24190_REG_CTTC_EN_TERM_SHIFT 7 | |
74 | #define BQ24190_REG_CTTC_TERM_STAT_MASK BIT(6) | |
75 | #define BQ24190_REG_CTTC_TERM_STAT_SHIFT 6 | |
76 | #define BQ24190_REG_CTTC_WATCHDOG_MASK (BIT(5) | BIT(4)) | |
77 | #define BQ24190_REG_CTTC_WATCHDOG_SHIFT 4 | |
78 | #define BQ24190_REG_CTTC_EN_TIMER_MASK BIT(3) | |
79 | #define BQ24190_REG_CTTC_EN_TIMER_SHIFT 3 | |
80 | #define BQ24190_REG_CTTC_CHG_TIMER_MASK (BIT(2) | BIT(1)) | |
81 | #define BQ24190_REG_CTTC_CHG_TIMER_SHIFT 1 | |
82 | #define BQ24190_REG_CTTC_JEITA_ISET_MASK BIT(0) | |
83 | #define BQ24190_REG_CTTC_JEITA_ISET_SHIFT 0 | |
84 | ||
85 | #define BQ24190_REG_ICTRC 0x06 /* IR Comp/Thermal Regulation Control */ | |
86 | #define BQ24190_REG_ICTRC_BAT_COMP_MASK (BIT(7) | BIT(6) | BIT(5)) | |
87 | #define BQ24190_REG_ICTRC_BAT_COMP_SHIFT 5 | |
88 | #define BQ24190_REG_ICTRC_VCLAMP_MASK (BIT(4) | BIT(3) | BIT(2)) | |
89 | #define BQ24190_REG_ICTRC_VCLAMP_SHIFT 2 | |
90 | #define BQ24190_REG_ICTRC_TREG_MASK (BIT(1) | BIT(0)) | |
91 | #define BQ24190_REG_ICTRC_TREG_SHIFT 0 | |
92 | ||
93 | #define BQ24190_REG_MOC 0x07 /* Misc. Operation Control */ | |
94 | #define BQ24190_REG_MOC_DPDM_EN_MASK BIT(7) | |
95 | #define BQ24190_REG_MOC_DPDM_EN_SHIFT 7 | |
96 | #define BQ24190_REG_MOC_TMR2X_EN_MASK BIT(6) | |
97 | #define BQ24190_REG_MOC_TMR2X_EN_SHIFT 6 | |
98 | #define BQ24190_REG_MOC_BATFET_DISABLE_MASK BIT(5) | |
99 | #define BQ24190_REG_MOC_BATFET_DISABLE_SHIFT 5 | |
100 | #define BQ24190_REG_MOC_JEITA_VSET_MASK BIT(4) | |
101 | #define BQ24190_REG_MOC_JEITA_VSET_SHIFT 4 | |
102 | #define BQ24190_REG_MOC_INT_MASK_MASK (BIT(1) | BIT(0)) | |
103 | #define BQ24190_REG_MOC_INT_MASK_SHIFT 0 | |
104 | ||
105 | #define BQ24190_REG_SS 0x08 /* System Status */ | |
106 | #define BQ24190_REG_SS_VBUS_STAT_MASK (BIT(7) | BIT(6)) | |
107 | #define BQ24190_REG_SS_VBUS_STAT_SHIFT 6 | |
108 | #define BQ24190_REG_SS_CHRG_STAT_MASK (BIT(5) | BIT(4)) | |
109 | #define BQ24190_REG_SS_CHRG_STAT_SHIFT 4 | |
110 | #define BQ24190_REG_SS_DPM_STAT_MASK BIT(3) | |
111 | #define BQ24190_REG_SS_DPM_STAT_SHIFT 3 | |
112 | #define BQ24190_REG_SS_PG_STAT_MASK BIT(2) | |
113 | #define BQ24190_REG_SS_PG_STAT_SHIFT 2 | |
114 | #define BQ24190_REG_SS_THERM_STAT_MASK BIT(1) | |
115 | #define BQ24190_REG_SS_THERM_STAT_SHIFT 1 | |
116 | #define BQ24190_REG_SS_VSYS_STAT_MASK BIT(0) | |
117 | #define BQ24190_REG_SS_VSYS_STAT_SHIFT 0 | |
118 | ||
119 | #define BQ24190_REG_F 0x09 /* Fault */ | |
120 | #define BQ24190_REG_F_WATCHDOG_FAULT_MASK BIT(7) | |
121 | #define BQ24190_REG_F_WATCHDOG_FAULT_SHIFT 7 | |
122 | #define BQ24190_REG_F_BOOST_FAULT_MASK BIT(6) | |
123 | #define BQ24190_REG_F_BOOST_FAULT_SHIFT 6 | |
124 | #define BQ24190_REG_F_CHRG_FAULT_MASK (BIT(5) | BIT(4)) | |
125 | #define BQ24190_REG_F_CHRG_FAULT_SHIFT 4 | |
126 | #define BQ24190_REG_F_BAT_FAULT_MASK BIT(3) | |
127 | #define BQ24190_REG_F_BAT_FAULT_SHIFT 3 | |
128 | #define BQ24190_REG_F_NTC_FAULT_MASK (BIT(2) | BIT(1) | BIT(0)) | |
129 | #define BQ24190_REG_F_NTC_FAULT_SHIFT 0 | |
130 | ||
131 | #define BQ24190_REG_VPRS 0x0A /* Vendor/Part/Revision Status */ | |
132 | #define BQ24190_REG_VPRS_PN_MASK (BIT(5) | BIT(4) | BIT(3)) | |
133 | #define BQ24190_REG_VPRS_PN_SHIFT 3 | |
134 | #define BQ24190_REG_VPRS_PN_24190 0x4 | |
135 | #define BQ24190_REG_VPRS_PN_24192 0x5 /* Also 24193 */ | |
136 | #define BQ24190_REG_VPRS_PN_24192I 0x3 | |
137 | #define BQ24190_REG_VPRS_TS_PROFILE_MASK BIT(2) | |
138 | #define BQ24190_REG_VPRS_TS_PROFILE_SHIFT 2 | |
139 | #define BQ24190_REG_VPRS_DEV_REG_MASK (BIT(1) | BIT(0)) | |
140 | #define BQ24190_REG_VPRS_DEV_REG_SHIFT 0 | |
141 | ||
142 | /* | |
143 | * The FAULT register is latched by the bq24190 (except for NTC_FAULT) | |
144 | * so the first read after a fault returns the latched value and subsequent | |
145 | * reads return the current value. In order to return the fault status | |
146 | * to the user, have the interrupt handler save the reg's value and retrieve | |
147 | * it in the appropriate health/status routine. Each routine has its own | |
148 | * flag indicating whether it should use the value stored by the last run | |
149 | * of the interrupt handler or do an actual reg read. That way each routine | |
150 | * can report back whatever fault may have occured. | |
151 | */ | |
152 | struct bq24190_dev_info { | |
153 | struct i2c_client *client; | |
154 | struct device *dev; | |
297d716f KK |
155 | struct power_supply *charger; |
156 | struct power_supply *battery; | |
d7bf353f MG |
157 | char model_name[I2C_NAME_SIZE]; |
158 | kernel_ulong_t model; | |
159 | unsigned int gpio_int; | |
160 | unsigned int irq; | |
161 | struct mutex f_reg_lock; | |
162 | bool first_time; | |
163 | bool charger_health_valid; | |
164 | bool battery_health_valid; | |
165 | bool battery_status_valid; | |
166 | u8 f_reg; | |
167 | u8 ss_reg; | |
168 | u8 watchdog; | |
169 | }; | |
170 | ||
171 | /* | |
172 | * The tables below provide a 2-way mapping for the value that goes in | |
173 | * the register field and the real-world value that it represents. | |
174 | * The index of the array is the value that goes in the register; the | |
175 | * number at that index in the array is the real-world value that it | |
176 | * represents. | |
177 | */ | |
178 | /* REG02[7:2] (ICHG) in uAh */ | |
179 | static const int bq24190_ccc_ichg_values[] = { | |
180 | 512000, 576000, 640000, 704000, 768000, 832000, 896000, 960000, | |
181 | 1024000, 1088000, 1152000, 1216000, 1280000, 1344000, 1408000, 1472000, | |
182 | 1536000, 1600000, 1664000, 1728000, 1792000, 1856000, 1920000, 1984000, | |
183 | 2048000, 2112000, 2176000, 2240000, 2304000, 2368000, 2432000, 2496000, | |
184 | 2560000, 2624000, 2688000, 2752000, 2816000, 2880000, 2944000, 3008000, | |
185 | 3072000, 3136000, 3200000, 3264000, 3328000, 3392000, 3456000, 3520000, | |
186 | 3584000, 3648000, 3712000, 3776000, 3840000, 3904000, 3968000, 4032000, | |
187 | 4096000, 4160000, 4224000, 4288000, 4352000, 4416000, 4480000, 4544000 | |
188 | }; | |
189 | ||
190 | /* REG04[7:2] (VREG) in uV */ | |
191 | static const int bq24190_cvc_vreg_values[] = { | |
192 | 3504000, 3520000, 3536000, 3552000, 3568000, 3584000, 3600000, 3616000, | |
193 | 3632000, 3648000, 3664000, 3680000, 3696000, 3712000, 3728000, 3744000, | |
194 | 3760000, 3776000, 3792000, 3808000, 3824000, 3840000, 3856000, 3872000, | |
195 | 3888000, 3904000, 3920000, 3936000, 3952000, 3968000, 3984000, 4000000, | |
196 | 4016000, 4032000, 4048000, 4064000, 4080000, 4096000, 4112000, 4128000, | |
197 | 4144000, 4160000, 4176000, 4192000, 4208000, 4224000, 4240000, 4256000, | |
198 | 4272000, 4288000, 4304000, 4320000, 4336000, 4352000, 4368000, 4384000, | |
199 | 4400000 | |
200 | }; | |
201 | ||
202 | /* REG06[1:0] (TREG) in tenths of degrees Celcius */ | |
203 | static const int bq24190_ictrc_treg_values[] = { | |
204 | 600, 800, 1000, 1200 | |
205 | }; | |
206 | ||
207 | /* | |
208 | * Return the index in 'tbl' of greatest value that is less than or equal to | |
209 | * 'val'. The index range returned is 0 to 'tbl_size' - 1. Assumes that | |
210 | * the values in 'tbl' are sorted from smallest to largest and 'tbl_size' | |
211 | * is less than 2^8. | |
212 | */ | |
213 | static u8 bq24190_find_idx(const int tbl[], int tbl_size, int v) | |
214 | { | |
215 | int i; | |
216 | ||
217 | for (i = 1; i < tbl_size; i++) | |
218 | if (v < tbl[i]) | |
219 | break; | |
220 | ||
221 | return i - 1; | |
222 | } | |
223 | ||
224 | /* Basic driver I/O routines */ | |
225 | ||
226 | static int bq24190_read(struct bq24190_dev_info *bdi, u8 reg, u8 *data) | |
227 | { | |
228 | int ret; | |
229 | ||
230 | ret = i2c_smbus_read_byte_data(bdi->client, reg); | |
231 | if (ret < 0) | |
232 | return ret; | |
233 | ||
234 | *data = ret; | |
235 | return 0; | |
236 | } | |
237 | ||
238 | static int bq24190_write(struct bq24190_dev_info *bdi, u8 reg, u8 data) | |
239 | { | |
240 | return i2c_smbus_write_byte_data(bdi->client, reg, data); | |
241 | } | |
242 | ||
243 | static int bq24190_read_mask(struct bq24190_dev_info *bdi, u8 reg, | |
244 | u8 mask, u8 shift, u8 *data) | |
245 | { | |
246 | u8 v; | |
247 | int ret; | |
248 | ||
249 | ret = bq24190_read(bdi, reg, &v); | |
250 | if (ret < 0) | |
251 | return ret; | |
252 | ||
253 | v &= mask; | |
254 | v >>= shift; | |
255 | *data = v; | |
256 | ||
257 | return 0; | |
258 | } | |
259 | ||
260 | static int bq24190_write_mask(struct bq24190_dev_info *bdi, u8 reg, | |
261 | u8 mask, u8 shift, u8 data) | |
262 | { | |
263 | u8 v; | |
264 | int ret; | |
265 | ||
266 | ret = bq24190_read(bdi, reg, &v); | |
267 | if (ret < 0) | |
268 | return ret; | |
269 | ||
270 | v &= ~mask; | |
271 | v |= ((data << shift) & mask); | |
272 | ||
273 | return bq24190_write(bdi, reg, v); | |
274 | } | |
275 | ||
276 | static int bq24190_get_field_val(struct bq24190_dev_info *bdi, | |
277 | u8 reg, u8 mask, u8 shift, | |
278 | const int tbl[], int tbl_size, | |
279 | int *val) | |
280 | { | |
281 | u8 v; | |
282 | int ret; | |
283 | ||
284 | ret = bq24190_read_mask(bdi, reg, mask, shift, &v); | |
285 | if (ret < 0) | |
286 | return ret; | |
287 | ||
288 | v = (v >= tbl_size) ? (tbl_size - 1) : v; | |
289 | *val = tbl[v]; | |
290 | ||
291 | return 0; | |
292 | } | |
293 | ||
294 | static int bq24190_set_field_val(struct bq24190_dev_info *bdi, | |
295 | u8 reg, u8 mask, u8 shift, | |
296 | const int tbl[], int tbl_size, | |
297 | int val) | |
298 | { | |
299 | u8 idx; | |
300 | ||
301 | idx = bq24190_find_idx(tbl, tbl_size, val); | |
302 | ||
303 | return bq24190_write_mask(bdi, reg, mask, shift, idx); | |
304 | } | |
305 | ||
306 | #ifdef CONFIG_SYSFS | |
307 | /* | |
308 | * There are a numerous options that are configurable on the bq24190 | |
309 | * that go well beyond what the power_supply properties provide access to. | |
310 | * Provide sysfs access to them so they can be examined and possibly modified | |
311 | * on the fly. They will be provided for the charger power_supply object only | |
312 | * and will be prefixed by 'f_' to make them easier to recognize. | |
313 | */ | |
314 | ||
315 | #define BQ24190_SYSFS_FIELD(_name, r, f, m, store) \ | |
316 | { \ | |
317 | .attr = __ATTR(f_##_name, m, bq24190_sysfs_show, store), \ | |
318 | .reg = BQ24190_REG_##r, \ | |
319 | .mask = BQ24190_REG_##r##_##f##_MASK, \ | |
320 | .shift = BQ24190_REG_##r##_##f##_SHIFT, \ | |
321 | } | |
322 | ||
323 | #define BQ24190_SYSFS_FIELD_RW(_name, r, f) \ | |
324 | BQ24190_SYSFS_FIELD(_name, r, f, S_IWUSR | S_IRUGO, \ | |
325 | bq24190_sysfs_store) | |
326 | ||
327 | #define BQ24190_SYSFS_FIELD_RO(_name, r, f) \ | |
328 | BQ24190_SYSFS_FIELD(_name, r, f, S_IRUGO, NULL) | |
329 | ||
330 | static ssize_t bq24190_sysfs_show(struct device *dev, | |
331 | struct device_attribute *attr, char *buf); | |
332 | static ssize_t bq24190_sysfs_store(struct device *dev, | |
333 | struct device_attribute *attr, const char *buf, size_t count); | |
334 | ||
335 | struct bq24190_sysfs_field_info { | |
336 | struct device_attribute attr; | |
337 | u8 reg; | |
338 | u8 mask; | |
339 | u8 shift; | |
340 | }; | |
341 | ||
d24fed39 AV |
342 | /* On i386 ptrace-abi.h defines SS that breaks the macro calls below. */ |
343 | #undef SS | |
344 | ||
d7bf353f MG |
345 | static struct bq24190_sysfs_field_info bq24190_sysfs_field_tbl[] = { |
346 | /* sysfs name reg field in reg */ | |
347 | BQ24190_SYSFS_FIELD_RW(en_hiz, ISC, EN_HIZ), | |
348 | BQ24190_SYSFS_FIELD_RW(vindpm, ISC, VINDPM), | |
349 | BQ24190_SYSFS_FIELD_RW(iinlim, ISC, IINLIM), | |
350 | BQ24190_SYSFS_FIELD_RW(chg_config, POC, CHG_CONFIG), | |
351 | BQ24190_SYSFS_FIELD_RW(sys_min, POC, SYS_MIN), | |
352 | BQ24190_SYSFS_FIELD_RW(boost_lim, POC, BOOST_LIM), | |
353 | BQ24190_SYSFS_FIELD_RW(ichg, CCC, ICHG), | |
354 | BQ24190_SYSFS_FIELD_RW(force_20_pct, CCC, FORCE_20PCT), | |
355 | BQ24190_SYSFS_FIELD_RW(iprechg, PCTCC, IPRECHG), | |
356 | BQ24190_SYSFS_FIELD_RW(iterm, PCTCC, ITERM), | |
357 | BQ24190_SYSFS_FIELD_RW(vreg, CVC, VREG), | |
358 | BQ24190_SYSFS_FIELD_RW(batlowv, CVC, BATLOWV), | |
359 | BQ24190_SYSFS_FIELD_RW(vrechg, CVC, VRECHG), | |
360 | BQ24190_SYSFS_FIELD_RW(en_term, CTTC, EN_TERM), | |
361 | BQ24190_SYSFS_FIELD_RW(term_stat, CTTC, TERM_STAT), | |
362 | BQ24190_SYSFS_FIELD_RO(watchdog, CTTC, WATCHDOG), | |
363 | BQ24190_SYSFS_FIELD_RW(en_timer, CTTC, EN_TIMER), | |
364 | BQ24190_SYSFS_FIELD_RW(chg_timer, CTTC, CHG_TIMER), | |
365 | BQ24190_SYSFS_FIELD_RW(jeta_iset, CTTC, JEITA_ISET), | |
366 | BQ24190_SYSFS_FIELD_RW(bat_comp, ICTRC, BAT_COMP), | |
367 | BQ24190_SYSFS_FIELD_RW(vclamp, ICTRC, VCLAMP), | |
368 | BQ24190_SYSFS_FIELD_RW(treg, ICTRC, TREG), | |
369 | BQ24190_SYSFS_FIELD_RW(dpdm_en, MOC, DPDM_EN), | |
370 | BQ24190_SYSFS_FIELD_RW(tmr2x_en, MOC, TMR2X_EN), | |
371 | BQ24190_SYSFS_FIELD_RW(batfet_disable, MOC, BATFET_DISABLE), | |
372 | BQ24190_SYSFS_FIELD_RW(jeita_vset, MOC, JEITA_VSET), | |
373 | BQ24190_SYSFS_FIELD_RO(int_mask, MOC, INT_MASK), | |
374 | BQ24190_SYSFS_FIELD_RO(vbus_stat, SS, VBUS_STAT), | |
375 | BQ24190_SYSFS_FIELD_RO(chrg_stat, SS, CHRG_STAT), | |
376 | BQ24190_SYSFS_FIELD_RO(dpm_stat, SS, DPM_STAT), | |
377 | BQ24190_SYSFS_FIELD_RO(pg_stat, SS, PG_STAT), | |
378 | BQ24190_SYSFS_FIELD_RO(therm_stat, SS, THERM_STAT), | |
379 | BQ24190_SYSFS_FIELD_RO(vsys_stat, SS, VSYS_STAT), | |
380 | BQ24190_SYSFS_FIELD_RO(watchdog_fault, F, WATCHDOG_FAULT), | |
381 | BQ24190_SYSFS_FIELD_RO(boost_fault, F, BOOST_FAULT), | |
382 | BQ24190_SYSFS_FIELD_RO(chrg_fault, F, CHRG_FAULT), | |
383 | BQ24190_SYSFS_FIELD_RO(bat_fault, F, BAT_FAULT), | |
384 | BQ24190_SYSFS_FIELD_RO(ntc_fault, F, NTC_FAULT), | |
385 | BQ24190_SYSFS_FIELD_RO(pn, VPRS, PN), | |
386 | BQ24190_SYSFS_FIELD_RO(ts_profile, VPRS, TS_PROFILE), | |
387 | BQ24190_SYSFS_FIELD_RO(dev_reg, VPRS, DEV_REG), | |
388 | }; | |
389 | ||
390 | static struct attribute * | |
391 | bq24190_sysfs_attrs[ARRAY_SIZE(bq24190_sysfs_field_tbl) + 1]; | |
392 | ||
393 | static const struct attribute_group bq24190_sysfs_attr_group = { | |
394 | .attrs = bq24190_sysfs_attrs, | |
395 | }; | |
396 | ||
397 | static void bq24190_sysfs_init_attrs(void) | |
398 | { | |
399 | int i, limit = ARRAY_SIZE(bq24190_sysfs_field_tbl); | |
400 | ||
401 | for (i = 0; i < limit; i++) | |
402 | bq24190_sysfs_attrs[i] = &bq24190_sysfs_field_tbl[i].attr.attr; | |
403 | ||
404 | bq24190_sysfs_attrs[limit] = NULL; /* Has additional entry for this */ | |
405 | } | |
406 | ||
407 | static struct bq24190_sysfs_field_info *bq24190_sysfs_field_lookup( | |
408 | const char *name) | |
409 | { | |
410 | int i, limit = ARRAY_SIZE(bq24190_sysfs_field_tbl); | |
411 | ||
412 | for (i = 0; i < limit; i++) | |
413 | if (!strcmp(name, bq24190_sysfs_field_tbl[i].attr.attr.name)) | |
414 | break; | |
415 | ||
416 | if (i >= limit) | |
417 | return NULL; | |
418 | ||
419 | return &bq24190_sysfs_field_tbl[i]; | |
420 | } | |
421 | ||
422 | static ssize_t bq24190_sysfs_show(struct device *dev, | |
423 | struct device_attribute *attr, char *buf) | |
424 | { | |
425 | struct power_supply *psy = dev_get_drvdata(dev); | |
297d716f | 426 | struct bq24190_dev_info *bdi = power_supply_get_drvdata(psy); |
d7bf353f MG |
427 | struct bq24190_sysfs_field_info *info; |
428 | int ret; | |
429 | u8 v; | |
430 | ||
431 | info = bq24190_sysfs_field_lookup(attr->attr.name); | |
432 | if (!info) | |
433 | return -EINVAL; | |
434 | ||
435 | ret = bq24190_read_mask(bdi, info->reg, info->mask, info->shift, &v); | |
436 | if (ret) | |
437 | return ret; | |
438 | ||
439 | return scnprintf(buf, PAGE_SIZE, "%hhx\n", v); | |
440 | } | |
441 | ||
442 | static ssize_t bq24190_sysfs_store(struct device *dev, | |
443 | struct device_attribute *attr, const char *buf, size_t count) | |
444 | { | |
445 | struct power_supply *psy = dev_get_drvdata(dev); | |
297d716f | 446 | struct bq24190_dev_info *bdi = power_supply_get_drvdata(psy); |
d7bf353f MG |
447 | struct bq24190_sysfs_field_info *info; |
448 | int ret; | |
449 | u8 v; | |
450 | ||
451 | info = bq24190_sysfs_field_lookup(attr->attr.name); | |
452 | if (!info) | |
453 | return -EINVAL; | |
454 | ||
455 | ret = kstrtou8(buf, 0, &v); | |
456 | if (ret < 0) | |
457 | return ret; | |
458 | ||
459 | ret = bq24190_write_mask(bdi, info->reg, info->mask, info->shift, v); | |
460 | if (ret) | |
461 | return ret; | |
462 | ||
463 | return count; | |
464 | } | |
465 | ||
466 | static int bq24190_sysfs_create_group(struct bq24190_dev_info *bdi) | |
467 | { | |
468 | bq24190_sysfs_init_attrs(); | |
469 | ||
297d716f | 470 | return sysfs_create_group(&bdi->charger->dev.kobj, |
d7bf353f MG |
471 | &bq24190_sysfs_attr_group); |
472 | } | |
473 | ||
474 | static void bq24190_sysfs_remove_group(struct bq24190_dev_info *bdi) | |
475 | { | |
297d716f | 476 | sysfs_remove_group(&bdi->charger->dev.kobj, &bq24190_sysfs_attr_group); |
d7bf353f MG |
477 | } |
478 | #else | |
479 | static int bq24190_sysfs_create_group(struct bq24190_dev_info *bdi) | |
480 | { | |
481 | return 0; | |
482 | } | |
483 | ||
484 | static inline void bq24190_sysfs_remove_group(struct bq24190_dev_info *bdi) {} | |
485 | #endif | |
486 | ||
487 | /* | |
488 | * According to the "Host Mode and default Mode" section of the | |
489 | * manual, a write to any register causes the bq24190 to switch | |
490 | * from default mode to host mode. It will switch back to default | |
491 | * mode after a WDT timeout unless the WDT is turned off as well. | |
492 | * So, by simply turning off the WDT, we accomplish both with the | |
493 | * same write. | |
494 | */ | |
495 | static int bq24190_set_mode_host(struct bq24190_dev_info *bdi) | |
496 | { | |
497 | int ret; | |
498 | u8 v; | |
499 | ||
500 | ret = bq24190_read(bdi, BQ24190_REG_CTTC, &v); | |
501 | if (ret < 0) | |
502 | return ret; | |
503 | ||
504 | bdi->watchdog = ((v & BQ24190_REG_CTTC_WATCHDOG_MASK) >> | |
505 | BQ24190_REG_CTTC_WATCHDOG_SHIFT); | |
506 | v &= ~BQ24190_REG_CTTC_WATCHDOG_MASK; | |
507 | ||
508 | return bq24190_write(bdi, BQ24190_REG_CTTC, v); | |
509 | } | |
510 | ||
511 | static int bq24190_register_reset(struct bq24190_dev_info *bdi) | |
512 | { | |
513 | int ret, limit = 100; | |
514 | u8 v; | |
515 | ||
516 | /* Reset the registers */ | |
517 | ret = bq24190_write_mask(bdi, BQ24190_REG_POC, | |
518 | BQ24190_REG_POC_RESET_MASK, | |
519 | BQ24190_REG_POC_RESET_SHIFT, | |
520 | 0x1); | |
521 | if (ret < 0) | |
522 | return ret; | |
523 | ||
524 | /* Reset bit will be cleared by hardware so poll until it is */ | |
525 | do { | |
526 | ret = bq24190_read_mask(bdi, BQ24190_REG_POC, | |
527 | BQ24190_REG_POC_RESET_MASK, | |
528 | BQ24190_REG_POC_RESET_SHIFT, | |
529 | &v); | |
530 | if (ret < 0) | |
531 | return ret; | |
532 | ||
533 | if (!v) | |
534 | break; | |
535 | ||
536 | udelay(10); | |
537 | } while (--limit); | |
538 | ||
539 | if (!limit) | |
540 | return -EIO; | |
541 | ||
542 | return 0; | |
543 | } | |
544 | ||
545 | /* Charger power supply property routines */ | |
546 | ||
547 | static int bq24190_charger_get_charge_type(struct bq24190_dev_info *bdi, | |
548 | union power_supply_propval *val) | |
549 | { | |
550 | u8 v; | |
551 | int type, ret; | |
552 | ||
553 | ret = bq24190_read_mask(bdi, BQ24190_REG_POC, | |
554 | BQ24190_REG_POC_CHG_CONFIG_MASK, | |
555 | BQ24190_REG_POC_CHG_CONFIG_SHIFT, | |
556 | &v); | |
557 | if (ret < 0) | |
558 | return ret; | |
559 | ||
560 | /* If POC[CHG_CONFIG] (REG01[5:4]) == 0, charge is disabled */ | |
561 | if (!v) { | |
562 | type = POWER_SUPPLY_CHARGE_TYPE_NONE; | |
563 | } else { | |
564 | ret = bq24190_read_mask(bdi, BQ24190_REG_CCC, | |
565 | BQ24190_REG_CCC_FORCE_20PCT_MASK, | |
566 | BQ24190_REG_CCC_FORCE_20PCT_SHIFT, | |
567 | &v); | |
568 | if (ret < 0) | |
569 | return ret; | |
570 | ||
571 | type = (v) ? POWER_SUPPLY_CHARGE_TYPE_TRICKLE : | |
572 | POWER_SUPPLY_CHARGE_TYPE_FAST; | |
573 | } | |
574 | ||
575 | val->intval = type; | |
576 | ||
577 | return 0; | |
578 | } | |
579 | ||
580 | static int bq24190_charger_set_charge_type(struct bq24190_dev_info *bdi, | |
581 | const union power_supply_propval *val) | |
582 | { | |
583 | u8 chg_config, force_20pct, en_term; | |
584 | int ret; | |
585 | ||
586 | /* | |
587 | * According to the "Termination when REG02[0] = 1" section of | |
588 | * the bq24190 manual, the trickle charge could be less than the | |
589 | * termination current so it recommends turning off the termination | |
590 | * function. | |
591 | * | |
592 | * Note: AFAICT from the datasheet, the user will have to manually | |
593 | * turn off the charging when in 20% mode. If its not turned off, | |
594 | * there could be battery damage. So, use this mode at your own risk. | |
595 | */ | |
596 | switch (val->intval) { | |
597 | case POWER_SUPPLY_CHARGE_TYPE_NONE: | |
598 | chg_config = 0x0; | |
599 | break; | |
600 | case POWER_SUPPLY_CHARGE_TYPE_TRICKLE: | |
601 | chg_config = 0x1; | |
602 | force_20pct = 0x1; | |
603 | en_term = 0x0; | |
604 | break; | |
605 | case POWER_SUPPLY_CHARGE_TYPE_FAST: | |
606 | chg_config = 0x1; | |
607 | force_20pct = 0x0; | |
608 | en_term = 0x1; | |
609 | break; | |
610 | default: | |
611 | return -EINVAL; | |
612 | } | |
613 | ||
614 | if (chg_config) { /* Enabling the charger */ | |
615 | ret = bq24190_write_mask(bdi, BQ24190_REG_CCC, | |
616 | BQ24190_REG_CCC_FORCE_20PCT_MASK, | |
617 | BQ24190_REG_CCC_FORCE_20PCT_SHIFT, | |
618 | force_20pct); | |
619 | if (ret < 0) | |
620 | return ret; | |
621 | ||
622 | ret = bq24190_write_mask(bdi, BQ24190_REG_CTTC, | |
623 | BQ24190_REG_CTTC_EN_TERM_MASK, | |
624 | BQ24190_REG_CTTC_EN_TERM_SHIFT, | |
625 | en_term); | |
626 | if (ret < 0) | |
627 | return ret; | |
628 | } | |
629 | ||
630 | return bq24190_write_mask(bdi, BQ24190_REG_POC, | |
631 | BQ24190_REG_POC_CHG_CONFIG_MASK, | |
632 | BQ24190_REG_POC_CHG_CONFIG_SHIFT, chg_config); | |
633 | } | |
634 | ||
635 | static int bq24190_charger_get_health(struct bq24190_dev_info *bdi, | |
636 | union power_supply_propval *val) | |
637 | { | |
638 | u8 v; | |
639 | int health, ret; | |
640 | ||
641 | mutex_lock(&bdi->f_reg_lock); | |
642 | ||
643 | if (bdi->charger_health_valid) { | |
644 | v = bdi->f_reg; | |
645 | bdi->charger_health_valid = false; | |
646 | mutex_unlock(&bdi->f_reg_lock); | |
647 | } else { | |
648 | mutex_unlock(&bdi->f_reg_lock); | |
649 | ||
650 | ret = bq24190_read(bdi, BQ24190_REG_F, &v); | |
651 | if (ret < 0) | |
652 | return ret; | |
653 | } | |
654 | ||
655 | if (v & BQ24190_REG_F_BOOST_FAULT_MASK) { | |
656 | /* | |
657 | * This could be over-current or over-voltage but there's | |
658 | * no way to tell which. Return 'OVERVOLTAGE' since there | |
659 | * isn't an 'OVERCURRENT' value defined that we can return | |
660 | * even if it was over-current. | |
661 | */ | |
662 | health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; | |
663 | } else { | |
664 | v &= BQ24190_REG_F_CHRG_FAULT_MASK; | |
665 | v >>= BQ24190_REG_F_CHRG_FAULT_SHIFT; | |
666 | ||
667 | switch (v) { | |
668 | case 0x0: /* Normal */ | |
669 | health = POWER_SUPPLY_HEALTH_GOOD; | |
670 | break; | |
671 | case 0x1: /* Input Fault (VBUS OVP or VBAT<VBUS<3.8V) */ | |
672 | /* | |
673 | * This could be over-voltage or under-voltage | |
674 | * and there's no way to tell which. Instead | |
675 | * of looking foolish and returning 'OVERVOLTAGE' | |
676 | * when its really under-voltage, just return | |
677 | * 'UNSPEC_FAILURE'. | |
678 | */ | |
679 | health = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; | |
680 | break; | |
681 | case 0x2: /* Thermal Shutdown */ | |
682 | health = POWER_SUPPLY_HEALTH_OVERHEAT; | |
683 | break; | |
684 | case 0x3: /* Charge Safety Timer Expiration */ | |
685 | health = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE; | |
686 | break; | |
687 | default: | |
688 | health = POWER_SUPPLY_HEALTH_UNKNOWN; | |
689 | } | |
690 | } | |
691 | ||
692 | val->intval = health; | |
693 | ||
694 | return 0; | |
695 | } | |
696 | ||
697 | static int bq24190_charger_get_online(struct bq24190_dev_info *bdi, | |
698 | union power_supply_propval *val) | |
699 | { | |
700 | u8 v; | |
701 | int ret; | |
702 | ||
703 | ret = bq24190_read_mask(bdi, BQ24190_REG_SS, | |
704 | BQ24190_REG_SS_PG_STAT_MASK, | |
705 | BQ24190_REG_SS_PG_STAT_SHIFT, &v); | |
706 | if (ret < 0) | |
707 | return ret; | |
708 | ||
709 | val->intval = v; | |
710 | return 0; | |
711 | } | |
712 | ||
713 | static int bq24190_charger_get_current(struct bq24190_dev_info *bdi, | |
714 | union power_supply_propval *val) | |
715 | { | |
716 | u8 v; | |
717 | int curr, ret; | |
718 | ||
719 | ret = bq24190_get_field_val(bdi, BQ24190_REG_CCC, | |
720 | BQ24190_REG_CCC_ICHG_MASK, BQ24190_REG_CCC_ICHG_SHIFT, | |
721 | bq24190_ccc_ichg_values, | |
722 | ARRAY_SIZE(bq24190_ccc_ichg_values), &curr); | |
723 | if (ret < 0) | |
724 | return ret; | |
725 | ||
726 | ret = bq24190_read_mask(bdi, BQ24190_REG_CCC, | |
727 | BQ24190_REG_CCC_FORCE_20PCT_MASK, | |
728 | BQ24190_REG_CCC_FORCE_20PCT_SHIFT, &v); | |
729 | if (ret < 0) | |
730 | return ret; | |
731 | ||
732 | /* If FORCE_20PCT is enabled, then current is 20% of ICHG value */ | |
733 | if (v) | |
734 | curr /= 5; | |
735 | ||
736 | val->intval = curr; | |
737 | return 0; | |
738 | } | |
739 | ||
740 | static int bq24190_charger_get_current_max(struct bq24190_dev_info *bdi, | |
741 | union power_supply_propval *val) | |
742 | { | |
743 | int idx = ARRAY_SIZE(bq24190_ccc_ichg_values) - 1; | |
744 | ||
745 | val->intval = bq24190_ccc_ichg_values[idx]; | |
746 | return 0; | |
747 | } | |
748 | ||
749 | static int bq24190_charger_set_current(struct bq24190_dev_info *bdi, | |
750 | const union power_supply_propval *val) | |
751 | { | |
752 | u8 v; | |
753 | int ret, curr = val->intval; | |
754 | ||
755 | ret = bq24190_read_mask(bdi, BQ24190_REG_CCC, | |
756 | BQ24190_REG_CCC_FORCE_20PCT_MASK, | |
757 | BQ24190_REG_CCC_FORCE_20PCT_SHIFT, &v); | |
758 | if (ret < 0) | |
759 | return ret; | |
760 | ||
761 | /* If FORCE_20PCT is enabled, have to multiply value passed in by 5 */ | |
762 | if (v) | |
763 | curr *= 5; | |
764 | ||
765 | return bq24190_set_field_val(bdi, BQ24190_REG_CCC, | |
766 | BQ24190_REG_CCC_ICHG_MASK, BQ24190_REG_CCC_ICHG_SHIFT, | |
767 | bq24190_ccc_ichg_values, | |
768 | ARRAY_SIZE(bq24190_ccc_ichg_values), curr); | |
769 | } | |
770 | ||
771 | static int bq24190_charger_get_voltage(struct bq24190_dev_info *bdi, | |
772 | union power_supply_propval *val) | |
773 | { | |
774 | int voltage, ret; | |
775 | ||
776 | ret = bq24190_get_field_val(bdi, BQ24190_REG_CVC, | |
777 | BQ24190_REG_CVC_VREG_MASK, BQ24190_REG_CVC_VREG_SHIFT, | |
778 | bq24190_cvc_vreg_values, | |
779 | ARRAY_SIZE(bq24190_cvc_vreg_values), &voltage); | |
780 | if (ret < 0) | |
781 | return ret; | |
782 | ||
783 | val->intval = voltage; | |
784 | return 0; | |
785 | } | |
786 | ||
787 | static int bq24190_charger_get_voltage_max(struct bq24190_dev_info *bdi, | |
788 | union power_supply_propval *val) | |
789 | { | |
790 | int idx = ARRAY_SIZE(bq24190_cvc_vreg_values) - 1; | |
791 | ||
792 | val->intval = bq24190_cvc_vreg_values[idx]; | |
793 | return 0; | |
794 | } | |
795 | ||
796 | static int bq24190_charger_set_voltage(struct bq24190_dev_info *bdi, | |
797 | const union power_supply_propval *val) | |
798 | { | |
799 | return bq24190_set_field_val(bdi, BQ24190_REG_CVC, | |
800 | BQ24190_REG_CVC_VREG_MASK, BQ24190_REG_CVC_VREG_SHIFT, | |
801 | bq24190_cvc_vreg_values, | |
802 | ARRAY_SIZE(bq24190_cvc_vreg_values), val->intval); | |
803 | } | |
804 | ||
805 | static int bq24190_charger_get_property(struct power_supply *psy, | |
806 | enum power_supply_property psp, union power_supply_propval *val) | |
807 | { | |
297d716f | 808 | struct bq24190_dev_info *bdi = power_supply_get_drvdata(psy); |
d7bf353f MG |
809 | int ret; |
810 | ||
811 | dev_dbg(bdi->dev, "prop: %d\n", psp); | |
812 | ||
813 | pm_runtime_get_sync(bdi->dev); | |
814 | ||
815 | switch (psp) { | |
816 | case POWER_SUPPLY_PROP_CHARGE_TYPE: | |
817 | ret = bq24190_charger_get_charge_type(bdi, val); | |
818 | break; | |
819 | case POWER_SUPPLY_PROP_HEALTH: | |
820 | ret = bq24190_charger_get_health(bdi, val); | |
821 | break; | |
822 | case POWER_SUPPLY_PROP_ONLINE: | |
823 | ret = bq24190_charger_get_online(bdi, val); | |
824 | break; | |
825 | case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: | |
826 | ret = bq24190_charger_get_current(bdi, val); | |
827 | break; | |
828 | case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: | |
829 | ret = bq24190_charger_get_current_max(bdi, val); | |
830 | break; | |
831 | case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: | |
832 | ret = bq24190_charger_get_voltage(bdi, val); | |
833 | break; | |
834 | case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: | |
835 | ret = bq24190_charger_get_voltage_max(bdi, val); | |
836 | break; | |
837 | case POWER_SUPPLY_PROP_SCOPE: | |
838 | val->intval = POWER_SUPPLY_SCOPE_SYSTEM; | |
839 | ret = 0; | |
840 | break; | |
841 | case POWER_SUPPLY_PROP_MODEL_NAME: | |
842 | val->strval = bdi->model_name; | |
843 | ret = 0; | |
844 | break; | |
845 | case POWER_SUPPLY_PROP_MANUFACTURER: | |
846 | val->strval = BQ24190_MANUFACTURER; | |
847 | ret = 0; | |
848 | break; | |
849 | default: | |
850 | ret = -ENODATA; | |
851 | } | |
852 | ||
853 | pm_runtime_put_sync(bdi->dev); | |
854 | return ret; | |
855 | } | |
856 | ||
857 | static int bq24190_charger_set_property(struct power_supply *psy, | |
858 | enum power_supply_property psp, | |
859 | const union power_supply_propval *val) | |
860 | { | |
297d716f | 861 | struct bq24190_dev_info *bdi = power_supply_get_drvdata(psy); |
d7bf353f MG |
862 | int ret; |
863 | ||
864 | dev_dbg(bdi->dev, "prop: %d\n", psp); | |
865 | ||
866 | pm_runtime_get_sync(bdi->dev); | |
867 | ||
868 | switch (psp) { | |
869 | case POWER_SUPPLY_PROP_CHARGE_TYPE: | |
870 | ret = bq24190_charger_set_charge_type(bdi, val); | |
871 | break; | |
872 | case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: | |
873 | ret = bq24190_charger_set_current(bdi, val); | |
874 | break; | |
875 | case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: | |
876 | ret = bq24190_charger_set_voltage(bdi, val); | |
877 | break; | |
878 | default: | |
879 | ret = -EINVAL; | |
880 | } | |
881 | ||
882 | pm_runtime_put_sync(bdi->dev); | |
883 | return ret; | |
884 | } | |
885 | ||
886 | static int bq24190_charger_property_is_writeable(struct power_supply *psy, | |
887 | enum power_supply_property psp) | |
888 | { | |
889 | int ret; | |
890 | ||
891 | switch (psp) { | |
892 | case POWER_SUPPLY_PROP_CHARGE_TYPE: | |
893 | case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: | |
894 | case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: | |
895 | ret = 1; | |
896 | break; | |
897 | default: | |
898 | ret = 0; | |
899 | } | |
900 | ||
901 | return ret; | |
902 | } | |
903 | ||
904 | static enum power_supply_property bq24190_charger_properties[] = { | |
c9f85a90 | 905 | POWER_SUPPLY_PROP_CHARGE_TYPE, |
d7bf353f MG |
906 | POWER_SUPPLY_PROP_HEALTH, |
907 | POWER_SUPPLY_PROP_ONLINE, | |
908 | POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, | |
909 | POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, | |
910 | POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, | |
911 | POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, | |
912 | POWER_SUPPLY_PROP_SCOPE, | |
913 | POWER_SUPPLY_PROP_MODEL_NAME, | |
914 | POWER_SUPPLY_PROP_MANUFACTURER, | |
915 | }; | |
916 | ||
917 | static char *bq24190_charger_supplied_to[] = { | |
918 | "main-battery", | |
919 | }; | |
920 | ||
297d716f KK |
921 | static const struct power_supply_desc bq24190_charger_desc = { |
922 | .name = "bq24190-charger", | |
923 | .type = POWER_SUPPLY_TYPE_USB, | |
924 | .properties = bq24190_charger_properties, | |
925 | .num_properties = ARRAY_SIZE(bq24190_charger_properties), | |
926 | .get_property = bq24190_charger_get_property, | |
927 | .set_property = bq24190_charger_set_property, | |
928 | .property_is_writeable = bq24190_charger_property_is_writeable, | |
929 | }; | |
d7bf353f MG |
930 | |
931 | /* Battery power supply property routines */ | |
932 | ||
933 | static int bq24190_battery_get_status(struct bq24190_dev_info *bdi, | |
934 | union power_supply_propval *val) | |
935 | { | |
936 | u8 ss_reg, chrg_fault; | |
937 | int status, ret; | |
938 | ||
939 | mutex_lock(&bdi->f_reg_lock); | |
940 | ||
941 | if (bdi->battery_status_valid) { | |
942 | chrg_fault = bdi->f_reg; | |
943 | bdi->battery_status_valid = false; | |
944 | mutex_unlock(&bdi->f_reg_lock); | |
945 | } else { | |
946 | mutex_unlock(&bdi->f_reg_lock); | |
947 | ||
948 | ret = bq24190_read(bdi, BQ24190_REG_F, &chrg_fault); | |
949 | if (ret < 0) | |
950 | return ret; | |
951 | } | |
952 | ||
953 | chrg_fault &= BQ24190_REG_F_CHRG_FAULT_MASK; | |
954 | chrg_fault >>= BQ24190_REG_F_CHRG_FAULT_SHIFT; | |
955 | ||
956 | ret = bq24190_read(bdi, BQ24190_REG_SS, &ss_reg); | |
957 | if (ret < 0) | |
958 | return ret; | |
959 | ||
960 | /* | |
961 | * The battery must be discharging when any of these are true: | |
962 | * - there is no good power source; | |
963 | * - there is a charge fault. | |
964 | * Could also be discharging when in "supplement mode" but | |
965 | * there is no way to tell when its in that mode. | |
966 | */ | |
967 | if (!(ss_reg & BQ24190_REG_SS_PG_STAT_MASK) || chrg_fault) { | |
968 | status = POWER_SUPPLY_STATUS_DISCHARGING; | |
969 | } else { | |
970 | ss_reg &= BQ24190_REG_SS_CHRG_STAT_MASK; | |
971 | ss_reg >>= BQ24190_REG_SS_CHRG_STAT_SHIFT; | |
972 | ||
973 | switch (ss_reg) { | |
974 | case 0x0: /* Not Charging */ | |
975 | status = POWER_SUPPLY_STATUS_NOT_CHARGING; | |
976 | break; | |
977 | case 0x1: /* Pre-charge */ | |
978 | case 0x2: /* Fast Charging */ | |
979 | status = POWER_SUPPLY_STATUS_CHARGING; | |
980 | break; | |
981 | case 0x3: /* Charge Termination Done */ | |
982 | status = POWER_SUPPLY_STATUS_FULL; | |
983 | break; | |
984 | default: | |
985 | ret = -EIO; | |
986 | } | |
987 | } | |
988 | ||
989 | if (!ret) | |
990 | val->intval = status; | |
991 | ||
992 | return ret; | |
993 | } | |
994 | ||
995 | static int bq24190_battery_get_health(struct bq24190_dev_info *bdi, | |
996 | union power_supply_propval *val) | |
997 | { | |
998 | u8 v; | |
999 | int health, ret; | |
1000 | ||
1001 | mutex_lock(&bdi->f_reg_lock); | |
1002 | ||
1003 | if (bdi->battery_health_valid) { | |
1004 | v = bdi->f_reg; | |
1005 | bdi->battery_health_valid = false; | |
1006 | mutex_unlock(&bdi->f_reg_lock); | |
1007 | } else { | |
1008 | mutex_unlock(&bdi->f_reg_lock); | |
1009 | ||
1010 | ret = bq24190_read(bdi, BQ24190_REG_F, &v); | |
1011 | if (ret < 0) | |
1012 | return ret; | |
1013 | } | |
1014 | ||
1015 | if (v & BQ24190_REG_F_BAT_FAULT_MASK) { | |
1016 | health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; | |
1017 | } else { | |
1018 | v &= BQ24190_REG_F_NTC_FAULT_MASK; | |
1019 | v >>= BQ24190_REG_F_NTC_FAULT_SHIFT; | |
1020 | ||
1021 | switch (v) { | |
1022 | case 0x0: /* Normal */ | |
1023 | health = POWER_SUPPLY_HEALTH_GOOD; | |
1024 | break; | |
1025 | case 0x1: /* TS1 Cold */ | |
1026 | case 0x3: /* TS2 Cold */ | |
1027 | case 0x5: /* Both Cold */ | |
1028 | health = POWER_SUPPLY_HEALTH_COLD; | |
1029 | break; | |
1030 | case 0x2: /* TS1 Hot */ | |
1031 | case 0x4: /* TS2 Hot */ | |
1032 | case 0x6: /* Both Hot */ | |
1033 | health = POWER_SUPPLY_HEALTH_OVERHEAT; | |
1034 | break; | |
1035 | default: | |
1036 | health = POWER_SUPPLY_HEALTH_UNKNOWN; | |
1037 | } | |
1038 | } | |
1039 | ||
1040 | val->intval = health; | |
1041 | return 0; | |
1042 | } | |
1043 | ||
1044 | static int bq24190_battery_get_online(struct bq24190_dev_info *bdi, | |
1045 | union power_supply_propval *val) | |
1046 | { | |
1047 | u8 batfet_disable; | |
1048 | int ret; | |
1049 | ||
1050 | ret = bq24190_read_mask(bdi, BQ24190_REG_MOC, | |
1051 | BQ24190_REG_MOC_BATFET_DISABLE_MASK, | |
1052 | BQ24190_REG_MOC_BATFET_DISABLE_SHIFT, &batfet_disable); | |
1053 | if (ret < 0) | |
1054 | return ret; | |
1055 | ||
1056 | val->intval = !batfet_disable; | |
1057 | return 0; | |
1058 | } | |
1059 | ||
1060 | static int bq24190_battery_set_online(struct bq24190_dev_info *bdi, | |
1061 | const union power_supply_propval *val) | |
1062 | { | |
1063 | return bq24190_write_mask(bdi, BQ24190_REG_MOC, | |
1064 | BQ24190_REG_MOC_BATFET_DISABLE_MASK, | |
1065 | BQ24190_REG_MOC_BATFET_DISABLE_SHIFT, !val->intval); | |
1066 | } | |
1067 | ||
1068 | static int bq24190_battery_get_temp_alert_max(struct bq24190_dev_info *bdi, | |
1069 | union power_supply_propval *val) | |
1070 | { | |
1071 | int temp, ret; | |
1072 | ||
1073 | ret = bq24190_get_field_val(bdi, BQ24190_REG_ICTRC, | |
1074 | BQ24190_REG_ICTRC_TREG_MASK, | |
1075 | BQ24190_REG_ICTRC_TREG_SHIFT, | |
1076 | bq24190_ictrc_treg_values, | |
1077 | ARRAY_SIZE(bq24190_ictrc_treg_values), &temp); | |
1078 | if (ret < 0) | |
1079 | return ret; | |
1080 | ||
1081 | val->intval = temp; | |
1082 | return 0; | |
1083 | } | |
1084 | ||
1085 | static int bq24190_battery_set_temp_alert_max(struct bq24190_dev_info *bdi, | |
1086 | const union power_supply_propval *val) | |
1087 | { | |
1088 | return bq24190_set_field_val(bdi, BQ24190_REG_ICTRC, | |
1089 | BQ24190_REG_ICTRC_TREG_MASK, | |
1090 | BQ24190_REG_ICTRC_TREG_SHIFT, | |
1091 | bq24190_ictrc_treg_values, | |
1092 | ARRAY_SIZE(bq24190_ictrc_treg_values), val->intval); | |
1093 | } | |
1094 | ||
1095 | static int bq24190_battery_get_property(struct power_supply *psy, | |
1096 | enum power_supply_property psp, union power_supply_propval *val) | |
1097 | { | |
297d716f | 1098 | struct bq24190_dev_info *bdi = power_supply_get_drvdata(psy); |
d7bf353f MG |
1099 | int ret; |
1100 | ||
1101 | dev_dbg(bdi->dev, "prop: %d\n", psp); | |
1102 | ||
1103 | pm_runtime_get_sync(bdi->dev); | |
1104 | ||
1105 | switch (psp) { | |
1106 | case POWER_SUPPLY_PROP_STATUS: | |
1107 | ret = bq24190_battery_get_status(bdi, val); | |
1108 | break; | |
1109 | case POWER_SUPPLY_PROP_HEALTH: | |
1110 | ret = bq24190_battery_get_health(bdi, val); | |
1111 | break; | |
1112 | case POWER_SUPPLY_PROP_ONLINE: | |
1113 | ret = bq24190_battery_get_online(bdi, val); | |
1114 | break; | |
1115 | case POWER_SUPPLY_PROP_TECHNOLOGY: | |
1116 | /* Could be Li-on or Li-polymer but no way to tell which */ | |
1117 | val->intval = POWER_SUPPLY_TECHNOLOGY_UNKNOWN; | |
1118 | ret = 0; | |
1119 | break; | |
1120 | case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: | |
1121 | ret = bq24190_battery_get_temp_alert_max(bdi, val); | |
1122 | break; | |
1123 | case POWER_SUPPLY_PROP_SCOPE: | |
1124 | val->intval = POWER_SUPPLY_SCOPE_SYSTEM; | |
1125 | ret = 0; | |
1126 | break; | |
1127 | default: | |
1128 | ret = -ENODATA; | |
1129 | } | |
1130 | ||
1131 | pm_runtime_put_sync(bdi->dev); | |
1132 | return ret; | |
1133 | } | |
1134 | ||
1135 | static int bq24190_battery_set_property(struct power_supply *psy, | |
1136 | enum power_supply_property psp, | |
1137 | const union power_supply_propval *val) | |
1138 | { | |
297d716f | 1139 | struct bq24190_dev_info *bdi = power_supply_get_drvdata(psy); |
d7bf353f MG |
1140 | int ret; |
1141 | ||
1142 | dev_dbg(bdi->dev, "prop: %d\n", psp); | |
1143 | ||
1144 | pm_runtime_put_sync(bdi->dev); | |
1145 | ||
1146 | switch (psp) { | |
1147 | case POWER_SUPPLY_PROP_ONLINE: | |
1148 | ret = bq24190_battery_set_online(bdi, val); | |
1149 | break; | |
1150 | case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: | |
1151 | ret = bq24190_battery_set_temp_alert_max(bdi, val); | |
1152 | break; | |
1153 | default: | |
1154 | ret = -EINVAL; | |
1155 | } | |
1156 | ||
1157 | pm_runtime_put_sync(bdi->dev); | |
1158 | return ret; | |
1159 | } | |
1160 | ||
1161 | static int bq24190_battery_property_is_writeable(struct power_supply *psy, | |
1162 | enum power_supply_property psp) | |
1163 | { | |
1164 | int ret; | |
1165 | ||
1166 | switch (psp) { | |
1167 | case POWER_SUPPLY_PROP_ONLINE: | |
1168 | case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: | |
1169 | ret = 1; | |
1170 | break; | |
1171 | default: | |
1172 | ret = 0; | |
1173 | } | |
1174 | ||
1175 | return ret; | |
1176 | } | |
1177 | ||
1178 | static enum power_supply_property bq24190_battery_properties[] = { | |
1179 | POWER_SUPPLY_PROP_STATUS, | |
1180 | POWER_SUPPLY_PROP_HEALTH, | |
1181 | POWER_SUPPLY_PROP_ONLINE, | |
1182 | POWER_SUPPLY_PROP_TECHNOLOGY, | |
1183 | POWER_SUPPLY_PROP_TEMP_ALERT_MAX, | |
1184 | POWER_SUPPLY_PROP_SCOPE, | |
1185 | }; | |
1186 | ||
297d716f KK |
1187 | static const struct power_supply_desc bq24190_battery_desc = { |
1188 | .name = "bq24190-battery", | |
1189 | .type = POWER_SUPPLY_TYPE_BATTERY, | |
1190 | .properties = bq24190_battery_properties, | |
1191 | .num_properties = ARRAY_SIZE(bq24190_battery_properties), | |
1192 | .get_property = bq24190_battery_get_property, | |
1193 | .set_property = bq24190_battery_set_property, | |
1194 | .property_is_writeable = bq24190_battery_property_is_writeable, | |
1195 | }; | |
d7bf353f MG |
1196 | |
1197 | static irqreturn_t bq24190_irq_handler_thread(int irq, void *data) | |
1198 | { | |
1199 | struct bq24190_dev_info *bdi = data; | |
1200 | bool alert_userspace = false; | |
31f50e48 | 1201 | u8 ss_reg = 0, f_reg = 0; |
d7bf353f MG |
1202 | int ret; |
1203 | ||
1204 | pm_runtime_get_sync(bdi->dev); | |
1205 | ||
1206 | ret = bq24190_read(bdi, BQ24190_REG_SS, &ss_reg); | |
1207 | if (ret < 0) { | |
1208 | dev_err(bdi->dev, "Can't read SS reg: %d\n", ret); | |
1209 | goto out; | |
1210 | } | |
1211 | ||
1212 | if (ss_reg != bdi->ss_reg) { | |
1213 | /* | |
1214 | * The device is in host mode so when PG_STAT goes from 1->0 | |
1215 | * (i.e., power removed) HIZ needs to be disabled. | |
1216 | */ | |
1217 | if ((bdi->ss_reg & BQ24190_REG_SS_PG_STAT_MASK) && | |
1218 | !(ss_reg & BQ24190_REG_SS_PG_STAT_MASK)) { | |
1219 | ret = bq24190_write_mask(bdi, BQ24190_REG_ISC, | |
1220 | BQ24190_REG_ISC_EN_HIZ_MASK, | |
1221 | BQ24190_REG_ISC_EN_HIZ_SHIFT, | |
1222 | 0); | |
1223 | if (ret < 0) | |
1224 | dev_err(bdi->dev, "Can't access ISC reg: %d\n", | |
1225 | ret); | |
1226 | } | |
1227 | ||
1228 | bdi->ss_reg = ss_reg; | |
1229 | alert_userspace = true; | |
1230 | } | |
1231 | ||
1232 | mutex_lock(&bdi->f_reg_lock); | |
1233 | ||
1234 | ret = bq24190_read(bdi, BQ24190_REG_F, &f_reg); | |
1235 | if (ret < 0) { | |
1236 | mutex_unlock(&bdi->f_reg_lock); | |
1237 | dev_err(bdi->dev, "Can't read F reg: %d\n", ret); | |
1238 | goto out; | |
1239 | } | |
1240 | ||
1241 | if (f_reg != bdi->f_reg) { | |
1242 | bdi->f_reg = f_reg; | |
1243 | bdi->charger_health_valid = true; | |
1244 | bdi->battery_health_valid = true; | |
1245 | bdi->battery_status_valid = true; | |
1246 | ||
1247 | alert_userspace = true; | |
1248 | } | |
1249 | ||
1250 | mutex_unlock(&bdi->f_reg_lock); | |
1251 | ||
1252 | /* | |
1253 | * Sometimes bq24190 gives a steady trickle of interrupts even | |
1254 | * though the watchdog timer is turned off and neither the STATUS | |
1255 | * nor FAULT registers have changed. Weed out these sprurious | |
1256 | * interrupts so userspace isn't alerted for no reason. | |
1257 | * In addition, the chip always generates an interrupt after | |
1258 | * register reset so we should ignore that one (the very first | |
1259 | * interrupt received). | |
1260 | */ | |
cd054ee1 TE |
1261 | if (alert_userspace) { |
1262 | if (!bdi->first_time) { | |
1263 | power_supply_changed(bdi->charger); | |
1264 | power_supply_changed(bdi->battery); | |
1265 | } else { | |
1266 | bdi->first_time = false; | |
1267 | } | |
d7bf353f MG |
1268 | } |
1269 | ||
1270 | out: | |
1271 | pm_runtime_put_sync(bdi->dev); | |
1272 | ||
1273 | dev_dbg(bdi->dev, "ss_reg: 0x%02x, f_reg: 0x%02x\n", ss_reg, f_reg); | |
1274 | ||
1275 | return IRQ_HANDLED; | |
1276 | } | |
1277 | ||
1278 | static int bq24190_hw_init(struct bq24190_dev_info *bdi) | |
1279 | { | |
1280 | u8 v; | |
1281 | int ret; | |
1282 | ||
1283 | pm_runtime_get_sync(bdi->dev); | |
1284 | ||
1285 | /* First check that the device really is what its supposed to be */ | |
1286 | ret = bq24190_read_mask(bdi, BQ24190_REG_VPRS, | |
1287 | BQ24190_REG_VPRS_PN_MASK, | |
1288 | BQ24190_REG_VPRS_PN_SHIFT, | |
1289 | &v); | |
1290 | if (ret < 0) | |
1291 | goto out; | |
1292 | ||
1293 | if (v != bdi->model) { | |
1294 | ret = -ENODEV; | |
1295 | goto out; | |
1296 | } | |
1297 | ||
1298 | ret = bq24190_register_reset(bdi); | |
1299 | if (ret < 0) | |
1300 | goto out; | |
1301 | ||
1302 | ret = bq24190_set_mode_host(bdi); | |
1303 | out: | |
1304 | pm_runtime_put_sync(bdi->dev); | |
1305 | return ret; | |
1306 | } | |
1307 | ||
1308 | #ifdef CONFIG_OF | |
1309 | static int bq24190_setup_dt(struct bq24190_dev_info *bdi) | |
1310 | { | |
1311 | bdi->irq = irq_of_parse_and_map(bdi->dev->of_node, 0); | |
1312 | if (bdi->irq <= 0) | |
1313 | return -1; | |
1314 | ||
1315 | return 0; | |
1316 | } | |
1317 | #else | |
1318 | static int bq24190_setup_dt(struct bq24190_dev_info *bdi) | |
1319 | { | |
1320 | return -1; | |
1321 | } | |
1322 | #endif | |
1323 | ||
1324 | static int bq24190_setup_pdata(struct bq24190_dev_info *bdi, | |
1325 | struct bq24190_platform_data *pdata) | |
1326 | { | |
1327 | int ret; | |
1328 | ||
1329 | if (!gpio_is_valid(pdata->gpio_int)) | |
1330 | return -1; | |
1331 | ||
1332 | ret = gpio_request(pdata->gpio_int, dev_name(bdi->dev)); | |
1333 | if (ret < 0) | |
1334 | return -1; | |
1335 | ||
1336 | ret = gpio_direction_input(pdata->gpio_int); | |
1337 | if (ret < 0) | |
1338 | goto out; | |
1339 | ||
1340 | bdi->irq = gpio_to_irq(pdata->gpio_int); | |
1341 | if (!bdi->irq) | |
1342 | goto out; | |
1343 | ||
1344 | bdi->gpio_int = pdata->gpio_int; | |
1345 | return 0; | |
1346 | ||
1347 | out: | |
1348 | gpio_free(pdata->gpio_int); | |
1349 | return -1; | |
1350 | } | |
1351 | ||
1352 | static int bq24190_probe(struct i2c_client *client, | |
1353 | const struct i2c_device_id *id) | |
1354 | { | |
1355 | struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); | |
1356 | struct device *dev = &client->dev; | |
1357 | struct bq24190_platform_data *pdata = client->dev.platform_data; | |
297d716f | 1358 | struct power_supply_config charger_cfg = {}, battery_cfg = {}; |
d7bf353f MG |
1359 | struct bq24190_dev_info *bdi; |
1360 | int ret; | |
1361 | ||
1362 | if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { | |
1363 | dev_err(dev, "No support for SMBUS_BYTE_DATA\n"); | |
1364 | return -ENODEV; | |
1365 | } | |
1366 | ||
1367 | bdi = devm_kzalloc(dev, sizeof(*bdi), GFP_KERNEL); | |
1368 | if (!bdi) { | |
1369 | dev_err(dev, "Can't alloc bdi struct\n"); | |
1370 | return -ENOMEM; | |
1371 | } | |
1372 | ||
1373 | bdi->client = client; | |
1374 | bdi->dev = dev; | |
1375 | bdi->model = id->driver_data; | |
1376 | strncpy(bdi->model_name, id->name, I2C_NAME_SIZE); | |
1377 | mutex_init(&bdi->f_reg_lock); | |
1378 | bdi->first_time = true; | |
1379 | bdi->charger_health_valid = false; | |
1380 | bdi->battery_health_valid = false; | |
1381 | bdi->battery_status_valid = false; | |
1382 | ||
1383 | i2c_set_clientdata(client, bdi); | |
1384 | ||
1385 | if (dev->of_node) | |
1386 | ret = bq24190_setup_dt(bdi); | |
1387 | else | |
1388 | ret = bq24190_setup_pdata(bdi, pdata); | |
1389 | ||
1390 | if (ret) { | |
1391 | dev_err(dev, "Can't get irq info\n"); | |
1392 | return -EINVAL; | |
1393 | } | |
1394 | ||
1395 | ret = devm_request_threaded_irq(dev, bdi->irq, NULL, | |
1396 | bq24190_irq_handler_thread, | |
1397 | IRQF_TRIGGER_RISING | IRQF_ONESHOT, | |
1398 | "bq24190-charger", bdi); | |
1399 | if (ret < 0) { | |
1400 | dev_err(dev, "Can't set up irq handler\n"); | |
1401 | goto out1; | |
1402 | } | |
1403 | ||
1404 | pm_runtime_enable(dev); | |
1405 | pm_runtime_resume(dev); | |
1406 | ||
1407 | ret = bq24190_hw_init(bdi); | |
1408 | if (ret < 0) { | |
1409 | dev_err(dev, "Hardware init failed\n"); | |
1410 | goto out2; | |
1411 | } | |
1412 | ||
297d716f KK |
1413 | charger_cfg.drv_data = bdi; |
1414 | charger_cfg.supplied_to = bq24190_charger_supplied_to; | |
1415 | charger_cfg.num_supplicants = ARRAY_SIZE(bq24190_charger_supplied_to), | |
1416 | bdi->charger = power_supply_register(dev, &bq24190_charger_desc, | |
1417 | &charger_cfg); | |
1418 | if (IS_ERR(bdi->charger)) { | |
d7bf353f | 1419 | dev_err(dev, "Can't register charger\n"); |
297d716f | 1420 | ret = PTR_ERR(bdi->charger); |
d7bf353f MG |
1421 | goto out2; |
1422 | } | |
1423 | ||
297d716f KK |
1424 | battery_cfg.drv_data = bdi; |
1425 | bdi->battery = power_supply_register(dev, &bq24190_battery_desc, | |
1426 | &battery_cfg); | |
1427 | if (IS_ERR(bdi->battery)) { | |
d7bf353f | 1428 | dev_err(dev, "Can't register battery\n"); |
297d716f | 1429 | ret = PTR_ERR(bdi->battery); |
d7bf353f MG |
1430 | goto out3; |
1431 | } | |
1432 | ||
1433 | ret = bq24190_sysfs_create_group(bdi); | |
1434 | if (ret) { | |
1435 | dev_err(dev, "Can't create sysfs entries\n"); | |
1436 | goto out4; | |
1437 | } | |
1438 | ||
1439 | return 0; | |
1440 | ||
1441 | out4: | |
297d716f | 1442 | power_supply_unregister(bdi->battery); |
d7bf353f | 1443 | out3: |
297d716f | 1444 | power_supply_unregister(bdi->charger); |
d7bf353f MG |
1445 | out2: |
1446 | pm_runtime_disable(dev); | |
1447 | out1: | |
1448 | if (bdi->gpio_int) | |
1449 | gpio_free(bdi->gpio_int); | |
1450 | ||
1451 | return ret; | |
1452 | } | |
1453 | ||
1454 | static int bq24190_remove(struct i2c_client *client) | |
1455 | { | |
1456 | struct bq24190_dev_info *bdi = i2c_get_clientdata(client); | |
1457 | ||
1458 | pm_runtime_get_sync(bdi->dev); | |
1459 | bq24190_register_reset(bdi); | |
1460 | pm_runtime_put_sync(bdi->dev); | |
1461 | ||
1462 | bq24190_sysfs_remove_group(bdi); | |
297d716f KK |
1463 | power_supply_unregister(bdi->battery); |
1464 | power_supply_unregister(bdi->charger); | |
d7bf353f MG |
1465 | pm_runtime_disable(bdi->dev); |
1466 | ||
1467 | if (bdi->gpio_int) | |
1468 | gpio_free(bdi->gpio_int); | |
1469 | ||
1470 | return 0; | |
1471 | } | |
1472 | ||
1473 | #ifdef CONFIG_PM_SLEEP | |
1474 | static int bq24190_pm_suspend(struct device *dev) | |
1475 | { | |
1476 | struct i2c_client *client = to_i2c_client(dev); | |
1477 | struct bq24190_dev_info *bdi = i2c_get_clientdata(client); | |
1478 | ||
1479 | pm_runtime_get_sync(bdi->dev); | |
1480 | bq24190_register_reset(bdi); | |
1481 | pm_runtime_put_sync(bdi->dev); | |
1482 | ||
1483 | return 0; | |
1484 | } | |
1485 | ||
1486 | static int bq24190_pm_resume(struct device *dev) | |
1487 | { | |
1488 | struct i2c_client *client = to_i2c_client(dev); | |
1489 | struct bq24190_dev_info *bdi = i2c_get_clientdata(client); | |
1490 | ||
1491 | bdi->charger_health_valid = false; | |
1492 | bdi->battery_health_valid = false; | |
1493 | bdi->battery_status_valid = false; | |
1494 | ||
1495 | pm_runtime_get_sync(bdi->dev); | |
1496 | bq24190_register_reset(bdi); | |
1497 | pm_runtime_put_sync(bdi->dev); | |
1498 | ||
1499 | /* Things may have changed while suspended so alert upper layer */ | |
297d716f KK |
1500 | power_supply_changed(bdi->charger); |
1501 | power_supply_changed(bdi->battery); | |
d7bf353f MG |
1502 | |
1503 | return 0; | |
1504 | } | |
1505 | #endif | |
1506 | ||
1507 | static SIMPLE_DEV_PM_OPS(bq24190_pm_ops, bq24190_pm_suspend, bq24190_pm_resume); | |
1508 | ||
1509 | /* | |
1510 | * Only support the bq24190 right now. The bq24192, bq24192i, and bq24193 | |
1511 | * are similar but not identical so the driver needs to be extended to | |
1512 | * support them. | |
1513 | */ | |
1514 | static const struct i2c_device_id bq24190_i2c_ids[] = { | |
1515 | { "bq24190", BQ24190_REG_VPRS_PN_24190 }, | |
1516 | { }, | |
1517 | }; | |
63369b2b | 1518 | MODULE_DEVICE_TABLE(i2c, bq24190_i2c_ids); |
d7bf353f MG |
1519 | |
1520 | #ifdef CONFIG_OF | |
1521 | static const struct of_device_id bq24190_of_match[] = { | |
1522 | { .compatible = "ti,bq24190", }, | |
1523 | { }, | |
1524 | }; | |
1525 | MODULE_DEVICE_TABLE(of, bq24190_of_match); | |
1526 | #else | |
1527 | static const struct of_device_id bq24190_of_match[] = { | |
1528 | { }, | |
1529 | }; | |
1530 | #endif | |
1531 | ||
1532 | static struct i2c_driver bq24190_driver = { | |
1533 | .probe = bq24190_probe, | |
1534 | .remove = bq24190_remove, | |
1535 | .id_table = bq24190_i2c_ids, | |
1536 | .driver = { | |
1537 | .name = "bq24190-charger", | |
d7bf353f MG |
1538 | .pm = &bq24190_pm_ops, |
1539 | .of_match_table = of_match_ptr(bq24190_of_match), | |
1540 | }, | |
1541 | }; | |
1542 | module_i2c_driver(bq24190_driver); | |
1543 | ||
1544 | MODULE_LICENSE("GPL"); | |
1545 | MODULE_AUTHOR("Mark A. Greer <mgreer@animalcreek.com>"); | |
d7bf353f | 1546 | MODULE_DESCRIPTION("TI BQ24190 Charger Driver"); |