1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * iPAQ h1930/h1940/rx1950 battery controller driver
3*4882a593Smuzhiyun * Copyright (c) Vasily Khoruzhick
4*4882a593Smuzhiyun * Based on h1940_battery.c by Arnaud Patard
5*4882a593Smuzhiyun *
6*4882a593Smuzhiyun * This file is subject to the terms and conditions of the GNU General Public
7*4882a593Smuzhiyun * License. See the file COPYING in the main directory of this archive for
8*4882a593Smuzhiyun * more details.
9*4882a593Smuzhiyun *
10*4882a593Smuzhiyun */
11*4882a593Smuzhiyun
12*4882a593Smuzhiyun #include <linux/interrupt.h>
13*4882a593Smuzhiyun #include <linux/platform_device.h>
14*4882a593Smuzhiyun #include <linux/power_supply.h>
15*4882a593Smuzhiyun #include <linux/leds.h>
16*4882a593Smuzhiyun #include <linux/gpio.h>
17*4882a593Smuzhiyun #include <linux/err.h>
18*4882a593Smuzhiyun #include <linux/timer.h>
19*4882a593Smuzhiyun #include <linux/jiffies.h>
20*4882a593Smuzhiyun #include <linux/s3c_adc_battery.h>
21*4882a593Smuzhiyun #include <linux/errno.h>
22*4882a593Smuzhiyun #include <linux/init.h>
23*4882a593Smuzhiyun #include <linux/module.h>
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun #include <linux/soc/samsung/s3c-adc.h>
26*4882a593Smuzhiyun
27*4882a593Smuzhiyun #define BAT_POLL_INTERVAL 10000 /* ms */
28*4882a593Smuzhiyun #define JITTER_DELAY 500 /* ms */
29*4882a593Smuzhiyun
30*4882a593Smuzhiyun struct s3c_adc_bat {
31*4882a593Smuzhiyun struct power_supply *psy;
32*4882a593Smuzhiyun struct s3c_adc_client *client;
33*4882a593Smuzhiyun struct s3c_adc_bat_pdata *pdata;
34*4882a593Smuzhiyun int volt_value;
35*4882a593Smuzhiyun int cur_value;
36*4882a593Smuzhiyun unsigned int timestamp;
37*4882a593Smuzhiyun int level;
38*4882a593Smuzhiyun int status;
39*4882a593Smuzhiyun int cable_plugged:1;
40*4882a593Smuzhiyun };
41*4882a593Smuzhiyun
42*4882a593Smuzhiyun static struct delayed_work bat_work;
43*4882a593Smuzhiyun
s3c_adc_bat_ext_power_changed(struct power_supply * psy)44*4882a593Smuzhiyun static void s3c_adc_bat_ext_power_changed(struct power_supply *psy)
45*4882a593Smuzhiyun {
46*4882a593Smuzhiyun schedule_delayed_work(&bat_work,
47*4882a593Smuzhiyun msecs_to_jiffies(JITTER_DELAY));
48*4882a593Smuzhiyun }
49*4882a593Smuzhiyun
gather_samples(struct s3c_adc_client * client,int num,int channel)50*4882a593Smuzhiyun static int gather_samples(struct s3c_adc_client *client, int num, int channel)
51*4882a593Smuzhiyun {
52*4882a593Smuzhiyun int value, i;
53*4882a593Smuzhiyun
54*4882a593Smuzhiyun /* default to 1 if nothing is set */
55*4882a593Smuzhiyun if (num < 1)
56*4882a593Smuzhiyun num = 1;
57*4882a593Smuzhiyun
58*4882a593Smuzhiyun value = 0;
59*4882a593Smuzhiyun for (i = 0; i < num; i++)
60*4882a593Smuzhiyun value += s3c_adc_read(client, channel);
61*4882a593Smuzhiyun value /= num;
62*4882a593Smuzhiyun
63*4882a593Smuzhiyun return value;
64*4882a593Smuzhiyun }
65*4882a593Smuzhiyun
66*4882a593Smuzhiyun static enum power_supply_property s3c_adc_backup_bat_props[] = {
67*4882a593Smuzhiyun POWER_SUPPLY_PROP_VOLTAGE_NOW,
68*4882a593Smuzhiyun POWER_SUPPLY_PROP_VOLTAGE_MIN,
69*4882a593Smuzhiyun POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
70*4882a593Smuzhiyun };
71*4882a593Smuzhiyun
s3c_adc_backup_bat_get_property(struct power_supply * psy,enum power_supply_property psp,union power_supply_propval * val)72*4882a593Smuzhiyun static int s3c_adc_backup_bat_get_property(struct power_supply *psy,
73*4882a593Smuzhiyun enum power_supply_property psp,
74*4882a593Smuzhiyun union power_supply_propval *val)
75*4882a593Smuzhiyun {
76*4882a593Smuzhiyun struct s3c_adc_bat *bat = power_supply_get_drvdata(psy);
77*4882a593Smuzhiyun
78*4882a593Smuzhiyun if (!bat) {
79*4882a593Smuzhiyun dev_err(&psy->dev, "%s: no battery infos ?!\n", __func__);
80*4882a593Smuzhiyun return -EINVAL;
81*4882a593Smuzhiyun }
82*4882a593Smuzhiyun
83*4882a593Smuzhiyun if (bat->volt_value < 0 ||
84*4882a593Smuzhiyun jiffies_to_msecs(jiffies - bat->timestamp) >
85*4882a593Smuzhiyun BAT_POLL_INTERVAL) {
86*4882a593Smuzhiyun bat->volt_value = gather_samples(bat->client,
87*4882a593Smuzhiyun bat->pdata->backup_volt_samples,
88*4882a593Smuzhiyun bat->pdata->backup_volt_channel);
89*4882a593Smuzhiyun bat->volt_value *= bat->pdata->backup_volt_mult;
90*4882a593Smuzhiyun bat->timestamp = jiffies;
91*4882a593Smuzhiyun }
92*4882a593Smuzhiyun
93*4882a593Smuzhiyun switch (psp) {
94*4882a593Smuzhiyun case POWER_SUPPLY_PROP_VOLTAGE_NOW:
95*4882a593Smuzhiyun val->intval = bat->volt_value;
96*4882a593Smuzhiyun return 0;
97*4882a593Smuzhiyun case POWER_SUPPLY_PROP_VOLTAGE_MIN:
98*4882a593Smuzhiyun val->intval = bat->pdata->backup_volt_min;
99*4882a593Smuzhiyun return 0;
100*4882a593Smuzhiyun case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
101*4882a593Smuzhiyun val->intval = bat->pdata->backup_volt_max;
102*4882a593Smuzhiyun return 0;
103*4882a593Smuzhiyun default:
104*4882a593Smuzhiyun return -EINVAL;
105*4882a593Smuzhiyun }
106*4882a593Smuzhiyun }
107*4882a593Smuzhiyun
108*4882a593Smuzhiyun static const struct power_supply_desc backup_bat_desc = {
109*4882a593Smuzhiyun .name = "backup-battery",
110*4882a593Smuzhiyun .type = POWER_SUPPLY_TYPE_BATTERY,
111*4882a593Smuzhiyun .properties = s3c_adc_backup_bat_props,
112*4882a593Smuzhiyun .num_properties = ARRAY_SIZE(s3c_adc_backup_bat_props),
113*4882a593Smuzhiyun .get_property = s3c_adc_backup_bat_get_property,
114*4882a593Smuzhiyun .use_for_apm = 1,
115*4882a593Smuzhiyun };
116*4882a593Smuzhiyun
117*4882a593Smuzhiyun static struct s3c_adc_bat backup_bat;
118*4882a593Smuzhiyun
119*4882a593Smuzhiyun static enum power_supply_property s3c_adc_main_bat_props[] = {
120*4882a593Smuzhiyun POWER_SUPPLY_PROP_STATUS,
121*4882a593Smuzhiyun POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
122*4882a593Smuzhiyun POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN,
123*4882a593Smuzhiyun POWER_SUPPLY_PROP_CHARGE_NOW,
124*4882a593Smuzhiyun POWER_SUPPLY_PROP_VOLTAGE_NOW,
125*4882a593Smuzhiyun POWER_SUPPLY_PROP_CURRENT_NOW,
126*4882a593Smuzhiyun };
127*4882a593Smuzhiyun
calc_full_volt(int volt_val,int cur_val,int impedance)128*4882a593Smuzhiyun static int calc_full_volt(int volt_val, int cur_val, int impedance)
129*4882a593Smuzhiyun {
130*4882a593Smuzhiyun return volt_val + cur_val * impedance / 1000;
131*4882a593Smuzhiyun }
132*4882a593Smuzhiyun
charge_finished(struct s3c_adc_bat * bat)133*4882a593Smuzhiyun static int charge_finished(struct s3c_adc_bat *bat)
134*4882a593Smuzhiyun {
135*4882a593Smuzhiyun return bat->pdata->gpio_inverted ?
136*4882a593Smuzhiyun !gpio_get_value(bat->pdata->gpio_charge_finished) :
137*4882a593Smuzhiyun gpio_get_value(bat->pdata->gpio_charge_finished);
138*4882a593Smuzhiyun }
139*4882a593Smuzhiyun
s3c_adc_bat_get_property(struct power_supply * psy,enum power_supply_property psp,union power_supply_propval * val)140*4882a593Smuzhiyun static int s3c_adc_bat_get_property(struct power_supply *psy,
141*4882a593Smuzhiyun enum power_supply_property psp,
142*4882a593Smuzhiyun union power_supply_propval *val)
143*4882a593Smuzhiyun {
144*4882a593Smuzhiyun struct s3c_adc_bat *bat = power_supply_get_drvdata(psy);
145*4882a593Smuzhiyun
146*4882a593Smuzhiyun int new_level;
147*4882a593Smuzhiyun int full_volt;
148*4882a593Smuzhiyun const struct s3c_adc_bat_thresh *lut;
149*4882a593Smuzhiyun unsigned int lut_size;
150*4882a593Smuzhiyun
151*4882a593Smuzhiyun if (!bat) {
152*4882a593Smuzhiyun dev_err(&psy->dev, "no battery infos ?!\n");
153*4882a593Smuzhiyun return -EINVAL;
154*4882a593Smuzhiyun }
155*4882a593Smuzhiyun
156*4882a593Smuzhiyun lut = bat->pdata->lut_noac;
157*4882a593Smuzhiyun lut_size = bat->pdata->lut_noac_cnt;
158*4882a593Smuzhiyun
159*4882a593Smuzhiyun if (bat->volt_value < 0 || bat->cur_value < 0 ||
160*4882a593Smuzhiyun jiffies_to_msecs(jiffies - bat->timestamp) >
161*4882a593Smuzhiyun BAT_POLL_INTERVAL) {
162*4882a593Smuzhiyun bat->volt_value = gather_samples(bat->client,
163*4882a593Smuzhiyun bat->pdata->volt_samples,
164*4882a593Smuzhiyun bat->pdata->volt_channel) * bat->pdata->volt_mult;
165*4882a593Smuzhiyun bat->cur_value = gather_samples(bat->client,
166*4882a593Smuzhiyun bat->pdata->current_samples,
167*4882a593Smuzhiyun bat->pdata->current_channel) * bat->pdata->current_mult;
168*4882a593Smuzhiyun bat->timestamp = jiffies;
169*4882a593Smuzhiyun }
170*4882a593Smuzhiyun
171*4882a593Smuzhiyun if (bat->cable_plugged &&
172*4882a593Smuzhiyun ((bat->pdata->gpio_charge_finished < 0) ||
173*4882a593Smuzhiyun !charge_finished(bat))) {
174*4882a593Smuzhiyun lut = bat->pdata->lut_acin;
175*4882a593Smuzhiyun lut_size = bat->pdata->lut_acin_cnt;
176*4882a593Smuzhiyun }
177*4882a593Smuzhiyun
178*4882a593Smuzhiyun new_level = 100000;
179*4882a593Smuzhiyun full_volt = calc_full_volt((bat->volt_value / 1000),
180*4882a593Smuzhiyun (bat->cur_value / 1000), bat->pdata->internal_impedance);
181*4882a593Smuzhiyun
182*4882a593Smuzhiyun if (full_volt < calc_full_volt(lut->volt, lut->cur,
183*4882a593Smuzhiyun bat->pdata->internal_impedance)) {
184*4882a593Smuzhiyun lut_size--;
185*4882a593Smuzhiyun while (lut_size--) {
186*4882a593Smuzhiyun int lut_volt1;
187*4882a593Smuzhiyun int lut_volt2;
188*4882a593Smuzhiyun
189*4882a593Smuzhiyun lut_volt1 = calc_full_volt(lut[0].volt, lut[0].cur,
190*4882a593Smuzhiyun bat->pdata->internal_impedance);
191*4882a593Smuzhiyun lut_volt2 = calc_full_volt(lut[1].volt, lut[1].cur,
192*4882a593Smuzhiyun bat->pdata->internal_impedance);
193*4882a593Smuzhiyun if (full_volt < lut_volt1 && full_volt >= lut_volt2) {
194*4882a593Smuzhiyun new_level = (lut[1].level +
195*4882a593Smuzhiyun (lut[0].level - lut[1].level) *
196*4882a593Smuzhiyun (full_volt - lut_volt2) /
197*4882a593Smuzhiyun (lut_volt1 - lut_volt2)) * 1000;
198*4882a593Smuzhiyun break;
199*4882a593Smuzhiyun }
200*4882a593Smuzhiyun new_level = lut[1].level * 1000;
201*4882a593Smuzhiyun lut++;
202*4882a593Smuzhiyun }
203*4882a593Smuzhiyun }
204*4882a593Smuzhiyun
205*4882a593Smuzhiyun bat->level = new_level;
206*4882a593Smuzhiyun
207*4882a593Smuzhiyun switch (psp) {
208*4882a593Smuzhiyun case POWER_SUPPLY_PROP_STATUS:
209*4882a593Smuzhiyun if (bat->pdata->gpio_charge_finished < 0)
210*4882a593Smuzhiyun val->intval = bat->level == 100000 ?
211*4882a593Smuzhiyun POWER_SUPPLY_STATUS_FULL : bat->status;
212*4882a593Smuzhiyun else
213*4882a593Smuzhiyun val->intval = bat->status;
214*4882a593Smuzhiyun return 0;
215*4882a593Smuzhiyun case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
216*4882a593Smuzhiyun val->intval = 100000;
217*4882a593Smuzhiyun return 0;
218*4882a593Smuzhiyun case POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN:
219*4882a593Smuzhiyun val->intval = 0;
220*4882a593Smuzhiyun return 0;
221*4882a593Smuzhiyun case POWER_SUPPLY_PROP_CHARGE_NOW:
222*4882a593Smuzhiyun val->intval = bat->level;
223*4882a593Smuzhiyun return 0;
224*4882a593Smuzhiyun case POWER_SUPPLY_PROP_VOLTAGE_NOW:
225*4882a593Smuzhiyun val->intval = bat->volt_value;
226*4882a593Smuzhiyun return 0;
227*4882a593Smuzhiyun case POWER_SUPPLY_PROP_CURRENT_NOW:
228*4882a593Smuzhiyun val->intval = bat->cur_value;
229*4882a593Smuzhiyun return 0;
230*4882a593Smuzhiyun default:
231*4882a593Smuzhiyun return -EINVAL;
232*4882a593Smuzhiyun }
233*4882a593Smuzhiyun }
234*4882a593Smuzhiyun
235*4882a593Smuzhiyun static const struct power_supply_desc main_bat_desc = {
236*4882a593Smuzhiyun .name = "main-battery",
237*4882a593Smuzhiyun .type = POWER_SUPPLY_TYPE_BATTERY,
238*4882a593Smuzhiyun .properties = s3c_adc_main_bat_props,
239*4882a593Smuzhiyun .num_properties = ARRAY_SIZE(s3c_adc_main_bat_props),
240*4882a593Smuzhiyun .get_property = s3c_adc_bat_get_property,
241*4882a593Smuzhiyun .external_power_changed = s3c_adc_bat_ext_power_changed,
242*4882a593Smuzhiyun .use_for_apm = 1,
243*4882a593Smuzhiyun };
244*4882a593Smuzhiyun
245*4882a593Smuzhiyun static struct s3c_adc_bat main_bat;
246*4882a593Smuzhiyun
s3c_adc_bat_work(struct work_struct * work)247*4882a593Smuzhiyun static void s3c_adc_bat_work(struct work_struct *work)
248*4882a593Smuzhiyun {
249*4882a593Smuzhiyun struct s3c_adc_bat *bat = &main_bat;
250*4882a593Smuzhiyun int is_charged;
251*4882a593Smuzhiyun int is_plugged;
252*4882a593Smuzhiyun static int was_plugged;
253*4882a593Smuzhiyun
254*4882a593Smuzhiyun is_plugged = power_supply_am_i_supplied(bat->psy);
255*4882a593Smuzhiyun bat->cable_plugged = is_plugged;
256*4882a593Smuzhiyun if (is_plugged != was_plugged) {
257*4882a593Smuzhiyun was_plugged = is_plugged;
258*4882a593Smuzhiyun if (is_plugged) {
259*4882a593Smuzhiyun if (bat->pdata->enable_charger)
260*4882a593Smuzhiyun bat->pdata->enable_charger();
261*4882a593Smuzhiyun bat->status = POWER_SUPPLY_STATUS_CHARGING;
262*4882a593Smuzhiyun } else {
263*4882a593Smuzhiyun if (bat->pdata->disable_charger)
264*4882a593Smuzhiyun bat->pdata->disable_charger();
265*4882a593Smuzhiyun bat->status = POWER_SUPPLY_STATUS_DISCHARGING;
266*4882a593Smuzhiyun }
267*4882a593Smuzhiyun } else {
268*4882a593Smuzhiyun if ((bat->pdata->gpio_charge_finished >= 0) && is_plugged) {
269*4882a593Smuzhiyun is_charged = charge_finished(&main_bat);
270*4882a593Smuzhiyun if (is_charged) {
271*4882a593Smuzhiyun if (bat->pdata->disable_charger)
272*4882a593Smuzhiyun bat->pdata->disable_charger();
273*4882a593Smuzhiyun bat->status = POWER_SUPPLY_STATUS_FULL;
274*4882a593Smuzhiyun } else {
275*4882a593Smuzhiyun if (bat->pdata->enable_charger)
276*4882a593Smuzhiyun bat->pdata->enable_charger();
277*4882a593Smuzhiyun bat->status = POWER_SUPPLY_STATUS_CHARGING;
278*4882a593Smuzhiyun }
279*4882a593Smuzhiyun }
280*4882a593Smuzhiyun }
281*4882a593Smuzhiyun
282*4882a593Smuzhiyun power_supply_changed(bat->psy);
283*4882a593Smuzhiyun }
284*4882a593Smuzhiyun
s3c_adc_bat_charged(int irq,void * dev_id)285*4882a593Smuzhiyun static irqreturn_t s3c_adc_bat_charged(int irq, void *dev_id)
286*4882a593Smuzhiyun {
287*4882a593Smuzhiyun schedule_delayed_work(&bat_work,
288*4882a593Smuzhiyun msecs_to_jiffies(JITTER_DELAY));
289*4882a593Smuzhiyun return IRQ_HANDLED;
290*4882a593Smuzhiyun }
291*4882a593Smuzhiyun
s3c_adc_bat_probe(struct platform_device * pdev)292*4882a593Smuzhiyun static int s3c_adc_bat_probe(struct platform_device *pdev)
293*4882a593Smuzhiyun {
294*4882a593Smuzhiyun struct s3c_adc_client *client;
295*4882a593Smuzhiyun struct s3c_adc_bat_pdata *pdata = pdev->dev.platform_data;
296*4882a593Smuzhiyun struct power_supply_config psy_cfg = {};
297*4882a593Smuzhiyun int ret;
298*4882a593Smuzhiyun
299*4882a593Smuzhiyun client = s3c_adc_register(pdev, NULL, NULL, 0);
300*4882a593Smuzhiyun if (IS_ERR(client)) {
301*4882a593Smuzhiyun dev_err(&pdev->dev, "cannot register adc\n");
302*4882a593Smuzhiyun return PTR_ERR(client);
303*4882a593Smuzhiyun }
304*4882a593Smuzhiyun
305*4882a593Smuzhiyun platform_set_drvdata(pdev, client);
306*4882a593Smuzhiyun
307*4882a593Smuzhiyun main_bat.client = client;
308*4882a593Smuzhiyun main_bat.pdata = pdata;
309*4882a593Smuzhiyun main_bat.volt_value = -1;
310*4882a593Smuzhiyun main_bat.cur_value = -1;
311*4882a593Smuzhiyun main_bat.cable_plugged = 0;
312*4882a593Smuzhiyun main_bat.status = POWER_SUPPLY_STATUS_DISCHARGING;
313*4882a593Smuzhiyun psy_cfg.drv_data = &main_bat;
314*4882a593Smuzhiyun
315*4882a593Smuzhiyun main_bat.psy = power_supply_register(&pdev->dev, &main_bat_desc, &psy_cfg);
316*4882a593Smuzhiyun if (IS_ERR(main_bat.psy)) {
317*4882a593Smuzhiyun ret = PTR_ERR(main_bat.psy);
318*4882a593Smuzhiyun goto err_reg_main;
319*4882a593Smuzhiyun }
320*4882a593Smuzhiyun if (pdata->backup_volt_mult) {
321*4882a593Smuzhiyun const struct power_supply_config backup_psy_cfg
322*4882a593Smuzhiyun = { .drv_data = &backup_bat, };
323*4882a593Smuzhiyun
324*4882a593Smuzhiyun backup_bat.client = client;
325*4882a593Smuzhiyun backup_bat.pdata = pdev->dev.platform_data;
326*4882a593Smuzhiyun backup_bat.volt_value = -1;
327*4882a593Smuzhiyun backup_bat.psy = power_supply_register(&pdev->dev,
328*4882a593Smuzhiyun &backup_bat_desc,
329*4882a593Smuzhiyun &backup_psy_cfg);
330*4882a593Smuzhiyun if (IS_ERR(backup_bat.psy)) {
331*4882a593Smuzhiyun ret = PTR_ERR(backup_bat.psy);
332*4882a593Smuzhiyun goto err_reg_backup;
333*4882a593Smuzhiyun }
334*4882a593Smuzhiyun }
335*4882a593Smuzhiyun
336*4882a593Smuzhiyun INIT_DELAYED_WORK(&bat_work, s3c_adc_bat_work);
337*4882a593Smuzhiyun
338*4882a593Smuzhiyun if (pdata->gpio_charge_finished >= 0) {
339*4882a593Smuzhiyun ret = gpio_request(pdata->gpio_charge_finished, "charged");
340*4882a593Smuzhiyun if (ret)
341*4882a593Smuzhiyun goto err_gpio;
342*4882a593Smuzhiyun
343*4882a593Smuzhiyun ret = request_irq(gpio_to_irq(pdata->gpio_charge_finished),
344*4882a593Smuzhiyun s3c_adc_bat_charged,
345*4882a593Smuzhiyun IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
346*4882a593Smuzhiyun "battery charged", NULL);
347*4882a593Smuzhiyun if (ret)
348*4882a593Smuzhiyun goto err_irq;
349*4882a593Smuzhiyun }
350*4882a593Smuzhiyun
351*4882a593Smuzhiyun if (pdata->init) {
352*4882a593Smuzhiyun ret = pdata->init();
353*4882a593Smuzhiyun if (ret)
354*4882a593Smuzhiyun goto err_platform;
355*4882a593Smuzhiyun }
356*4882a593Smuzhiyun
357*4882a593Smuzhiyun dev_info(&pdev->dev, "successfully loaded\n");
358*4882a593Smuzhiyun device_init_wakeup(&pdev->dev, 1);
359*4882a593Smuzhiyun
360*4882a593Smuzhiyun /* Schedule timer to check current status */
361*4882a593Smuzhiyun schedule_delayed_work(&bat_work,
362*4882a593Smuzhiyun msecs_to_jiffies(JITTER_DELAY));
363*4882a593Smuzhiyun
364*4882a593Smuzhiyun return 0;
365*4882a593Smuzhiyun
366*4882a593Smuzhiyun err_platform:
367*4882a593Smuzhiyun if (pdata->gpio_charge_finished >= 0)
368*4882a593Smuzhiyun free_irq(gpio_to_irq(pdata->gpio_charge_finished), NULL);
369*4882a593Smuzhiyun err_irq:
370*4882a593Smuzhiyun if (pdata->gpio_charge_finished >= 0)
371*4882a593Smuzhiyun gpio_free(pdata->gpio_charge_finished);
372*4882a593Smuzhiyun err_gpio:
373*4882a593Smuzhiyun if (pdata->backup_volt_mult)
374*4882a593Smuzhiyun power_supply_unregister(backup_bat.psy);
375*4882a593Smuzhiyun err_reg_backup:
376*4882a593Smuzhiyun power_supply_unregister(main_bat.psy);
377*4882a593Smuzhiyun err_reg_main:
378*4882a593Smuzhiyun return ret;
379*4882a593Smuzhiyun }
380*4882a593Smuzhiyun
s3c_adc_bat_remove(struct platform_device * pdev)381*4882a593Smuzhiyun static int s3c_adc_bat_remove(struct platform_device *pdev)
382*4882a593Smuzhiyun {
383*4882a593Smuzhiyun struct s3c_adc_client *client = platform_get_drvdata(pdev);
384*4882a593Smuzhiyun struct s3c_adc_bat_pdata *pdata = pdev->dev.platform_data;
385*4882a593Smuzhiyun
386*4882a593Smuzhiyun power_supply_unregister(main_bat.psy);
387*4882a593Smuzhiyun if (pdata->backup_volt_mult)
388*4882a593Smuzhiyun power_supply_unregister(backup_bat.psy);
389*4882a593Smuzhiyun
390*4882a593Smuzhiyun s3c_adc_release(client);
391*4882a593Smuzhiyun
392*4882a593Smuzhiyun if (pdata->gpio_charge_finished >= 0) {
393*4882a593Smuzhiyun free_irq(gpio_to_irq(pdata->gpio_charge_finished), NULL);
394*4882a593Smuzhiyun gpio_free(pdata->gpio_charge_finished);
395*4882a593Smuzhiyun }
396*4882a593Smuzhiyun
397*4882a593Smuzhiyun cancel_delayed_work_sync(&bat_work);
398*4882a593Smuzhiyun
399*4882a593Smuzhiyun if (pdata->exit)
400*4882a593Smuzhiyun pdata->exit();
401*4882a593Smuzhiyun
402*4882a593Smuzhiyun return 0;
403*4882a593Smuzhiyun }
404*4882a593Smuzhiyun
405*4882a593Smuzhiyun #ifdef CONFIG_PM
s3c_adc_bat_suspend(struct platform_device * pdev,pm_message_t state)406*4882a593Smuzhiyun static int s3c_adc_bat_suspend(struct platform_device *pdev,
407*4882a593Smuzhiyun pm_message_t state)
408*4882a593Smuzhiyun {
409*4882a593Smuzhiyun struct s3c_adc_bat_pdata *pdata = pdev->dev.platform_data;
410*4882a593Smuzhiyun
411*4882a593Smuzhiyun if (pdata->gpio_charge_finished >= 0) {
412*4882a593Smuzhiyun if (device_may_wakeup(&pdev->dev))
413*4882a593Smuzhiyun enable_irq_wake(
414*4882a593Smuzhiyun gpio_to_irq(pdata->gpio_charge_finished));
415*4882a593Smuzhiyun else {
416*4882a593Smuzhiyun disable_irq(gpio_to_irq(pdata->gpio_charge_finished));
417*4882a593Smuzhiyun main_bat.pdata->disable_charger();
418*4882a593Smuzhiyun }
419*4882a593Smuzhiyun }
420*4882a593Smuzhiyun
421*4882a593Smuzhiyun return 0;
422*4882a593Smuzhiyun }
423*4882a593Smuzhiyun
s3c_adc_bat_resume(struct platform_device * pdev)424*4882a593Smuzhiyun static int s3c_adc_bat_resume(struct platform_device *pdev)
425*4882a593Smuzhiyun {
426*4882a593Smuzhiyun struct s3c_adc_bat_pdata *pdata = pdev->dev.platform_data;
427*4882a593Smuzhiyun
428*4882a593Smuzhiyun if (pdata->gpio_charge_finished >= 0) {
429*4882a593Smuzhiyun if (device_may_wakeup(&pdev->dev))
430*4882a593Smuzhiyun disable_irq_wake(
431*4882a593Smuzhiyun gpio_to_irq(pdata->gpio_charge_finished));
432*4882a593Smuzhiyun else
433*4882a593Smuzhiyun enable_irq(gpio_to_irq(pdata->gpio_charge_finished));
434*4882a593Smuzhiyun }
435*4882a593Smuzhiyun
436*4882a593Smuzhiyun /* Schedule timer to check current status */
437*4882a593Smuzhiyun schedule_delayed_work(&bat_work,
438*4882a593Smuzhiyun msecs_to_jiffies(JITTER_DELAY));
439*4882a593Smuzhiyun
440*4882a593Smuzhiyun return 0;
441*4882a593Smuzhiyun }
442*4882a593Smuzhiyun #else
443*4882a593Smuzhiyun #define s3c_adc_bat_suspend NULL
444*4882a593Smuzhiyun #define s3c_adc_bat_resume NULL
445*4882a593Smuzhiyun #endif
446*4882a593Smuzhiyun
447*4882a593Smuzhiyun static struct platform_driver s3c_adc_bat_driver = {
448*4882a593Smuzhiyun .driver = {
449*4882a593Smuzhiyun .name = "s3c-adc-battery",
450*4882a593Smuzhiyun },
451*4882a593Smuzhiyun .probe = s3c_adc_bat_probe,
452*4882a593Smuzhiyun .remove = s3c_adc_bat_remove,
453*4882a593Smuzhiyun .suspend = s3c_adc_bat_suspend,
454*4882a593Smuzhiyun .resume = s3c_adc_bat_resume,
455*4882a593Smuzhiyun };
456*4882a593Smuzhiyun
457*4882a593Smuzhiyun module_platform_driver(s3c_adc_bat_driver);
458*4882a593Smuzhiyun
459*4882a593Smuzhiyun MODULE_AUTHOR("Vasily Khoruzhick <anarsoul@gmail.com>");
460*4882a593Smuzhiyun MODULE_DESCRIPTION("iPAQ H1930/H1940/RX1950 battery controller driver");
461*4882a593Smuzhiyun MODULE_LICENSE("GPL");
462