]>
Commit | Line | Data |
---|---|---|
ed1a230f BR |
1 | /* |
2 | * Summit Microelectronics SMB347 Battery Charger Driver | |
3 | * | |
4 | * Copyright (C) 2011, Intel Corporation | |
5 | * | |
6 | * Authors: Bruce E. Robertson <[email protected]> | |
7 | * Mika Westerberg <[email protected]> | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or modify | |
10 | * it under the terms of the GNU General Public License version 2 as | |
11 | * published by the Free Software Foundation. | |
12 | */ | |
13 | ||
ed1a230f BR |
14 | #include <linux/gpio.h> |
15 | #include <linux/kernel.h> | |
16 | #include <linux/module.h> | |
17 | #include <linux/init.h> | |
18 | #include <linux/interrupt.h> | |
19 | #include <linux/i2c.h> | |
20 | #include <linux/mutex.h> | |
21 | #include <linux/power_supply.h> | |
22 | #include <linux/power/smb347-charger.h> | |
34298d40 | 23 | #include <linux/regmap.h> |
ed1a230f BR |
24 | |
25 | /* | |
26 | * Configuration registers. These are mirrored to volatile RAM and can be | |
27 | * written once %CMD_A_ALLOW_WRITE is set in %CMD_A register. They will be | |
28 | * reloaded from non-volatile registers after POR. | |
29 | */ | |
30 | #define CFG_CHARGE_CURRENT 0x00 | |
31 | #define CFG_CHARGE_CURRENT_FCC_MASK 0xe0 | |
32 | #define CFG_CHARGE_CURRENT_FCC_SHIFT 5 | |
33 | #define CFG_CHARGE_CURRENT_PCC_MASK 0x18 | |
34 | #define CFG_CHARGE_CURRENT_PCC_SHIFT 3 | |
35 | #define CFG_CHARGE_CURRENT_TC_MASK 0x07 | |
36 | #define CFG_CURRENT_LIMIT 0x01 | |
37 | #define CFG_CURRENT_LIMIT_DC_MASK 0xf0 | |
38 | #define CFG_CURRENT_LIMIT_DC_SHIFT 4 | |
39 | #define CFG_CURRENT_LIMIT_USB_MASK 0x0f | |
40 | #define CFG_FLOAT_VOLTAGE 0x03 | |
34298d40 | 41 | #define CFG_FLOAT_VOLTAGE_FLOAT_MASK 0x3f |
ed1a230f BR |
42 | #define CFG_FLOAT_VOLTAGE_THRESHOLD_MASK 0xc0 |
43 | #define CFG_FLOAT_VOLTAGE_THRESHOLD_SHIFT 6 | |
44 | #define CFG_STAT 0x05 | |
45 | #define CFG_STAT_DISABLED BIT(5) | |
46 | #define CFG_STAT_ACTIVE_HIGH BIT(7) | |
47 | #define CFG_PIN 0x06 | |
48 | #define CFG_PIN_EN_CTRL_MASK 0x60 | |
49 | #define CFG_PIN_EN_CTRL_ACTIVE_HIGH 0x40 | |
50 | #define CFG_PIN_EN_CTRL_ACTIVE_LOW 0x60 | |
51 | #define CFG_PIN_EN_APSD_IRQ BIT(1) | |
52 | #define CFG_PIN_EN_CHARGER_ERROR BIT(2) | |
53 | #define CFG_THERM 0x07 | |
54 | #define CFG_THERM_SOFT_HOT_COMPENSATION_MASK 0x03 | |
55 | #define CFG_THERM_SOFT_HOT_COMPENSATION_SHIFT 0 | |
56 | #define CFG_THERM_SOFT_COLD_COMPENSATION_MASK 0x0c | |
57 | #define CFG_THERM_SOFT_COLD_COMPENSATION_SHIFT 2 | |
58 | #define CFG_THERM_MONITOR_DISABLED BIT(4) | |
59 | #define CFG_SYSOK 0x08 | |
60 | #define CFG_SYSOK_SUSPEND_HARD_LIMIT_DISABLED BIT(2) | |
61 | #define CFG_OTHER 0x09 | |
62 | #define CFG_OTHER_RID_MASK 0xc0 | |
63 | #define CFG_OTHER_RID_ENABLED_AUTO_OTG 0xc0 | |
64 | #define CFG_OTG 0x0a | |
65 | #define CFG_OTG_TEMP_THRESHOLD_MASK 0x30 | |
66 | #define CFG_OTG_TEMP_THRESHOLD_SHIFT 4 | |
67 | #define CFG_OTG_CC_COMPENSATION_MASK 0xc0 | |
68 | #define CFG_OTG_CC_COMPENSATION_SHIFT 6 | |
69 | #define CFG_TEMP_LIMIT 0x0b | |
70 | #define CFG_TEMP_LIMIT_SOFT_HOT_MASK 0x03 | |
71 | #define CFG_TEMP_LIMIT_SOFT_HOT_SHIFT 0 | |
72 | #define CFG_TEMP_LIMIT_SOFT_COLD_MASK 0x0c | |
73 | #define CFG_TEMP_LIMIT_SOFT_COLD_SHIFT 2 | |
74 | #define CFG_TEMP_LIMIT_HARD_HOT_MASK 0x30 | |
75 | #define CFG_TEMP_LIMIT_HARD_HOT_SHIFT 4 | |
76 | #define CFG_TEMP_LIMIT_HARD_COLD_MASK 0xc0 | |
77 | #define CFG_TEMP_LIMIT_HARD_COLD_SHIFT 6 | |
78 | #define CFG_FAULT_IRQ 0x0c | |
79 | #define CFG_FAULT_IRQ_DCIN_UV BIT(2) | |
80 | #define CFG_STATUS_IRQ 0x0d | |
81 | #define CFG_STATUS_IRQ_TERMINATION_OR_TAPER BIT(4) | |
82 | #define CFG_ADDRESS 0x0e | |
83 | ||
84 | /* Command registers */ | |
85 | #define CMD_A 0x30 | |
86 | #define CMD_A_CHG_ENABLED BIT(1) | |
87 | #define CMD_A_SUSPEND_ENABLED BIT(2) | |
88 | #define CMD_A_ALLOW_WRITE BIT(7) | |
89 | #define CMD_B 0x31 | |
90 | #define CMD_C 0x33 | |
91 | ||
92 | /* Interrupt Status registers */ | |
93 | #define IRQSTAT_A 0x35 | |
94 | #define IRQSTAT_C 0x37 | |
95 | #define IRQSTAT_C_TERMINATION_STAT BIT(0) | |
96 | #define IRQSTAT_C_TERMINATION_IRQ BIT(1) | |
97 | #define IRQSTAT_C_TAPER_IRQ BIT(3) | |
98 | #define IRQSTAT_E 0x39 | |
99 | #define IRQSTAT_E_USBIN_UV_STAT BIT(0) | |
100 | #define IRQSTAT_E_USBIN_UV_IRQ BIT(1) | |
101 | #define IRQSTAT_E_DCIN_UV_STAT BIT(4) | |
102 | #define IRQSTAT_E_DCIN_UV_IRQ BIT(5) | |
103 | #define IRQSTAT_F 0x3a | |
104 | ||
105 | /* Status registers */ | |
106 | #define STAT_A 0x3b | |
107 | #define STAT_A_FLOAT_VOLTAGE_MASK 0x3f | |
108 | #define STAT_B 0x3c | |
109 | #define STAT_C 0x3d | |
110 | #define STAT_C_CHG_ENABLED BIT(0) | |
111 | #define STAT_C_CHG_MASK 0x06 | |
112 | #define STAT_C_CHG_SHIFT 1 | |
113 | #define STAT_C_CHARGER_ERROR BIT(6) | |
114 | #define STAT_E 0x3f | |
115 | ||
34298d40 MW |
116 | #define SMB347_MAX_REGISTER 0x3f |
117 | ||
ed1a230f BR |
118 | /** |
119 | * struct smb347_charger - smb347 charger instance | |
120 | * @lock: protects concurrent access to online variables | |
34298d40 MW |
121 | * @dev: pointer to device |
122 | * @regmap: pointer to driver regmap | |
ed1a230f BR |
123 | * @mains: power_supply instance for AC/DC power |
124 | * @usb: power_supply instance for USB power | |
125 | * @battery: power_supply instance for battery | |
126 | * @mains_online: is AC/DC input connected | |
127 | * @usb_online: is USB input connected | |
128 | * @charging_enabled: is charging enabled | |
ed1a230f BR |
129 | * @pdata: pointer to platform data |
130 | */ | |
131 | struct smb347_charger { | |
132 | struct mutex lock; | |
34298d40 MW |
133 | struct device *dev; |
134 | struct regmap *regmap; | |
ed1a230f BR |
135 | struct power_supply mains; |
136 | struct power_supply usb; | |
137 | struct power_supply battery; | |
138 | bool mains_online; | |
139 | bool usb_online; | |
140 | bool charging_enabled; | |
ed1a230f BR |
141 | const struct smb347_charger_platform_data *pdata; |
142 | }; | |
143 | ||
144 | /* Fast charge current in uA */ | |
145 | static const unsigned int fcc_tbl[] = { | |
146 | 700000, | |
147 | 900000, | |
148 | 1200000, | |
149 | 1500000, | |
150 | 1800000, | |
151 | 2000000, | |
152 | 2200000, | |
153 | 2500000, | |
154 | }; | |
155 | ||
156 | /* Pre-charge current in uA */ | |
157 | static const unsigned int pcc_tbl[] = { | |
158 | 100000, | |
159 | 150000, | |
160 | 200000, | |
161 | 250000, | |
162 | }; | |
163 | ||
164 | /* Termination current in uA */ | |
165 | static const unsigned int tc_tbl[] = { | |
166 | 37500, | |
167 | 50000, | |
168 | 100000, | |
169 | 150000, | |
170 | 200000, | |
171 | 250000, | |
172 | 500000, | |
173 | 600000, | |
174 | }; | |
175 | ||
176 | /* Input current limit in uA */ | |
177 | static const unsigned int icl_tbl[] = { | |
178 | 300000, | |
179 | 500000, | |
180 | 700000, | |
181 | 900000, | |
182 | 1200000, | |
183 | 1500000, | |
184 | 1800000, | |
185 | 2000000, | |
186 | 2200000, | |
187 | 2500000, | |
188 | }; | |
189 | ||
190 | /* Charge current compensation in uA */ | |
191 | static const unsigned int ccc_tbl[] = { | |
192 | 250000, | |
193 | 700000, | |
194 | 900000, | |
195 | 1200000, | |
196 | }; | |
197 | ||
ed1a230f BR |
198 | /* Convert current to register value using lookup table */ |
199 | static int current_to_hw(const unsigned int *tbl, size_t size, unsigned int val) | |
200 | { | |
201 | size_t i; | |
202 | ||
203 | for (i = 0; i < size; i++) | |
204 | if (val < tbl[i]) | |
205 | break; | |
206 | return i > 0 ? i - 1 : -EINVAL; | |
207 | } | |
208 | ||
ed1a230f | 209 | /** |
055d7f0f | 210 | * smb347_update_ps_status - refreshes the power source status |
ed1a230f BR |
211 | * @smb: pointer to smb347 charger instance |
212 | * | |
055d7f0f MW |
213 | * Function checks whether any power source is connected to the charger and |
214 | * updates internal state accordingly. If there is a change to previous state | |
215 | * function returns %1, otherwise %0 and negative errno in case of errror. | |
ed1a230f | 216 | */ |
055d7f0f | 217 | static int smb347_update_ps_status(struct smb347_charger *smb) |
ed1a230f BR |
218 | { |
219 | bool usb = false; | |
220 | bool dc = false; | |
34298d40 | 221 | unsigned int val; |
ed1a230f BR |
222 | int ret; |
223 | ||
34298d40 | 224 | ret = regmap_read(smb->regmap, IRQSTAT_E, &val); |
ed1a230f BR |
225 | if (ret < 0) |
226 | return ret; | |
227 | ||
228 | /* | |
229 | * Dc and usb are set depending on whether they are enabled in | |
230 | * platform data _and_ whether corresponding undervoltage is set. | |
231 | */ | |
232 | if (smb->pdata->use_mains) | |
34298d40 | 233 | dc = !(val & IRQSTAT_E_DCIN_UV_STAT); |
ed1a230f | 234 | if (smb->pdata->use_usb) |
34298d40 | 235 | usb = !(val & IRQSTAT_E_USBIN_UV_STAT); |
ed1a230f BR |
236 | |
237 | mutex_lock(&smb->lock); | |
238 | ret = smb->mains_online != dc || smb->usb_online != usb; | |
239 | smb->mains_online = dc; | |
240 | smb->usb_online = usb; | |
241 | mutex_unlock(&smb->lock); | |
242 | ||
243 | return ret; | |
244 | } | |
245 | ||
246 | /* | |
055d7f0f | 247 | * smb347_is_ps_online - returns whether input power source is connected |
ed1a230f BR |
248 | * @smb: pointer to smb347 charger instance |
249 | * | |
250 | * Returns %true if input power source is connected. Note that this is | |
251 | * dependent on what platform has configured for usable power sources. For | |
055d7f0f MW |
252 | * example if USB is disabled, this will return %false even if the USB cable |
253 | * is connected. | |
ed1a230f | 254 | */ |
055d7f0f | 255 | static bool smb347_is_ps_online(struct smb347_charger *smb) |
ed1a230f BR |
256 | { |
257 | bool ret; | |
258 | ||
259 | mutex_lock(&smb->lock); | |
260 | ret = smb->usb_online || smb->mains_online; | |
261 | mutex_unlock(&smb->lock); | |
262 | ||
263 | return ret; | |
264 | } | |
265 | ||
266 | /** | |
267 | * smb347_charging_status - returns status of charging | |
268 | * @smb: pointer to smb347 charger instance | |
269 | * | |
270 | * Function returns charging status. %0 means no charging is in progress, | |
271 | * %1 means pre-charging, %2 fast-charging and %3 taper-charging. | |
272 | */ | |
273 | static int smb347_charging_status(struct smb347_charger *smb) | |
274 | { | |
34298d40 | 275 | unsigned int val; |
ed1a230f BR |
276 | int ret; |
277 | ||
055d7f0f | 278 | if (!smb347_is_ps_online(smb)) |
ed1a230f BR |
279 | return 0; |
280 | ||
34298d40 | 281 | ret = regmap_read(smb->regmap, STAT_C, &val); |
ed1a230f BR |
282 | if (ret < 0) |
283 | return 0; | |
284 | ||
34298d40 | 285 | return (val & STAT_C_CHG_MASK) >> STAT_C_CHG_SHIFT; |
ed1a230f BR |
286 | } |
287 | ||
288 | static int smb347_charging_set(struct smb347_charger *smb, bool enable) | |
289 | { | |
290 | int ret = 0; | |
291 | ||
292 | if (smb->pdata->enable_control != SMB347_CHG_ENABLE_SW) { | |
34298d40 | 293 | dev_dbg(smb->dev, "charging enable/disable in SW disabled\n"); |
ed1a230f BR |
294 | return 0; |
295 | } | |
296 | ||
297 | mutex_lock(&smb->lock); | |
298 | if (smb->charging_enabled != enable) { | |
34298d40 MW |
299 | ret = regmap_update_bits(smb->regmap, CMD_A, CMD_A_CHG_ENABLED, |
300 | enable ? CMD_A_CHG_ENABLED : 0); | |
301 | if (!ret) | |
302 | smb->charging_enabled = enable; | |
ed1a230f | 303 | } |
ed1a230f BR |
304 | mutex_unlock(&smb->lock); |
305 | return ret; | |
306 | } | |
307 | ||
308 | static inline int smb347_charging_enable(struct smb347_charger *smb) | |
309 | { | |
310 | return smb347_charging_set(smb, true); | |
311 | } | |
312 | ||
313 | static inline int smb347_charging_disable(struct smb347_charger *smb) | |
314 | { | |
315 | return smb347_charging_set(smb, false); | |
316 | } | |
317 | ||
055d7f0f | 318 | static int smb347_start_stop_charging(struct smb347_charger *smb) |
ed1a230f BR |
319 | { |
320 | int ret; | |
321 | ||
322 | /* | |
323 | * Depending on whether valid power source is connected or not, we | |
324 | * disable or enable the charging. We do it manually because it | |
325 | * depends on how the platform has configured the valid inputs. | |
326 | */ | |
055d7f0f | 327 | if (smb347_is_ps_online(smb)) { |
ed1a230f BR |
328 | ret = smb347_charging_enable(smb); |
329 | if (ret < 0) | |
34298d40 | 330 | dev_err(smb->dev, "failed to enable charging\n"); |
ed1a230f BR |
331 | } else { |
332 | ret = smb347_charging_disable(smb); | |
333 | if (ret < 0) | |
34298d40 | 334 | dev_err(smb->dev, "failed to disable charging\n"); |
ed1a230f BR |
335 | } |
336 | ||
337 | return ret; | |
338 | } | |
339 | ||
340 | static int smb347_set_charge_current(struct smb347_charger *smb) | |
341 | { | |
34298d40 | 342 | int ret; |
ed1a230f BR |
343 | |
344 | if (smb->pdata->max_charge_current) { | |
34298d40 | 345 | ret = current_to_hw(fcc_tbl, ARRAY_SIZE(fcc_tbl), |
ed1a230f | 346 | smb->pdata->max_charge_current); |
34298d40 MW |
347 | if (ret < 0) |
348 | return ret; | |
ed1a230f | 349 | |
34298d40 MW |
350 | ret = regmap_update_bits(smb->regmap, CFG_CHARGE_CURRENT, |
351 | CFG_CHARGE_CURRENT_FCC_MASK, | |
352 | ret << CFG_CHARGE_CURRENT_FCC_SHIFT); | |
353 | if (ret < 0) | |
354 | return ret; | |
ed1a230f BR |
355 | } |
356 | ||
357 | if (smb->pdata->pre_charge_current) { | |
34298d40 | 358 | ret = current_to_hw(pcc_tbl, ARRAY_SIZE(pcc_tbl), |
ed1a230f | 359 | smb->pdata->pre_charge_current); |
34298d40 MW |
360 | if (ret < 0) |
361 | return ret; | |
ed1a230f | 362 | |
34298d40 MW |
363 | ret = regmap_update_bits(smb->regmap, CFG_CHARGE_CURRENT, |
364 | CFG_CHARGE_CURRENT_PCC_MASK, | |
365 | ret << CFG_CHARGE_CURRENT_PCC_SHIFT); | |
366 | if (ret < 0) | |
367 | return ret; | |
ed1a230f BR |
368 | } |
369 | ||
370 | if (smb->pdata->termination_current) { | |
34298d40 | 371 | ret = current_to_hw(tc_tbl, ARRAY_SIZE(tc_tbl), |
ed1a230f | 372 | smb->pdata->termination_current); |
34298d40 MW |
373 | if (ret < 0) |
374 | return ret; | |
ed1a230f | 375 | |
34298d40 MW |
376 | ret = regmap_update_bits(smb->regmap, CFG_CHARGE_CURRENT, |
377 | CFG_CHARGE_CURRENT_TC_MASK, ret); | |
378 | if (ret < 0) | |
379 | return ret; | |
ed1a230f BR |
380 | } |
381 | ||
34298d40 | 382 | return 0; |
ed1a230f BR |
383 | } |
384 | ||
385 | static int smb347_set_current_limits(struct smb347_charger *smb) | |
386 | { | |
34298d40 | 387 | int ret; |
ed1a230f BR |
388 | |
389 | if (smb->pdata->mains_current_limit) { | |
34298d40 | 390 | ret = current_to_hw(icl_tbl, ARRAY_SIZE(icl_tbl), |
ed1a230f | 391 | smb->pdata->mains_current_limit); |
34298d40 MW |
392 | if (ret < 0) |
393 | return ret; | |
ed1a230f | 394 | |
34298d40 MW |
395 | ret = regmap_update_bits(smb->regmap, CFG_CURRENT_LIMIT, |
396 | CFG_CURRENT_LIMIT_DC_MASK, | |
397 | ret << CFG_CURRENT_LIMIT_DC_SHIFT); | |
398 | if (ret < 0) | |
399 | return ret; | |
ed1a230f BR |
400 | } |
401 | ||
402 | if (smb->pdata->usb_hc_current_limit) { | |
34298d40 | 403 | ret = current_to_hw(icl_tbl, ARRAY_SIZE(icl_tbl), |
ed1a230f | 404 | smb->pdata->usb_hc_current_limit); |
34298d40 MW |
405 | if (ret < 0) |
406 | return ret; | |
ed1a230f | 407 | |
34298d40 MW |
408 | ret = regmap_update_bits(smb->regmap, CFG_CURRENT_LIMIT, |
409 | CFG_CURRENT_LIMIT_USB_MASK, ret); | |
410 | if (ret < 0) | |
411 | return ret; | |
ed1a230f BR |
412 | } |
413 | ||
34298d40 | 414 | return 0; |
ed1a230f BR |
415 | } |
416 | ||
417 | static int smb347_set_voltage_limits(struct smb347_charger *smb) | |
418 | { | |
34298d40 | 419 | int ret; |
ed1a230f BR |
420 | |
421 | if (smb->pdata->pre_to_fast_voltage) { | |
34298d40 | 422 | ret = smb->pdata->pre_to_fast_voltage; |
ed1a230f BR |
423 | |
424 | /* uV */ | |
34298d40 MW |
425 | ret = clamp_val(ret, 2400000, 3000000) - 2400000; |
426 | ret /= 200000; | |
ed1a230f | 427 | |
34298d40 MW |
428 | ret = regmap_update_bits(smb->regmap, CFG_FLOAT_VOLTAGE, |
429 | CFG_FLOAT_VOLTAGE_THRESHOLD_MASK, | |
430 | ret << CFG_FLOAT_VOLTAGE_THRESHOLD_SHIFT); | |
431 | if (ret < 0) | |
432 | return ret; | |
ed1a230f BR |
433 | } |
434 | ||
435 | if (smb->pdata->max_charge_voltage) { | |
34298d40 | 436 | ret = smb->pdata->max_charge_voltage; |
ed1a230f BR |
437 | |
438 | /* uV */ | |
34298d40 MW |
439 | ret = clamp_val(ret, 3500000, 4500000) - 3500000; |
440 | ret /= 20000; | |
ed1a230f | 441 | |
34298d40 MW |
442 | ret = regmap_update_bits(smb->regmap, CFG_FLOAT_VOLTAGE, |
443 | CFG_FLOAT_VOLTAGE_FLOAT_MASK, ret); | |
444 | if (ret < 0) | |
445 | return ret; | |
ed1a230f BR |
446 | } |
447 | ||
34298d40 | 448 | return 0; |
ed1a230f BR |
449 | } |
450 | ||
451 | static int smb347_set_temp_limits(struct smb347_charger *smb) | |
452 | { | |
453 | bool enable_therm_monitor = false; | |
34298d40 MW |
454 | int ret = 0; |
455 | int val; | |
ed1a230f BR |
456 | |
457 | if (smb->pdata->chip_temp_threshold) { | |
458 | val = smb->pdata->chip_temp_threshold; | |
459 | ||
460 | /* degree C */ | |
461 | val = clamp_val(val, 100, 130) - 100; | |
462 | val /= 10; | |
463 | ||
34298d40 MW |
464 | ret = regmap_update_bits(smb->regmap, CFG_OTG, |
465 | CFG_OTG_TEMP_THRESHOLD_MASK, | |
466 | val << CFG_OTG_TEMP_THRESHOLD_SHIFT); | |
ed1a230f BR |
467 | if (ret < 0) |
468 | return ret; | |
469 | } | |
470 | ||
ed1a230f BR |
471 | if (smb->pdata->soft_cold_temp_limit != SMB347_TEMP_USE_DEFAULT) { |
472 | val = smb->pdata->soft_cold_temp_limit; | |
473 | ||
474 | val = clamp_val(val, 0, 15); | |
475 | val /= 5; | |
476 | /* this goes from higher to lower so invert the value */ | |
477 | val = ~val & 0x3; | |
478 | ||
34298d40 MW |
479 | ret = regmap_update_bits(smb->regmap, CFG_TEMP_LIMIT, |
480 | CFG_TEMP_LIMIT_SOFT_COLD_MASK, | |
481 | val << CFG_TEMP_LIMIT_SOFT_COLD_SHIFT); | |
482 | if (ret < 0) | |
483 | return ret; | |
ed1a230f BR |
484 | |
485 | enable_therm_monitor = true; | |
486 | } | |
487 | ||
488 | if (smb->pdata->soft_hot_temp_limit != SMB347_TEMP_USE_DEFAULT) { | |
489 | val = smb->pdata->soft_hot_temp_limit; | |
490 | ||
491 | val = clamp_val(val, 40, 55) - 40; | |
492 | val /= 5; | |
493 | ||
34298d40 MW |
494 | ret = regmap_update_bits(smb->regmap, CFG_TEMP_LIMIT, |
495 | CFG_TEMP_LIMIT_SOFT_HOT_MASK, | |
496 | val << CFG_TEMP_LIMIT_SOFT_HOT_SHIFT); | |
497 | if (ret < 0) | |
498 | return ret; | |
ed1a230f BR |
499 | |
500 | enable_therm_monitor = true; | |
501 | } | |
502 | ||
503 | if (smb->pdata->hard_cold_temp_limit != SMB347_TEMP_USE_DEFAULT) { | |
504 | val = smb->pdata->hard_cold_temp_limit; | |
505 | ||
506 | val = clamp_val(val, -5, 10) + 5; | |
507 | val /= 5; | |
508 | /* this goes from higher to lower so invert the value */ | |
509 | val = ~val & 0x3; | |
510 | ||
34298d40 MW |
511 | ret = regmap_update_bits(smb->regmap, CFG_TEMP_LIMIT, |
512 | CFG_TEMP_LIMIT_HARD_COLD_MASK, | |
513 | val << CFG_TEMP_LIMIT_HARD_COLD_SHIFT); | |
514 | if (ret < 0) | |
515 | return ret; | |
ed1a230f BR |
516 | |
517 | enable_therm_monitor = true; | |
518 | } | |
519 | ||
520 | if (smb->pdata->hard_hot_temp_limit != SMB347_TEMP_USE_DEFAULT) { | |
521 | val = smb->pdata->hard_hot_temp_limit; | |
522 | ||
523 | val = clamp_val(val, 50, 65) - 50; | |
524 | val /= 5; | |
525 | ||
34298d40 MW |
526 | ret = regmap_update_bits(smb->regmap, CFG_TEMP_LIMIT, |
527 | CFG_TEMP_LIMIT_HARD_HOT_MASK, | |
528 | val << CFG_TEMP_LIMIT_HARD_HOT_SHIFT); | |
529 | if (ret < 0) | |
530 | return ret; | |
ed1a230f BR |
531 | |
532 | enable_therm_monitor = true; | |
533 | } | |
534 | ||
ed1a230f BR |
535 | /* |
536 | * If any of the temperature limits are set, we also enable the | |
537 | * thermistor monitoring. | |
538 | * | |
539 | * When soft limits are hit, the device will start to compensate | |
540 | * current and/or voltage depending on the configuration. | |
541 | * | |
542 | * When hard limit is hit, the device will suspend charging | |
543 | * depending on the configuration. | |
544 | */ | |
545 | if (enable_therm_monitor) { | |
34298d40 MW |
546 | ret = regmap_update_bits(smb->regmap, CFG_THERM, |
547 | CFG_THERM_MONITOR_DISABLED, 0); | |
ed1a230f BR |
548 | if (ret < 0) |
549 | return ret; | |
550 | } | |
551 | ||
552 | if (smb->pdata->suspend_on_hard_temp_limit) { | |
34298d40 MW |
553 | ret = regmap_update_bits(smb->regmap, CFG_SYSOK, |
554 | CFG_SYSOK_SUSPEND_HARD_LIMIT_DISABLED, 0); | |
ed1a230f BR |
555 | if (ret < 0) |
556 | return ret; | |
557 | } | |
558 | ||
559 | if (smb->pdata->soft_temp_limit_compensation != | |
560 | SMB347_SOFT_TEMP_COMPENSATE_DEFAULT) { | |
561 | val = smb->pdata->soft_temp_limit_compensation & 0x3; | |
562 | ||
34298d40 MW |
563 | ret = regmap_update_bits(smb->regmap, CFG_THERM, |
564 | CFG_THERM_SOFT_HOT_COMPENSATION_MASK, | |
565 | val << CFG_THERM_SOFT_HOT_COMPENSATION_SHIFT); | |
ed1a230f BR |
566 | if (ret < 0) |
567 | return ret; | |
568 | ||
34298d40 MW |
569 | ret = regmap_update_bits(smb->regmap, CFG_THERM, |
570 | CFG_THERM_SOFT_COLD_COMPENSATION_MASK, | |
571 | val << CFG_THERM_SOFT_COLD_COMPENSATION_SHIFT); | |
ed1a230f BR |
572 | if (ret < 0) |
573 | return ret; | |
574 | } | |
575 | ||
576 | if (smb->pdata->charge_current_compensation) { | |
577 | val = current_to_hw(ccc_tbl, ARRAY_SIZE(ccc_tbl), | |
578 | smb->pdata->charge_current_compensation); | |
579 | if (val < 0) | |
580 | return val; | |
581 | ||
34298d40 MW |
582 | ret = regmap_update_bits(smb->regmap, CFG_OTG, |
583 | CFG_OTG_CC_COMPENSATION_MASK, | |
584 | (val & 0x3) << CFG_OTG_CC_COMPENSATION_SHIFT); | |
ed1a230f BR |
585 | if (ret < 0) |
586 | return ret; | |
587 | } | |
588 | ||
589 | return ret; | |
590 | } | |
591 | ||
592 | /* | |
593 | * smb347_set_writable - enables/disables writing to non-volatile registers | |
594 | * @smb: pointer to smb347 charger instance | |
595 | * | |
596 | * You can enable/disable writing to the non-volatile configuration | |
597 | * registers by calling this function. | |
598 | * | |
599 | * Returns %0 on success and negative errno in case of failure. | |
600 | */ | |
601 | static int smb347_set_writable(struct smb347_charger *smb, bool writable) | |
602 | { | |
34298d40 MW |
603 | return regmap_update_bits(smb->regmap, CMD_A, CMD_A_ALLOW_WRITE, |
604 | writable ? CMD_A_ALLOW_WRITE : 0); | |
ed1a230f BR |
605 | } |
606 | ||
607 | static int smb347_hw_init(struct smb347_charger *smb) | |
608 | { | |
34298d40 | 609 | unsigned int val; |
ed1a230f BR |
610 | int ret; |
611 | ||
612 | ret = smb347_set_writable(smb, true); | |
613 | if (ret < 0) | |
614 | return ret; | |
615 | ||
616 | /* | |
617 | * Program the platform specific configuration values to the device | |
618 | * first. | |
619 | */ | |
620 | ret = smb347_set_charge_current(smb); | |
621 | if (ret < 0) | |
622 | goto fail; | |
623 | ||
624 | ret = smb347_set_current_limits(smb); | |
625 | if (ret < 0) | |
626 | goto fail; | |
627 | ||
628 | ret = smb347_set_voltage_limits(smb); | |
629 | if (ret < 0) | |
630 | goto fail; | |
631 | ||
632 | ret = smb347_set_temp_limits(smb); | |
633 | if (ret < 0) | |
634 | goto fail; | |
635 | ||
636 | /* If USB charging is disabled we put the USB in suspend mode */ | |
637 | if (!smb->pdata->use_usb) { | |
34298d40 MW |
638 | ret = regmap_update_bits(smb->regmap, CMD_A, |
639 | CMD_A_SUSPEND_ENABLED, | |
640 | CMD_A_SUSPEND_ENABLED); | |
ed1a230f BR |
641 | if (ret < 0) |
642 | goto fail; | |
643 | } | |
644 | ||
ed1a230f BR |
645 | /* |
646 | * If configured by platform data, we enable hardware Auto-OTG | |
647 | * support for driving VBUS. Otherwise we disable it. | |
648 | */ | |
34298d40 MW |
649 | ret = regmap_update_bits(smb->regmap, CFG_OTHER, CFG_OTHER_RID_MASK, |
650 | smb->pdata->use_usb_otg ? CFG_OTHER_RID_ENABLED_AUTO_OTG : 0); | |
ed1a230f BR |
651 | if (ret < 0) |
652 | goto fail; | |
653 | ||
654 | /* | |
655 | * Make the charging functionality controllable by a write to the | |
656 | * command register unless pin control is specified in the platform | |
657 | * data. | |
658 | */ | |
ed1a230f | 659 | switch (smb->pdata->enable_control) { |
ed1a230f | 660 | case SMB347_CHG_ENABLE_PIN_ACTIVE_LOW: |
34298d40 | 661 | val = CFG_PIN_EN_CTRL_ACTIVE_LOW; |
ed1a230f BR |
662 | break; |
663 | case SMB347_CHG_ENABLE_PIN_ACTIVE_HIGH: | |
34298d40 MW |
664 | val = CFG_PIN_EN_CTRL_ACTIVE_HIGH; |
665 | break; | |
666 | default: | |
667 | val = 0; | |
ed1a230f BR |
668 | break; |
669 | } | |
670 | ||
34298d40 MW |
671 | ret = regmap_update_bits(smb->regmap, CFG_PIN, CFG_PIN_EN_CTRL_MASK, |
672 | val); | |
673 | if (ret < 0) | |
674 | goto fail; | |
ed1a230f | 675 | |
34298d40 MW |
676 | /* Disable Automatic Power Source Detection (APSD) interrupt. */ |
677 | ret = regmap_update_bits(smb->regmap, CFG_PIN, CFG_PIN_EN_APSD_IRQ, 0); | |
ed1a230f BR |
678 | if (ret < 0) |
679 | goto fail; | |
680 | ||
055d7f0f | 681 | ret = smb347_update_ps_status(smb); |
ed1a230f BR |
682 | if (ret < 0) |
683 | goto fail; | |
684 | ||
055d7f0f | 685 | ret = smb347_start_stop_charging(smb); |
ed1a230f BR |
686 | |
687 | fail: | |
688 | smb347_set_writable(smb, false); | |
689 | return ret; | |
690 | } | |
691 | ||
692 | static irqreturn_t smb347_interrupt(int irq, void *data) | |
693 | { | |
694 | struct smb347_charger *smb = data; | |
34298d40 MW |
695 | unsigned int stat_c, irqstat_e, irqstat_c; |
696 | bool handled = false; | |
697 | int ret; | |
ed1a230f | 698 | |
34298d40 MW |
699 | ret = regmap_read(smb->regmap, STAT_C, &stat_c); |
700 | if (ret < 0) { | |
701 | dev_warn(smb->dev, "reading STAT_C failed\n"); | |
ed1a230f BR |
702 | return IRQ_NONE; |
703 | } | |
704 | ||
34298d40 MW |
705 | ret = regmap_read(smb->regmap, IRQSTAT_C, &irqstat_c); |
706 | if (ret < 0) { | |
707 | dev_warn(smb->dev, "reading IRQSTAT_C failed\n"); | |
ed1a230f BR |
708 | return IRQ_NONE; |
709 | } | |
710 | ||
34298d40 MW |
711 | ret = regmap_read(smb->regmap, IRQSTAT_E, &irqstat_e); |
712 | if (ret < 0) { | |
713 | dev_warn(smb->dev, "reading IRQSTAT_E failed\n"); | |
ed1a230f BR |
714 | return IRQ_NONE; |
715 | } | |
716 | ||
717 | /* | |
718 | * If we get charger error we report the error back to user and | |
719 | * disable charging. | |
720 | */ | |
721 | if (stat_c & STAT_C_CHARGER_ERROR) { | |
34298d40 | 722 | dev_err(smb->dev, "error in charger, disabling charging\n"); |
ed1a230f BR |
723 | |
724 | smb347_charging_disable(smb); | |
725 | power_supply_changed(&smb->battery); | |
34298d40 | 726 | handled = true; |
ed1a230f BR |
727 | } |
728 | ||
729 | /* | |
730 | * If we reached the termination current the battery is charged and | |
731 | * we can update the status now. Charging is automatically | |
732 | * disabled by the hardware. | |
733 | */ | |
734 | if (irqstat_c & (IRQSTAT_C_TERMINATION_IRQ | IRQSTAT_C_TAPER_IRQ)) { | |
735 | if (irqstat_c & IRQSTAT_C_TERMINATION_STAT) | |
736 | power_supply_changed(&smb->battery); | |
34298d40 | 737 | handled = true; |
ed1a230f BR |
738 | } |
739 | ||
740 | /* | |
741 | * If we got an under voltage interrupt it means that AC/USB input | |
742 | * was connected or disconnected. | |
743 | */ | |
744 | if (irqstat_e & (IRQSTAT_E_USBIN_UV_IRQ | IRQSTAT_E_DCIN_UV_IRQ)) { | |
055d7f0f MW |
745 | if (smb347_update_ps_status(smb) > 0) { |
746 | smb347_start_stop_charging(smb); | |
e6fe3597 RP |
747 | if (smb->pdata->use_mains) |
748 | power_supply_changed(&smb->mains); | |
749 | if (smb->pdata->use_usb) | |
750 | power_supply_changed(&smb->usb); | |
ed1a230f | 751 | } |
34298d40 | 752 | handled = true; |
ed1a230f BR |
753 | } |
754 | ||
34298d40 | 755 | return handled ? IRQ_HANDLED : IRQ_NONE; |
ed1a230f BR |
756 | } |
757 | ||
758 | static int smb347_irq_set(struct smb347_charger *smb, bool enable) | |
759 | { | |
760 | int ret; | |
761 | ||
762 | ret = smb347_set_writable(smb, true); | |
763 | if (ret < 0) | |
764 | return ret; | |
765 | ||
766 | /* | |
767 | * Enable/disable interrupts for: | |
768 | * - under voltage | |
769 | * - termination current reached | |
770 | * - charger error | |
771 | */ | |
34298d40 MW |
772 | ret = regmap_update_bits(smb->regmap, CFG_FAULT_IRQ, 0xff, |
773 | enable ? CFG_FAULT_IRQ_DCIN_UV : 0); | |
774 | if (ret < 0) | |
775 | goto fail; | |
ed1a230f | 776 | |
34298d40 MW |
777 | ret = regmap_update_bits(smb->regmap, CFG_STATUS_IRQ, 0xff, |
778 | enable ? CFG_STATUS_IRQ_TERMINATION_OR_TAPER : 0); | |
779 | if (ret < 0) | |
780 | goto fail; | |
ed1a230f | 781 | |
34298d40 MW |
782 | ret = regmap_update_bits(smb->regmap, CFG_PIN, CFG_PIN_EN_CHARGER_ERROR, |
783 | enable ? CFG_PIN_EN_CHARGER_ERROR : 0); | |
ed1a230f BR |
784 | fail: |
785 | smb347_set_writable(smb, false); | |
786 | return ret; | |
787 | } | |
788 | ||
789 | static inline int smb347_irq_enable(struct smb347_charger *smb) | |
790 | { | |
791 | return smb347_irq_set(smb, true); | |
792 | } | |
793 | ||
794 | static inline int smb347_irq_disable(struct smb347_charger *smb) | |
795 | { | |
796 | return smb347_irq_set(smb, false); | |
797 | } | |
798 | ||
34298d40 MW |
799 | static int smb347_irq_init(struct smb347_charger *smb, |
800 | struct i2c_client *client) | |
ed1a230f BR |
801 | { |
802 | const struct smb347_charger_platform_data *pdata = smb->pdata; | |
803 | int ret, irq = gpio_to_irq(pdata->irq_gpio); | |
804 | ||
34298d40 | 805 | ret = gpio_request_one(pdata->irq_gpio, GPIOF_IN, client->name); |
ed1a230f BR |
806 | if (ret < 0) |
807 | goto fail; | |
808 | ||
809 | ret = request_threaded_irq(irq, NULL, smb347_interrupt, | |
34298d40 | 810 | IRQF_TRIGGER_FALLING, client->name, smb); |
ed1a230f BR |
811 | if (ret < 0) |
812 | goto fail_gpio; | |
813 | ||
814 | ret = smb347_set_writable(smb, true); | |
815 | if (ret < 0) | |
816 | goto fail_irq; | |
817 | ||
818 | /* | |
819 | * Configure the STAT output to be suitable for interrupts: disable | |
820 | * all other output (except interrupts) and make it active low. | |
821 | */ | |
34298d40 MW |
822 | ret = regmap_update_bits(smb->regmap, CFG_STAT, |
823 | CFG_STAT_ACTIVE_HIGH | CFG_STAT_DISABLED, | |
824 | CFG_STAT_DISABLED); | |
ed1a230f BR |
825 | if (ret < 0) |
826 | goto fail_readonly; | |
827 | ||
ed1a230f | 828 | smb347_set_writable(smb, false); |
34298d40 | 829 | client->irq = irq; |
ed1a230f BR |
830 | return 0; |
831 | ||
832 | fail_readonly: | |
833 | smb347_set_writable(smb, false); | |
834 | fail_irq: | |
835 | free_irq(irq, smb); | |
836 | fail_gpio: | |
837 | gpio_free(pdata->irq_gpio); | |
838 | fail: | |
34298d40 | 839 | client->irq = 0; |
ed1a230f BR |
840 | return ret; |
841 | } | |
842 | ||
843 | static int smb347_mains_get_property(struct power_supply *psy, | |
844 | enum power_supply_property prop, | |
845 | union power_supply_propval *val) | |
846 | { | |
847 | struct smb347_charger *smb = | |
848 | container_of(psy, struct smb347_charger, mains); | |
849 | ||
850 | if (prop == POWER_SUPPLY_PROP_ONLINE) { | |
851 | val->intval = smb->mains_online; | |
852 | return 0; | |
853 | } | |
854 | return -EINVAL; | |
855 | } | |
856 | ||
857 | static enum power_supply_property smb347_mains_properties[] = { | |
858 | POWER_SUPPLY_PROP_ONLINE, | |
859 | }; | |
860 | ||
861 | static int smb347_usb_get_property(struct power_supply *psy, | |
862 | enum power_supply_property prop, | |
863 | union power_supply_propval *val) | |
864 | { | |
865 | struct smb347_charger *smb = | |
866 | container_of(psy, struct smb347_charger, usb); | |
867 | ||
868 | if (prop == POWER_SUPPLY_PROP_ONLINE) { | |
869 | val->intval = smb->usb_online; | |
870 | return 0; | |
871 | } | |
872 | return -EINVAL; | |
873 | } | |
874 | ||
875 | static enum power_supply_property smb347_usb_properties[] = { | |
876 | POWER_SUPPLY_PROP_ONLINE, | |
877 | }; | |
878 | ||
879 | static int smb347_battery_get_property(struct power_supply *psy, | |
880 | enum power_supply_property prop, | |
881 | union power_supply_propval *val) | |
882 | { | |
883 | struct smb347_charger *smb = | |
884 | container_of(psy, struct smb347_charger, battery); | |
885 | const struct smb347_charger_platform_data *pdata = smb->pdata; | |
886 | int ret; | |
887 | ||
055d7f0f | 888 | ret = smb347_update_ps_status(smb); |
ed1a230f BR |
889 | if (ret < 0) |
890 | return ret; | |
891 | ||
892 | switch (prop) { | |
893 | case POWER_SUPPLY_PROP_STATUS: | |
055d7f0f | 894 | if (!smb347_is_ps_online(smb)) { |
ed1a230f BR |
895 | val->intval = POWER_SUPPLY_STATUS_DISCHARGING; |
896 | break; | |
897 | } | |
898 | if (smb347_charging_status(smb)) | |
899 | val->intval = POWER_SUPPLY_STATUS_CHARGING; | |
900 | else | |
901 | val->intval = POWER_SUPPLY_STATUS_FULL; | |
902 | break; | |
903 | ||
904 | case POWER_SUPPLY_PROP_CHARGE_TYPE: | |
055d7f0f | 905 | if (!smb347_is_ps_online(smb)) |
ed1a230f BR |
906 | return -ENODATA; |
907 | ||
908 | /* | |
909 | * We handle trickle and pre-charging the same, and taper | |
910 | * and none the same. | |
911 | */ | |
912 | switch (smb347_charging_status(smb)) { | |
913 | case 1: | |
914 | val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; | |
915 | break; | |
916 | case 2: | |
917 | val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST; | |
918 | break; | |
919 | default: | |
920 | val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE; | |
921 | break; | |
922 | } | |
923 | break; | |
924 | ||
925 | case POWER_SUPPLY_PROP_TECHNOLOGY: | |
926 | val->intval = pdata->battery_info.technology; | |
927 | break; | |
928 | ||
929 | case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: | |
930 | val->intval = pdata->battery_info.voltage_min_design; | |
931 | break; | |
932 | ||
933 | case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: | |
934 | val->intval = pdata->battery_info.voltage_max_design; | |
935 | break; | |
936 | ||
ed1a230f BR |
937 | case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: |
938 | val->intval = pdata->battery_info.charge_full_design; | |
939 | break; | |
940 | ||
941 | case POWER_SUPPLY_PROP_MODEL_NAME: | |
942 | val->strval = pdata->battery_info.name; | |
943 | break; | |
944 | ||
945 | default: | |
946 | return -EINVAL; | |
947 | } | |
948 | ||
949 | return 0; | |
950 | } | |
951 | ||
952 | static enum power_supply_property smb347_battery_properties[] = { | |
953 | POWER_SUPPLY_PROP_STATUS, | |
954 | POWER_SUPPLY_PROP_CHARGE_TYPE, | |
955 | POWER_SUPPLY_PROP_TECHNOLOGY, | |
956 | POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, | |
957 | POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, | |
ed1a230f BR |
958 | POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, |
959 | POWER_SUPPLY_PROP_MODEL_NAME, | |
960 | }; | |
961 | ||
34298d40 | 962 | static bool smb347_volatile_reg(struct device *dev, unsigned int reg) |
ed1a230f | 963 | { |
34298d40 MW |
964 | switch (reg) { |
965 | case IRQSTAT_A: | |
966 | case IRQSTAT_C: | |
967 | case IRQSTAT_E: | |
968 | case IRQSTAT_F: | |
969 | case STAT_A: | |
970 | case STAT_B: | |
971 | case STAT_C: | |
972 | case STAT_E: | |
973 | return true; | |
ed1a230f BR |
974 | } |
975 | ||
34298d40 | 976 | return false; |
ed1a230f BR |
977 | } |
978 | ||
34298d40 | 979 | static bool smb347_readable_reg(struct device *dev, unsigned int reg) |
ed1a230f | 980 | { |
34298d40 MW |
981 | switch (reg) { |
982 | case CFG_CHARGE_CURRENT: | |
983 | case CFG_CURRENT_LIMIT: | |
984 | case CFG_FLOAT_VOLTAGE: | |
985 | case CFG_STAT: | |
986 | case CFG_PIN: | |
987 | case CFG_THERM: | |
988 | case CFG_SYSOK: | |
989 | case CFG_OTHER: | |
990 | case CFG_OTG: | |
991 | case CFG_TEMP_LIMIT: | |
992 | case CFG_FAULT_IRQ: | |
993 | case CFG_STATUS_IRQ: | |
994 | case CFG_ADDRESS: | |
995 | case CMD_A: | |
996 | case CMD_B: | |
997 | case CMD_C: | |
998 | return true; | |
999 | } | |
1000 | ||
1001 | return smb347_volatile_reg(dev, reg); | |
ed1a230f BR |
1002 | } |
1003 | ||
34298d40 MW |
1004 | static const struct regmap_config smb347_regmap = { |
1005 | .reg_bits = 8, | |
1006 | .val_bits = 8, | |
1007 | .max_register = SMB347_MAX_REGISTER, | |
1008 | .volatile_reg = smb347_volatile_reg, | |
1009 | .readable_reg = smb347_readable_reg, | |
ed1a230f BR |
1010 | }; |
1011 | ||
1012 | static int smb347_probe(struct i2c_client *client, | |
1013 | const struct i2c_device_id *id) | |
1014 | { | |
1015 | static char *battery[] = { "smb347-battery" }; | |
1016 | const struct smb347_charger_platform_data *pdata; | |
1017 | struct device *dev = &client->dev; | |
1018 | struct smb347_charger *smb; | |
1019 | int ret; | |
1020 | ||
1021 | pdata = dev->platform_data; | |
1022 | if (!pdata) | |
1023 | return -EINVAL; | |
1024 | ||
1025 | if (!pdata->use_mains && !pdata->use_usb) | |
1026 | return -EINVAL; | |
1027 | ||
1028 | smb = devm_kzalloc(dev, sizeof(*smb), GFP_KERNEL); | |
1029 | if (!smb) | |
1030 | return -ENOMEM; | |
1031 | ||
1032 | i2c_set_clientdata(client, smb); | |
1033 | ||
1034 | mutex_init(&smb->lock); | |
34298d40 | 1035 | smb->dev = &client->dev; |
ed1a230f BR |
1036 | smb->pdata = pdata; |
1037 | ||
34298d40 MW |
1038 | smb->regmap = devm_regmap_init_i2c(client, &smb347_regmap); |
1039 | if (IS_ERR(smb->regmap)) | |
1040 | return PTR_ERR(smb->regmap); | |
1041 | ||
ed1a230f BR |
1042 | ret = smb347_hw_init(smb); |
1043 | if (ret < 0) | |
1044 | return ret; | |
1045 | ||
e6fe3597 RP |
1046 | if (smb->pdata->use_mains) { |
1047 | smb->mains.name = "smb347-mains"; | |
1048 | smb->mains.type = POWER_SUPPLY_TYPE_MAINS; | |
1049 | smb->mains.get_property = smb347_mains_get_property; | |
1050 | smb->mains.properties = smb347_mains_properties; | |
1051 | smb->mains.num_properties = ARRAY_SIZE(smb347_mains_properties); | |
1052 | smb->mains.supplied_to = battery; | |
1053 | smb->mains.num_supplicants = ARRAY_SIZE(battery); | |
1054 | ret = power_supply_register(dev, &smb->mains); | |
1055 | if (ret < 0) | |
1056 | return ret; | |
1057 | } | |
1058 | ||
1059 | if (smb->pdata->use_usb) { | |
1060 | smb->usb.name = "smb347-usb"; | |
1061 | smb->usb.type = POWER_SUPPLY_TYPE_USB; | |
1062 | smb->usb.get_property = smb347_usb_get_property; | |
1063 | smb->usb.properties = smb347_usb_properties; | |
1064 | smb->usb.num_properties = ARRAY_SIZE(smb347_usb_properties); | |
1065 | smb->usb.supplied_to = battery; | |
1066 | smb->usb.num_supplicants = ARRAY_SIZE(battery); | |
1067 | ret = power_supply_register(dev, &smb->usb); | |
1068 | if (ret < 0) { | |
1069 | if (smb->pdata->use_mains) | |
1070 | power_supply_unregister(&smb->mains); | |
1071 | return ret; | |
1072 | } | |
1073 | } | |
ed1a230f BR |
1074 | |
1075 | smb->battery.name = "smb347-battery"; | |
1076 | smb->battery.type = POWER_SUPPLY_TYPE_BATTERY; | |
1077 | smb->battery.get_property = smb347_battery_get_property; | |
1078 | smb->battery.properties = smb347_battery_properties; | |
1079 | smb->battery.num_properties = ARRAY_SIZE(smb347_battery_properties); | |
1080 | ||
ed1a230f BR |
1081 | |
1082 | ret = power_supply_register(dev, &smb->battery); | |
1083 | if (ret < 0) { | |
e6fe3597 RP |
1084 | if (smb->pdata->use_usb) |
1085 | power_supply_unregister(&smb->usb); | |
1086 | if (smb->pdata->use_mains) | |
1087 | power_supply_unregister(&smb->mains); | |
ed1a230f BR |
1088 | return ret; |
1089 | } | |
1090 | ||
1091 | /* | |
1092 | * Interrupt pin is optional. If it is connected, we setup the | |
1093 | * interrupt support here. | |
1094 | */ | |
1095 | if (pdata->irq_gpio >= 0) { | |
34298d40 | 1096 | ret = smb347_irq_init(smb, client); |
ed1a230f BR |
1097 | if (ret < 0) { |
1098 | dev_warn(dev, "failed to initialize IRQ: %d\n", ret); | |
1099 | dev_warn(dev, "disabling IRQ support\n"); | |
d72bade7 MW |
1100 | } else { |
1101 | smb347_irq_enable(smb); | |
ed1a230f BR |
1102 | } |
1103 | } | |
1104 | ||
ed1a230f BR |
1105 | return 0; |
1106 | } | |
1107 | ||
1108 | static int smb347_remove(struct i2c_client *client) | |
1109 | { | |
1110 | struct smb347_charger *smb = i2c_get_clientdata(client); | |
1111 | ||
ed1a230f BR |
1112 | if (client->irq) { |
1113 | smb347_irq_disable(smb); | |
1114 | free_irq(client->irq, smb); | |
1115 | gpio_free(smb->pdata->irq_gpio); | |
1116 | } | |
1117 | ||
1118 | power_supply_unregister(&smb->battery); | |
e6fe3597 RP |
1119 | if (smb->pdata->use_usb) |
1120 | power_supply_unregister(&smb->usb); | |
1121 | if (smb->pdata->use_mains) | |
1122 | power_supply_unregister(&smb->mains); | |
ed1a230f BR |
1123 | return 0; |
1124 | } | |
1125 | ||
1126 | static const struct i2c_device_id smb347_id[] = { | |
1127 | { "smb347", 0 }, | |
1128 | { } | |
1129 | }; | |
1130 | MODULE_DEVICE_TABLE(i2c, smb347_id); | |
1131 | ||
1132 | static struct i2c_driver smb347_driver = { | |
1133 | .driver = { | |
1134 | .name = "smb347", | |
1135 | }, | |
1136 | .probe = smb347_probe, | |
1137 | .remove = __devexit_p(smb347_remove), | |
1138 | .id_table = smb347_id, | |
1139 | }; | |
1140 | ||
b75ef1d8 | 1141 | module_i2c_driver(smb347_driver); |
ed1a230f BR |
1142 | |
1143 | MODULE_AUTHOR("Bruce E. Robertson <[email protected]>"); | |
1144 | MODULE_AUTHOR("Mika Westerberg <[email protected]>"); | |
1145 | MODULE_DESCRIPTION("SMB347 battery charger driver"); | |
1146 | MODULE_LICENSE("GPL"); | |
1147 | MODULE_ALIAS("i2c:smb347"); |