blob: 12875477c74ba699fc08102f26545b1060162874 [file] [log] [blame]
Duy Truong790f06d2013-02-13 16:38:12 -08001/* Copyright (c) 2010, The Linux Foundation. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 */
13
14#include <linux/module.h>
15#include <linux/platform_device.h>
16#include <linux/errno.h>
17#include <linux/mfd/pmic8058.h>
18#include <linux/interrupt.h>
19#include <linux/delay.h>
20#include <linux/bitops.h>
21#include <linux/workqueue.h>
22#include <linux/debugfs.h>
23#include <linux/slab.h>
24
25#include <mach/msm_xo.h>
26#include <mach/msm_hsusb.h>
27
28/* Config Regs and their bits*/
29#define PM8058_CHG_TEST 0x75
30#define IGNORE_LL 2
31
32#define PM8058_CHG_TEST_2 0xEA
33#define PM8058_CHG_TEST_3 0xEB
34#define PM8058_OVP_TEST_REG 0xF6
35#define FORCE_OVP_OFF 3
36
37#define PM8058_CHG_CNTRL 0x1E
38#define CHG_TRICKLE_EN 7
39#define CHG_USB_SUSPEND 6
40#define CHG_IMON_CAL 5
41#define CHG_IMON_GAIN 4
42#define CHG_VBUS_FROM_BOOST_OVRD 2
43#define CHG_CHARGE_DIS 1
44#define CHG_VCP_EN 0
45
46#define PM8058_CHG_CNTRL_2 0xD8
47#define ATC_DIS 7 /* coincell backed */
48#define CHARGE_AUTO_DIS 6
49#define DUMB_CHG_OVRD 5 /* coincell backed */
50#define ENUM_DONE 4
51#define CHG_TEMP_MODE 3
52#define CHG_BATT_TEMP_DIS 1 /* coincell backed */
53#define CHG_FAILED_CLEAR 0
54
55#define PM8058_CHG_VMAX_SEL 0x21
56#define PM8058_CHG_VBAT_DET 0xD9
57#define PM8058_CHG_IMAX 0x1F
58#define PM8058_CHG_TRICKLE 0xDB
59#define PM8058_CHG_ITERM 0xDC
60#define PM8058_CHG_TTRKL_MAX 0xE1
61#define PM8058_CHG_TCHG_MAX 0xE4
62#define PM8058_CHG_TEMP_THRESH 0xE2
63#define PM8058_CHG_TEMP_REG 0xE3
64#define PM8058_CHG_PULSE 0x22
65
66/* IRQ STATUS and CLEAR */
67#define PM8058_CHG_STATUS_CLEAR_IRQ_1 0x31
68#define PM8058_CHG_STATUS_CLEAR_IRQ_3 0x33
69#define PM8058_CHG_STATUS_CLEAR_IRQ_10 0xB3
70#define PM8058_CHG_STATUS_CLEAR_IRQ_11 0xB4
71
72/* IRQ MASKS */
73#define PM8058_CHG_MASK_IRQ_1 0x38
74
75#define PM8058_CHG_MASK_IRQ_3 0x3A
76#define PM8058_CHG_MASK_IRQ_10 0xBA
77#define PM8058_CHG_MASK_IRQ_11 0xBB
78
79/* IRQ Real time status regs */
80#define PM8058_CHG_STATUS_RT_1 0x3F
81#define STATUS_RTCHGVAL 7
82#define STATUS_RTCHGINVAL 6
83#define STATUS_RTBATT_REPLACE 5
84#define STATUS_RTVBATDET_LOW 4
85#define STATUS_RTCHGILIM 3
86#define STATUS_RTPCTDONE 1
87#define STATUS_RTVCP 0
88#define PM8058_CHG_STATUS_RT_3 0x41
89#define PM8058_CHG_STATUS_RT_10 0xC1
90#define PM8058_CHG_STATUS_RT_11 0xC2
91
92/* VTRIM */
93#define PM8058_CHG_VTRIM 0x1D
94#define PM8058_CHG_VBATDET_TRIM 0x1E
95#define PM8058_CHG_ITRIM 0x1F
96#define PM8058_CHG_TTRIM 0x20
97
98#define AUTO_CHARGING_VMAXSEL 4200
99#define AUTO_CHARGING_FAST_TIME_MAX_MINUTES 512
100#define AUTO_CHARGING_TRICKLE_TIME_MINUTES 30
101#define AUTO_CHARGING_VEOC_ITERM 100
102#define AUTO_CHARGING_IEOC_ITERM 160
103
104#define AUTO_CHARGING_VBATDET 4150
105#define AUTO_CHARGING_VEOC_VBATDET 4100
106#define AUTO_CHARGING_VEOC_TCHG 16
107#define AUTO_CHARGING_VEOC_TCHG_FINAL_CYCLE 32
108#define AUTO_CHARGING_VEOC_BEGIN_TIME_MS 5400000
109
110#define AUTO_CHARGING_VEOC_VBAT_LOW_CHECK_TIME_MS 60000
111#define AUTO_CHARGING_RESUME_CHARGE_DETECTION_COUNTER 5
112
113#define PM8058_CHG_I_STEP_MA 50
114#define PM8058_CHG_I_MIN_MA 50
115#define PM8058_CHG_T_TCHG_SHIFT 2
116#define PM8058_CHG_I_TERM_STEP_MA 10
117#define PM8058_CHG_V_STEP_MV 25
118#define PM8058_CHG_V_MIN_MV 2400
119/*
120 * enum pmic_chg_interrupts: pmic interrupts
121 * @CHGVAL_IRQ: charger V between 3.3 and 7.9
122 * @CHGINVAL_IRQ: charger V outside 3.3 and 7.9
123 * @VBATDET_LOW_IRQ: VBAT < VBATDET
124 * @VCP_IRQ: VDD went below VBAT: BAT_FET is turned on
125 * @CHGILIM_IRQ: mA consumed>IMAXSEL: chgloop draws less mA
126 * @ATC_DONE_IRQ: Auto Trickle done
127 * @ATCFAIL_IRQ: Auto Trickle fail
128 * @AUTO_CHGDONE_IRQ: Auto chg done
129 * @AUTO_CHGFAIL_IRQ: time exceeded w/o reaching term current
130 * @CHGSTATE_IRQ: something happend causing a state change
131 * @FASTCHG_IRQ: trkl charging completed: moving to fastchg
132 * @CHG_END_IRQ: mA has dropped to termination current
133 * @BATTTEMP_IRQ: batt temp is out of range
134 * @CHGHOT_IRQ: the pass device is too hot
135 * @CHGTLIMIT_IRQ: unused
136 * @CHG_GONE_IRQ: charger was removed
137 * @VCPMAJOR_IRQ: vcp major
138 * @VBATDET_IRQ: VBAT >= VBATDET
139 * @BATFET_IRQ: BATFET closed
140 * @BATT_REPLACE_IRQ:
141 * @BATTCONNECT_IRQ:
142 */
143enum pmic_chg_interrupts {
144 CHGVAL_IRQ,
145 CHGINVAL_IRQ,
146 VBATDET_LOW_IRQ,
147 VCP_IRQ,
148 CHGILIM_IRQ,
149 ATC_DONE_IRQ,
150 ATCFAIL_IRQ,
151 AUTO_CHGDONE_IRQ,
152 AUTO_CHGFAIL_IRQ,
153 CHGSTATE_IRQ,
154 FASTCHG_IRQ,
155 CHG_END_IRQ,
156 BATTTEMP_IRQ,
157 CHGHOT_IRQ,
158 CHGTLIMIT_IRQ,
159 CHG_GONE_IRQ,
160 VCPMAJOR_IRQ,
161 VBATDET_IRQ,
162 BATFET_IRQ,
163 BATT_REPLACE_IRQ,
164 BATTCONNECT_IRQ,
165 PMIC_CHG_MAX_INTS
166};
167
168struct pm8058_charger {
169 struct pmic_charger_pdata *pdata;
170 struct pm8058_chip *pm_chip;
171 struct device *dev;
172
173 int pmic_chg_irq[PMIC_CHG_MAX_INTS];
174 DECLARE_BITMAP(enabled_irqs, PMIC_CHG_MAX_INTS);
175
176 struct delayed_work check_vbat_low_work;
177 struct delayed_work veoc_begin_work;
178 int waiting_for_topoff;
179 int waiting_for_veoc;
180 int current_charger_current;
181
182 struct msm_xo_voter *voter;
183 struct dentry *dent;
184};
185
186static struct pm8058_charger pm8058_chg;
187
188static int pm_chg_get_rt_status(int irq)
189{
190 int count = 3;
191 int ret;
192
193 while ((ret =
194 pm8058_irq_get_rt_status(pm8058_chg.pm_chip, irq)) == -EAGAIN
195 && count--) {
196 dev_info(pm8058_chg.dev, "%s trycount=%d\n", __func__, count);
197 cpu_relax();
198 }
199 if (ret == -EAGAIN)
200 return 0;
201 else
202 return ret;
203}
204
205static int is_chg_plugged_in(void)
206{
207 return pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[CHGVAL_IRQ]);
208}
209
210static irqreturn_t pm8058_chg_chgval_handler(int irq, void *dev_id)
211{
212 u8 old, temp;
213 int ret;
214
215 if (!is_chg_plugged_in()) { /*this debounces it */
216 ret = pm8058_read(pm8058_chg.pm_chip, PM8058_OVP_TEST_REG,
217 &old, 1);
218 temp = old | BIT(FORCE_OVP_OFF);
219 ret = pm8058_write(pm8058_chg.pm_chip, PM8058_OVP_TEST_REG,
220 &temp, 1);
221 temp = 0xFC;
222 ret = pm8058_write(pm8058_chg.pm_chip, PM8058_CHG_TEST,
223 &temp, 1);
224 pr_debug("%s forced wrote 0xFC to test ret=%d\n",
225 __func__, ret);
226 /* 20 ms sleep is for the VCHG to discharge */
227 msleep(20);
228 temp = 0xF0;
229 ret = pm8058_write(pm8058_chg.pm_chip, PM8058_CHG_TEST,
230 &temp, 1);
231 ret = pm8058_write(pm8058_chg.pm_chip, PM8058_OVP_TEST_REG,
232 &old, 1);
233 }
234
235 return IRQ_HANDLED;
236}
237
238static void free_irqs(void)
239{
240 int i;
241
242 for (i = 0; i < PMIC_CHG_MAX_INTS; i++)
243 if (pm8058_chg.pmic_chg_irq[i]) {
244 free_irq(pm8058_chg.pmic_chg_irq[i], NULL);
245 pm8058_chg.pmic_chg_irq[i] = 0;
246 }
247}
248
249static int __devinit request_irqs(struct platform_device *pdev)
250{
251 struct resource *res;
252 int ret;
253
254 ret = 0;
255 bitmap_fill(pm8058_chg.enabled_irqs, PMIC_CHG_MAX_INTS);
256
257 res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "CHGVAL");
258 if (res == NULL) {
259 dev_err(pm8058_chg.dev,
260 "%s:couldnt find resource CHGVAL\n", __func__);
261 goto err_out;
262 } else {
263 ret = request_any_context_irq(res->start,
264 pm8058_chg_chgval_handler,
265 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
266 res->name, NULL);
267 if (ret < 0) {
268 dev_err(pm8058_chg.dev, "%s:couldnt request %d %d\n",
269 __func__, res->start, ret);
270 goto err_out;
271 } else {
272 pm8058_chg.pmic_chg_irq[CHGVAL_IRQ] = res->start;
273 }
274 }
275
276 return 0;
277
278err_out:
279 free_irqs();
280 return -EINVAL;
281}
282
283static int pm8058_usb_voltage_lower_limit(void)
284{
285 u8 temp, old;
286 int ret = 0;
287
288 temp = 0x10;
289 ret |= pm8058_write(pm8058_chg.pm_chip, PM8058_CHG_TEST, &temp, 1);
290 ret |= pm8058_read(pm8058_chg.pm_chip, PM8058_CHG_TEST, &old, 1);
291 old = old & ~BIT(IGNORE_LL);
292 temp = 0x90 | (0xF & old);
293 pr_debug("%s writing 0x%x to test\n", __func__, temp);
294 ret |= pm8058_write(pm8058_chg.pm_chip, PM8058_CHG_TEST, &temp, 1);
295
296 return ret;
297}
298
299static int __devinit pm8058_charger_probe(struct platform_device *pdev)
300{
301 struct pm8058_chip *pm_chip;
302
303 pm_chip = dev_get_drvdata(pdev->dev.parent);
304 if (pm_chip == NULL) {
305 pr_err("%s:no parent data passed in.\n", __func__);
306 return -EFAULT;
307 }
308
309 pm8058_chg.pm_chip = pm_chip;
310 pm8058_chg.pdata = pdev->dev.platform_data;
311 pm8058_chg.dev = &pdev->dev;
312
313 if (request_irqs(pdev)) {
314 pr_err("%s: couldnt register interrupts\n", __func__);
315 return -EINVAL;
316 }
317
318 if (pm8058_usb_voltage_lower_limit()) {
319 pr_err("%s: couldnt write to IGNORE_LL\n", __func__);
320 return -EINVAL;
321 }
322
323 return 0;
324}
325
326static int __devexit pm8058_charger_remove(struct platform_device *pdev)
327{
328 free_irqs();
329 return 0;
330}
331
332static struct platform_driver pm8058_charger_driver = {
333 .probe = pm8058_charger_probe,
334 .remove = __devexit_p(pm8058_charger_remove),
335 .driver = {
336 .name = "pm-usb-fix",
337 .owner = THIS_MODULE,
338 },
339};
340
341static int __init pm8058_charger_init(void)
342{
343 return platform_driver_register(&pm8058_charger_driver);
344}
345
346static void __exit pm8058_charger_exit(void)
347{
348 platform_driver_unregister(&pm8058_charger_driver);
349}
350
351late_initcall(pm8058_charger_init);
352module_exit(pm8058_charger_exit);
353
354MODULE_LICENSE("GPL v2");
355MODULE_DESCRIPTION("PMIC8058 BATTERY driver");
356MODULE_VERSION("1.0");
357MODULE_ALIAS("platform:pm8058_charger");