blob: 36270cff7203cbae8cb725a2051d185a2ab8834d [file] [log] [blame]
Duy Truong790f06d2013-02-13 16:38:12 -08001/* Copyright (c) 2010-2011, 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/moduleparam.h>
16#include <linux/platform_device.h>
17#include <linux/errno.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070018#include <linux/interrupt.h>
19#include <linux/delay.h>
20#include <linux/bitops.h>
21#include <linux/workqueue.h>
22#include <linux/msm-charger.h>
23#include <linux/debugfs.h>
24#include <linux/slab.h>
25#include <linux/msm_adc.h>
26#include <linux/notifier.h>
Anirudh Ghayalc2019332011-11-12 06:29:10 +053027#include <linux/mfd/pm8xxx/core.h>
28#include <linux/mfd/pmic8058.h>
Abhijeet Dharmapurikarf734c672011-08-11 10:20:27 -070029#include <linux/pmic8058-charger.h>
Anirudh Ghayalc2019332011-11-12 06:29:10 +053030#include <linux/mfd/pm8xxx/batt-alarm.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070031
32#include <mach/msm_xo.h>
33#include <mach/msm_hsusb.h>
34
35/* Config Regs and their bits*/
36#define PM8058_CHG_TEST 0x75
37#define IGNORE_LL 2
38#define PM8058_CHG_TEST_2 0xEA
39#define PM8058_CHG_TEST_3 0xEB
40#define PM8058_OVP_TEST_REG 0xF6
41#define FORCE_OVP_OFF 3
42
43#define PM8058_CHG_CNTRL 0x1E
44#define CHG_TRICKLE_EN 7
45#define CHG_USB_SUSPEND 6
46#define CHG_IMON_CAL 5
47#define CHG_IMON_GAIN 4
48#define CHG_CHARGE_BAT 3
49#define CHG_VBUS_FROM_BOOST_OVRD 2
50#define CHG_CHARGE_DIS 1
51#define CHG_VCP_EN 0
52
53#define PM8058_CHG_CNTRL_2 0xD8
54#define ATC_DIS 7 /* coincell backed */
55#define CHARGE_AUTO_DIS 6
56#define DUMB_CHG_OVRD 5 /* coincell backed */
57#define ENUM_DONE 4
58#define CHG_TEMP_MODE 3
59#define CHG_BATT_TEMP_DIS 1 /* coincell backed */
60#define CHG_FAILED_CLEAR 0
61
62#define PM8058_CHG_VMAX_SEL 0x21
63#define PM8058_CHG_VBAT_DET 0xD9
64#define PM8058_CHG_IMAX 0x1F
65#define PM8058_CHG_TRICKLE 0xDB
66#define PM8058_CHG_ITERM 0xDC
67#define PM8058_CHG_TTRKL_MAX 0xE1
68#define PM8058_CHG_TCHG_MAX 0xE4
69#define PM8058_CHG_TEMP_THRESH 0xE2
70#define PM8058_CHG_TEMP_REG 0xE3
71#define PM8058_CHG_PULSE 0x22
72
73/* IRQ STATUS and CLEAR */
74#define PM8058_CHG_STATUS_CLEAR_IRQ_1 0x31
75#define PM8058_CHG_STATUS_CLEAR_IRQ_3 0x33
76#define PM8058_CHG_STATUS_CLEAR_IRQ_10 0xB3
77#define PM8058_CHG_STATUS_CLEAR_IRQ_11 0xB4
78
79/* IRQ MASKS */
80#define PM8058_CHG_MASK_IRQ_1 0x38
81
82#define PM8058_CHG_MASK_IRQ_3 0x3A
83#define PM8058_CHG_MASK_IRQ_10 0xBA
84#define PM8058_CHG_MASK_IRQ_11 0xBB
85
86/* IRQ Real time status regs */
87#define PM8058_CHG_STATUS_RT_1 0x3F
88#define STATUS_RTCHGVAL 7
89#define STATUS_RTCHGINVAL 6
90#define STATUS_RTBATT_REPLACE 5
91#define STATUS_RTVBATDET_LOW 4
92#define STATUS_RTCHGILIM 3
93#define STATUS_RTPCTDONE 1
94#define STATUS_RTVCP 0
95#define PM8058_CHG_STATUS_RT_3 0x41
96#define PM8058_CHG_STATUS_RT_10 0xC1
97#define PM8058_CHG_STATUS_RT_11 0xC2
98
99/* VTRIM */
100#define PM8058_CHG_VTRIM 0x1D
101#define PM8058_CHG_VBATDET_TRIM 0x1E
102#define PM8058_CHG_ITRIM 0x1F
103#define PM8058_CHG_TTRIM 0x20
104
105#define AUTO_CHARGING_VMAXSEL 4200
106#define AUTO_CHARGING_FAST_TIME_MAX_MINUTES 512
107#define AUTO_CHARGING_TRICKLE_TIME_MINUTES 30
108#define AUTO_CHARGING_VEOC_ITERM 100
109#define AUTO_CHARGING_IEOC_ITERM 160
110#define AUTO_CHARGING_RESUME_MV 4100
111
112#define AUTO_CHARGING_VBATDET 4150
113#define AUTO_CHARGING_VBATDET_DEBOUNCE_TIME_MS 3000
114#define AUTO_CHARGING_VEOC_VBATDET 4100
115#define AUTO_CHARGING_VEOC_TCHG 16
116#define AUTO_CHARGING_VEOC_TCHG_FINAL_CYCLE 32
117#define AUTO_CHARGING_VEOC_BEGIN_TIME_MS 5400000
118
119#define AUTO_CHARGING_VEOC_VBAT_LOW_CHECK_TIME_MS 60000
120#define AUTO_CHARGING_RESUME_CHARGE_DETECTION_COUNTER 5
121
122#define AUTO_CHARGING_DONE_CHECK_TIME_MS 1000
123
124#define PM8058_CHG_I_STEP_MA 50
125#define PM8058_CHG_I_MIN_MA 50
126#define PM8058_CHG_T_TCHG_SHIFT 2
127#define PM8058_CHG_I_TERM_STEP_MA 10
128#define PM8058_CHG_V_STEP_MV 25
129#define PM8058_CHG_V_MIN_MV 2400
130/*
131 * enum pmic_chg_interrupts: pmic interrupts
132 * @CHGVAL_IRQ: charger V between 3.3 and 7.9
133 * @CHGINVAL_IRQ: charger V outside 3.3 and 7.9
134 * @VBATDET_LOW_IRQ: VBAT < VBATDET
135 * @VCP_IRQ: VDD went below VBAT: BAT_FET is turned on
136 * @CHGILIM_IRQ: mA consumed>IMAXSEL: chgloop draws less mA
137 * @ATC_DONE_IRQ: Auto Trickle done
138 * @ATCFAIL_IRQ: Auto Trickle fail
139 * @AUTO_CHGDONE_IRQ: Auto chg done
140 * @AUTO_CHGFAIL_IRQ: time exceeded w/o reaching term current
141 * @CHGSTATE_IRQ: something happend causing a state change
142 * @FASTCHG_IRQ: trkl charging completed: moving to fastchg
143 * @CHG_END_IRQ: mA has dropped to termination current
144 * @BATTTEMP_IRQ: batt temp is out of range
145 * @CHGHOT_IRQ: the pass device is too hot
146 * @CHGTLIMIT_IRQ: unused
147 * @CHG_GONE_IRQ: charger was removed
148 * @VCPMAJOR_IRQ: vcp major
149 * @VBATDET_IRQ: VBAT >= VBATDET
150 * @BATFET_IRQ: BATFET closed
151 * @BATT_REPLACE_IRQ:
152 * @BATTCONNECT_IRQ:
153 */
154enum pmic_chg_interrupts {
155 CHGVAL_IRQ,
156 CHGINVAL_IRQ,
157 VBATDET_LOW_IRQ,
158 VCP_IRQ,
159 CHGILIM_IRQ,
160 ATC_DONE_IRQ,
161 ATCFAIL_IRQ,
162 AUTO_CHGDONE_IRQ,
163 AUTO_CHGFAIL_IRQ,
164 CHGSTATE_IRQ,
165 FASTCHG_IRQ,
166 CHG_END_IRQ,
167 BATTTEMP_IRQ,
168 CHGHOT_IRQ,
169 CHGTLIMIT_IRQ,
170 CHG_GONE_IRQ,
171 VCPMAJOR_IRQ,
172 VBATDET_IRQ,
173 BATFET_IRQ,
174 BATT_REPLACE_IRQ,
175 BATTCONNECT_IRQ,
176 PMIC_CHG_MAX_INTS
177};
178
179struct pm8058_charger {
180 struct pmic_charger_pdata *pdata;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700181 struct device *dev;
182
183 int pmic_chg_irq[PMIC_CHG_MAX_INTS];
184 DECLARE_BITMAP(enabled_irqs, PMIC_CHG_MAX_INTS);
185
186 struct delayed_work chg_done_check_work;
187 struct delayed_work check_vbat_low_work;
188 struct delayed_work veoc_begin_work;
189 struct delayed_work charging_check_work;
190 int waiting_for_topoff;
191 int waiting_for_veoc;
192 int vbatdet;
193 struct msm_hardware_charger hw_chg;
194 int current_charger_current;
195 int disabled;
196
197 struct msm_xo_voter *voter;
198 struct dentry *dent;
199
200 int inited;
201 int present;
202};
203
204static struct pm8058_charger pm8058_chg;
205static struct msm_hardware_charger usb_hw_chg;
Terence Hampson13163082011-08-09 10:17:06 -0400206static struct pmic8058_charger_data chg_data;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700207
208static int msm_battery_gauge_alarm_notify(struct notifier_block *nb,
209 unsigned long status, void *unused);
210
211static struct notifier_block alarm_notifier = {
212 .notifier_call = msm_battery_gauge_alarm_notify,
213};
214
215static int resume_mv = AUTO_CHARGING_RESUME_MV;
216static DEFINE_MUTEX(batt_alarm_lock);
217static int resume_mv_set(const char *val, struct kernel_param *kp);
218module_param_call(resume_mv, resume_mv_set, param_get_int,
219 &resume_mv, S_IRUGO | S_IWUSR);
220
221static int resume_mv_set(const char *val, struct kernel_param *kp)
222{
223 int rc;
224
225 mutex_lock(&batt_alarm_lock);
226
227 rc = param_set_int(val, kp);
228 if (rc)
229 goto out;
230
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530231 rc = pm8xxx_batt_alarm_threshold_set(
232 PM8XXX_BATT_ALARM_LOWER_COMPARATOR, resume_mv);
233 if (!rc)
234 rc = pm8xxx_batt_alarm_threshold_set(
235 PM8XXX_BATT_ALARM_UPPER_COMPARATOR, 4300);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700236
237out:
238 mutex_unlock(&batt_alarm_lock);
239 return rc;
240}
241
242static void pm8058_chg_enable_irq(int interrupt)
243{
244 if (!__test_and_set_bit(interrupt, pm8058_chg.enabled_irqs)) {
245 dev_dbg(pm8058_chg.dev, "%s %d\n", __func__,
246 pm8058_chg.pmic_chg_irq[interrupt]);
247 enable_irq(pm8058_chg.pmic_chg_irq[interrupt]);
248 }
249}
250
251static void pm8058_chg_disable_irq(int interrupt)
252{
253 if (__test_and_clear_bit(interrupt, pm8058_chg.enabled_irqs)) {
254 dev_dbg(pm8058_chg.dev, "%s %d\n", __func__,
255 pm8058_chg.pmic_chg_irq[interrupt]);
256 disable_irq_nosync(pm8058_chg.pmic_chg_irq[interrupt]);
257 }
258}
259
260static int pm_chg_get_rt_status(int irq)
261{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700262 int ret;
263
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530264 ret = pm8xxx_read_irq_stat(pm8058_chg.dev->parent, irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700265 if (ret == -EAGAIN)
266 return 0;
267 else
268 return ret;
269}
270
271static int is_chg_plugged_in(void)
272{
273 return pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[CHGVAL_IRQ]);
274}
275
276#ifdef DEBUG
277static void __dump_chg_regs(void)
278{
279 u8 temp;
280 int temp2;
281
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530282 pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_CNTRL, &temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700283 dev_dbg(pm8058_chg.dev, "PM8058_CHG_CNTRL = 0x%x\n", temp);
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530284 pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_CNTRL_2, &temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700285 dev_dbg(pm8058_chg.dev, "PM8058_CHG_CNTRL_2 = 0x%x\n", temp);
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530286 pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_VMAX_SEL, &temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700287 dev_dbg(pm8058_chg.dev, "PM8058_CHG_VMAX_SEL = 0x%x\n", temp);
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530288 pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_VBAT_DET, &temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700289 dev_dbg(pm8058_chg.dev, "PM8058_CHG_VBAT_DET = 0x%x\n", temp);
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530290 pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_IMAX, &temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700291 dev_dbg(pm8058_chg.dev, "PM8058_CHG_IMAX = 0x%x\n", temp);
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530292 pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_TRICKLE, &temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700293 dev_dbg(pm8058_chg.dev, "PM8058_CHG_TRICKLE = 0x%x\n", temp);
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530294 pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_ITERM, &temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700295 dev_dbg(pm8058_chg.dev, "PM8058_CHG_ITERM = 0x%x\n", temp);
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530296 pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_TTRKL_MAX, &temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700297 dev_dbg(pm8058_chg.dev, "PM8058_CHG_TTRKL_MAX = 0x%x\n", temp);
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530298 pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_TCHG_MAX, &temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700299 dev_dbg(pm8058_chg.dev, "PM8058_CHG_TCHG_MAX = 0x%x\n", temp);
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530300 pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_TEMP_THRESH, &temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700301 dev_dbg(pm8058_chg.dev, "PM8058_CHG_TEMP_THRESH = 0x%x\n", temp);
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530302 pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_TEMP_REG, &temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700303 dev_dbg(pm8058_chg.dev, "PM8058_CHG_TEMP_REG = 0x%x\n", temp);
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530304 pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_PULSE, &temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700305 dev_dbg(pm8058_chg.dev, "PM8058_CHG_PULSE = 0x%x\n", temp);
306
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530307 pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_STATUS_CLEAR_IRQ_1,
308 &temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700309 dev_dbg(pm8058_chg.dev, "PM8058_CHG_STATUS_CLEAR_IRQ_1 = 0x%x\n", temp);
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530310 pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_STATUS_CLEAR_IRQ_3,
311 &temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700312 dev_dbg(pm8058_chg.dev, "PM8058_CHG_STATUS_CLEAR_IRQ_3 = 0x%x\n", temp);
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530313 pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_STATUS_CLEAR_IRQ_10,
314 &temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700315 dev_dbg(pm8058_chg.dev, "PM8058_CHG_STATUS_CLEAR_IRQ_10 = 0x%x\n",
316 temp);
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530317 pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_STATUS_CLEAR_IRQ_11,
318 &temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700319 dev_dbg(pm8058_chg.dev, "PM8058_CHG_STATUS_CLEAR_IRQ_11 = 0x%x\n",
320 temp);
321
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530322 pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_MASK_IRQ_1, &temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700323 dev_dbg(pm8058_chg.dev, "PM8058_CHG_MASK_IRQ_1 = 0x%x\n", temp);
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530324 pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_MASK_IRQ_3, &temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700325 dev_dbg(pm8058_chg.dev, "PM8058_CHG_MASK_IRQ_3 = 0x%x\n", temp);
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530326 pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_MASK_IRQ_10, &temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700327 dev_dbg(pm8058_chg.dev, "PM8058_CHG_MASK_IRQ_10 = 0x%x\n", temp);
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530328 pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_MASK_IRQ_11, &temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700329 dev_dbg(pm8058_chg.dev, "PM8058_CHG_MASK_IRQ_11 = 0x%x\n", temp);
330
331 temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[CHGVAL_IRQ]);
332 dev_dbg(pm8058_chg.dev, "CHGVAL_IRQ = %d\n", temp2);
333
334 temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[CHGINVAL_IRQ]);
335 dev_dbg(pm8058_chg.dev, "CHGINVAL_IRQ = %d\n", temp2);
336
337 temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[VBATDET_LOW_IRQ]);
338 dev_dbg(pm8058_chg.dev, "VBATDET_LOW_IRQ= %d\n", temp2);
339
340 temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[VCP_IRQ]);
341 dev_dbg(pm8058_chg.dev, "VCP_IRQ= %d\n", temp2);
342
343 temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[CHGILIM_IRQ]);
344 dev_dbg(pm8058_chg.dev, "CHGILIM_IRQ= %d\n", temp2);
345
346 temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[ATC_DONE_IRQ]);
347 dev_dbg(pm8058_chg.dev, "ATC_DONE_IRQ= %d\n", temp2);
348
349 temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[ATCFAIL_IRQ]);
350 dev_dbg(pm8058_chg.dev, "ATCFAIL_IRQ= %d\n", temp2);
351
352 temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[AUTO_CHGDONE_IRQ]);
353 dev_dbg(pm8058_chg.dev, "AUTO_CHGDONE_IRQ= %d\n", temp2);
354
355 temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[AUTO_CHGFAIL_IRQ]);
356 dev_dbg(pm8058_chg.dev, "AUTO_CHGFAIL_IRQ= %d\n", temp2);
357
358 temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[CHGSTATE_IRQ]);
359 dev_dbg(pm8058_chg.dev, "CHGSTATE_IRQ= %d\n", temp2);
360
361 temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[FASTCHG_IRQ]);
362 dev_dbg(pm8058_chg.dev, "FASTCHG_IRQ= %d\n", temp2);
363
364 temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[CHG_END_IRQ]);
365 dev_dbg(pm8058_chg.dev, "CHG_END_IRQ= %d\n", temp2);
366
367 temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[BATTTEMP_IRQ]);
368 dev_dbg(pm8058_chg.dev, "BATTTEMP_IRQ= %d\n", temp2);
369
370 temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[CHGHOT_IRQ]);
371 dev_dbg(pm8058_chg.dev, "CHGHOT_IRQ= %d\n", temp2);
372
373 temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[CHGTLIMIT_IRQ]);
374 dev_dbg(pm8058_chg.dev, "CHGTLIMIT_IRQ= %d\n", temp2);
375
376 temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[CHG_GONE_IRQ]);
377 dev_dbg(pm8058_chg.dev, "CHG_GONE_IRQ= %d\n", temp2);
378
379 temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[VCPMAJOR_IRQ]);
380 dev_dbg(pm8058_chg.dev, "VCPMAJOR_IRQ= %d\n", temp2);
381
382 temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[VBATDET_IRQ]);
383 dev_dbg(pm8058_chg.dev, "VBATDET_IRQ= %d\n", temp2);
384
385 temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[BATFET_IRQ]);
386 dev_dbg(pm8058_chg.dev, "BATFET_IRQ= %d\n", temp2);
387
388 temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[BATT_REPLACE_IRQ]);
389 dev_dbg(pm8058_chg.dev, "BATT_REPLACE_IRQ= %d\n", temp2);
390
391 temp2 = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[BATTCONNECT_IRQ]);
392 dev_dbg(pm8058_chg.dev, "BATTCONNECT_IRQ= %d\n", temp2);
393}
394#else
395static inline void __dump_chg_regs(void)
396{
397}
398#endif
399
400/* SSBI register access helper functions */
401static int pm_chg_suspend(int value)
402{
403 u8 temp;
404 int ret;
405
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530406 ret = pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_CNTRL, &temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700407 if (ret)
408 return ret;
409 if (value)
410 temp |= BIT(CHG_USB_SUSPEND);
411 else
412 temp &= ~BIT(CHG_USB_SUSPEND);
413
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530414 return pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_CNTRL, temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700415}
416
417static int pm_chg_auto_disable(int value)
418{
419 u8 temp;
420 int ret;
421
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530422 ret = pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_CNTRL_2, &temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700423 if (ret)
424 return ret;
425 if (value)
426 temp |= BIT(CHARGE_AUTO_DIS);
427 else
428 temp &= ~BIT(CHARGE_AUTO_DIS);
429
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530430 return pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_CNTRL_2, temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700431}
432
433static int pm_chg_batt_temp_disable(int value)
434{
435 u8 temp;
436 int ret;
437
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530438 ret = pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_CNTRL_2, &temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700439 if (ret)
440 return ret;
441 if (value)
442 temp |= BIT(CHG_BATT_TEMP_DIS);
443 else
444 temp &= ~BIT(CHG_BATT_TEMP_DIS);
445
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530446 return pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_CNTRL_2, temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700447}
448
449static int pm_chg_vbatdet_set(int voltage)
450{
451 u8 temp;
452 int diff;
453
454 diff = (voltage - PM8058_CHG_V_MIN_MV);
455 if (diff < 0) {
456 dev_warn(pm8058_chg.dev, "%s bad mV=%d asked to set\n",
457 __func__, voltage);
458 return -EINVAL;
459 }
460
461 temp = diff / PM8058_CHG_V_STEP_MV;
462 dev_dbg(pm8058_chg.dev, "%s voltage=%d setting %02x\n", __func__,
463 voltage, temp);
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530464 return pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_VBAT_DET, temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700465}
466
467static int pm_chg_imaxsel_set(int chg_current)
468{
469 u8 temp;
470 int diff;
471
472 diff = chg_current - PM8058_CHG_I_MIN_MA;
473 if (diff < 0) {
474 dev_warn(pm8058_chg.dev, "%s bad mA=%d asked to set\n",
475 __func__, chg_current);
476 return -EINVAL;
477 }
478 temp = diff / PM8058_CHG_I_STEP_MA;
479 /* make sure we arent writing more than 5 bits of data */
480 if (temp > 31) {
481 dev_warn(pm8058_chg.dev, "%s max mA=1500 requested mA=%d\n",
482 __func__, chg_current);
483 temp = 31;
484 }
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530485 return pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_IMAX, temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700486}
487
488#define PM8058_CHG_VMAX_MIN 3300
489#define PM8058_CHG_VMAX_MAX 5500
490static int pm_chg_vmaxsel_set(int voltage)
491{
492 u8 temp;
493
494 if (voltage < PM8058_CHG_VMAX_MIN || voltage > PM8058_CHG_VMAX_MAX) {
495 dev_warn(pm8058_chg.dev, "%s bad mV=%d asked to set\n",
496 __func__, voltage);
497 return -EINVAL;
498 }
499 temp = (voltage - PM8058_CHG_V_MIN_MV) / PM8058_CHG_V_STEP_MV;
500 dev_dbg(pm8058_chg.dev, "%s mV=%d setting %02x\n", __func__, voltage,
501 temp);
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530502 return pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_VMAX_SEL, temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700503}
504
505static int pm_chg_failed_clear(int value)
506{
507 u8 temp;
508 int ret;
509
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530510 ret = pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_CNTRL_2, &temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700511 if (ret)
512 return ret;
513 if (value)
514 temp |= BIT(CHG_FAILED_CLEAR);
515 else
516 temp &= ~BIT(CHG_FAILED_CLEAR);
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530517 return pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_CNTRL_2, temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700518}
519
520static int pm_chg_iterm_set(int chg_current)
521{
522 u8 temp;
523
524 temp = (chg_current / PM8058_CHG_I_TERM_STEP_MA) - 1;
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530525 return pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_ITERM, temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700526}
527
528static int pm_chg_tchg_set(int minutes)
529{
530 u8 temp;
531
532 temp = (minutes >> PM8058_CHG_T_TCHG_SHIFT) - 1;
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530533 return pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_TCHG_MAX, temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700534}
535
536static int pm_chg_ttrkl_set(int minutes)
537{
538 u8 temp;
539
540 temp = minutes - 1;
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530541 return pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_TTRKL_MAX,
542 temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700543}
544
545static int pm_chg_enum_done_enable(int value)
546{
547 u8 temp;
548 int ret;
549
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530550 ret = pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_CNTRL_2, &temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700551 if (ret)
552 return ret;
553 if (value)
554 temp |= BIT(ENUM_DONE);
555 else
556 temp &= ~BIT(ENUM_DONE);
557
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530558 return pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_CNTRL_2, temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700559}
560
561static uint32_t get_fsm_state(void)
562{
563 u8 temp;
564
565 temp = 0x00;
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530566 pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_TEST_3, temp);
567 pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_TEST_3, &temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700568 return (uint32_t)temp;
569}
570
571static int get_fsm_status(void *data, u64 * val)
572{
573 *val = get_fsm_state();
574 return 0;
575}
576
Abhijeet Dharmapurikarf734c672011-08-11 10:20:27 -0700577enum pmic8058_chg_state pmic8058_get_fsm_state(void)
578{
579 if (!pm8058_chg.inited) {
580 pr_err("%s: called when not inited\n", __func__);
581 return -EINVAL;
582 }
583
584 return get_fsm_state();
585}
586
Abhijeet Dharmapurikar0758e1e2011-07-14 18:11:41 -0700587static int pm_chg_disable(int value)
588{
589 u8 temp;
590 int ret;
591
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530592 ret = pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_CNTRL, &temp);
Abhijeet Dharmapurikar0758e1e2011-07-14 18:11:41 -0700593 if (ret)
594 return ret;
595 if (value)
596 temp |= BIT(CHG_CHARGE_DIS);
597 else
598 temp &= ~BIT(CHG_CHARGE_DIS);
599
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530600 return pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_CNTRL, temp);
Abhijeet Dharmapurikar0758e1e2011-07-14 18:11:41 -0700601}
602
603static void pm8058_start_system_current(struct msm_hardware_charger *hw_chg,
604 int max_current)
605{
606 int ret = 0;
607
608 if (pm8058_chg.disabled)
609 return;
610
611 ret = pm_chg_imaxsel_set(max_current);
612 ret |= pm_chg_enum_done_enable(1);
613 ret |= pm_chg_disable(0);
614 if (ret)
615 pr_err("%s: failed to turn on system power err=%d",
616 __func__, ret);
617}
618
619static void pm8058_stop_system_current(struct msm_hardware_charger *hw_chg)
620{
621 int ret = 0;
622
623 ret = pm_chg_enum_done_enable(0);
624 ret |= pm_chg_disable(1);
625 if (ret)
626 pr_err("%s: failed to turn off system power err=%d",
627 __func__, ret);
628}
629
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700630static int __pm8058_start_charging(int chg_current, int termination_current,
631 int time)
632{
633 int ret = 0;
634
635 if (pm8058_chg.disabled)
636 goto out;
637
638 dev_info(pm8058_chg.dev, "%s %dmA %dmin\n",
639 __func__, chg_current, time);
640
641 ret = pm_chg_auto_disable(1);
642 if (ret)
643 goto out;
644
645 ret = pm_chg_suspend(0);
646 if (ret)
647 goto out;
648
649 ret = pm_chg_imaxsel_set(chg_current);
650 if (ret)
651 goto out;
652
653 ret = pm_chg_failed_clear(1);
654 if (ret)
655 goto out;
656
657 ret = pm_chg_iterm_set(termination_current);
658 if (ret)
659 goto out;
660
661 ret = pm_chg_tchg_set(time);
662 if (ret)
663 goto out;
664
665 ret = pm_chg_ttrkl_set(AUTO_CHARGING_TRICKLE_TIME_MINUTES);
666 if (ret)
667 goto out;
668
669 ret = pm_chg_batt_temp_disable(0);
670 if (ret)
671 goto out;
672
673 if (pm8058_chg.voter == NULL)
674 pm8058_chg.voter = msm_xo_get(MSM_XO_TCXO_D1, "pm8058_charger");
675 msm_xo_mode_vote(pm8058_chg.voter, MSM_XO_MODE_ON);
676
677 ret = pm_chg_enum_done_enable(1);
678 if (ret)
679 goto out;
680
681 wmb();
682
683 ret = pm_chg_auto_disable(0);
684 if (ret)
685 goto out;
686
687 /* wait for the enable to update interrupt status*/
688 msleep(20);
689
690 pm8058_chg_enable_irq(AUTO_CHGFAIL_IRQ);
691 pm8058_chg_enable_irq(CHGHOT_IRQ);
692 pm8058_chg_enable_irq(AUTO_CHGDONE_IRQ);
693 pm8058_chg_enable_irq(CHG_END_IRQ);
694 pm8058_chg_enable_irq(CHGSTATE_IRQ);
695
696out:
697 return ret;
698}
699
700static void chg_done_cleanup(void)
701{
702 dev_info(pm8058_chg.dev, "%s notify pm8058 charging completion"
703 "\n", __func__);
704
705 pm8058_chg_disable_irq(AUTO_CHGDONE_IRQ);
706 cancel_delayed_work_sync(&pm8058_chg.veoc_begin_work);
707 cancel_delayed_work_sync(&pm8058_chg.check_vbat_low_work);
708
709 pm8058_chg_disable_irq(CHG_END_IRQ);
710
711 pm8058_chg_disable_irq(VBATDET_LOW_IRQ);
712 pm8058_chg_disable_irq(VBATDET_IRQ);
713 pm8058_chg.waiting_for_veoc = 0;
714 pm8058_chg.waiting_for_topoff = 0;
715
716 pm_chg_auto_disable(1);
717
718 msm_charger_notify_event(&usb_hw_chg, CHG_DONE_EVENT);
719}
720
721static void chg_done_check_work(struct work_struct *work)
722{
723 chg_done_cleanup();
724}
725
726static void charging_check_work(struct work_struct *work)
727{
728 uint32_t fsm_state = get_fsm_state();
729 int rc;
730
731 switch (fsm_state) {
732 /* We're charging, so disarm alarm */
Abhijeet Dharmapurikarf734c672011-08-11 10:20:27 -0700733 case PMIC8058_CHG_STATE_ATC:
734 case PMIC8058_CHG_STATE_FAST_CHG:
735 case PMIC8058_CHG_STATE_TRKL_CHG:
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530736 rc = pm8xxx_batt_alarm_disable(
737 PM8XXX_BATT_ALARM_UPPER_COMPARATOR);
738 if (!rc)
739 rc = pm8xxx_batt_alarm_disable(
740 PM8XXX_BATT_ALARM_LOWER_COMPARATOR);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700741 if (rc)
742 dev_err(pm8058_chg.dev,
743 "%s: unable to set alarm state\n", __func__);
744 break;
745 default:
746 /* Still not charging, so update driver state */
747 chg_done_cleanup();
748 break;
749 };
750}
751
752static int pm8058_start_charging(struct msm_hardware_charger *hw_chg,
753 int chg_voltage, int chg_current)
754{
755 int vbat_higher_than_vbatdet;
756 int ret = 0;
757
758 cancel_delayed_work_sync(&pm8058_chg.charging_check_work);
759
760 /*
761 * adjust the max current for PC USB connection - set the higher limit
762 * to 450 and make sure we never cross it
763 */
764 if (chg_current == 500)
765 chg_current = 450;
Terence Hampson13163082011-08-09 10:17:06 -0400766
767 if (hw_chg->type == CHG_TYPE_AC && chg_data.max_source_current)
768 chg_current = chg_data.max_source_current;
769
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700770 pm8058_chg.current_charger_current = chg_current;
771 pm8058_chg_enable_irq(FASTCHG_IRQ);
772
773 ret = pm_chg_vmaxsel_set(chg_voltage);
774 if (ret)
775 goto out;
776
777 /* set vbat to CC to CV threshold */
778 ret = pm_chg_vbatdet_set(AUTO_CHARGING_VBATDET);
779 if (ret)
780 goto out;
781
782 pm8058_chg.vbatdet = AUTO_CHARGING_VBATDET;
783 /*
784 * get the state of vbat and if it is higher than
785 * AUTO_CHARGING_VBATDET we start the veoc start timer
786 * else wait for the voltage to go to AUTO_CHARGING_VBATDET
787 * and then start the 90 min timer
788 */
789 vbat_higher_than_vbatdet =
790 pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[VBATDET_IRQ]);
791 if (vbat_higher_than_vbatdet) {
792 /*
793 * we are in constant voltage phase of charging
794 * IEOC should happen withing 90 mins of this instant
795 * else we enable VEOC
796 */
797 dev_info(pm8058_chg.dev, "%s begin veoc timer\n", __func__);
798 schedule_delayed_work(&pm8058_chg.veoc_begin_work,
799 round_jiffies_relative(msecs_to_jiffies
800 (AUTO_CHARGING_VEOC_BEGIN_TIME_MS)));
801 } else
802 pm8058_chg_enable_irq(VBATDET_IRQ);
803
804 ret = __pm8058_start_charging(chg_current, AUTO_CHARGING_IEOC_ITERM,
805 AUTO_CHARGING_FAST_TIME_MAX_MINUTES);
806 pm8058_chg.current_charger_current = chg_current;
807
808 /*
809 * We want to check the FSM state to verify we're charging. We must
810 * wait before doing this to allow the VBATDET to settle. The worst
811 * case for this is two seconds. The batt alarm does not have this
812 * delay.
813 */
814 schedule_delayed_work(&pm8058_chg.charging_check_work,
815 round_jiffies_relative(msecs_to_jiffies
816 (AUTO_CHARGING_VBATDET_DEBOUNCE_TIME_MS)));
817
818out:
819 return ret;
820}
821
822static void veoc_begin_work(struct work_struct *work)
823{
824 /* we have been doing CV for 90mins with no signs of IEOC
825 * start checking for VEOC in addition with 16min charges*/
826 dev_info(pm8058_chg.dev, "%s begin veoc detection\n", __func__);
827 pm8058_chg.waiting_for_veoc = 1;
828 /*
829 * disable VBATDET irq we dont need it unless we are at the end of
830 * charge cycle
831 */
832 pm8058_chg_disable_irq(VBATDET_IRQ);
833 __pm8058_start_charging(pm8058_chg.current_charger_current,
834 AUTO_CHARGING_VEOC_ITERM,
835 AUTO_CHARGING_VEOC_TCHG);
836}
837
838static void vbat_low_work(struct work_struct *work)
839{
840 /*
841 * It has been one minute and the battery still holds voltage
842 * start the final topoff - charging is almost done
843 */
844 dev_info(pm8058_chg.dev, "%s vbatt maintains for a minute"
845 "starting topoff\n", __func__);
846 pm8058_chg.waiting_for_veoc = 0;
847 pm8058_chg.waiting_for_topoff = 1;
848 pm8058_chg_disable_irq(VBATDET_LOW_IRQ);
849 pm8058_chg_disable_irq(VBATDET_IRQ);
850 __pm8058_start_charging(pm8058_chg.current_charger_current,
851 AUTO_CHARGING_VEOC_ITERM,
852 AUTO_CHARGING_VEOC_TCHG_FINAL_CYCLE);
853}
854
855
856static irqreturn_t pm8058_chg_chgval_handler(int irq, void *dev_id)
857{
858 u8 old, temp;
859 int ret;
860
861 if (is_chg_plugged_in()) { /* this debounces it */
862 if (!pm8058_chg.present) {
863 msm_charger_notify_event(&usb_hw_chg,
864 CHG_INSERTED_EVENT);
865 pm8058_chg.present = 1;
866 }
867 } else {
868 if (pm8058_chg.present) {
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530869 ret = pm8xxx_readb(pm8058_chg.dev->parent,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700870 PM8058_OVP_TEST_REG,
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530871 &old);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700872 temp = old | BIT(FORCE_OVP_OFF);
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530873 ret = pm8xxx_writeb(pm8058_chg.dev->parent,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700874 PM8058_OVP_TEST_REG,
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530875 temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700876 temp = 0xFC;
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530877 ret = pm8xxx_writeb(pm8058_chg.dev->parent,
878 PM8058_CHG_TEST, temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700879 /* 10 ms sleep is for the VCHG to discharge */
880 msleep(10);
881 temp = 0xF0;
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530882 ret = pm8xxx_writeb(pm8058_chg.dev->parent,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700883 PM8058_CHG_TEST,
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530884 temp);
885 ret = pm8xxx_writeb(pm8058_chg.dev->parent,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700886 PM8058_OVP_TEST_REG,
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530887 old);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700888
889 pm_chg_enum_done_enable(0);
890 pm_chg_auto_disable(1);
891 msm_charger_notify_event(&usb_hw_chg,
892 CHG_REMOVED_EVENT);
893 pm8058_chg.present = 0;
894 }
895 }
896
897 return IRQ_HANDLED;
898}
899
900static irqreturn_t pm8058_chg_chginval_handler(int irq, void *dev_id)
901{
902 u8 old, temp;
903 int ret;
904
905 if (pm8058_chg.present) {
906 pm8058_chg_disable_irq(CHGINVAL_IRQ);
907
908 pm_chg_enum_done_enable(0);
909 pm_chg_auto_disable(1);
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530910 ret = pm8xxx_readb(pm8058_chg.dev->parent,
911 PM8058_OVP_TEST_REG, &old);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700912 temp = old | BIT(FORCE_OVP_OFF);
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530913 ret = pm8xxx_writeb(pm8058_chg.dev->parent,
914 PM8058_OVP_TEST_REG, temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700915 temp = 0xFC;
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530916 ret = pm8xxx_writeb(pm8058_chg.dev->parent,
917 PM8058_CHG_TEST, temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700918 /* 10 ms sleep is for the VCHG to discharge */
919 msleep(10);
920 temp = 0xF0;
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530921 ret = pm8xxx_writeb(pm8058_chg.dev->parent,
922 PM8058_CHG_TEST, temp);
923 ret = pm8xxx_writeb(pm8058_chg.dev->parent,
924 PM8058_OVP_TEST_REG, old);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700925
926 if (!is_chg_plugged_in()) {
927 msm_charger_notify_event(&usb_hw_chg,
928 CHG_REMOVED_EVENT);
929 pm8058_chg.present = 0;
930 } else {
931 /* was a fake */
932 pm8058_chg_enable_irq(CHGINVAL_IRQ);
933 }
934 }
935
936 return IRQ_HANDLED;
937}
938
939static irqreturn_t pm8058_chg_auto_chgdone_handler(int irq, void *dev_id)
940{
941 dev_info(pm8058_chg.dev, "%s waiting a sec to confirm\n",
942 __func__);
Abhijeet Dharmapurikar2dae2392011-07-14 18:31:28 -0700943 pm8058_chg_disable_irq(AUTO_CHGDONE_IRQ);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700944 pm8058_chg_disable_irq(VBATDET_IRQ);
945 if (!delayed_work_pending(&pm8058_chg.chg_done_check_work)) {
946 schedule_delayed_work(&pm8058_chg.chg_done_check_work,
947 round_jiffies_relative(msecs_to_jiffies
948 (AUTO_CHARGING_DONE_CHECK_TIME_MS)));
949 }
950 return IRQ_HANDLED;
951}
952
953/* can only happen with the pmic charger when it has been charing
954 * for either 16 mins wating for VEOC or 32 mins for topoff
955 * without a IEOC indication */
956static irqreturn_t pm8058_chg_auto_chgfail_handler(int irq, void *dev_id)
957{
958 pm8058_chg_disable_irq(AUTO_CHGFAIL_IRQ);
959
960 if (pm8058_chg.waiting_for_topoff == 1) {
961 dev_info(pm8058_chg.dev, "%s topoff done, charging done\n",
962 __func__);
963 pm8058_chg.waiting_for_topoff = 0;
964 /* notify we are done charging */
965 msm_charger_notify_event(&usb_hw_chg, CHG_DONE_EVENT);
966 } else {
967 /* start one minute timer and monitor VBATDET_LOW */
968 dev_info(pm8058_chg.dev, "%s monitoring vbat_low for a"
969 "minute\n", __func__);
970 schedule_delayed_work(&pm8058_chg.check_vbat_low_work,
971 round_jiffies_relative(msecs_to_jiffies
972 (AUTO_CHARGING_VEOC_VBAT_LOW_CHECK_TIME_MS)));
973
974 /* note we are waiting on veoc */
975 pm8058_chg.waiting_for_veoc = 1;
976
977 pm_chg_vbatdet_set(AUTO_CHARGING_VEOC_VBATDET);
978 pm8058_chg.vbatdet = AUTO_CHARGING_VEOC_VBATDET;
979 pm8058_chg_enable_irq(VBATDET_LOW_IRQ);
980 }
981 return IRQ_HANDLED;
982}
983
984static irqreturn_t pm8058_chg_chgstate_handler(int irq, void *dev_id)
985{
986 u8 temp;
987
988 temp = 0x00;
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530989 if (!pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_TEST_3, temp)) {
990 pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_TEST_3, &temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700991 dev_dbg(pm8058_chg.dev, "%s state=%d\n", __func__, temp);
992 }
993 return IRQ_HANDLED;
994}
995
996static irqreturn_t pm8058_chg_fastchg_handler(int irq, void *dev_id)
997{
998 pm8058_chg_disable_irq(FASTCHG_IRQ);
999
1000 /* we have begun the fast charging state */
1001 dev_info(pm8058_chg.dev, "%s begin fast charging"
1002 , __func__);
1003 msm_charger_notify_event(&usb_hw_chg, CHG_BATT_BEGIN_FAST_CHARGING);
1004 return IRQ_HANDLED;
1005}
1006
1007static irqreturn_t pm8058_chg_batttemp_handler(int irq, void *dev_id)
1008{
1009 int ret;
1010
1011 /* we could get temperature
1012 * interrupt when the battery is plugged out
1013 */
1014 ret = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[BATTCONNECT_IRQ]);
1015 if (ret) {
1016 msm_charger_notify_event(&usb_hw_chg, CHG_BATT_REMOVED);
1017 } else {
1018 /* read status to determine we are inrange or outofrange */
1019 ret =
1020 pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[BATTTEMP_IRQ]);
1021 if (ret)
1022 msm_charger_notify_event(&usb_hw_chg,
1023 CHG_BATT_TEMP_OUTOFRANGE);
1024 else
1025 msm_charger_notify_event(&usb_hw_chg,
1026 CHG_BATT_TEMP_INRANGE);
1027 }
1028
1029 return IRQ_HANDLED;
1030}
1031
1032static irqreturn_t pm8058_chg_vbatdet_handler(int irq, void *dev_id)
1033{
1034 int ret;
1035
1036 /* settling time */
1037 msleep(20);
1038 ret = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[VBATDET_IRQ]);
1039
1040 if (ret) {
1041 if (pm8058_chg.vbatdet == AUTO_CHARGING_VBATDET
1042 && !delayed_work_pending(&pm8058_chg.veoc_begin_work)) {
1043 /*
1044 * we are in constant voltage phase of charging
1045 * IEOC should happen withing 90 mins of this instant
1046 * else we enable VEOC
1047 */
1048 dev_info(pm8058_chg.dev, "%s entered constant voltage"
1049 "begin veoc timer\n", __func__);
1050 schedule_delayed_work(&pm8058_chg.veoc_begin_work,
1051 round_jiffies_relative
1052 (msecs_to_jiffies
1053 (AUTO_CHARGING_VEOC_BEGIN_TIME_MS)));
1054 }
1055 } else {
1056 if (pm8058_chg.vbatdet == AUTO_CHARGING_VEOC_VBATDET) {
1057 cancel_delayed_work_sync(
1058 &pm8058_chg.check_vbat_low_work);
1059
1060 if (pm8058_chg.waiting_for_topoff
1061 || pm8058_chg.waiting_for_veoc) {
1062 /*
1063 * the battery dropped its voltage below 4100
1064 * around a minute charge the battery for 16
1065 * mins and check vbat again for a minute
1066 */
1067 dev_info(pm8058_chg.dev, "%s batt dropped vlt"
1068 "within a minute\n", __func__);
1069 pm8058_chg.waiting_for_topoff = 0;
1070 pm8058_chg.waiting_for_veoc = 1;
1071 pm8058_chg_disable_irq(VBATDET_IRQ);
1072 __pm8058_start_charging(pm8058_chg.
1073 current_charger_current,
1074 AUTO_CHARGING_VEOC_ITERM,
1075 AUTO_CHARGING_VEOC_TCHG);
1076 }
1077 }
1078 }
1079 return IRQ_HANDLED;
1080}
1081
1082static irqreturn_t pm8058_chg_batt_replace_handler(int irq, void *dev_id)
1083{
1084 int ret;
1085
1086 pm8058_chg_disable_irq(BATT_REPLACE_IRQ);
1087 ret = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[BATT_REPLACE_IRQ]);
1088 if (ret) {
1089 msm_charger_notify_event(&usb_hw_chg, CHG_BATT_INSERTED);
1090 /*
1091 * battery is present enable batt removal
1092 * and batt temperatture interrupt
1093 */
1094 pm8058_chg_enable_irq(BATTCONNECT_IRQ);
1095 }
1096 return IRQ_HANDLED;
1097}
1098
1099static irqreturn_t pm8058_chg_battconnect_handler(int irq, void *dev_id)
1100{
1101 int ret;
1102
1103 ret = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[BATTCONNECT_IRQ]);
1104 if (ret) {
1105 msm_charger_notify_event(&usb_hw_chg, CHG_BATT_REMOVED);
1106 } else {
1107 msm_charger_notify_event(&usb_hw_chg, CHG_BATT_INSERTED);
1108 pm8058_chg_enable_irq(BATTTEMP_IRQ);
1109 }
1110
1111 return IRQ_HANDLED;
1112}
1113
1114static int get_rt_status(void *data, u64 * val)
1115{
1116 int i = (int)data;
1117 int ret;
1118
1119 ret = pm_chg_get_rt_status(i);
1120 *val = ret;
1121 return 0;
1122}
1123
1124DEFINE_SIMPLE_ATTRIBUTE(rt_fops, get_rt_status, NULL, "%llu\n");
1125DEFINE_SIMPLE_ATTRIBUTE(fsm_fops, get_fsm_status, NULL, "%llu\n");
1126
1127static void free_irqs(void)
1128{
1129 int i;
1130
1131 for (i = 0; i < PMIC_CHG_MAX_INTS; i++)
1132 if (pm8058_chg.pmic_chg_irq[i]) {
1133 free_irq(pm8058_chg.pmic_chg_irq[i], NULL);
1134 pm8058_chg.pmic_chg_irq[i] = 0;
1135 }
1136}
1137
1138static int __devinit request_irqs(struct platform_device *pdev)
1139{
1140 struct resource *res;
1141 int ret;
1142
1143 ret = 0;
1144 bitmap_fill(pm8058_chg.enabled_irqs, PMIC_CHG_MAX_INTS);
1145
1146 res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "CHGVAL");
1147 if (res == NULL) {
1148 dev_err(pm8058_chg.dev,
1149 "%s:couldnt find resource CHGVAL\n", __func__);
1150 goto err_out;
1151 } else {
1152 ret = request_threaded_irq(res->start, NULL,
1153 pm8058_chg_chgval_handler,
1154 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
1155 res->name, NULL);
1156 if (ret < 0) {
1157 dev_err(pm8058_chg.dev, "%s:couldnt request %d %d\n",
1158 __func__, res->start, ret);
1159 goto err_out;
1160 } else {
1161 pm8058_chg.pmic_chg_irq[CHGVAL_IRQ] = res->start;
1162 pm8058_chg_disable_irq(CHGVAL_IRQ);
1163 enable_irq_wake(pm8058_chg.pmic_chg_irq[CHGVAL_IRQ]);
1164 }
1165 }
1166
1167 res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "CHGINVAL");
1168 if (res == NULL) {
1169 dev_err(pm8058_chg.dev,
1170 "%s:couldnt find resource CHGINVAL\n", __func__);
1171 goto err_out;
1172 } else {
1173 ret = request_threaded_irq(res->start, NULL,
1174 pm8058_chg_chginval_handler,
1175 IRQF_TRIGGER_RISING, res->name, NULL);
1176 if (ret < 0) {
1177 dev_err(pm8058_chg.dev, "%s:couldnt request %d %d\n",
1178 __func__, res->start, ret);
1179 goto err_out;
1180 } else {
1181 pm8058_chg.pmic_chg_irq[CHGINVAL_IRQ] = res->start;
1182 pm8058_chg_disable_irq(CHGINVAL_IRQ);
1183 }
1184 }
1185
1186 res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
1187 "AUTO_CHGDONE");
1188 if (res == NULL) {
1189 dev_err(pm8058_chg.dev,
1190 "%s:couldnt find resource AUTO_CHGDONE\n", __func__);
1191 goto err_out;
1192 } else {
Anirudh Ghayalc2019332011-11-12 06:29:10 +05301193 ret = request_irq(res->start,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001194 pm8058_chg_auto_chgdone_handler,
Abhijeet Dharmapurikar2dae2392011-07-14 18:31:28 -07001195 IRQF_TRIGGER_RISING,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001196 res->name, NULL);
1197 if (ret < 0) {
1198 dev_err(pm8058_chg.dev, "%s:couldnt request %d %d\n",
1199 __func__, res->start, ret);
1200 goto err_out;
1201 } else {
1202 pm8058_chg.pmic_chg_irq[AUTO_CHGDONE_IRQ] = res->start;
1203 pm8058_chg_disable_irq(AUTO_CHGDONE_IRQ);
1204 }
1205 }
1206
1207 res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
1208 "AUTO_CHGFAIL");
1209 if (res == NULL) {
1210 dev_err(pm8058_chg.dev,
1211 "%s:couldnt find resource AUTO_CHGFAIL\n", __func__);
1212 goto err_out;
1213 } else {
Anirudh Ghayalc2019332011-11-12 06:29:10 +05301214 ret = request_irq(res->start,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001215 pm8058_chg_auto_chgfail_handler,
1216 IRQF_TRIGGER_RISING, res->name, NULL);
1217 if (ret < 0) {
1218 dev_err(pm8058_chg.dev, "%s:couldnt request %d %d\n",
1219 __func__, res->start, ret);
1220 goto err_out;
1221 } else {
1222 pm8058_chg.pmic_chg_irq[AUTO_CHGFAIL_IRQ] = res->start;
1223 pm8058_chg_disable_irq(AUTO_CHGFAIL_IRQ);
1224 }
1225 }
1226
1227 res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "CHGSTATE");
1228 if (res == NULL) {
1229 dev_err(pm8058_chg.dev,
1230 "%s:couldnt find resource CHGSTATE\n", __func__);
1231 goto err_out;
1232 } else {
Anirudh Ghayalc2019332011-11-12 06:29:10 +05301233 ret = request_irq(res->start,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001234 pm8058_chg_chgstate_handler,
1235 IRQF_TRIGGER_RISING, res->name, NULL);
1236 if (ret < 0) {
1237 dev_err(pm8058_chg.dev, "%s:couldnt request %d %d\n",
1238 __func__, res->start, ret);
1239 goto err_out;
1240 } else {
1241 pm8058_chg.pmic_chg_irq[CHGSTATE_IRQ] = res->start;
1242 pm8058_chg_disable_irq(CHGSTATE_IRQ);
1243 }
1244 }
1245
1246 res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "FASTCHG");
1247 if (res == NULL) {
1248 dev_err(pm8058_chg.dev,
1249 "%s:couldnt find resource FASTCHG\n", __func__);
1250 goto err_out;
1251 } else {
Anirudh Ghayalc2019332011-11-12 06:29:10 +05301252 ret = request_irq(res->start,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001253 pm8058_chg_fastchg_handler,
1254 IRQF_TRIGGER_RISING, res->name, NULL);
1255 if (ret < 0) {
1256 dev_err(pm8058_chg.dev, "%s:couldnt request %d %d\n",
1257 __func__, res->start, ret);
1258 goto err_out;
1259 } else {
1260 pm8058_chg.pmic_chg_irq[FASTCHG_IRQ] = res->start;
1261 pm8058_chg_disable_irq(FASTCHG_IRQ);
1262 }
1263 }
1264
1265 res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "BATTTEMP");
1266 if (res == NULL) {
1267 dev_err(pm8058_chg.dev,
1268 "%s:couldnt find resource CHG_END\n", __func__);
1269 goto err_out;
1270 } else {
Anirudh Ghayalc2019332011-11-12 06:29:10 +05301271 ret = request_irq(res->start,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001272 pm8058_chg_batttemp_handler,
1273 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
1274 res->name, NULL);
1275 if (ret < 0) {
1276 dev_err(pm8058_chg.dev, "%s:couldnt request %d %d\n",
1277 __func__, res->start, ret);
1278 goto err_out;
1279 } else {
1280 pm8058_chg.pmic_chg_irq[BATTTEMP_IRQ] = res->start;
1281 pm8058_chg_disable_irq(BATTTEMP_IRQ);
1282 }
1283 }
1284
1285 res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
1286 "BATT_REPLACE");
1287 if (res == NULL) {
1288 dev_err(pm8058_chg.dev,
1289 "%s:couldnt find resource BATT_REPLACE\n", __func__);
1290 goto err_out;
1291 } else {
Anirudh Ghayalc2019332011-11-12 06:29:10 +05301292 ret = request_irq(res->start,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001293 pm8058_chg_batt_replace_handler,
1294 IRQF_TRIGGER_RISING, res->name, NULL);
1295 if (ret < 0) {
1296 dev_err(pm8058_chg.dev, "%s:couldnt request %d %d\n",
1297 __func__, res->start, ret);
1298 goto err_out;
1299 } else {
1300 pm8058_chg.pmic_chg_irq[BATT_REPLACE_IRQ] = res->start;
1301 pm8058_chg_disable_irq(BATT_REPLACE_IRQ);
1302 }
1303 }
1304
1305 res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "BATTCONNECT");
1306 if (res == NULL) {
1307 dev_err(pm8058_chg.dev,
1308 "%s:couldnt find resource BATTCONNECT\n", __func__);
1309 goto err_out;
1310 } else {
Anirudh Ghayalc2019332011-11-12 06:29:10 +05301311 ret = request_irq(res->start,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001312 pm8058_chg_battconnect_handler,
1313 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
1314 res->name, NULL);
1315 if (ret < 0) {
1316 dev_err(pm8058_chg.dev, "%s:couldnt request %d %d\n",
1317 __func__, res->start, ret);
1318 goto err_out;
1319 } else {
1320 pm8058_chg.pmic_chg_irq[BATTCONNECT_IRQ] = res->start;
1321 pm8058_chg_disable_irq(BATTCONNECT_IRQ);
1322 }
1323 }
1324
1325 res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "VBATDET");
1326 if (res == NULL) {
1327 dev_err(pm8058_chg.dev,
1328 "%s:couldnt find resource VBATDET\n", __func__);
1329 goto err_out;
1330 } else {
1331 ret = request_threaded_irq(res->start, NULL,
1332 pm8058_chg_vbatdet_handler,
1333 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
1334 res->name, NULL);
1335 if (ret < 0) {
1336 dev_err(pm8058_chg.dev, "%s:couldnt request %d %d\n",
1337 __func__, res->start, ret);
1338 goto err_out;
1339 } else {
1340 pm8058_chg.pmic_chg_irq[VBATDET_IRQ] = res->start;
1341 pm8058_chg_disable_irq(VBATDET_IRQ);
1342 }
1343 }
1344
1345 return 0;
1346
1347err_out:
1348 free_irqs();
1349 return -EINVAL;
1350}
1351
1352static int pm8058_get_charge_batt(void)
1353{
1354 u8 temp;
1355 int rc;
1356
Anirudh Ghayalc2019332011-11-12 06:29:10 +05301357 rc = pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_CNTRL, &temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001358 if (rc)
1359 return rc;
1360
1361 temp &= BIT(CHG_CHARGE_BAT);
1362 if (temp)
1363 temp = 1;
1364 return temp;
1365}
1366EXPORT_SYMBOL(pm8058_get_charge_batt);
1367
1368static int pm8058_set_charge_batt(int on)
1369{
1370 u8 temp;
1371 int rc;
1372
Anirudh Ghayalc2019332011-11-12 06:29:10 +05301373 rc = pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_CNTRL, &temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001374 if (rc)
1375 return rc;
1376 if (on)
1377 temp |= BIT(CHG_CHARGE_BAT);
1378 else
1379 temp &= ~BIT(CHG_CHARGE_BAT);
1380
Anirudh Ghayalc2019332011-11-12 06:29:10 +05301381 return pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_CNTRL, temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001382
1383}
1384EXPORT_SYMBOL(pm8058_set_charge_batt);
1385
1386static int get_charge_batt(void *data, u64 * val)
1387{
1388 int ret;
1389
1390 ret = pm8058_get_charge_batt();
1391 if (ret < 0)
1392 return ret;
1393
1394 *val = ret;
1395 return 0;
1396}
1397
1398static int set_charge_batt(void *data, u64 val)
1399{
1400 return pm8058_set_charge_batt(val);
1401}
1402DEFINE_SIMPLE_ATTRIBUTE(fet_fops, get_charge_batt, set_charge_batt, "%llu\n");
1403
1404static void pm8058_chg_determine_initial_state(void)
1405{
1406 if (is_chg_plugged_in()) {
1407 pm8058_chg.present = 1;
1408 msm_charger_notify_event(&usb_hw_chg, CHG_INSERTED_EVENT);
1409 dev_info(pm8058_chg.dev, "%s charger present\n", __func__);
1410 } else {
1411 pm8058_chg.present = 0;
1412 dev_info(pm8058_chg.dev, "%s charger absent\n", __func__);
1413 }
1414 pm8058_chg_enable_irq(CHGVAL_IRQ);
1415}
1416
1417static int pm8058_stop_charging(struct msm_hardware_charger *hw_chg)
1418{
1419 int ret;
1420
1421 dev_info(pm8058_chg.dev, "%s stopping charging\n", __func__);
Abhijeet Dharmapurikar2dae2392011-07-14 18:31:28 -07001422
1423 /* disable the irqs enabled while charging */
1424 pm8058_chg_disable_irq(AUTO_CHGFAIL_IRQ);
1425 pm8058_chg_disable_irq(CHGHOT_IRQ);
1426 pm8058_chg_disable_irq(AUTO_CHGDONE_IRQ);
1427 pm8058_chg_disable_irq(FASTCHG_IRQ);
1428 pm8058_chg_disable_irq(CHG_END_IRQ);
1429 pm8058_chg_disable_irq(VBATDET_IRQ);
1430 pm8058_chg_disable_irq(VBATDET_LOW_IRQ);
1431
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001432 cancel_delayed_work_sync(&pm8058_chg.veoc_begin_work);
1433 cancel_delayed_work_sync(&pm8058_chg.check_vbat_low_work);
1434 cancel_delayed_work_sync(&pm8058_chg.chg_done_check_work);
1435 cancel_delayed_work_sync(&pm8058_chg.charging_check_work);
1436
1437 ret = pm_chg_get_rt_status(pm8058_chg.pmic_chg_irq[FASTCHG_IRQ]);
1438 if (ret == 1)
1439 pm_chg_suspend(1);
1440 else
1441 dev_err(pm8058_chg.dev,
1442 "%s called when not fast-charging\n", __func__);
1443
1444 pm_chg_failed_clear(1);
1445
1446 pm8058_chg.waiting_for_veoc = 0;
1447 pm8058_chg.waiting_for_topoff = 0;
1448
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001449 if (pm8058_chg.voter)
1450 msm_xo_mode_vote(pm8058_chg.voter, MSM_XO_MODE_OFF);
1451
1452 return 0;
1453}
1454
1455static int get_status(void *data, u64 * val)
1456{
1457 *val = pm8058_chg.current_charger_current;
1458 return 0;
1459}
1460
1461static int set_status(void *data, u64 val)
1462{
1463
1464 pm8058_chg.current_charger_current = val;
1465 if (pm8058_chg.current_charger_current)
1466 pm8058_start_charging(NULL,
1467 AUTO_CHARGING_VMAXSEL,
1468 pm8058_chg.current_charger_current);
1469 else
1470 pm8058_stop_charging(NULL);
1471 return 0;
1472}
1473DEFINE_SIMPLE_ATTRIBUTE(chg_fops, get_status, set_status, "%llu\n");
1474
1475static int set_disable_status_param(const char *val, struct kernel_param *kp)
1476{
1477 int ret;
1478
1479 ret = param_set_int(val, kp);
1480 if (ret)
1481 return ret;
1482
1483 if (pm8058_chg.inited && pm8058_chg.disabled) {
1484 /*
1485 * stop_charging is called during usb suspend
1486 * act as the usb is removed by disabling auto and enum
1487 */
1488 pm_chg_enum_done_enable(0);
1489 pm_chg_auto_disable(1);
1490 pm8058_stop_charging(NULL);
1491 }
1492 return 0;
1493}
1494module_param_call(disabled, set_disable_status_param, param_get_uint,
1495 &(pm8058_chg.disabled), 0644);
1496
1497static int pm8058_charging_switched(struct msm_hardware_charger *hw_chg)
1498{
1499 u8 temp;
1500
1501 temp = 0xA3;
Anirudh Ghayalc2019332011-11-12 06:29:10 +05301502 pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_TEST_2, temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001503 temp = 0x84;
Anirudh Ghayalc2019332011-11-12 06:29:10 +05301504 pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_TEST_2, temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001505 msleep(2);
1506 temp = 0x80;
Anirudh Ghayalc2019332011-11-12 06:29:10 +05301507 pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_TEST_2, temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001508 return 0;
1509}
1510
1511static int get_reg(void *data, u64 * val)
1512{
1513 int i = (int)data;
1514 int ret;
1515 u8 temp;
1516
Anirudh Ghayalc2019332011-11-12 06:29:10 +05301517 ret = pm8xxx_readb(pm8058_chg.dev->parent, i, &temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001518 if (ret)
1519 return -EAGAIN;
1520 *val = temp;
1521 return 0;
1522}
1523
1524static int set_reg(void *data, u64 val)
1525{
1526 int i = (int)data;
1527 int ret;
1528 u8 temp;
1529
1530 temp = (u8) val;
Anirudh Ghayalc2019332011-11-12 06:29:10 +05301531 ret = pm8xxx_writeb(pm8058_chg.dev->parent, i, temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001532 mb();
1533 if (ret)
1534 return -EAGAIN;
1535 return 0;
1536}
1537
1538DEFINE_SIMPLE_ATTRIBUTE(reg_fops, get_reg, set_reg, "%llu\n");
1539
1540#ifdef CONFIG_DEBUG_FS
1541static void create_debugfs_entries(void)
1542{
1543 pm8058_chg.dent = debugfs_create_dir("pm8058_usb_chg", NULL);
1544
1545 if (IS_ERR(pm8058_chg.dent)) {
1546 pr_err("pmic charger couldnt create debugfs dir\n");
1547 return;
1548 }
1549
1550 debugfs_create_file("CHG_CNTRL", 0644, pm8058_chg.dent,
1551 (void *)PM8058_CHG_CNTRL, &reg_fops);
1552 debugfs_create_file("CHG_CNTRL_2", 0644, pm8058_chg.dent,
1553 (void *)PM8058_CHG_CNTRL_2, &reg_fops);
1554 debugfs_create_file("CHG_VMAX_SEL", 0644, pm8058_chg.dent,
1555 (void *)PM8058_CHG_VMAX_SEL, &reg_fops);
1556 debugfs_create_file("CHG_VBAT_DET", 0644, pm8058_chg.dent,
1557 (void *)PM8058_CHG_VBAT_DET, &reg_fops);
1558 debugfs_create_file("CHG_IMAX", 0644, pm8058_chg.dent,
1559 (void *)PM8058_CHG_IMAX, &reg_fops);
1560 debugfs_create_file("CHG_TRICKLE", 0644, pm8058_chg.dent,
1561 (void *)PM8058_CHG_TRICKLE, &reg_fops);
1562 debugfs_create_file("CHG_ITERM", 0644, pm8058_chg.dent,
1563 (void *)PM8058_CHG_ITERM, &reg_fops);
1564 debugfs_create_file("CHG_TTRKL_MAX", 0644, pm8058_chg.dent,
1565 (void *)PM8058_CHG_TTRKL_MAX, &reg_fops);
1566 debugfs_create_file("CHG_TCHG_MAX", 0644, pm8058_chg.dent,
1567 (void *)PM8058_CHG_TCHG_MAX, &reg_fops);
1568 debugfs_create_file("CHG_TEMP_THRESH", 0644, pm8058_chg.dent,
1569 (void *)PM8058_CHG_TEMP_THRESH, &reg_fops);
1570 debugfs_create_file("CHG_TEMP_REG", 0644, pm8058_chg.dent,
1571 (void *)PM8058_CHG_TEMP_REG, &reg_fops);
1572
1573 debugfs_create_file("FSM_STATE", 0644, pm8058_chg.dent, NULL,
1574 &fsm_fops);
1575
1576 debugfs_create_file("stop", 0644, pm8058_chg.dent, NULL,
1577 &chg_fops);
1578
1579 if (pm8058_chg.pmic_chg_irq[CHGVAL_IRQ])
1580 debugfs_create_file("CHGVAL", 0444, pm8058_chg.dent,
1581 (void *)pm8058_chg.pmic_chg_irq[CHGVAL_IRQ],
1582 &rt_fops);
1583
1584 if (pm8058_chg.pmic_chg_irq[CHGINVAL_IRQ])
1585 debugfs_create_file("CHGINVAL", 0444, pm8058_chg.dent, (void *)
1586 pm8058_chg.pmic_chg_irq[CHGINVAL_IRQ],
1587 &rt_fops);
1588 if (pm8058_chg.pmic_chg_irq[CHGILIM_IRQ])
1589 debugfs_create_file("CHGILIM", 0444, pm8058_chg.dent, (void *)
1590 pm8058_chg.pmic_chg_irq[CHGILIM_IRQ],
1591 &rt_fops);
1592 if (pm8058_chg.pmic_chg_irq[VCP_IRQ])
1593 debugfs_create_file("VCP", 0444, pm8058_chg.dent,
1594 (void *)pm8058_chg.pmic_chg_irq[VCP_IRQ],
1595 &rt_fops);
1596 if (pm8058_chg.pmic_chg_irq[ATC_DONE_IRQ])
1597 debugfs_create_file("ATC_DONE", 0444, pm8058_chg.dent, (void *)
1598 pm8058_chg.pmic_chg_irq[ATC_DONE_IRQ],
1599 &rt_fops);
1600 if (pm8058_chg.pmic_chg_irq[ATCFAIL_IRQ])
1601 debugfs_create_file("ATCFAIL", 0444, pm8058_chg.dent, (void *)
1602 pm8058_chg.pmic_chg_irq[ATCFAIL_IRQ],
1603 &rt_fops);
1604 if (pm8058_chg.pmic_chg_irq[AUTO_CHGDONE_IRQ])
1605 debugfs_create_file("AUTO_CHGDONE", 0444, pm8058_chg.dent,
1606 (void *)
1607 pm8058_chg.pmic_chg_irq[AUTO_CHGDONE_IRQ],
1608 &rt_fops);
1609 if (pm8058_chg.pmic_chg_irq[AUTO_CHGFAIL_IRQ])
1610 debugfs_create_file("AUTO_CHGFAIL", 0444, pm8058_chg.dent,
1611 (void *)
1612 pm8058_chg.pmic_chg_irq[AUTO_CHGFAIL_IRQ],
1613 &rt_fops);
1614 if (pm8058_chg.pmic_chg_irq[CHGSTATE_IRQ])
1615 debugfs_create_file("CHGSTATE", 0444, pm8058_chg.dent, (void *)
1616 pm8058_chg.pmic_chg_irq[CHGSTATE_IRQ],
1617 &rt_fops);
1618 if (pm8058_chg.pmic_chg_irq[FASTCHG_IRQ])
1619 debugfs_create_file("FASTCHG", 0444, pm8058_chg.dent, (void *)
1620 pm8058_chg.pmic_chg_irq[FASTCHG_IRQ],
1621 &rt_fops);
1622 if (pm8058_chg.pmic_chg_irq[CHG_END_IRQ])
1623 debugfs_create_file("CHG_END", 0444, pm8058_chg.dent, (void *)
1624 pm8058_chg.pmic_chg_irq[CHG_END_IRQ],
1625 &rt_fops);
1626 if (pm8058_chg.pmic_chg_irq[BATTTEMP_IRQ])
1627 debugfs_create_file("BATTTEMP", 0444, pm8058_chg.dent, (void *)
1628 pm8058_chg.pmic_chg_irq[BATTTEMP_IRQ],
1629 &rt_fops);
1630 if (pm8058_chg.pmic_chg_irq[CHGHOT_IRQ])
1631 debugfs_create_file("CHGHOT", 0444, pm8058_chg.dent,
1632 (void *)pm8058_chg.pmic_chg_irq[CHGHOT_IRQ],
1633 &rt_fops);
1634 if (pm8058_chg.pmic_chg_irq[CHGTLIMIT_IRQ])
1635 debugfs_create_file("CHGTLIMIT", 0444, pm8058_chg.dent, (void *)
1636 pm8058_chg.pmic_chg_irq[CHGTLIMIT_IRQ],
1637 &rt_fops);
1638 if (pm8058_chg.pmic_chg_irq[CHG_GONE_IRQ])
1639 debugfs_create_file("CHG_GONE", 0444, pm8058_chg.dent, (void *)
1640 pm8058_chg.pmic_chg_irq[CHG_GONE_IRQ],
1641 &rt_fops);
1642 if (pm8058_chg.pmic_chg_irq[VCPMAJOR_IRQ])
1643 debugfs_create_file("VCPMAJOR", 0444, pm8058_chg.dent, (void *)
1644 pm8058_chg.pmic_chg_irq[VCPMAJOR_IRQ],
1645 &rt_fops);
1646 if (pm8058_chg.pmic_chg_irq[BATFET_IRQ])
1647 debugfs_create_file("BATFET", 0444, pm8058_chg.dent,
1648 (void *)pm8058_chg.pmic_chg_irq[BATFET_IRQ],
1649 &rt_fops);
1650 if (pm8058_chg.pmic_chg_irq[BATT_REPLACE_IRQ])
1651 debugfs_create_file("BATT_REPLACE", 0444, pm8058_chg.dent,
1652 (void *)
1653 pm8058_chg.pmic_chg_irq[BATT_REPLACE_IRQ],
1654 &rt_fops);
1655 if (pm8058_chg.pmic_chg_irq[BATTCONNECT_IRQ])
1656 debugfs_create_file("BATTCONNECT", 0444, pm8058_chg.dent,
1657 (void *)
1658 pm8058_chg.pmic_chg_irq[BATTCONNECT_IRQ],
1659 &rt_fops);
1660 if (pm8058_chg.pmic_chg_irq[VBATDET_IRQ])
1661 debugfs_create_file("VBATDET", 0444, pm8058_chg.dent, (void *)
1662 pm8058_chg.pmic_chg_irq[VBATDET_IRQ],
1663 &rt_fops);
1664 if (pm8058_chg.pmic_chg_irq[VBATDET_LOW_IRQ])
1665 debugfs_create_file("VBATDET_LOW", 0444, pm8058_chg.dent,
1666 (void *)
1667 pm8058_chg.pmic_chg_irq[VBATDET_LOW_IRQ],
1668 &rt_fops);
1669 debugfs_create_file("CHARGE_BATT", 0444, pm8058_chg.dent,
1670 NULL,
1671 &fet_fops);
1672}
1673#else
1674static inline void create_debugfs_entries(void)
1675{
1676}
1677#endif
1678
1679static void remove_debugfs_entries(void)
1680{
1681 debugfs_remove_recursive(pm8058_chg.dent);
1682}
1683
1684static struct msm_hardware_charger usb_hw_chg = {
Abhijeet Dharmapurikar0758e1e2011-07-14 18:11:41 -07001685 .type = CHG_TYPE_USB,
1686 .rating = 1,
1687 .name = "pm8058-usb",
1688 .start_charging = pm8058_start_charging,
1689 .stop_charging = pm8058_stop_charging,
1690 .charging_switched = pm8058_charging_switched,
1691 .start_system_current = pm8058_start_system_current,
1692 .stop_system_current = pm8058_stop_system_current,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001693};
1694
1695static int batt_read_adc(int channel, int *mv_reading)
1696{
1697 int ret;
1698 void *h;
1699 struct adc_chan_result adc_chan_result;
1700 struct completion conv_complete_evt;
1701
1702 pr_debug("%s: called for %d\n", __func__, channel);
1703 ret = adc_channel_open(channel, &h);
1704 if (ret) {
1705 pr_err("%s: couldnt open channel %d ret=%d\n",
1706 __func__, channel, ret);
1707 goto out;
1708 }
1709 init_completion(&conv_complete_evt);
1710 ret = adc_channel_request_conv(h, &conv_complete_evt);
1711 if (ret) {
1712 pr_err("%s: couldnt request conv channel %d ret=%d\n",
1713 __func__, channel, ret);
1714 goto out;
1715 }
1716 wait_for_completion(&conv_complete_evt);
1717 ret = adc_channel_read_result(h, &adc_chan_result);
1718 if (ret) {
1719 pr_err("%s: couldnt read result channel %d ret=%d\n",
1720 __func__, channel, ret);
1721 goto out;
1722 }
1723 ret = adc_channel_close(h);
1724 if (ret) {
1725 pr_err("%s: couldnt close channel %d ret=%d\n",
1726 __func__, channel, ret);
1727 }
1728 if (mv_reading)
1729 *mv_reading = adc_chan_result.measurement;
1730
1731 pr_debug("%s: done for %d\n", __func__, channel);
1732 return adc_chan_result.physical;
1733out:
1734 pr_debug("%s: done for %d\n", __func__, channel);
1735 return -EINVAL;
1736
1737}
1738
1739#define BATT_THERM_OPEN_MV 2000
1740static int pm8058_is_battery_present(void)
1741{
1742 int mv_reading;
1743
1744 mv_reading = 0;
1745 batt_read_adc(CHANNEL_ADC_BATT_THERM, &mv_reading);
1746 pr_debug("%s: therm_raw is %d\n", __func__, mv_reading);
1747 if (mv_reading > 0 && mv_reading < BATT_THERM_OPEN_MV)
1748 return 1;
1749
1750 return 0;
1751}
1752
1753static int pm8058_get_battery_temperature(void)
1754{
1755 return batt_read_adc(CHANNEL_ADC_BATT_THERM, NULL);
1756}
1757
1758#define BATT_THERM_OPERATIONAL_MAX_CELCIUS 40
1759#define BATT_THERM_OPERATIONAL_MIN_CELCIUS 0
1760static int pm8058_is_battery_temp_within_range(void)
1761{
1762 int therm_celcius;
1763
1764 therm_celcius = pm8058_get_battery_temperature();
1765 pr_debug("%s: therm_celcius is %d\n", __func__, therm_celcius);
1766 if (therm_celcius > 0
1767 && therm_celcius > BATT_THERM_OPERATIONAL_MIN_CELCIUS
1768 && therm_celcius < BATT_THERM_OPERATIONAL_MAX_CELCIUS)
1769 return 1;
1770
1771 return 0;
1772}
1773
1774#define BATT_ID_MAX_MV 800
1775#define BATT_ID_MIN_MV 600
1776static int pm8058_is_battery_id_valid(void)
1777{
1778 int batt_id_mv;
1779
1780 batt_id_mv = batt_read_adc(CHANNEL_ADC_BATT_ID, NULL);
1781 pr_debug("%s: batt_id_mv is %d\n", __func__, batt_id_mv);
1782
1783 /*
1784 * The readings are not in range
1785 * assume battery is present for now
1786 */
1787 return 1;
1788
1789 if (batt_id_mv > 0
1790 && batt_id_mv > BATT_ID_MIN_MV
1791 && batt_id_mv < BATT_ID_MAX_MV)
1792 return 1;
1793
1794 return 0;
1795}
1796
1797/* returns voltage in mV */
1798static int pm8058_get_battery_mvolts(void)
1799{
1800 int vbatt_mv;
1801
1802 vbatt_mv = batt_read_adc(CHANNEL_ADC_VBATT, NULL);
1803 pr_debug("%s: vbatt_mv is %d\n", __func__, vbatt_mv);
1804 if (vbatt_mv > 0)
1805 return vbatt_mv;
1806 /*
1807 * return 0 to tell the upper layers
1808 * we couldnt read the battery voltage
1809 */
1810 return 0;
1811}
1812
1813static int msm_battery_gauge_alarm_notify(struct notifier_block *nb,
1814 unsigned long status, void *unused)
1815{
1816 int rc;
1817
1818 pr_info("%s: status: %lu\n", __func__, status);
1819
1820 switch (status) {
1821 case 0:
1822 dev_err(pm8058_chg.dev,
1823 "%s: spurious interrupt\n", __func__);
1824 break;
1825 /* expected case - trip of low threshold */
1826 case 1:
Anirudh Ghayalc2019332011-11-12 06:29:10 +05301827 rc = pm8xxx_batt_alarm_disable(
1828 PM8XXX_BATT_ALARM_UPPER_COMPARATOR);
1829 if (!rc)
1830 rc = pm8xxx_batt_alarm_disable(
1831 PM8XXX_BATT_ALARM_LOWER_COMPARATOR);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001832 if (rc)
1833 dev_err(pm8058_chg.dev,
1834 "%s: unable to set alarm state\n", __func__);
1835 msm_charger_notify_event(NULL, CHG_BATT_NEEDS_RECHARGING);
1836 break;
1837 case 2:
1838 dev_err(pm8058_chg.dev,
1839 "%s: trip of high threshold\n", __func__);
1840 break;
1841 default:
1842 dev_err(pm8058_chg.dev,
1843 "%s: error received\n", __func__);
1844 };
1845
1846 return 0;
1847}
1848
1849static int pm8058_monitor_for_recharging(void)
1850{
Anirudh Ghayalc2019332011-11-12 06:29:10 +05301851 int rc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001852 /* enable low comparator */
Anirudh Ghayalc2019332011-11-12 06:29:10 +05301853 rc = pm8xxx_batt_alarm_disable(PM8XXX_BATT_ALARM_UPPER_COMPARATOR);
1854 if (!rc)
1855 return pm8xxx_batt_alarm_enable(
1856 PM8XXX_BATT_ALARM_LOWER_COMPARATOR);
1857
1858 return rc;
1859
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001860}
1861
1862static struct msm_battery_gauge pm8058_batt_gauge = {
1863 .get_battery_mvolts = pm8058_get_battery_mvolts,
1864 .get_battery_temperature = pm8058_get_battery_temperature,
1865 .is_battery_present = pm8058_is_battery_present,
1866 .is_battery_temp_within_range = pm8058_is_battery_temp_within_range,
1867 .is_battery_id_valid = pm8058_is_battery_id_valid,
1868 .monitor_for_recharging = pm8058_monitor_for_recharging,
1869};
1870
1871static int pm8058_usb_voltage_lower_limit(void)
1872{
1873 u8 temp, old;
1874 int ret = 0;
1875
1876 temp = 0x10;
Anirudh Ghayalc2019332011-11-12 06:29:10 +05301877 ret |= pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_TEST, temp);
1878 ret |= pm8xxx_readb(pm8058_chg.dev->parent, PM8058_CHG_TEST, &old);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001879 old = old & ~BIT(IGNORE_LL);
1880 temp = 0x90 | (0xF & old);
Anirudh Ghayalc2019332011-11-12 06:29:10 +05301881 ret |= pm8xxx_writeb(pm8058_chg.dev->parent, PM8058_CHG_TEST, temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001882
1883 return ret;
1884}
1885
1886static int __devinit pm8058_charger_probe(struct platform_device *pdev)
1887{
Terence Hampson13163082011-08-09 10:17:06 -04001888 struct pmic8058_charger_data *pdata;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001889 int rc = 0;
1890
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001891 pm8058_chg.pdata = pdev->dev.platform_data;
1892 pm8058_chg.dev = &pdev->dev;
Terence Hampson13163082011-08-09 10:17:06 -04001893 pdata = (struct pmic8058_charger_data *) pm8058_chg.pdata;
1894
Anirudh Ghayalc2019332011-11-12 06:29:10 +05301895 if (pdata == NULL) {
1896 pr_err("%s: pdata not present\n", __func__);
1897 return -EINVAL;
1898 }
1899
1900 if (pdata->charger_data_valid) {
Terence Hampson13163082011-08-09 10:17:06 -04001901 usb_hw_chg.type = pdata->charger_type;
1902 chg_data.charger_type = pdata->charger_type;
1903 chg_data.max_source_current = pdata->max_source_current;
1904 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001905
1906 rc = request_irqs(pdev);
1907 if (rc) {
1908 pr_err("%s: couldnt register interrupts\n", __func__);
1909 goto out;
1910 }
1911
1912 rc = pm8058_usb_voltage_lower_limit();
1913 if (rc) {
1914 pr_err("%s: couldnt set ignore lower limit bit to 0\n",
1915 __func__);
1916 goto free_irq;
1917 }
1918
1919 rc = msm_charger_register(&usb_hw_chg);
1920 if (rc) {
1921 pr_err("%s: msm_charger_register failed ret=%d\n",
1922 __func__, rc);
1923 goto free_irq;
1924 }
1925
1926 pm_chg_batt_temp_disable(0);
1927 msm_battery_gauge_register(&pm8058_batt_gauge);
1928 __dump_chg_regs();
1929
1930 create_debugfs_entries();
1931 INIT_DELAYED_WORK(&pm8058_chg.veoc_begin_work, veoc_begin_work);
1932 INIT_DELAYED_WORK(&pm8058_chg.check_vbat_low_work, vbat_low_work);
1933 INIT_DELAYED_WORK(&pm8058_chg.chg_done_check_work, chg_done_check_work);
1934 INIT_DELAYED_WORK(&pm8058_chg.charging_check_work, charging_check_work);
1935
1936 /* determine what state the charger is in */
1937 pm8058_chg_determine_initial_state();
1938
1939 pm8058_chg_enable_irq(BATTTEMP_IRQ);
1940 pm8058_chg_enable_irq(BATTCONNECT_IRQ);
1941
Anirudh Ghayalc2019332011-11-12 06:29:10 +05301942 rc = pm8xxx_batt_alarm_disable(PM8XXX_BATT_ALARM_UPPER_COMPARATOR);
1943 if (!rc)
1944 rc = pm8xxx_batt_alarm_disable(
1945 PM8XXX_BATT_ALARM_LOWER_COMPARATOR);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001946 if (rc) {
1947 pr_err("%s: unable to set batt alarm state\n", __func__);
1948 goto free_irq;
1949 }
1950
1951 /*
1952 * The batt-alarm driver requires sane values for both min / max,
1953 * regardless of whether they're both activated.
1954 */
Anirudh Ghayalc2019332011-11-12 06:29:10 +05301955 rc = pm8xxx_batt_alarm_threshold_set(
1956 PM8XXX_BATT_ALARM_LOWER_COMPARATOR, resume_mv);
1957 if (!rc)
1958 rc = pm8xxx_batt_alarm_threshold_set(
1959 PM8XXX_BATT_ALARM_UPPER_COMPARATOR, 4300);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001960 if (rc) {
1961 pr_err("%s: unable to set batt alarm threshold\n", __func__);
1962 goto free_irq;
1963 }
1964
Anirudh Ghayalc2019332011-11-12 06:29:10 +05301965 rc = pm8xxx_batt_alarm_hold_time_set(
1966 PM8XXX_BATT_ALARM_HOLD_TIME_16_MS);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001967 if (rc) {
1968 pr_err("%s: unable to set batt alarm hold time\n", __func__);
1969 goto free_irq;
1970 }
1971
1972 /* PWM enabled at 2Hz */
Anirudh Ghayalc2019332011-11-12 06:29:10 +05301973 rc = pm8xxx_batt_alarm_pwm_rate_set(1, 7, 4);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001974 if (rc) {
1975 pr_err("%s: unable to set batt alarm pwm rate\n", __func__);
1976 goto free_irq;
1977 }
1978
Anirudh Ghayalc2019332011-11-12 06:29:10 +05301979 rc = pm8xxx_batt_alarm_register_notifier(&alarm_notifier);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001980 if (rc) {
1981 pr_err("%s: unable to register alarm notifier\n", __func__);
1982 goto free_irq;
1983 }
1984
1985 pm8058_chg.inited = 1;
1986
1987 return 0;
1988
1989free_irq:
1990 free_irqs();
1991out:
1992 return rc;
1993}
1994
1995static int __devexit pm8058_charger_remove(struct platform_device *pdev)
1996{
1997 struct pm8058_charger_chip *chip = platform_get_drvdata(pdev);
1998 int rc;
1999
2000 msm_charger_notify_event(&usb_hw_chg, CHG_REMOVED_EVENT);
2001 msm_charger_unregister(&usb_hw_chg);
2002 cancel_delayed_work_sync(&pm8058_chg.veoc_begin_work);
2003 cancel_delayed_work_sync(&pm8058_chg.check_vbat_low_work);
2004 cancel_delayed_work_sync(&pm8058_chg.charging_check_work);
2005 free_irqs();
2006 remove_debugfs_entries();
2007 kfree(chip);
2008
Anirudh Ghayalc2019332011-11-12 06:29:10 +05302009 rc = pm8xxx_batt_alarm_disable(PM8XXX_BATT_ALARM_UPPER_COMPARATOR);
2010 if (!rc)
2011 rc = pm8xxx_batt_alarm_disable(
2012 PM8XXX_BATT_ALARM_LOWER_COMPARATOR);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002013 if (rc)
2014 pr_err("%s: unable to set batt alarm state\n", __func__);
2015
Anirudh Ghayalc2019332011-11-12 06:29:10 +05302016 rc |= pm8xxx_batt_alarm_unregister_notifier(&alarm_notifier);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002017 if (rc)
2018 pr_err("%s: unable to register alarm notifier\n", __func__);
2019 return rc;
2020}
2021
2022static struct platform_driver pm8058_charger_driver = {
2023 .probe = pm8058_charger_probe,
2024 .remove = __devexit_p(pm8058_charger_remove),
2025 .driver = {
2026 .name = "pm8058-charger",
2027 .owner = THIS_MODULE,
2028 },
2029};
2030
2031static int __init pm8058_charger_init(void)
2032{
2033 return platform_driver_register(&pm8058_charger_driver);
2034}
2035
2036static void __exit pm8058_charger_exit(void)
2037{
2038 platform_driver_unregister(&pm8058_charger_driver);
2039}
2040
2041late_initcall(pm8058_charger_init);
2042module_exit(pm8058_charger_exit);
2043
2044MODULE_LICENSE("GPL v2");
2045MODULE_DESCRIPTION("PMIC8058 BATTERY driver");
2046MODULE_VERSION("1.0");
2047MODULE_ALIAS("platform:pm8058_charger");