diff options
author | Bruce E. Robertson <bruce.e.robertson@intel.com> | 2012-02-06 10:59:01 -0500 |
---|---|---|
committer | Anton Vorontsov <anton.vorontsov@linaro.org> | 2012-03-26 12:41:01 -0400 |
commit | ed1a230f96eb4610f1f4296b8c3c067389ddf540 (patch) | |
tree | 42653c8122379507f3441b046dd85c6adff4c09f /drivers/power | |
parent | d2c0077c54794d668b3639079a4262a52088d5e2 (diff) |
Add I2C driver for Summit Microelectronics SMB347 Battery Charger.
Driver support for the Summit I²C battery charger. This is used in some
Intel devices.
Signed-off-by: Bruce E. Robertson <bruce.e.robertson@intel.com>
Signed-off-by: Alan Cox <alan@linux.intel.com>
Signed-off-by: Anton Vorontsov <anton.vorontsov@linaro.org>
Diffstat (limited to 'drivers/power')
-rw-r--r-- | drivers/power/Kconfig | 7 | ||||
-rw-r--r-- | drivers/power/Makefile | 1 | ||||
-rw-r--r-- | drivers/power/smb347-charger.c | 1294 |
3 files changed, 1302 insertions, 0 deletions
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 97680d8fa238..c728d26dca6c 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig | |||
@@ -274,4 +274,11 @@ config CHARGER_MAX8998 | |||
274 | Say Y to enable support for the battery charger control sysfs and | 274 | Say Y to enable support for the battery charger control sysfs and |
275 | platform data of MAX8998/LP3974 PMICs. | 275 | platform data of MAX8998/LP3974 PMICs. |
276 | 276 | ||
277 | config CHARGER_SMB347 | ||
278 | tristate "Summit Microelectronics SMB347 Battery Charger" | ||
279 | depends on I2C | ||
280 | help | ||
281 | Say Y to include support for Summit Microelectronics SMB347 | ||
282 | Battery Charger. | ||
283 | |||
277 | endif # POWER_SUPPLY | 284 | endif # POWER_SUPPLY |
diff --git a/drivers/power/Makefile b/drivers/power/Makefile index e429008eaf10..894710e336f0 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile | |||
@@ -41,3 +41,4 @@ obj-$(CONFIG_CHARGER_GPIO) += gpio-charger.o | |||
41 | obj-$(CONFIG_CHARGER_MANAGER) += charger-manager.o | 41 | obj-$(CONFIG_CHARGER_MANAGER) += charger-manager.o |
42 | obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o | 42 | obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o |
43 | obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o | 43 | obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o |
44 | obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o | ||
diff --git a/drivers/power/smb347-charger.c b/drivers/power/smb347-charger.c new file mode 100644 index 000000000000..ce1694d1a365 --- /dev/null +++ b/drivers/power/smb347-charger.c | |||
@@ -0,0 +1,1294 @@ | |||
1 | /* | ||
2 | * Summit Microelectronics SMB347 Battery Charger Driver | ||
3 | * | ||
4 | * Copyright (C) 2011, Intel Corporation | ||
5 | * | ||
6 | * Authors: Bruce E. Robertson <bruce.e.robertson@intel.com> | ||
7 | * Mika Westerberg <mika.westerberg@linux.intel.com> | ||
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 | |||
14 | #include <linux/debugfs.h> | ||
15 | #include <linux/gpio.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/module.h> | ||
18 | #include <linux/init.h> | ||
19 | #include <linux/interrupt.h> | ||
20 | #include <linux/i2c.h> | ||
21 | #include <linux/mutex.h> | ||
22 | #include <linux/power_supply.h> | ||
23 | #include <linux/power/smb347-charger.h> | ||
24 | #include <linux/seq_file.h> | ||
25 | |||
26 | /* | ||
27 | * Configuration registers. These are mirrored to volatile RAM and can be | ||
28 | * written once %CMD_A_ALLOW_WRITE is set in %CMD_A register. They will be | ||
29 | * reloaded from non-volatile registers after POR. | ||
30 | */ | ||
31 | #define CFG_CHARGE_CURRENT 0x00 | ||
32 | #define CFG_CHARGE_CURRENT_FCC_MASK 0xe0 | ||
33 | #define CFG_CHARGE_CURRENT_FCC_SHIFT 5 | ||
34 | #define CFG_CHARGE_CURRENT_PCC_MASK 0x18 | ||
35 | #define CFG_CHARGE_CURRENT_PCC_SHIFT 3 | ||
36 | #define CFG_CHARGE_CURRENT_TC_MASK 0x07 | ||
37 | #define CFG_CURRENT_LIMIT 0x01 | ||
38 | #define CFG_CURRENT_LIMIT_DC_MASK 0xf0 | ||
39 | #define CFG_CURRENT_LIMIT_DC_SHIFT 4 | ||
40 | #define CFG_CURRENT_LIMIT_USB_MASK 0x0f | ||
41 | #define CFG_FLOAT_VOLTAGE 0x03 | ||
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 | |||
116 | /** | ||
117 | * struct smb347_charger - smb347 charger instance | ||
118 | * @lock: protects concurrent access to online variables | ||
119 | * @client: pointer to i2c client | ||
120 | * @mains: power_supply instance for AC/DC power | ||
121 | * @usb: power_supply instance for USB power | ||
122 | * @battery: power_supply instance for battery | ||
123 | * @mains_online: is AC/DC input connected | ||
124 | * @usb_online: is USB input connected | ||
125 | * @charging_enabled: is charging enabled | ||
126 | * @dentry: for debugfs | ||
127 | * @pdata: pointer to platform data | ||
128 | */ | ||
129 | struct smb347_charger { | ||
130 | struct mutex lock; | ||
131 | struct i2c_client *client; | ||
132 | struct power_supply mains; | ||
133 | struct power_supply usb; | ||
134 | struct power_supply battery; | ||
135 | bool mains_online; | ||
136 | bool usb_online; | ||
137 | bool charging_enabled; | ||
138 | struct dentry *dentry; | ||
139 | const struct smb347_charger_platform_data *pdata; | ||
140 | }; | ||
141 | |||
142 | /* Fast charge current in uA */ | ||
143 | static const unsigned int fcc_tbl[] = { | ||
144 | 700000, | ||
145 | 900000, | ||
146 | 1200000, | ||
147 | 1500000, | ||
148 | 1800000, | ||
149 | 2000000, | ||
150 | 2200000, | ||
151 | 2500000, | ||
152 | }; | ||
153 | |||
154 | /* Pre-charge current in uA */ | ||
155 | static const unsigned int pcc_tbl[] = { | ||
156 | 100000, | ||
157 | 150000, | ||
158 | 200000, | ||
159 | 250000, | ||
160 | }; | ||
161 | |||
162 | /* Termination current in uA */ | ||
163 | static const unsigned int tc_tbl[] = { | ||
164 | 37500, | ||
165 | 50000, | ||
166 | 100000, | ||
167 | 150000, | ||
168 | 200000, | ||
169 | 250000, | ||
170 | 500000, | ||
171 | 600000, | ||
172 | }; | ||
173 | |||
174 | /* Input current limit in uA */ | ||
175 | static const unsigned int icl_tbl[] = { | ||
176 | 300000, | ||
177 | 500000, | ||
178 | 700000, | ||
179 | 900000, | ||
180 | 1200000, | ||
181 | 1500000, | ||
182 | 1800000, | ||
183 | 2000000, | ||
184 | 2200000, | ||
185 | 2500000, | ||
186 | }; | ||
187 | |||
188 | /* Charge current compensation in uA */ | ||
189 | static const unsigned int ccc_tbl[] = { | ||
190 | 250000, | ||
191 | 700000, | ||
192 | 900000, | ||
193 | 1200000, | ||
194 | }; | ||
195 | |||
196 | /* Convert register value to current using lookup table */ | ||
197 | static int hw_to_current(const unsigned int *tbl, size_t size, unsigned int val) | ||
198 | { | ||
199 | if (val >= size) | ||
200 | return -EINVAL; | ||
201 | return tbl[val]; | ||
202 | } | ||
203 | |||
204 | /* Convert current to register value using lookup table */ | ||
205 | static int current_to_hw(const unsigned int *tbl, size_t size, unsigned int val) | ||
206 | { | ||
207 | size_t i; | ||
208 | |||
209 | for (i = 0; i < size; i++) | ||
210 | if (val < tbl[i]) | ||
211 | break; | ||
212 | return i > 0 ? i - 1 : -EINVAL; | ||
213 | } | ||
214 | |||
215 | static int smb347_read(struct smb347_charger *smb, u8 reg) | ||
216 | { | ||
217 | int ret; | ||
218 | |||
219 | ret = i2c_smbus_read_byte_data(smb->client, reg); | ||
220 | if (ret < 0) | ||
221 | dev_warn(&smb->client->dev, "failed to read reg 0x%x: %d\n", | ||
222 | reg, ret); | ||
223 | return ret; | ||
224 | } | ||
225 | |||
226 | static int smb347_write(struct smb347_charger *smb, u8 reg, u8 val) | ||
227 | { | ||
228 | int ret; | ||
229 | |||
230 | ret = i2c_smbus_write_byte_data(smb->client, reg, val); | ||
231 | if (ret < 0) | ||
232 | dev_warn(&smb->client->dev, "failed to write reg 0x%x: %d\n", | ||
233 | reg, ret); | ||
234 | return ret; | ||
235 | } | ||
236 | |||
237 | /** | ||
238 | * smb347_update_status - updates the charging status | ||
239 | * @smb: pointer to smb347 charger instance | ||
240 | * | ||
241 | * Function checks status of the charging and updates internal state | ||
242 | * accordingly. Returns %0 if there is no change in status, %1 if the | ||
243 | * status has changed and negative errno in case of failure. | ||
244 | */ | ||
245 | static int smb347_update_status(struct smb347_charger *smb) | ||
246 | { | ||
247 | bool usb = false; | ||
248 | bool dc = false; | ||
249 | int ret; | ||
250 | |||
251 | ret = smb347_read(smb, IRQSTAT_E); | ||
252 | if (ret < 0) | ||
253 | return ret; | ||
254 | |||
255 | /* | ||
256 | * Dc and usb are set depending on whether they are enabled in | ||
257 | * platform data _and_ whether corresponding undervoltage is set. | ||
258 | */ | ||
259 | if (smb->pdata->use_mains) | ||
260 | dc = !(ret & IRQSTAT_E_DCIN_UV_STAT); | ||
261 | if (smb->pdata->use_usb) | ||
262 | usb = !(ret & IRQSTAT_E_USBIN_UV_STAT); | ||
263 | |||
264 | mutex_lock(&smb->lock); | ||
265 | ret = smb->mains_online != dc || smb->usb_online != usb; | ||
266 | smb->mains_online = dc; | ||
267 | smb->usb_online = usb; | ||
268 | mutex_unlock(&smb->lock); | ||
269 | |||
270 | return ret; | ||
271 | } | ||
272 | |||
273 | /* | ||
274 | * smb347_is_online - returns whether input power source is connected | ||
275 | * @smb: pointer to smb347 charger instance | ||
276 | * | ||
277 | * Returns %true if input power source is connected. Note that this is | ||
278 | * dependent on what platform has configured for usable power sources. For | ||
279 | * example if USB is disabled, this will return %false even if the USB | ||
280 | * cable is connected. | ||
281 | */ | ||
282 | static bool smb347_is_online(struct smb347_charger *smb) | ||
283 | { | ||
284 | bool ret; | ||
285 | |||
286 | mutex_lock(&smb->lock); | ||
287 | ret = smb->usb_online || smb->mains_online; | ||
288 | mutex_unlock(&smb->lock); | ||
289 | |||
290 | return ret; | ||
291 | } | ||
292 | |||
293 | /** | ||
294 | * smb347_charging_status - returns status of charging | ||
295 | * @smb: pointer to smb347 charger instance | ||
296 | * | ||
297 | * Function returns charging status. %0 means no charging is in progress, | ||
298 | * %1 means pre-charging, %2 fast-charging and %3 taper-charging. | ||
299 | */ | ||
300 | static int smb347_charging_status(struct smb347_charger *smb) | ||
301 | { | ||
302 | int ret; | ||
303 | |||
304 | if (!smb347_is_online(smb)) | ||
305 | return 0; | ||
306 | |||
307 | ret = smb347_read(smb, STAT_C); | ||
308 | if (ret < 0) | ||
309 | return 0; | ||
310 | |||
311 | return (ret & STAT_C_CHG_MASK) >> STAT_C_CHG_SHIFT; | ||
312 | } | ||
313 | |||
314 | static int smb347_charging_set(struct smb347_charger *smb, bool enable) | ||
315 | { | ||
316 | int ret = 0; | ||
317 | |||
318 | if (smb->pdata->enable_control != SMB347_CHG_ENABLE_SW) { | ||
319 | dev_dbg(&smb->client->dev, | ||
320 | "charging enable/disable in SW disabled\n"); | ||
321 | return 0; | ||
322 | } | ||
323 | |||
324 | mutex_lock(&smb->lock); | ||
325 | if (smb->charging_enabled != enable) { | ||
326 | ret = smb347_read(smb, CMD_A); | ||
327 | if (ret < 0) | ||
328 | goto out; | ||
329 | |||
330 | smb->charging_enabled = enable; | ||
331 | |||
332 | if (enable) | ||
333 | ret |= CMD_A_CHG_ENABLED; | ||
334 | else | ||
335 | ret &= ~CMD_A_CHG_ENABLED; | ||
336 | |||
337 | ret = smb347_write(smb, CMD_A, ret); | ||
338 | } | ||
339 | out: | ||
340 | mutex_unlock(&smb->lock); | ||
341 | return ret; | ||
342 | } | ||
343 | |||
344 | static inline int smb347_charging_enable(struct smb347_charger *smb) | ||
345 | { | ||
346 | return smb347_charging_set(smb, true); | ||
347 | } | ||
348 | |||
349 | static inline int smb347_charging_disable(struct smb347_charger *smb) | ||
350 | { | ||
351 | return smb347_charging_set(smb, false); | ||
352 | } | ||
353 | |||
354 | static int smb347_update_online(struct smb347_charger *smb) | ||
355 | { | ||
356 | int ret; | ||
357 | |||
358 | /* | ||
359 | * Depending on whether valid power source is connected or not, we | ||
360 | * disable or enable the charging. We do it manually because it | ||
361 | * depends on how the platform has configured the valid inputs. | ||
362 | */ | ||
363 | if (smb347_is_online(smb)) { | ||
364 | ret = smb347_charging_enable(smb); | ||
365 | if (ret < 0) | ||
366 | dev_err(&smb->client->dev, | ||
367 | "failed to enable charging\n"); | ||
368 | } else { | ||
369 | ret = smb347_charging_disable(smb); | ||
370 | if (ret < 0) | ||
371 | dev_err(&smb->client->dev, | ||
372 | "failed to disable charging\n"); | ||
373 | } | ||
374 | |||
375 | return ret; | ||
376 | } | ||
377 | |||
378 | static int smb347_set_charge_current(struct smb347_charger *smb) | ||
379 | { | ||
380 | int ret, val; | ||
381 | |||
382 | ret = smb347_read(smb, CFG_CHARGE_CURRENT); | ||
383 | if (ret < 0) | ||
384 | return ret; | ||
385 | |||
386 | if (smb->pdata->max_charge_current) { | ||
387 | val = current_to_hw(fcc_tbl, ARRAY_SIZE(fcc_tbl), | ||
388 | smb->pdata->max_charge_current); | ||
389 | if (val < 0) | ||
390 | return val; | ||
391 | |||
392 | ret &= ~CFG_CHARGE_CURRENT_FCC_MASK; | ||
393 | ret |= val << CFG_CHARGE_CURRENT_FCC_SHIFT; | ||
394 | } | ||
395 | |||
396 | if (smb->pdata->pre_charge_current) { | ||
397 | val = current_to_hw(pcc_tbl, ARRAY_SIZE(pcc_tbl), | ||
398 | smb->pdata->pre_charge_current); | ||
399 | if (val < 0) | ||
400 | return val; | ||
401 | |||
402 | ret &= ~CFG_CHARGE_CURRENT_PCC_MASK; | ||
403 | ret |= val << CFG_CHARGE_CURRENT_PCC_SHIFT; | ||
404 | } | ||
405 | |||
406 | if (smb->pdata->termination_current) { | ||
407 | val = current_to_hw(tc_tbl, ARRAY_SIZE(tc_tbl), | ||
408 | smb->pdata->termination_current); | ||
409 | if (val < 0) | ||
410 | return val; | ||
411 | |||
412 | ret &= ~CFG_CHARGE_CURRENT_TC_MASK; | ||
413 | ret |= val; | ||
414 | } | ||
415 | |||
416 | return smb347_write(smb, CFG_CHARGE_CURRENT, ret); | ||
417 | } | ||
418 | |||
419 | static int smb347_set_current_limits(struct smb347_charger *smb) | ||
420 | { | ||
421 | int ret, val; | ||
422 | |||
423 | ret = smb347_read(smb, CFG_CURRENT_LIMIT); | ||
424 | if (ret < 0) | ||
425 | return ret; | ||
426 | |||
427 | if (smb->pdata->mains_current_limit) { | ||
428 | val = current_to_hw(icl_tbl, ARRAY_SIZE(icl_tbl), | ||
429 | smb->pdata->mains_current_limit); | ||
430 | if (val < 0) | ||
431 | return val; | ||
432 | |||
433 | ret &= ~CFG_CURRENT_LIMIT_DC_MASK; | ||
434 | ret |= val << CFG_CURRENT_LIMIT_DC_SHIFT; | ||
435 | } | ||
436 | |||
437 | if (smb->pdata->usb_hc_current_limit) { | ||
438 | val = current_to_hw(icl_tbl, ARRAY_SIZE(icl_tbl), | ||
439 | smb->pdata->usb_hc_current_limit); | ||
440 | if (val < 0) | ||
441 | return val; | ||
442 | |||
443 | ret &= ~CFG_CURRENT_LIMIT_USB_MASK; | ||
444 | ret |= val; | ||
445 | } | ||
446 | |||
447 | return smb347_write(smb, CFG_CURRENT_LIMIT, ret); | ||
448 | } | ||
449 | |||
450 | static int smb347_set_voltage_limits(struct smb347_charger *smb) | ||
451 | { | ||
452 | int ret, val; | ||
453 | |||
454 | ret = smb347_read(smb, CFG_FLOAT_VOLTAGE); | ||
455 | if (ret < 0) | ||
456 | return ret; | ||
457 | |||
458 | if (smb->pdata->pre_to_fast_voltage) { | ||
459 | val = smb->pdata->pre_to_fast_voltage; | ||
460 | |||
461 | /* uV */ | ||
462 | val = clamp_val(val, 2400000, 3000000) - 2400000; | ||
463 | val /= 200000; | ||
464 | |||
465 | ret &= ~CFG_FLOAT_VOLTAGE_THRESHOLD_MASK; | ||
466 | ret |= val << CFG_FLOAT_VOLTAGE_THRESHOLD_SHIFT; | ||
467 | } | ||
468 | |||
469 | if (smb->pdata->max_charge_voltage) { | ||
470 | val = smb->pdata->max_charge_voltage; | ||
471 | |||
472 | /* uV */ | ||
473 | val = clamp_val(val, 3500000, 4500000) - 3500000; | ||
474 | val /= 20000; | ||
475 | |||
476 | ret |= val; | ||
477 | } | ||
478 | |||
479 | return smb347_write(smb, CFG_FLOAT_VOLTAGE, ret); | ||
480 | } | ||
481 | |||
482 | static int smb347_set_temp_limits(struct smb347_charger *smb) | ||
483 | { | ||
484 | bool enable_therm_monitor = false; | ||
485 | int ret, val; | ||
486 | |||
487 | if (smb->pdata->chip_temp_threshold) { | ||
488 | val = smb->pdata->chip_temp_threshold; | ||
489 | |||
490 | /* degree C */ | ||
491 | val = clamp_val(val, 100, 130) - 100; | ||
492 | val /= 10; | ||
493 | |||
494 | ret = smb347_read(smb, CFG_OTG); | ||
495 | if (ret < 0) | ||
496 | return ret; | ||
497 | |||
498 | ret &= ~CFG_OTG_TEMP_THRESHOLD_MASK; | ||
499 | ret |= val << CFG_OTG_TEMP_THRESHOLD_SHIFT; | ||
500 | |||
501 | ret = smb347_write(smb, CFG_OTG, ret); | ||
502 | if (ret < 0) | ||
503 | return ret; | ||
504 | } | ||
505 | |||
506 | ret = smb347_read(smb, CFG_TEMP_LIMIT); | ||
507 | if (ret < 0) | ||
508 | return ret; | ||
509 | |||
510 | if (smb->pdata->soft_cold_temp_limit != SMB347_TEMP_USE_DEFAULT) { | ||
511 | val = smb->pdata->soft_cold_temp_limit; | ||
512 | |||
513 | val = clamp_val(val, 0, 15); | ||
514 | val /= 5; | ||
515 | /* this goes from higher to lower so invert the value */ | ||
516 | val = ~val & 0x3; | ||
517 | |||
518 | ret &= ~CFG_TEMP_LIMIT_SOFT_COLD_MASK; | ||
519 | ret |= val << CFG_TEMP_LIMIT_SOFT_COLD_SHIFT; | ||
520 | |||
521 | enable_therm_monitor = true; | ||
522 | } | ||
523 | |||
524 | if (smb->pdata->soft_hot_temp_limit != SMB347_TEMP_USE_DEFAULT) { | ||
525 | val = smb->pdata->soft_hot_temp_limit; | ||
526 | |||
527 | val = clamp_val(val, 40, 55) - 40; | ||
528 | val /= 5; | ||
529 | |||
530 | ret &= ~CFG_TEMP_LIMIT_SOFT_HOT_MASK; | ||
531 | ret |= val << CFG_TEMP_LIMIT_SOFT_HOT_SHIFT; | ||
532 | |||
533 | enable_therm_monitor = true; | ||
534 | } | ||
535 | |||
536 | if (smb->pdata->hard_cold_temp_limit != SMB347_TEMP_USE_DEFAULT) { | ||
537 | val = smb->pdata->hard_cold_temp_limit; | ||
538 | |||
539 | val = clamp_val(val, -5, 10) + 5; | ||
540 | val /= 5; | ||
541 | /* this goes from higher to lower so invert the value */ | ||
542 | val = ~val & 0x3; | ||
543 | |||
544 | ret &= ~CFG_TEMP_LIMIT_HARD_COLD_MASK; | ||
545 | ret |= val << CFG_TEMP_LIMIT_HARD_COLD_SHIFT; | ||
546 | |||
547 | enable_therm_monitor = true; | ||
548 | } | ||
549 | |||
550 | if (smb->pdata->hard_hot_temp_limit != SMB347_TEMP_USE_DEFAULT) { | ||
551 | val = smb->pdata->hard_hot_temp_limit; | ||
552 | |||
553 | val = clamp_val(val, 50, 65) - 50; | ||
554 | val /= 5; | ||
555 | |||
556 | ret &= ~CFG_TEMP_LIMIT_HARD_HOT_MASK; | ||
557 | ret |= val << CFG_TEMP_LIMIT_HARD_HOT_SHIFT; | ||
558 | |||
559 | enable_therm_monitor = true; | ||
560 | } | ||
561 | |||
562 | ret = smb347_write(smb, CFG_TEMP_LIMIT, ret); | ||
563 | if (ret < 0) | ||
564 | return ret; | ||
565 | |||
566 | /* | ||
567 | * If any of the temperature limits are set, we also enable the | ||
568 | * thermistor monitoring. | ||
569 | * | ||
570 | * When soft limits are hit, the device will start to compensate | ||
571 | * current and/or voltage depending on the configuration. | ||
572 | * | ||
573 | * When hard limit is hit, the device will suspend charging | ||
574 | * depending on the configuration. | ||
575 | */ | ||
576 | if (enable_therm_monitor) { | ||
577 | ret = smb347_read(smb, CFG_THERM); | ||
578 | if (ret < 0) | ||
579 | return ret; | ||
580 | |||
581 | ret &= ~CFG_THERM_MONITOR_DISABLED; | ||
582 | |||
583 | ret = smb347_write(smb, CFG_THERM, ret); | ||
584 | if (ret < 0) | ||
585 | return ret; | ||
586 | } | ||
587 | |||
588 | if (smb->pdata->suspend_on_hard_temp_limit) { | ||
589 | ret = smb347_read(smb, CFG_SYSOK); | ||
590 | if (ret < 0) | ||
591 | return ret; | ||
592 | |||
593 | ret &= ~CFG_SYSOK_SUSPEND_HARD_LIMIT_DISABLED; | ||
594 | |||
595 | ret = smb347_write(smb, CFG_SYSOK, ret); | ||
596 | if (ret < 0) | ||
597 | return ret; | ||
598 | } | ||
599 | |||
600 | if (smb->pdata->soft_temp_limit_compensation != | ||
601 | SMB347_SOFT_TEMP_COMPENSATE_DEFAULT) { | ||
602 | val = smb->pdata->soft_temp_limit_compensation & 0x3; | ||
603 | |||
604 | ret = smb347_read(smb, CFG_THERM); | ||
605 | if (ret < 0) | ||
606 | return ret; | ||
607 | |||
608 | ret &= ~CFG_THERM_SOFT_HOT_COMPENSATION_MASK; | ||
609 | ret |= val << CFG_THERM_SOFT_HOT_COMPENSATION_SHIFT; | ||
610 | |||
611 | ret &= ~CFG_THERM_SOFT_COLD_COMPENSATION_MASK; | ||
612 | ret |= val << CFG_THERM_SOFT_COLD_COMPENSATION_SHIFT; | ||
613 | |||
614 | ret = smb347_write(smb, CFG_THERM, ret); | ||
615 | if (ret < 0) | ||
616 | return ret; | ||
617 | } | ||
618 | |||
619 | if (smb->pdata->charge_current_compensation) { | ||
620 | val = current_to_hw(ccc_tbl, ARRAY_SIZE(ccc_tbl), | ||
621 | smb->pdata->charge_current_compensation); | ||
622 | if (val < 0) | ||
623 | return val; | ||
624 | |||
625 | ret = smb347_read(smb, CFG_OTG); | ||
626 | if (ret < 0) | ||
627 | return ret; | ||
628 | |||
629 | ret &= ~CFG_OTG_CC_COMPENSATION_MASK; | ||
630 | ret |= (val & 0x3) << CFG_OTG_CC_COMPENSATION_SHIFT; | ||
631 | |||
632 | ret = smb347_write(smb, CFG_OTG, ret); | ||
633 | if (ret < 0) | ||
634 | return ret; | ||
635 | } | ||
636 | |||
637 | return ret; | ||
638 | } | ||
639 | |||
640 | /* | ||
641 | * smb347_set_writable - enables/disables writing to non-volatile registers | ||
642 | * @smb: pointer to smb347 charger instance | ||
643 | * | ||
644 | * You can enable/disable writing to the non-volatile configuration | ||
645 | * registers by calling this function. | ||
646 | * | ||
647 | * Returns %0 on success and negative errno in case of failure. | ||
648 | */ | ||
649 | static int smb347_set_writable(struct smb347_charger *smb, bool writable) | ||
650 | { | ||
651 | int ret; | ||
652 | |||
653 | ret = smb347_read(smb, CMD_A); | ||
654 | if (ret < 0) | ||
655 | return ret; | ||
656 | |||
657 | if (writable) | ||
658 | ret |= CMD_A_ALLOW_WRITE; | ||
659 | else | ||
660 | ret &= ~CMD_A_ALLOW_WRITE; | ||
661 | |||
662 | return smb347_write(smb, CMD_A, ret); | ||
663 | } | ||
664 | |||
665 | static int smb347_hw_init(struct smb347_charger *smb) | ||
666 | { | ||
667 | int ret; | ||
668 | |||
669 | ret = smb347_set_writable(smb, true); | ||
670 | if (ret < 0) | ||
671 | return ret; | ||
672 | |||
673 | /* | ||
674 | * Program the platform specific configuration values to the device | ||
675 | * first. | ||
676 | */ | ||
677 | ret = smb347_set_charge_current(smb); | ||
678 | if (ret < 0) | ||
679 | goto fail; | ||
680 | |||
681 | ret = smb347_set_current_limits(smb); | ||
682 | if (ret < 0) | ||
683 | goto fail; | ||
684 | |||
685 | ret = smb347_set_voltage_limits(smb); | ||
686 | if (ret < 0) | ||
687 | goto fail; | ||
688 | |||
689 | ret = smb347_set_temp_limits(smb); | ||
690 | if (ret < 0) | ||
691 | goto fail; | ||
692 | |||
693 | /* If USB charging is disabled we put the USB in suspend mode */ | ||
694 | if (!smb->pdata->use_usb) { | ||
695 | ret = smb347_read(smb, CMD_A); | ||
696 | if (ret < 0) | ||
697 | goto fail; | ||
698 | |||
699 | ret |= CMD_A_SUSPEND_ENABLED; | ||
700 | |||
701 | ret = smb347_write(smb, CMD_A, ret); | ||
702 | if (ret < 0) | ||
703 | goto fail; | ||
704 | } | ||
705 | |||
706 | ret = smb347_read(smb, CFG_OTHER); | ||
707 | if (ret < 0) | ||
708 | goto fail; | ||
709 | |||
710 | /* | ||
711 | * If configured by platform data, we enable hardware Auto-OTG | ||
712 | * support for driving VBUS. Otherwise we disable it. | ||
713 | */ | ||
714 | ret &= ~CFG_OTHER_RID_MASK; | ||
715 | if (smb->pdata->use_usb_otg) | ||
716 | ret |= CFG_OTHER_RID_ENABLED_AUTO_OTG; | ||
717 | |||
718 | ret = smb347_write(smb, CFG_OTHER, ret); | ||
719 | if (ret < 0) | ||
720 | goto fail; | ||
721 | |||
722 | ret = smb347_read(smb, CFG_PIN); | ||
723 | if (ret < 0) | ||
724 | goto fail; | ||
725 | |||
726 | /* | ||
727 | * Make the charging functionality controllable by a write to the | ||
728 | * command register unless pin control is specified in the platform | ||
729 | * data. | ||
730 | */ | ||
731 | ret &= ~CFG_PIN_EN_CTRL_MASK; | ||
732 | |||
733 | switch (smb->pdata->enable_control) { | ||
734 | case SMB347_CHG_ENABLE_SW: | ||
735 | /* Do nothing, 0 means i2c control */ | ||
736 | break; | ||
737 | case SMB347_CHG_ENABLE_PIN_ACTIVE_LOW: | ||
738 | ret |= CFG_PIN_EN_CTRL_ACTIVE_LOW; | ||
739 | break; | ||
740 | case SMB347_CHG_ENABLE_PIN_ACTIVE_HIGH: | ||
741 | ret |= CFG_PIN_EN_CTRL_ACTIVE_HIGH; | ||
742 | break; | ||
743 | } | ||
744 | |||
745 | /* Disable Automatic Power Source Detection (APSD) interrupt. */ | ||
746 | ret &= ~CFG_PIN_EN_APSD_IRQ; | ||
747 | |||
748 | ret = smb347_write(smb, CFG_PIN, ret); | ||
749 | if (ret < 0) | ||
750 | goto fail; | ||
751 | |||
752 | ret = smb347_update_status(smb); | ||
753 | if (ret < 0) | ||
754 | goto fail; | ||
755 | |||
756 | ret = smb347_update_online(smb); | ||
757 | |||
758 | fail: | ||
759 | smb347_set_writable(smb, false); | ||
760 | return ret; | ||
761 | } | ||
762 | |||
763 | static irqreturn_t smb347_interrupt(int irq, void *data) | ||
764 | { | ||
765 | struct smb347_charger *smb = data; | ||
766 | int stat_c, irqstat_e, irqstat_c; | ||
767 | irqreturn_t ret = IRQ_NONE; | ||
768 | |||
769 | stat_c = smb347_read(smb, STAT_C); | ||
770 | if (stat_c < 0) { | ||
771 | dev_warn(&smb->client->dev, "reading STAT_C failed\n"); | ||
772 | return IRQ_NONE; | ||
773 | } | ||
774 | |||
775 | irqstat_c = smb347_read(smb, IRQSTAT_C); | ||
776 | if (irqstat_c < 0) { | ||
777 | dev_warn(&smb->client->dev, "reading IRQSTAT_C failed\n"); | ||
778 | return IRQ_NONE; | ||
779 | } | ||
780 | |||
781 | irqstat_e = smb347_read(smb, IRQSTAT_E); | ||
782 | if (irqstat_e < 0) { | ||
783 | dev_warn(&smb->client->dev, "reading IRQSTAT_E failed\n"); | ||
784 | return IRQ_NONE; | ||
785 | } | ||
786 | |||
787 | /* | ||
788 | * If we get charger error we report the error back to user and | ||
789 | * disable charging. | ||
790 | */ | ||
791 | if (stat_c & STAT_C_CHARGER_ERROR) { | ||
792 | dev_err(&smb->client->dev, | ||
793 | "error in charger, disabling charging\n"); | ||
794 | |||
795 | smb347_charging_disable(smb); | ||
796 | power_supply_changed(&smb->battery); | ||
797 | |||
798 | ret = IRQ_HANDLED; | ||
799 | } | ||
800 | |||
801 | /* | ||
802 | * If we reached the termination current the battery is charged and | ||
803 | * we can update the status now. Charging is automatically | ||
804 | * disabled by the hardware. | ||
805 | */ | ||
806 | if (irqstat_c & (IRQSTAT_C_TERMINATION_IRQ | IRQSTAT_C_TAPER_IRQ)) { | ||
807 | if (irqstat_c & IRQSTAT_C_TERMINATION_STAT) | ||
808 | power_supply_changed(&smb->battery); | ||
809 | ret = IRQ_HANDLED; | ||
810 | } | ||
811 | |||
812 | /* | ||
813 | * If we got an under voltage interrupt it means that AC/USB input | ||
814 | * was connected or disconnected. | ||
815 | */ | ||
816 | if (irqstat_e & (IRQSTAT_E_USBIN_UV_IRQ | IRQSTAT_E_DCIN_UV_IRQ)) { | ||
817 | if (smb347_update_status(smb) > 0) { | ||
818 | smb347_update_online(smb); | ||
819 | power_supply_changed(&smb->mains); | ||
820 | power_supply_changed(&smb->usb); | ||
821 | } | ||
822 | ret = IRQ_HANDLED; | ||
823 | } | ||
824 | |||
825 | return ret; | ||
826 | } | ||
827 | |||
828 | static int smb347_irq_set(struct smb347_charger *smb, bool enable) | ||
829 | { | ||
830 | int ret; | ||
831 | |||
832 | ret = smb347_set_writable(smb, true); | ||
833 | if (ret < 0) | ||
834 | return ret; | ||
835 | |||
836 | /* | ||
837 | * Enable/disable interrupts for: | ||
838 | * - under voltage | ||
839 | * - termination current reached | ||
840 | * - charger error | ||
841 | */ | ||
842 | if (enable) { | ||
843 | ret = smb347_write(smb, CFG_FAULT_IRQ, CFG_FAULT_IRQ_DCIN_UV); | ||
844 | if (ret < 0) | ||
845 | goto fail; | ||
846 | |||
847 | ret = smb347_write(smb, CFG_STATUS_IRQ, | ||
848 | CFG_STATUS_IRQ_TERMINATION_OR_TAPER); | ||
849 | if (ret < 0) | ||
850 | goto fail; | ||
851 | |||
852 | ret = smb347_read(smb, CFG_PIN); | ||
853 | if (ret < 0) | ||
854 | goto fail; | ||
855 | |||
856 | ret |= CFG_PIN_EN_CHARGER_ERROR; | ||
857 | |||
858 | ret = smb347_write(smb, CFG_PIN, ret); | ||
859 | } else { | ||
860 | ret = smb347_write(smb, CFG_FAULT_IRQ, 0); | ||
861 | if (ret < 0) | ||
862 | goto fail; | ||
863 | |||
864 | ret = smb347_write(smb, CFG_STATUS_IRQ, 0); | ||
865 | if (ret < 0) | ||
866 | goto fail; | ||
867 | |||
868 | ret = smb347_read(smb, CFG_PIN); | ||
869 | if (ret < 0) | ||
870 | goto fail; | ||
871 | |||
872 | ret &= ~CFG_PIN_EN_CHARGER_ERROR; | ||
873 | |||
874 | ret = smb347_write(smb, CFG_PIN, ret); | ||
875 | } | ||
876 | |||
877 | fail: | ||
878 | smb347_set_writable(smb, false); | ||
879 | return ret; | ||
880 | } | ||
881 | |||
882 | static inline int smb347_irq_enable(struct smb347_charger *smb) | ||
883 | { | ||
884 | return smb347_irq_set(smb, true); | ||
885 | } | ||
886 | |||
887 | static inline int smb347_irq_disable(struct smb347_charger *smb) | ||
888 | { | ||
889 | return smb347_irq_set(smb, false); | ||
890 | } | ||
891 | |||
892 | static int smb347_irq_init(struct smb347_charger *smb) | ||
893 | { | ||
894 | const struct smb347_charger_platform_data *pdata = smb->pdata; | ||
895 | int ret, irq = gpio_to_irq(pdata->irq_gpio); | ||
896 | |||
897 | ret = gpio_request_one(pdata->irq_gpio, GPIOF_IN, smb->client->name); | ||
898 | if (ret < 0) | ||
899 | goto fail; | ||
900 | |||
901 | ret = request_threaded_irq(irq, NULL, smb347_interrupt, | ||
902 | IRQF_TRIGGER_FALLING, smb->client->name, | ||
903 | smb); | ||
904 | if (ret < 0) | ||
905 | goto fail_gpio; | ||
906 | |||
907 | ret = smb347_set_writable(smb, true); | ||
908 | if (ret < 0) | ||
909 | goto fail_irq; | ||
910 | |||
911 | /* | ||
912 | * Configure the STAT output to be suitable for interrupts: disable | ||
913 | * all other output (except interrupts) and make it active low. | ||
914 | */ | ||
915 | ret = smb347_read(smb, CFG_STAT); | ||
916 | if (ret < 0) | ||
917 | goto fail_readonly; | ||
918 | |||
919 | ret &= ~CFG_STAT_ACTIVE_HIGH; | ||
920 | ret |= CFG_STAT_DISABLED; | ||
921 | |||
922 | ret = smb347_write(smb, CFG_STAT, ret); | ||
923 | if (ret < 0) | ||
924 | goto fail_readonly; | ||
925 | |||
926 | ret = smb347_irq_enable(smb); | ||
927 | if (ret < 0) | ||
928 | goto fail_readonly; | ||
929 | |||
930 | smb347_set_writable(smb, false); | ||
931 | smb->client->irq = irq; | ||
932 | return 0; | ||
933 | |||
934 | fail_readonly: | ||
935 | smb347_set_writable(smb, false); | ||
936 | fail_irq: | ||
937 | free_irq(irq, smb); | ||
938 | fail_gpio: | ||
939 | gpio_free(pdata->irq_gpio); | ||
940 | fail: | ||
941 | smb->client->irq = 0; | ||
942 | return ret; | ||
943 | } | ||
944 | |||
945 | static int smb347_mains_get_property(struct power_supply *psy, | ||
946 | enum power_supply_property prop, | ||
947 | union power_supply_propval *val) | ||
948 | { | ||
949 | struct smb347_charger *smb = | ||
950 | container_of(psy, struct smb347_charger, mains); | ||
951 | |||
952 | if (prop == POWER_SUPPLY_PROP_ONLINE) { | ||
953 | val->intval = smb->mains_online; | ||
954 | return 0; | ||
955 | } | ||
956 | return -EINVAL; | ||
957 | } | ||
958 | |||
959 | static enum power_supply_property smb347_mains_properties[] = { | ||
960 | POWER_SUPPLY_PROP_ONLINE, | ||
961 | }; | ||
962 | |||
963 | static int smb347_usb_get_property(struct power_supply *psy, | ||
964 | enum power_supply_property prop, | ||
965 | union power_supply_propval *val) | ||
966 | { | ||
967 | struct smb347_charger *smb = | ||
968 | container_of(psy, struct smb347_charger, usb); | ||
969 | |||
970 | if (prop == POWER_SUPPLY_PROP_ONLINE) { | ||
971 | val->intval = smb->usb_online; | ||
972 | return 0; | ||
973 | } | ||
974 | return -EINVAL; | ||
975 | } | ||
976 | |||
977 | static enum power_supply_property smb347_usb_properties[] = { | ||
978 | POWER_SUPPLY_PROP_ONLINE, | ||
979 | }; | ||
980 | |||
981 | static int smb347_battery_get_property(struct power_supply *psy, | ||
982 | enum power_supply_property prop, | ||
983 | union power_supply_propval *val) | ||
984 | { | ||
985 | struct smb347_charger *smb = | ||
986 | container_of(psy, struct smb347_charger, battery); | ||
987 | const struct smb347_charger_platform_data *pdata = smb->pdata; | ||
988 | int ret; | ||
989 | |||
990 | ret = smb347_update_status(smb); | ||
991 | if (ret < 0) | ||
992 | return ret; | ||
993 | |||
994 | switch (prop) { | ||
995 | case POWER_SUPPLY_PROP_STATUS: | ||
996 | if (!smb347_is_online(smb)) { | ||
997 | val->intval = POWER_SUPPLY_STATUS_DISCHARGING; | ||
998 | break; | ||
999 | } | ||
1000 | if (smb347_charging_status(smb)) | ||
1001 | val->intval = POWER_SUPPLY_STATUS_CHARGING; | ||
1002 | else | ||
1003 | val->intval = POWER_SUPPLY_STATUS_FULL; | ||
1004 | break; | ||
1005 | |||
1006 | case POWER_SUPPLY_PROP_CHARGE_TYPE: | ||
1007 | if (!smb347_is_online(smb)) | ||
1008 | return -ENODATA; | ||
1009 | |||
1010 | /* | ||
1011 | * We handle trickle and pre-charging the same, and taper | ||
1012 | * and none the same. | ||
1013 | */ | ||
1014 | switch (smb347_charging_status(smb)) { | ||
1015 | case 1: | ||
1016 | val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; | ||
1017 | break; | ||
1018 | case 2: | ||
1019 | val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST; | ||
1020 | break; | ||
1021 | default: | ||
1022 | val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE; | ||
1023 | break; | ||
1024 | } | ||
1025 | break; | ||
1026 | |||
1027 | case POWER_SUPPLY_PROP_TECHNOLOGY: | ||
1028 | val->intval = pdata->battery_info.technology; | ||
1029 | break; | ||
1030 | |||
1031 | case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: | ||
1032 | val->intval = pdata->battery_info.voltage_min_design; | ||
1033 | break; | ||
1034 | |||
1035 | case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: | ||
1036 | val->intval = pdata->battery_info.voltage_max_design; | ||
1037 | break; | ||
1038 | |||
1039 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: | ||
1040 | if (!smb347_is_online(smb)) | ||
1041 | return -ENODATA; | ||
1042 | ret = smb347_read(smb, STAT_A); | ||
1043 | if (ret < 0) | ||
1044 | return ret; | ||
1045 | |||
1046 | ret &= STAT_A_FLOAT_VOLTAGE_MASK; | ||
1047 | if (ret > 0x3d) | ||
1048 | ret = 0x3d; | ||
1049 | |||
1050 | val->intval = 3500000 + ret * 20000; | ||
1051 | break; | ||
1052 | |||
1053 | case POWER_SUPPLY_PROP_CURRENT_NOW: | ||
1054 | if (!smb347_is_online(smb)) | ||
1055 | return -ENODATA; | ||
1056 | |||
1057 | ret = smb347_read(smb, STAT_B); | ||
1058 | if (ret < 0) | ||
1059 | return ret; | ||
1060 | |||
1061 | /* | ||
1062 | * The current value is composition of FCC and PCC values | ||
1063 | * and we can detect which table to use from bit 5. | ||
1064 | */ | ||
1065 | if (ret & 0x20) { | ||
1066 | val->intval = hw_to_current(fcc_tbl, | ||
1067 | ARRAY_SIZE(fcc_tbl), | ||
1068 | ret & 7); | ||
1069 | } else { | ||
1070 | ret >>= 3; | ||
1071 | val->intval = hw_to_current(pcc_tbl, | ||
1072 | ARRAY_SIZE(pcc_tbl), | ||
1073 | ret & 7); | ||
1074 | } | ||
1075 | break; | ||
1076 | |||
1077 | case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: | ||
1078 | val->intval = pdata->battery_info.charge_full_design; | ||
1079 | break; | ||
1080 | |||
1081 | case POWER_SUPPLY_PROP_MODEL_NAME: | ||
1082 | val->strval = pdata->battery_info.name; | ||
1083 | break; | ||
1084 | |||
1085 | default: | ||
1086 | return -EINVAL; | ||
1087 | } | ||
1088 | |||
1089 | return 0; | ||
1090 | } | ||
1091 | |||
1092 | static enum power_supply_property smb347_battery_properties[] = { | ||
1093 | POWER_SUPPLY_PROP_STATUS, | ||
1094 | POWER_SUPPLY_PROP_CHARGE_TYPE, | ||
1095 | POWER_SUPPLY_PROP_TECHNOLOGY, | ||
1096 | POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, | ||
1097 | POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, | ||
1098 | POWER_SUPPLY_PROP_VOLTAGE_NOW, | ||
1099 | POWER_SUPPLY_PROP_CURRENT_NOW, | ||
1100 | POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, | ||
1101 | POWER_SUPPLY_PROP_MODEL_NAME, | ||
1102 | }; | ||
1103 | |||
1104 | static int smb347_debugfs_show(struct seq_file *s, void *data) | ||
1105 | { | ||
1106 | struct smb347_charger *smb = s->private; | ||
1107 | int ret; | ||
1108 | u8 reg; | ||
1109 | |||
1110 | seq_printf(s, "Control registers:\n"); | ||
1111 | seq_printf(s, "==================\n"); | ||
1112 | for (reg = CFG_CHARGE_CURRENT; reg <= CFG_ADDRESS; reg++) { | ||
1113 | ret = smb347_read(smb, reg); | ||
1114 | seq_printf(s, "0x%02x:\t0x%02x\n", reg, ret); | ||
1115 | } | ||
1116 | seq_printf(s, "\n"); | ||
1117 | |||
1118 | seq_printf(s, "Command registers:\n"); | ||
1119 | seq_printf(s, "==================\n"); | ||
1120 | ret = smb347_read(smb, CMD_A); | ||
1121 | seq_printf(s, "0x%02x:\t0x%02x\n", CMD_A, ret); | ||
1122 | ret = smb347_read(smb, CMD_B); | ||
1123 | seq_printf(s, "0x%02x:\t0x%02x\n", CMD_B, ret); | ||
1124 | ret = smb347_read(smb, CMD_C); | ||
1125 | seq_printf(s, "0x%02x:\t0x%02x\n", CMD_C, ret); | ||
1126 | seq_printf(s, "\n"); | ||
1127 | |||
1128 | seq_printf(s, "Interrupt status registers:\n"); | ||
1129 | seq_printf(s, "===========================\n"); | ||
1130 | for (reg = IRQSTAT_A; reg <= IRQSTAT_F; reg++) { | ||
1131 | ret = smb347_read(smb, reg); | ||
1132 | seq_printf(s, "0x%02x:\t0x%02x\n", reg, ret); | ||
1133 | } | ||
1134 | seq_printf(s, "\n"); | ||
1135 | |||
1136 | seq_printf(s, "Status registers:\n"); | ||
1137 | seq_printf(s, "=================\n"); | ||
1138 | for (reg = STAT_A; reg <= STAT_E; reg++) { | ||
1139 | ret = smb347_read(smb, reg); | ||
1140 | seq_printf(s, "0x%02x:\t0x%02x\n", reg, ret); | ||
1141 | } | ||
1142 | |||
1143 | return 0; | ||
1144 | } | ||
1145 | |||
1146 | static int smb347_debugfs_open(struct inode *inode, struct file *file) | ||
1147 | { | ||
1148 | return single_open(file, smb347_debugfs_show, inode->i_private); | ||
1149 | } | ||
1150 | |||
1151 | static const struct file_operations smb347_debugfs_fops = { | ||
1152 | .open = smb347_debugfs_open, | ||
1153 | .read = seq_read, | ||
1154 | .llseek = seq_lseek, | ||
1155 | .release = single_release, | ||
1156 | }; | ||
1157 | |||
1158 | static int smb347_probe(struct i2c_client *client, | ||
1159 | const struct i2c_device_id *id) | ||
1160 | { | ||
1161 | static char *battery[] = { "smb347-battery" }; | ||
1162 | const struct smb347_charger_platform_data *pdata; | ||
1163 | struct device *dev = &client->dev; | ||
1164 | struct smb347_charger *smb; | ||
1165 | int ret; | ||
1166 | |||
1167 | pdata = dev->platform_data; | ||
1168 | if (!pdata) | ||
1169 | return -EINVAL; | ||
1170 | |||
1171 | if (!pdata->use_mains && !pdata->use_usb) | ||
1172 | return -EINVAL; | ||
1173 | |||
1174 | smb = devm_kzalloc(dev, sizeof(*smb), GFP_KERNEL); | ||
1175 | if (!smb) | ||
1176 | return -ENOMEM; | ||
1177 | |||
1178 | i2c_set_clientdata(client, smb); | ||
1179 | |||
1180 | mutex_init(&smb->lock); | ||
1181 | smb->client = client; | ||
1182 | smb->pdata = pdata; | ||
1183 | |||
1184 | ret = smb347_hw_init(smb); | ||
1185 | if (ret < 0) | ||
1186 | return ret; | ||
1187 | |||
1188 | smb->mains.name = "smb347-mains"; | ||
1189 | smb->mains.type = POWER_SUPPLY_TYPE_MAINS; | ||
1190 | smb->mains.get_property = smb347_mains_get_property; | ||
1191 | smb->mains.properties = smb347_mains_properties; | ||
1192 | smb->mains.num_properties = ARRAY_SIZE(smb347_mains_properties); | ||
1193 | smb->mains.supplied_to = battery; | ||
1194 | smb->mains.num_supplicants = ARRAY_SIZE(battery); | ||
1195 | |||
1196 | smb->usb.name = "smb347-usb"; | ||
1197 | smb->usb.type = POWER_SUPPLY_TYPE_USB; | ||
1198 | smb->usb.get_property = smb347_usb_get_property; | ||
1199 | smb->usb.properties = smb347_usb_properties; | ||
1200 | smb->usb.num_properties = ARRAY_SIZE(smb347_usb_properties); | ||
1201 | smb->usb.supplied_to = battery; | ||
1202 | smb->usb.num_supplicants = ARRAY_SIZE(battery); | ||
1203 | |||
1204 | smb->battery.name = "smb347-battery"; | ||
1205 | smb->battery.type = POWER_SUPPLY_TYPE_BATTERY; | ||
1206 | smb->battery.get_property = smb347_battery_get_property; | ||
1207 | smb->battery.properties = smb347_battery_properties; | ||
1208 | smb->battery.num_properties = ARRAY_SIZE(smb347_battery_properties); | ||
1209 | |||
1210 | ret = power_supply_register(dev, &smb->mains); | ||
1211 | if (ret < 0) | ||
1212 | return ret; | ||
1213 | |||
1214 | ret = power_supply_register(dev, &smb->usb); | ||
1215 | if (ret < 0) { | ||
1216 | power_supply_unregister(&smb->mains); | ||
1217 | return ret; | ||
1218 | } | ||
1219 | |||
1220 | ret = power_supply_register(dev, &smb->battery); | ||
1221 | if (ret < 0) { | ||
1222 | power_supply_unregister(&smb->usb); | ||
1223 | power_supply_unregister(&smb->mains); | ||
1224 | return ret; | ||
1225 | } | ||
1226 | |||
1227 | /* | ||
1228 | * Interrupt pin is optional. If it is connected, we setup the | ||
1229 | * interrupt support here. | ||
1230 | */ | ||
1231 | if (pdata->irq_gpio >= 0) { | ||
1232 | ret = smb347_irq_init(smb); | ||
1233 | if (ret < 0) { | ||
1234 | dev_warn(dev, "failed to initialize IRQ: %d\n", ret); | ||
1235 | dev_warn(dev, "disabling IRQ support\n"); | ||
1236 | } | ||
1237 | } | ||
1238 | |||
1239 | smb->dentry = debugfs_create_file("smb347-regs", S_IRUSR, NULL, smb, | ||
1240 | &smb347_debugfs_fops); | ||
1241 | return 0; | ||
1242 | } | ||
1243 | |||
1244 | static int smb347_remove(struct i2c_client *client) | ||
1245 | { | ||
1246 | struct smb347_charger *smb = i2c_get_clientdata(client); | ||
1247 | |||
1248 | if (!IS_ERR_OR_NULL(smb->dentry)) | ||
1249 | debugfs_remove(smb->dentry); | ||
1250 | |||
1251 | if (client->irq) { | ||
1252 | smb347_irq_disable(smb); | ||
1253 | free_irq(client->irq, smb); | ||
1254 | gpio_free(smb->pdata->irq_gpio); | ||
1255 | } | ||
1256 | |||
1257 | power_supply_unregister(&smb->battery); | ||
1258 | power_supply_unregister(&smb->usb); | ||
1259 | power_supply_unregister(&smb->mains); | ||
1260 | return 0; | ||
1261 | } | ||
1262 | |||
1263 | static const struct i2c_device_id smb347_id[] = { | ||
1264 | { "smb347", 0 }, | ||
1265 | { } | ||
1266 | }; | ||
1267 | MODULE_DEVICE_TABLE(i2c, smb347_id); | ||
1268 | |||
1269 | static struct i2c_driver smb347_driver = { | ||
1270 | .driver = { | ||
1271 | .name = "smb347", | ||
1272 | }, | ||
1273 | .probe = smb347_probe, | ||
1274 | .remove = __devexit_p(smb347_remove), | ||
1275 | .id_table = smb347_id, | ||
1276 | }; | ||
1277 | |||
1278 | static int __init smb347_init(void) | ||
1279 | { | ||
1280 | return i2c_add_driver(&smb347_driver); | ||
1281 | } | ||
1282 | module_init(smb347_init); | ||
1283 | |||
1284 | static void __exit smb347_exit(void) | ||
1285 | { | ||
1286 | i2c_del_driver(&smb347_driver); | ||
1287 | } | ||
1288 | module_exit(smb347_exit); | ||
1289 | |||
1290 | MODULE_AUTHOR("Bruce E. Robertson <bruce.e.robertson@intel.com>"); | ||
1291 | MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>"); | ||
1292 | MODULE_DESCRIPTION("SMB347 battery charger driver"); | ||
1293 | MODULE_LICENSE("GPL"); | ||
1294 | MODULE_ALIAS("i2c:smb347"); | ||