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; | |
155 | struct power_supply charger; | |
156 | struct power_supply battery; | |
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); | |
426 | struct bq24190_dev_info *bdi = | |
427 | container_of(psy, struct bq24190_dev_info, charger); | |
428 | struct bq24190_sysfs_field_info *info; | |
429 | int ret; | |
430 | u8 v; | |
431 | ||
432 | info = bq24190_sysfs_field_lookup(attr->attr.name); | |
433 | if (!info) | |
434 | return -EINVAL; | |
435 | ||
436 | ret = bq24190_read_mask(bdi, info->reg, info->mask, info->shift, &v); | |
437 | if (ret) | |
438 | return ret; | |
439 | ||
440 | return scnprintf(buf, PAGE_SIZE, "%hhx\n", v); | |
441 | } | |
442 | ||
443 | static ssize_t bq24190_sysfs_store(struct device *dev, | |
444 | struct device_attribute *attr, const char *buf, size_t count) | |
445 | { | |
446 | struct power_supply *psy = dev_get_drvdata(dev); | |
447 | struct bq24190_dev_info *bdi = | |
448 | container_of(psy, struct bq24190_dev_info, charger); | |
449 | struct bq24190_sysfs_field_info *info; | |
450 | int ret; | |
451 | u8 v; | |
452 | ||
453 | info = bq24190_sysfs_field_lookup(attr->attr.name); | |
454 | if (!info) | |
455 | return -EINVAL; | |
456 | ||
457 | ret = kstrtou8(buf, 0, &v); | |
458 | if (ret < 0) | |
459 | return ret; | |
460 | ||
461 | ret = bq24190_write_mask(bdi, info->reg, info->mask, info->shift, v); | |
462 | if (ret) | |
463 | return ret; | |
464 | ||
465 | return count; | |
466 | } | |
467 | ||
468 | static int bq24190_sysfs_create_group(struct bq24190_dev_info *bdi) | |
469 | { | |
470 | bq24190_sysfs_init_attrs(); | |
471 | ||
472 | return sysfs_create_group(&bdi->charger.dev->kobj, | |
473 | &bq24190_sysfs_attr_group); | |
474 | } | |
475 | ||
476 | static void bq24190_sysfs_remove_group(struct bq24190_dev_info *bdi) | |
477 | { | |
478 | sysfs_remove_group(&bdi->charger.dev->kobj, &bq24190_sysfs_attr_group); | |
479 | } | |
480 | #else | |
481 | static int bq24190_sysfs_create_group(struct bq24190_dev_info *bdi) | |
482 | { | |
483 | return 0; | |
484 | } | |
485 | ||
486 | static inline void bq24190_sysfs_remove_group(struct bq24190_dev_info *bdi) {} | |
487 | #endif | |
488 | ||
489 | /* | |
490 | * According to the "Host Mode and default Mode" section of the | |
491 | * manual, a write to any register causes the bq24190 to switch | |
492 | * from default mode to host mode. It will switch back to default | |
493 | * mode after a WDT timeout unless the WDT is turned off as well. | |
494 | * So, by simply turning off the WDT, we accomplish both with the | |
495 | * same write. | |
496 | */ | |
497 | static int bq24190_set_mode_host(struct bq24190_dev_info *bdi) | |
498 | { | |
499 | int ret; | |
500 | u8 v; | |
501 | ||
502 | ret = bq24190_read(bdi, BQ24190_REG_CTTC, &v); | |
503 | if (ret < 0) | |
504 | return ret; | |
505 | ||
506 | bdi->watchdog = ((v & BQ24190_REG_CTTC_WATCHDOG_MASK) >> | |
507 | BQ24190_REG_CTTC_WATCHDOG_SHIFT); | |
508 | v &= ~BQ24190_REG_CTTC_WATCHDOG_MASK; | |
509 | ||
510 | return bq24190_write(bdi, BQ24190_REG_CTTC, v); | |
511 | } | |
512 | ||
513 | static int bq24190_register_reset(struct bq24190_dev_info *bdi) | |
514 | { | |
515 | int ret, limit = 100; | |
516 | u8 v; | |
517 | ||
518 | /* Reset the registers */ | |
519 | ret = bq24190_write_mask(bdi, BQ24190_REG_POC, | |
520 | BQ24190_REG_POC_RESET_MASK, | |
521 | BQ24190_REG_POC_RESET_SHIFT, | |
522 | 0x1); | |
523 | if (ret < 0) | |
524 | return ret; | |
525 | ||
526 | /* Reset bit will be cleared by hardware so poll until it is */ | |
527 | do { | |
528 | ret = bq24190_read_mask(bdi, BQ24190_REG_POC, | |
529 | BQ24190_REG_POC_RESET_MASK, | |
530 | BQ24190_REG_POC_RESET_SHIFT, | |
531 | &v); | |
532 | if (ret < 0) | |
533 | return ret; | |
534 | ||
535 | if (!v) | |
536 | break; | |
537 | ||
538 | udelay(10); | |
539 | } while (--limit); | |
540 | ||
541 | if (!limit) | |
542 | return -EIO; | |
543 | ||
544 | return 0; | |
545 | } | |
546 | ||
547 | /* Charger power supply property routines */ | |
548 | ||
549 | static int bq24190_charger_get_charge_type(struct bq24190_dev_info *bdi, | |
550 | union power_supply_propval *val) | |
551 | { | |
552 | u8 v; | |
553 | int type, ret; | |
554 | ||
555 | ret = bq24190_read_mask(bdi, BQ24190_REG_POC, | |
556 | BQ24190_REG_POC_CHG_CONFIG_MASK, | |
557 | BQ24190_REG_POC_CHG_CONFIG_SHIFT, | |
558 | &v); | |
559 | if (ret < 0) | |
560 | return ret; | |
561 | ||
562 | /* If POC[CHG_CONFIG] (REG01[5:4]) == 0, charge is disabled */ | |
563 | if (!v) { | |
564 | type = POWER_SUPPLY_CHARGE_TYPE_NONE; | |
565 | } else { | |
566 | ret = bq24190_read_mask(bdi, BQ24190_REG_CCC, | |
567 | BQ24190_REG_CCC_FORCE_20PCT_MASK, | |
568 | BQ24190_REG_CCC_FORCE_20PCT_SHIFT, | |
569 | &v); | |
570 | if (ret < 0) | |
571 | return ret; | |
572 | ||
573 | type = (v) ? POWER_SUPPLY_CHARGE_TYPE_TRICKLE : | |
574 | POWER_SUPPLY_CHARGE_TYPE_FAST; | |
575 | } | |
576 | ||
577 | val->intval = type; | |
578 | ||
579 | return 0; | |
580 | } | |
581 | ||
582 | static int bq24190_charger_set_charge_type(struct bq24190_dev_info *bdi, | |
583 | const union power_supply_propval *val) | |
584 | { | |
585 | u8 chg_config, force_20pct, en_term; | |
586 | int ret; | |
587 | ||
588 | /* | |
589 | * According to the "Termination when REG02[0] = 1" section of | |
590 | * the bq24190 manual, the trickle charge could be less than the | |
591 | * termination current so it recommends turning off the termination | |
592 | * function. | |
593 | * | |
594 | * Note: AFAICT from the datasheet, the user will have to manually | |
595 | * turn off the charging when in 20% mode. If its not turned off, | |
596 | * there could be battery damage. So, use this mode at your own risk. | |
597 | */ | |
598 | switch (val->intval) { | |
599 | case POWER_SUPPLY_CHARGE_TYPE_NONE: | |
600 | chg_config = 0x0; | |
601 | break; | |
602 | case POWER_SUPPLY_CHARGE_TYPE_TRICKLE: | |
603 | chg_config = 0x1; | |
604 | force_20pct = 0x1; | |
605 | en_term = 0x0; | |
606 | break; | |
607 | case POWER_SUPPLY_CHARGE_TYPE_FAST: | |
608 | chg_config = 0x1; | |
609 | force_20pct = 0x0; | |
610 | en_term = 0x1; | |
611 | break; | |
612 | default: | |
613 | return -EINVAL; | |
614 | } | |
615 | ||
616 | if (chg_config) { /* Enabling the charger */ | |
617 | ret = bq24190_write_mask(bdi, BQ24190_REG_CCC, | |
618 | BQ24190_REG_CCC_FORCE_20PCT_MASK, | |
619 | BQ24190_REG_CCC_FORCE_20PCT_SHIFT, | |
620 | force_20pct); | |
621 | if (ret < 0) | |
622 | return ret; | |
623 | ||
624 | ret = bq24190_write_mask(bdi, BQ24190_REG_CTTC, | |
625 | BQ24190_REG_CTTC_EN_TERM_MASK, | |
626 | BQ24190_REG_CTTC_EN_TERM_SHIFT, | |
627 | en_term); | |
628 | if (ret < 0) | |
629 | return ret; | |
630 | } | |
631 | ||
632 | return bq24190_write_mask(bdi, BQ24190_REG_POC, | |
633 | BQ24190_REG_POC_CHG_CONFIG_MASK, | |
634 | BQ24190_REG_POC_CHG_CONFIG_SHIFT, chg_config); | |
635 | } | |
636 | ||
637 | static int bq24190_charger_get_health(struct bq24190_dev_info *bdi, | |
638 | union power_supply_propval *val) | |
639 | { | |
640 | u8 v; | |
641 | int health, ret; | |
642 | ||
643 | mutex_lock(&bdi->f_reg_lock); | |
644 | ||
645 | if (bdi->charger_health_valid) { | |
646 | v = bdi->f_reg; | |
647 | bdi->charger_health_valid = false; | |
648 | mutex_unlock(&bdi->f_reg_lock); | |
649 | } else { | |
650 | mutex_unlock(&bdi->f_reg_lock); | |
651 | ||
652 | ret = bq24190_read(bdi, BQ24190_REG_F, &v); | |
653 | if (ret < 0) | |
654 | return ret; | |
655 | } | |
656 | ||
657 | if (v & BQ24190_REG_F_BOOST_FAULT_MASK) { | |
658 | /* | |
659 | * This could be over-current or over-voltage but there's | |
660 | * no way to tell which. Return 'OVERVOLTAGE' since there | |
661 | * isn't an 'OVERCURRENT' value defined that we can return | |
662 | * even if it was over-current. | |
663 | */ | |
664 | health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; | |
665 | } else { | |
666 | v &= BQ24190_REG_F_CHRG_FAULT_MASK; | |
667 | v >>= BQ24190_REG_F_CHRG_FAULT_SHIFT; | |
668 | ||
669 | switch (v) { | |
670 | case 0x0: /* Normal */ | |
671 | health = POWER_SUPPLY_HEALTH_GOOD; | |
672 | break; | |
673 | case 0x1: /* Input Fault (VBUS OVP or VBAT<VBUS<3.8V) */ | |
674 | /* | |
675 | * This could be over-voltage or under-voltage | |
676 | * and there's no way to tell which. Instead | |
677 | * of looking foolish and returning 'OVERVOLTAGE' | |
678 | * when its really under-voltage, just return | |
679 | * 'UNSPEC_FAILURE'. | |
680 | */ | |
681 | health = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; | |
682 | break; | |
683 | case 0x2: /* Thermal Shutdown */ | |
684 | health = POWER_SUPPLY_HEALTH_OVERHEAT; | |
685 | break; | |
686 | case 0x3: /* Charge Safety Timer Expiration */ | |
687 | health = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE; | |
688 | break; | |
689 | default: | |
690 | health = POWER_SUPPLY_HEALTH_UNKNOWN; | |
691 | } | |
692 | } | |
693 | ||
694 | val->intval = health; | |
695 | ||
696 | return 0; | |
697 | } | |
698 | ||
699 | static int bq24190_charger_get_online(struct bq24190_dev_info *bdi, | |
700 | union power_supply_propval *val) | |
701 | { | |
702 | u8 v; | |
703 | int ret; | |
704 | ||
705 | ret = bq24190_read_mask(bdi, BQ24190_REG_SS, | |
706 | BQ24190_REG_SS_PG_STAT_MASK, | |
707 | BQ24190_REG_SS_PG_STAT_SHIFT, &v); | |
708 | if (ret < 0) | |
709 | return ret; | |
710 | ||
711 | val->intval = v; | |
712 | return 0; | |
713 | } | |
714 | ||
715 | static int bq24190_charger_get_current(struct bq24190_dev_info *bdi, | |
716 | union power_supply_propval *val) | |
717 | { | |
718 | u8 v; | |
719 | int curr, ret; | |
720 | ||
721 | ret = bq24190_get_field_val(bdi, BQ24190_REG_CCC, | |
722 | BQ24190_REG_CCC_ICHG_MASK, BQ24190_REG_CCC_ICHG_SHIFT, | |
723 | bq24190_ccc_ichg_values, | |
724 | ARRAY_SIZE(bq24190_ccc_ichg_values), &curr); | |
725 | if (ret < 0) | |
726 | return ret; | |
727 | ||
728 | ret = bq24190_read_mask(bdi, BQ24190_REG_CCC, | |
729 | BQ24190_REG_CCC_FORCE_20PCT_MASK, | |
730 | BQ24190_REG_CCC_FORCE_20PCT_SHIFT, &v); | |
731 | if (ret < 0) | |
732 | return ret; | |
733 | ||
734 | /* If FORCE_20PCT is enabled, then current is 20% of ICHG value */ | |
735 | if (v) | |
736 | curr /= 5; | |
737 | ||
738 | val->intval = curr; | |
739 | return 0; | |
740 | } | |
741 | ||
742 | static int bq24190_charger_get_current_max(struct bq24190_dev_info *bdi, | |
743 | union power_supply_propval *val) | |
744 | { | |
745 | int idx = ARRAY_SIZE(bq24190_ccc_ichg_values) - 1; | |
746 | ||
747 | val->intval = bq24190_ccc_ichg_values[idx]; | |
748 | return 0; | |
749 | } | |
750 | ||
751 | static int bq24190_charger_set_current(struct bq24190_dev_info *bdi, | |
752 | const union power_supply_propval *val) | |
753 | { | |
754 | u8 v; | |
755 | int ret, curr = val->intval; | |
756 | ||
757 | ret = bq24190_read_mask(bdi, BQ24190_REG_CCC, | |
758 | BQ24190_REG_CCC_FORCE_20PCT_MASK, | |
759 | BQ24190_REG_CCC_FORCE_20PCT_SHIFT, &v); | |
760 | if (ret < 0) | |
761 | return ret; | |
762 | ||
763 | /* If FORCE_20PCT is enabled, have to multiply value passed in by 5 */ | |
764 | if (v) | |
765 | curr *= 5; | |
766 | ||
767 | return bq24190_set_field_val(bdi, BQ24190_REG_CCC, | |
768 | BQ24190_REG_CCC_ICHG_MASK, BQ24190_REG_CCC_ICHG_SHIFT, | |
769 | bq24190_ccc_ichg_values, | |
770 | ARRAY_SIZE(bq24190_ccc_ichg_values), curr); | |
771 | } | |
772 | ||
773 | static int bq24190_charger_get_voltage(struct bq24190_dev_info *bdi, | |
774 | union power_supply_propval *val) | |
775 | { | |
776 | int voltage, ret; | |
777 | ||
778 | ret = bq24190_get_field_val(bdi, BQ24190_REG_CVC, | |
779 | BQ24190_REG_CVC_VREG_MASK, BQ24190_REG_CVC_VREG_SHIFT, | |
780 | bq24190_cvc_vreg_values, | |
781 | ARRAY_SIZE(bq24190_cvc_vreg_values), &voltage); | |
782 | if (ret < 0) | |
783 | return ret; | |
784 | ||
785 | val->intval = voltage; | |
786 | return 0; | |
787 | } | |
788 | ||
789 | static int bq24190_charger_get_voltage_max(struct bq24190_dev_info *bdi, | |
790 | union power_supply_propval *val) | |
791 | { | |
792 | int idx = ARRAY_SIZE(bq24190_cvc_vreg_values) - 1; | |
793 | ||
794 | val->intval = bq24190_cvc_vreg_values[idx]; | |
795 | return 0; | |
796 | } | |
797 | ||
798 | static int bq24190_charger_set_voltage(struct bq24190_dev_info *bdi, | |
799 | const union power_supply_propval *val) | |
800 | { | |
801 | return bq24190_set_field_val(bdi, BQ24190_REG_CVC, | |
802 | BQ24190_REG_CVC_VREG_MASK, BQ24190_REG_CVC_VREG_SHIFT, | |
803 | bq24190_cvc_vreg_values, | |
804 | ARRAY_SIZE(bq24190_cvc_vreg_values), val->intval); | |
805 | } | |
806 | ||
807 | static int bq24190_charger_get_property(struct power_supply *psy, | |
808 | enum power_supply_property psp, union power_supply_propval *val) | |
809 | { | |
810 | struct bq24190_dev_info *bdi = | |
811 | container_of(psy, struct bq24190_dev_info, charger); | |
812 | int ret; | |
813 | ||
814 | dev_dbg(bdi->dev, "prop: %d\n", psp); | |
815 | ||
816 | pm_runtime_get_sync(bdi->dev); | |
817 | ||
818 | switch (psp) { | |
819 | case POWER_SUPPLY_PROP_CHARGE_TYPE: | |
820 | ret = bq24190_charger_get_charge_type(bdi, val); | |
821 | break; | |
822 | case POWER_SUPPLY_PROP_HEALTH: | |
823 | ret = bq24190_charger_get_health(bdi, val); | |
824 | break; | |
825 | case POWER_SUPPLY_PROP_ONLINE: | |
826 | ret = bq24190_charger_get_online(bdi, val); | |
827 | break; | |
828 | case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: | |
829 | ret = bq24190_charger_get_current(bdi, val); | |
830 | break; | |
831 | case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: | |
832 | ret = bq24190_charger_get_current_max(bdi, val); | |
833 | break; | |
834 | case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: | |
835 | ret = bq24190_charger_get_voltage(bdi, val); | |
836 | break; | |
837 | case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: | |
838 | ret = bq24190_charger_get_voltage_max(bdi, val); | |
839 | break; | |
840 | case POWER_SUPPLY_PROP_SCOPE: | |
841 | val->intval = POWER_SUPPLY_SCOPE_SYSTEM; | |
842 | ret = 0; | |
843 | break; | |
844 | case POWER_SUPPLY_PROP_MODEL_NAME: | |
845 | val->strval = bdi->model_name; | |
846 | ret = 0; | |
847 | break; | |
848 | case POWER_SUPPLY_PROP_MANUFACTURER: | |
849 | val->strval = BQ24190_MANUFACTURER; | |
850 | ret = 0; | |
851 | break; | |
852 | default: | |
853 | ret = -ENODATA; | |
854 | } | |
855 | ||
856 | pm_runtime_put_sync(bdi->dev); | |
857 | return ret; | |
858 | } | |
859 | ||
860 | static int bq24190_charger_set_property(struct power_supply *psy, | |
861 | enum power_supply_property psp, | |
862 | const union power_supply_propval *val) | |
863 | { | |
864 | struct bq24190_dev_info *bdi = | |
865 | container_of(psy, struct bq24190_dev_info, charger); | |
866 | int ret; | |
867 | ||
868 | dev_dbg(bdi->dev, "prop: %d\n", psp); | |
869 | ||
870 | pm_runtime_get_sync(bdi->dev); | |
871 | ||
872 | switch (psp) { | |
873 | case POWER_SUPPLY_PROP_CHARGE_TYPE: | |
874 | ret = bq24190_charger_set_charge_type(bdi, val); | |
875 | break; | |
876 | case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: | |
877 | ret = bq24190_charger_set_current(bdi, val); | |
878 | break; | |
879 | case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: | |
880 | ret = bq24190_charger_set_voltage(bdi, val); | |
881 | break; | |
882 | default: | |
883 | ret = -EINVAL; | |
884 | } | |
885 | ||
886 | pm_runtime_put_sync(bdi->dev); | |
887 | return ret; | |
888 | } | |
889 | ||
890 | static int bq24190_charger_property_is_writeable(struct power_supply *psy, | |
891 | enum power_supply_property psp) | |
892 | { | |
893 | int ret; | |
894 | ||
895 | switch (psp) { | |
896 | case POWER_SUPPLY_PROP_CHARGE_TYPE: | |
897 | case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: | |
898 | case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: | |
899 | ret = 1; | |
900 | break; | |
901 | default: | |
902 | ret = 0; | |
903 | } | |
904 | ||
905 | return ret; | |
906 | } | |
907 | ||
908 | static enum power_supply_property bq24190_charger_properties[] = { | |
909 | POWER_SUPPLY_PROP_TYPE, | |
910 | POWER_SUPPLY_PROP_HEALTH, | |
911 | POWER_SUPPLY_PROP_ONLINE, | |
912 | POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, | |
913 | POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, | |
914 | POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, | |
915 | POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, | |
916 | POWER_SUPPLY_PROP_SCOPE, | |
917 | POWER_SUPPLY_PROP_MODEL_NAME, | |
918 | POWER_SUPPLY_PROP_MANUFACTURER, | |
919 | }; | |
920 | ||
921 | static char *bq24190_charger_supplied_to[] = { | |
922 | "main-battery", | |
923 | }; | |
924 | ||
925 | static void bq24190_charger_init(struct power_supply *charger) | |
926 | { | |
927 | charger->name = "bq24190-charger"; | |
928 | charger->type = POWER_SUPPLY_TYPE_USB; | |
929 | charger->properties = bq24190_charger_properties; | |
930 | charger->num_properties = ARRAY_SIZE(bq24190_charger_properties); | |
931 | charger->supplied_to = bq24190_charger_supplied_to; | |
932 | charger->num_supplies = ARRAY_SIZE(bq24190_charger_supplied_to); | |
933 | charger->get_property = bq24190_charger_get_property; | |
934 | charger->set_property = bq24190_charger_set_property; | |
935 | charger->property_is_writeable = bq24190_charger_property_is_writeable; | |
936 | } | |
937 | ||
938 | /* Battery power supply property routines */ | |
939 | ||
940 | static int bq24190_battery_get_status(struct bq24190_dev_info *bdi, | |
941 | union power_supply_propval *val) | |
942 | { | |
943 | u8 ss_reg, chrg_fault; | |
944 | int status, ret; | |
945 | ||
946 | mutex_lock(&bdi->f_reg_lock); | |
947 | ||
948 | if (bdi->battery_status_valid) { | |
949 | chrg_fault = bdi->f_reg; | |
950 | bdi->battery_status_valid = false; | |
951 | mutex_unlock(&bdi->f_reg_lock); | |
952 | } else { | |
953 | mutex_unlock(&bdi->f_reg_lock); | |
954 | ||
955 | ret = bq24190_read(bdi, BQ24190_REG_F, &chrg_fault); | |
956 | if (ret < 0) | |
957 | return ret; | |
958 | } | |
959 | ||
960 | chrg_fault &= BQ24190_REG_F_CHRG_FAULT_MASK; | |
961 | chrg_fault >>= BQ24190_REG_F_CHRG_FAULT_SHIFT; | |
962 | ||
963 | ret = bq24190_read(bdi, BQ24190_REG_SS, &ss_reg); | |
964 | if (ret < 0) | |
965 | return ret; | |
966 | ||
967 | /* | |
968 | * The battery must be discharging when any of these are true: | |
969 | * - there is no good power source; | |
970 | * - there is a charge fault. | |
971 | * Could also be discharging when in "supplement mode" but | |
972 | * there is no way to tell when its in that mode. | |
973 | */ | |
974 | if (!(ss_reg & BQ24190_REG_SS_PG_STAT_MASK) || chrg_fault) { | |
975 | status = POWER_SUPPLY_STATUS_DISCHARGING; | |
976 | } else { | |
977 | ss_reg &= BQ24190_REG_SS_CHRG_STAT_MASK; | |
978 | ss_reg >>= BQ24190_REG_SS_CHRG_STAT_SHIFT; | |
979 | ||
980 | switch (ss_reg) { | |
981 | case 0x0: /* Not Charging */ | |
982 | status = POWER_SUPPLY_STATUS_NOT_CHARGING; | |
983 | break; | |
984 | case 0x1: /* Pre-charge */ | |
985 | case 0x2: /* Fast Charging */ | |
986 | status = POWER_SUPPLY_STATUS_CHARGING; | |
987 | break; | |
988 | case 0x3: /* Charge Termination Done */ | |
989 | status = POWER_SUPPLY_STATUS_FULL; | |
990 | break; | |
991 | default: | |
992 | ret = -EIO; | |
993 | } | |
994 | } | |
995 | ||
996 | if (!ret) | |
997 | val->intval = status; | |
998 | ||
999 | return ret; | |
1000 | } | |
1001 | ||
1002 | static int bq24190_battery_get_health(struct bq24190_dev_info *bdi, | |
1003 | union power_supply_propval *val) | |
1004 | { | |
1005 | u8 v; | |
1006 | int health, ret; | |
1007 | ||
1008 | mutex_lock(&bdi->f_reg_lock); | |
1009 | ||
1010 | if (bdi->battery_health_valid) { | |
1011 | v = bdi->f_reg; | |
1012 | bdi->battery_health_valid = false; | |
1013 | mutex_unlock(&bdi->f_reg_lock); | |
1014 | } else { | |
1015 | mutex_unlock(&bdi->f_reg_lock); | |
1016 | ||
1017 | ret = bq24190_read(bdi, BQ24190_REG_F, &v); | |
1018 | if (ret < 0) | |
1019 | return ret; | |
1020 | } | |
1021 | ||
1022 | if (v & BQ24190_REG_F_BAT_FAULT_MASK) { | |
1023 | health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; | |
1024 | } else { | |
1025 | v &= BQ24190_REG_F_NTC_FAULT_MASK; | |
1026 | v >>= BQ24190_REG_F_NTC_FAULT_SHIFT; | |
1027 | ||
1028 | switch (v) { | |
1029 | case 0x0: /* Normal */ | |
1030 | health = POWER_SUPPLY_HEALTH_GOOD; | |
1031 | break; | |
1032 | case 0x1: /* TS1 Cold */ | |
1033 | case 0x3: /* TS2 Cold */ | |
1034 | case 0x5: /* Both Cold */ | |
1035 | health = POWER_SUPPLY_HEALTH_COLD; | |
1036 | break; | |
1037 | case 0x2: /* TS1 Hot */ | |
1038 | case 0x4: /* TS2 Hot */ | |
1039 | case 0x6: /* Both Hot */ | |
1040 | health = POWER_SUPPLY_HEALTH_OVERHEAT; | |
1041 | break; | |
1042 | default: | |
1043 | health = POWER_SUPPLY_HEALTH_UNKNOWN; | |
1044 | } | |
1045 | } | |
1046 | ||
1047 | val->intval = health; | |
1048 | return 0; | |
1049 | } | |
1050 | ||
1051 | static int bq24190_battery_get_online(struct bq24190_dev_info *bdi, | |
1052 | union power_supply_propval *val) | |
1053 | { | |
1054 | u8 batfet_disable; | |
1055 | int ret; | |
1056 | ||
1057 | ret = bq24190_read_mask(bdi, BQ24190_REG_MOC, | |
1058 | BQ24190_REG_MOC_BATFET_DISABLE_MASK, | |
1059 | BQ24190_REG_MOC_BATFET_DISABLE_SHIFT, &batfet_disable); | |
1060 | if (ret < 0) | |
1061 | return ret; | |
1062 | ||
1063 | val->intval = !batfet_disable; | |
1064 | return 0; | |
1065 | } | |
1066 | ||
1067 | static int bq24190_battery_set_online(struct bq24190_dev_info *bdi, | |
1068 | const union power_supply_propval *val) | |
1069 | { | |
1070 | return bq24190_write_mask(bdi, BQ24190_REG_MOC, | |
1071 | BQ24190_REG_MOC_BATFET_DISABLE_MASK, | |
1072 | BQ24190_REG_MOC_BATFET_DISABLE_SHIFT, !val->intval); | |
1073 | } | |
1074 | ||
1075 | static int bq24190_battery_get_temp_alert_max(struct bq24190_dev_info *bdi, | |
1076 | union power_supply_propval *val) | |
1077 | { | |
1078 | int temp, ret; | |
1079 | ||
1080 | ret = bq24190_get_field_val(bdi, BQ24190_REG_ICTRC, | |
1081 | BQ24190_REG_ICTRC_TREG_MASK, | |
1082 | BQ24190_REG_ICTRC_TREG_SHIFT, | |
1083 | bq24190_ictrc_treg_values, | |
1084 | ARRAY_SIZE(bq24190_ictrc_treg_values), &temp); | |
1085 | if (ret < 0) | |
1086 | return ret; | |
1087 | ||
1088 | val->intval = temp; | |
1089 | return 0; | |
1090 | } | |
1091 | ||
1092 | static int bq24190_battery_set_temp_alert_max(struct bq24190_dev_info *bdi, | |
1093 | const union power_supply_propval *val) | |
1094 | { | |
1095 | return bq24190_set_field_val(bdi, BQ24190_REG_ICTRC, | |
1096 | BQ24190_REG_ICTRC_TREG_MASK, | |
1097 | BQ24190_REG_ICTRC_TREG_SHIFT, | |
1098 | bq24190_ictrc_treg_values, | |
1099 | ARRAY_SIZE(bq24190_ictrc_treg_values), val->intval); | |
1100 | } | |
1101 | ||
1102 | static int bq24190_battery_get_property(struct power_supply *psy, | |
1103 | enum power_supply_property psp, union power_supply_propval *val) | |
1104 | { | |
1105 | struct bq24190_dev_info *bdi = | |
1106 | container_of(psy, struct bq24190_dev_info, battery); | |
1107 | int ret; | |
1108 | ||
1109 | dev_dbg(bdi->dev, "prop: %d\n", psp); | |
1110 | ||
1111 | pm_runtime_get_sync(bdi->dev); | |
1112 | ||
1113 | switch (psp) { | |
1114 | case POWER_SUPPLY_PROP_STATUS: | |
1115 | ret = bq24190_battery_get_status(bdi, val); | |
1116 | break; | |
1117 | case POWER_SUPPLY_PROP_HEALTH: | |
1118 | ret = bq24190_battery_get_health(bdi, val); | |
1119 | break; | |
1120 | case POWER_SUPPLY_PROP_ONLINE: | |
1121 | ret = bq24190_battery_get_online(bdi, val); | |
1122 | break; | |
1123 | case POWER_SUPPLY_PROP_TECHNOLOGY: | |
1124 | /* Could be Li-on or Li-polymer but no way to tell which */ | |
1125 | val->intval = POWER_SUPPLY_TECHNOLOGY_UNKNOWN; | |
1126 | ret = 0; | |
1127 | break; | |
1128 | case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: | |
1129 | ret = bq24190_battery_get_temp_alert_max(bdi, val); | |
1130 | break; | |
1131 | case POWER_SUPPLY_PROP_SCOPE: | |
1132 | val->intval = POWER_SUPPLY_SCOPE_SYSTEM; | |
1133 | ret = 0; | |
1134 | break; | |
1135 | default: | |
1136 | ret = -ENODATA; | |
1137 | } | |
1138 | ||
1139 | pm_runtime_put_sync(bdi->dev); | |
1140 | return ret; | |
1141 | } | |
1142 | ||
1143 | static int bq24190_battery_set_property(struct power_supply *psy, | |
1144 | enum power_supply_property psp, | |
1145 | const union power_supply_propval *val) | |
1146 | { | |
1147 | struct bq24190_dev_info *bdi = | |
1148 | container_of(psy, struct bq24190_dev_info, battery); | |
1149 | int ret; | |
1150 | ||
1151 | dev_dbg(bdi->dev, "prop: %d\n", psp); | |
1152 | ||
1153 | pm_runtime_put_sync(bdi->dev); | |
1154 | ||
1155 | switch (psp) { | |
1156 | case POWER_SUPPLY_PROP_ONLINE: | |
1157 | ret = bq24190_battery_set_online(bdi, val); | |
1158 | break; | |
1159 | case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: | |
1160 | ret = bq24190_battery_set_temp_alert_max(bdi, val); | |
1161 | break; | |
1162 | default: | |
1163 | ret = -EINVAL; | |
1164 | } | |
1165 | ||
1166 | pm_runtime_put_sync(bdi->dev); | |
1167 | return ret; | |
1168 | } | |
1169 | ||
1170 | static int bq24190_battery_property_is_writeable(struct power_supply *psy, | |
1171 | enum power_supply_property psp) | |
1172 | { | |
1173 | int ret; | |
1174 | ||
1175 | switch (psp) { | |
1176 | case POWER_SUPPLY_PROP_ONLINE: | |
1177 | case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: | |
1178 | ret = 1; | |
1179 | break; | |
1180 | default: | |
1181 | ret = 0; | |
1182 | } | |
1183 | ||
1184 | return ret; | |
1185 | } | |
1186 | ||
1187 | static enum power_supply_property bq24190_battery_properties[] = { | |
1188 | POWER_SUPPLY_PROP_STATUS, | |
1189 | POWER_SUPPLY_PROP_HEALTH, | |
1190 | POWER_SUPPLY_PROP_ONLINE, | |
1191 | POWER_SUPPLY_PROP_TECHNOLOGY, | |
1192 | POWER_SUPPLY_PROP_TEMP_ALERT_MAX, | |
1193 | POWER_SUPPLY_PROP_SCOPE, | |
1194 | }; | |
1195 | ||
1196 | static void bq24190_battery_init(struct power_supply *battery) | |
1197 | { | |
1198 | battery->name = "bq24190-battery"; | |
1199 | battery->type = POWER_SUPPLY_TYPE_BATTERY; | |
1200 | battery->properties = bq24190_battery_properties; | |
1201 | battery->num_properties = ARRAY_SIZE(bq24190_battery_properties); | |
1202 | battery->get_property = bq24190_battery_get_property; | |
1203 | battery->set_property = bq24190_battery_set_property; | |
1204 | battery->property_is_writeable = bq24190_battery_property_is_writeable; | |
1205 | } | |
1206 | ||
1207 | static irqreturn_t bq24190_irq_handler_thread(int irq, void *data) | |
1208 | { | |
1209 | struct bq24190_dev_info *bdi = data; | |
1210 | bool alert_userspace = false; | |
1211 | u8 ss_reg, f_reg; | |
1212 | int ret; | |
1213 | ||
1214 | pm_runtime_get_sync(bdi->dev); | |
1215 | ||
1216 | ret = bq24190_read(bdi, BQ24190_REG_SS, &ss_reg); | |
1217 | if (ret < 0) { | |
1218 | dev_err(bdi->dev, "Can't read SS reg: %d\n", ret); | |
1219 | goto out; | |
1220 | } | |
1221 | ||
1222 | if (ss_reg != bdi->ss_reg) { | |
1223 | /* | |
1224 | * The device is in host mode so when PG_STAT goes from 1->0 | |
1225 | * (i.e., power removed) HIZ needs to be disabled. | |
1226 | */ | |
1227 | if ((bdi->ss_reg & BQ24190_REG_SS_PG_STAT_MASK) && | |
1228 | !(ss_reg & BQ24190_REG_SS_PG_STAT_MASK)) { | |
1229 | ret = bq24190_write_mask(bdi, BQ24190_REG_ISC, | |
1230 | BQ24190_REG_ISC_EN_HIZ_MASK, | |
1231 | BQ24190_REG_ISC_EN_HIZ_SHIFT, | |
1232 | 0); | |
1233 | if (ret < 0) | |
1234 | dev_err(bdi->dev, "Can't access ISC reg: %d\n", | |
1235 | ret); | |
1236 | } | |
1237 | ||
1238 | bdi->ss_reg = ss_reg; | |
1239 | alert_userspace = true; | |
1240 | } | |
1241 | ||
1242 | mutex_lock(&bdi->f_reg_lock); | |
1243 | ||
1244 | ret = bq24190_read(bdi, BQ24190_REG_F, &f_reg); | |
1245 | if (ret < 0) { | |
1246 | mutex_unlock(&bdi->f_reg_lock); | |
1247 | dev_err(bdi->dev, "Can't read F reg: %d\n", ret); | |
1248 | goto out; | |
1249 | } | |
1250 | ||
1251 | if (f_reg != bdi->f_reg) { | |
1252 | bdi->f_reg = f_reg; | |
1253 | bdi->charger_health_valid = true; | |
1254 | bdi->battery_health_valid = true; | |
1255 | bdi->battery_status_valid = true; | |
1256 | ||
1257 | alert_userspace = true; | |
1258 | } | |
1259 | ||
1260 | mutex_unlock(&bdi->f_reg_lock); | |
1261 | ||
1262 | /* | |
1263 | * Sometimes bq24190 gives a steady trickle of interrupts even | |
1264 | * though the watchdog timer is turned off and neither the STATUS | |
1265 | * nor FAULT registers have changed. Weed out these sprurious | |
1266 | * interrupts so userspace isn't alerted for no reason. | |
1267 | * In addition, the chip always generates an interrupt after | |
1268 | * register reset so we should ignore that one (the very first | |
1269 | * interrupt received). | |
1270 | */ | |
1271 | if (alert_userspace && !bdi->first_time) { | |
1272 | power_supply_changed(&bdi->charger); | |
1273 | power_supply_changed(&bdi->battery); | |
1274 | bdi->first_time = false; | |
1275 | } | |
1276 | ||
1277 | out: | |
1278 | pm_runtime_put_sync(bdi->dev); | |
1279 | ||
1280 | dev_dbg(bdi->dev, "ss_reg: 0x%02x, f_reg: 0x%02x\n", ss_reg, f_reg); | |
1281 | ||
1282 | return IRQ_HANDLED; | |
1283 | } | |
1284 | ||
1285 | static int bq24190_hw_init(struct bq24190_dev_info *bdi) | |
1286 | { | |
1287 | u8 v; | |
1288 | int ret; | |
1289 | ||
1290 | pm_runtime_get_sync(bdi->dev); | |
1291 | ||
1292 | /* First check that the device really is what its supposed to be */ | |
1293 | ret = bq24190_read_mask(bdi, BQ24190_REG_VPRS, | |
1294 | BQ24190_REG_VPRS_PN_MASK, | |
1295 | BQ24190_REG_VPRS_PN_SHIFT, | |
1296 | &v); | |
1297 | if (ret < 0) | |
1298 | goto out; | |
1299 | ||
1300 | if (v != bdi->model) { | |
1301 | ret = -ENODEV; | |
1302 | goto out; | |
1303 | } | |
1304 | ||
1305 | ret = bq24190_register_reset(bdi); | |
1306 | if (ret < 0) | |
1307 | goto out; | |
1308 | ||
1309 | ret = bq24190_set_mode_host(bdi); | |
1310 | out: | |
1311 | pm_runtime_put_sync(bdi->dev); | |
1312 | return ret; | |
1313 | } | |
1314 | ||
1315 | #ifdef CONFIG_OF | |
1316 | static int bq24190_setup_dt(struct bq24190_dev_info *bdi) | |
1317 | { | |
1318 | bdi->irq = irq_of_parse_and_map(bdi->dev->of_node, 0); | |
1319 | if (bdi->irq <= 0) | |
1320 | return -1; | |
1321 | ||
1322 | return 0; | |
1323 | } | |
1324 | #else | |
1325 | static int bq24190_setup_dt(struct bq24190_dev_info *bdi) | |
1326 | { | |
1327 | return -1; | |
1328 | } | |
1329 | #endif | |
1330 | ||
1331 | static int bq24190_setup_pdata(struct bq24190_dev_info *bdi, | |
1332 | struct bq24190_platform_data *pdata) | |
1333 | { | |
1334 | int ret; | |
1335 | ||
1336 | if (!gpio_is_valid(pdata->gpio_int)) | |
1337 | return -1; | |
1338 | ||
1339 | ret = gpio_request(pdata->gpio_int, dev_name(bdi->dev)); | |
1340 | if (ret < 0) | |
1341 | return -1; | |
1342 | ||
1343 | ret = gpio_direction_input(pdata->gpio_int); | |
1344 | if (ret < 0) | |
1345 | goto out; | |
1346 | ||
1347 | bdi->irq = gpio_to_irq(pdata->gpio_int); | |
1348 | if (!bdi->irq) | |
1349 | goto out; | |
1350 | ||
1351 | bdi->gpio_int = pdata->gpio_int; | |
1352 | return 0; | |
1353 | ||
1354 | out: | |
1355 | gpio_free(pdata->gpio_int); | |
1356 | return -1; | |
1357 | } | |
1358 | ||
1359 | static int bq24190_probe(struct i2c_client *client, | |
1360 | const struct i2c_device_id *id) | |
1361 | { | |
1362 | struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); | |
1363 | struct device *dev = &client->dev; | |
1364 | struct bq24190_platform_data *pdata = client->dev.platform_data; | |
1365 | struct bq24190_dev_info *bdi; | |
1366 | int ret; | |
1367 | ||
1368 | if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { | |
1369 | dev_err(dev, "No support for SMBUS_BYTE_DATA\n"); | |
1370 | return -ENODEV; | |
1371 | } | |
1372 | ||
1373 | bdi = devm_kzalloc(dev, sizeof(*bdi), GFP_KERNEL); | |
1374 | if (!bdi) { | |
1375 | dev_err(dev, "Can't alloc bdi struct\n"); | |
1376 | return -ENOMEM; | |
1377 | } | |
1378 | ||
1379 | bdi->client = client; | |
1380 | bdi->dev = dev; | |
1381 | bdi->model = id->driver_data; | |
1382 | strncpy(bdi->model_name, id->name, I2C_NAME_SIZE); | |
1383 | mutex_init(&bdi->f_reg_lock); | |
1384 | bdi->first_time = true; | |
1385 | bdi->charger_health_valid = false; | |
1386 | bdi->battery_health_valid = false; | |
1387 | bdi->battery_status_valid = false; | |
1388 | ||
1389 | i2c_set_clientdata(client, bdi); | |
1390 | ||
1391 | if (dev->of_node) | |
1392 | ret = bq24190_setup_dt(bdi); | |
1393 | else | |
1394 | ret = bq24190_setup_pdata(bdi, pdata); | |
1395 | ||
1396 | if (ret) { | |
1397 | dev_err(dev, "Can't get irq info\n"); | |
1398 | return -EINVAL; | |
1399 | } | |
1400 | ||
1401 | ret = devm_request_threaded_irq(dev, bdi->irq, NULL, | |
1402 | bq24190_irq_handler_thread, | |
1403 | IRQF_TRIGGER_RISING | IRQF_ONESHOT, | |
1404 | "bq24190-charger", bdi); | |
1405 | if (ret < 0) { | |
1406 | dev_err(dev, "Can't set up irq handler\n"); | |
1407 | goto out1; | |
1408 | } | |
1409 | ||
1410 | pm_runtime_enable(dev); | |
1411 | pm_runtime_resume(dev); | |
1412 | ||
1413 | ret = bq24190_hw_init(bdi); | |
1414 | if (ret < 0) { | |
1415 | dev_err(dev, "Hardware init failed\n"); | |
1416 | goto out2; | |
1417 | } | |
1418 | ||
1419 | bq24190_charger_init(&bdi->charger); | |
1420 | ||
1421 | ret = power_supply_register(dev, &bdi->charger); | |
1422 | if (ret) { | |
1423 | dev_err(dev, "Can't register charger\n"); | |
1424 | goto out2; | |
1425 | } | |
1426 | ||
1427 | bq24190_battery_init(&bdi->battery); | |
1428 | ||
1429 | ret = power_supply_register(dev, &bdi->battery); | |
1430 | if (ret) { | |
1431 | dev_err(dev, "Can't register battery\n"); | |
1432 | goto out3; | |
1433 | } | |
1434 | ||
1435 | ret = bq24190_sysfs_create_group(bdi); | |
1436 | if (ret) { | |
1437 | dev_err(dev, "Can't create sysfs entries\n"); | |
1438 | goto out4; | |
1439 | } | |
1440 | ||
1441 | return 0; | |
1442 | ||
1443 | out4: | |
1444 | power_supply_unregister(&bdi->battery); | |
1445 | out3: | |
1446 | power_supply_unregister(&bdi->charger); | |
1447 | out2: | |
1448 | pm_runtime_disable(dev); | |
1449 | out1: | |
1450 | if (bdi->gpio_int) | |
1451 | gpio_free(bdi->gpio_int); | |
1452 | ||
1453 | return ret; | |
1454 | } | |
1455 | ||
1456 | static int bq24190_remove(struct i2c_client *client) | |
1457 | { | |
1458 | struct bq24190_dev_info *bdi = i2c_get_clientdata(client); | |
1459 | ||
1460 | pm_runtime_get_sync(bdi->dev); | |
1461 | bq24190_register_reset(bdi); | |
1462 | pm_runtime_put_sync(bdi->dev); | |
1463 | ||
1464 | bq24190_sysfs_remove_group(bdi); | |
1465 | power_supply_unregister(&bdi->battery); | |
1466 | power_supply_unregister(&bdi->charger); | |
1467 | pm_runtime_disable(bdi->dev); | |
1468 | ||
1469 | if (bdi->gpio_int) | |
1470 | gpio_free(bdi->gpio_int); | |
1471 | ||
1472 | return 0; | |
1473 | } | |
1474 | ||
1475 | #ifdef CONFIG_PM_SLEEP | |
1476 | static int bq24190_pm_suspend(struct device *dev) | |
1477 | { | |
1478 | struct i2c_client *client = to_i2c_client(dev); | |
1479 | struct bq24190_dev_info *bdi = i2c_get_clientdata(client); | |
1480 | ||
1481 | pm_runtime_get_sync(bdi->dev); | |
1482 | bq24190_register_reset(bdi); | |
1483 | pm_runtime_put_sync(bdi->dev); | |
1484 | ||
1485 | return 0; | |
1486 | } | |
1487 | ||
1488 | static int bq24190_pm_resume(struct device *dev) | |
1489 | { | |
1490 | struct i2c_client *client = to_i2c_client(dev); | |
1491 | struct bq24190_dev_info *bdi = i2c_get_clientdata(client); | |
1492 | ||
1493 | bdi->charger_health_valid = false; | |
1494 | bdi->battery_health_valid = false; | |
1495 | bdi->battery_status_valid = false; | |
1496 | ||
1497 | pm_runtime_get_sync(bdi->dev); | |
1498 | bq24190_register_reset(bdi); | |
1499 | pm_runtime_put_sync(bdi->dev); | |
1500 | ||
1501 | /* Things may have changed while suspended so alert upper layer */ | |
1502 | power_supply_changed(&bdi->charger); | |
1503 | power_supply_changed(&bdi->battery); | |
1504 | ||
1505 | return 0; | |
1506 | } | |
1507 | #endif | |
1508 | ||
1509 | static SIMPLE_DEV_PM_OPS(bq24190_pm_ops, bq24190_pm_suspend, bq24190_pm_resume); | |
1510 | ||
1511 | /* | |
1512 | * Only support the bq24190 right now. The bq24192, bq24192i, and bq24193 | |
1513 | * are similar but not identical so the driver needs to be extended to | |
1514 | * support them. | |
1515 | */ | |
1516 | static const struct i2c_device_id bq24190_i2c_ids[] = { | |
1517 | { "bq24190", BQ24190_REG_VPRS_PN_24190 }, | |
1518 | { }, | |
1519 | }; | |
1520 | ||
1521 | #ifdef CONFIG_OF | |
1522 | static const struct of_device_id bq24190_of_match[] = { | |
1523 | { .compatible = "ti,bq24190", }, | |
1524 | { }, | |
1525 | }; | |
1526 | MODULE_DEVICE_TABLE(of, bq24190_of_match); | |
1527 | #else | |
1528 | static const struct of_device_id bq24190_of_match[] = { | |
1529 | { }, | |
1530 | }; | |
1531 | #endif | |
1532 | ||
1533 | static struct i2c_driver bq24190_driver = { | |
1534 | .probe = bq24190_probe, | |
1535 | .remove = bq24190_remove, | |
1536 | .id_table = bq24190_i2c_ids, | |
1537 | .driver = { | |
1538 | .name = "bq24190-charger", | |
1539 | .owner = THIS_MODULE, | |
1540 | .pm = &bq24190_pm_ops, | |
1541 | .of_match_table = of_match_ptr(bq24190_of_match), | |
1542 | }, | |
1543 | }; | |
1544 | module_i2c_driver(bq24190_driver); | |
1545 | ||
1546 | MODULE_LICENSE("GPL"); | |
1547 | MODULE_AUTHOR("Mark A. Greer <mgreer@animalcreek.com>"); | |
1548 | MODULE_ALIAS("i2c:bq24190-charger"); | |
1549 | MODULE_DESCRIPTION("TI BQ24190 Charger Driver"); |