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