1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Battery and Power Management code for the Sharp SL-6000x
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (c) 2005 Dirk Opfer
6*4882a593Smuzhiyun * Copyright (c) 2008 Dmitry Baryshkov
7*4882a593Smuzhiyun */
8*4882a593Smuzhiyun #include <linux/kernel.h>
9*4882a593Smuzhiyun #include <linux/module.h>
10*4882a593Smuzhiyun #include <linux/power_supply.h>
11*4882a593Smuzhiyun #include <linux/wm97xx.h>
12*4882a593Smuzhiyun #include <linux/delay.h>
13*4882a593Smuzhiyun #include <linux/spinlock.h>
14*4882a593Smuzhiyun #include <linux/interrupt.h>
15*4882a593Smuzhiyun #include <linux/gpio.h>
16*4882a593Smuzhiyun
17*4882a593Smuzhiyun #include <asm/mach-types.h>
18*4882a593Smuzhiyun #include <mach/tosa.h>
19*4882a593Smuzhiyun
20*4882a593Smuzhiyun static DEFINE_MUTEX(bat_lock); /* protects gpio pins */
21*4882a593Smuzhiyun static struct work_struct bat_work;
22*4882a593Smuzhiyun
23*4882a593Smuzhiyun struct tosa_bat {
24*4882a593Smuzhiyun int status;
25*4882a593Smuzhiyun struct power_supply *psy;
26*4882a593Smuzhiyun int full_chrg;
27*4882a593Smuzhiyun
28*4882a593Smuzhiyun struct mutex work_lock; /* protects data */
29*4882a593Smuzhiyun
30*4882a593Smuzhiyun bool (*is_present)(struct tosa_bat *bat);
31*4882a593Smuzhiyun int gpio_full;
32*4882a593Smuzhiyun int gpio_charge_off;
33*4882a593Smuzhiyun
34*4882a593Smuzhiyun int technology;
35*4882a593Smuzhiyun
36*4882a593Smuzhiyun int gpio_bat;
37*4882a593Smuzhiyun int adc_bat;
38*4882a593Smuzhiyun int adc_bat_divider;
39*4882a593Smuzhiyun int bat_max;
40*4882a593Smuzhiyun int bat_min;
41*4882a593Smuzhiyun
42*4882a593Smuzhiyun int gpio_temp;
43*4882a593Smuzhiyun int adc_temp;
44*4882a593Smuzhiyun int adc_temp_divider;
45*4882a593Smuzhiyun };
46*4882a593Smuzhiyun
47*4882a593Smuzhiyun static struct tosa_bat tosa_bat_main;
48*4882a593Smuzhiyun static struct tosa_bat tosa_bat_jacket;
49*4882a593Smuzhiyun
tosa_read_bat(struct tosa_bat * bat)50*4882a593Smuzhiyun static unsigned long tosa_read_bat(struct tosa_bat *bat)
51*4882a593Smuzhiyun {
52*4882a593Smuzhiyun unsigned long value = 0;
53*4882a593Smuzhiyun
54*4882a593Smuzhiyun if (bat->gpio_bat < 0 || bat->adc_bat < 0)
55*4882a593Smuzhiyun return 0;
56*4882a593Smuzhiyun
57*4882a593Smuzhiyun mutex_lock(&bat_lock);
58*4882a593Smuzhiyun gpio_set_value(bat->gpio_bat, 1);
59*4882a593Smuzhiyun msleep(5);
60*4882a593Smuzhiyun value = wm97xx_read_aux_adc(dev_get_drvdata(bat->psy->dev.parent),
61*4882a593Smuzhiyun bat->adc_bat);
62*4882a593Smuzhiyun gpio_set_value(bat->gpio_bat, 0);
63*4882a593Smuzhiyun mutex_unlock(&bat_lock);
64*4882a593Smuzhiyun
65*4882a593Smuzhiyun value = value * 1000000 / bat->adc_bat_divider;
66*4882a593Smuzhiyun
67*4882a593Smuzhiyun return value;
68*4882a593Smuzhiyun }
69*4882a593Smuzhiyun
tosa_read_temp(struct tosa_bat * bat)70*4882a593Smuzhiyun static unsigned long tosa_read_temp(struct tosa_bat *bat)
71*4882a593Smuzhiyun {
72*4882a593Smuzhiyun unsigned long value = 0;
73*4882a593Smuzhiyun
74*4882a593Smuzhiyun if (bat->gpio_temp < 0 || bat->adc_temp < 0)
75*4882a593Smuzhiyun return 0;
76*4882a593Smuzhiyun
77*4882a593Smuzhiyun mutex_lock(&bat_lock);
78*4882a593Smuzhiyun gpio_set_value(bat->gpio_temp, 1);
79*4882a593Smuzhiyun msleep(5);
80*4882a593Smuzhiyun value = wm97xx_read_aux_adc(dev_get_drvdata(bat->psy->dev.parent),
81*4882a593Smuzhiyun bat->adc_temp);
82*4882a593Smuzhiyun gpio_set_value(bat->gpio_temp, 0);
83*4882a593Smuzhiyun mutex_unlock(&bat_lock);
84*4882a593Smuzhiyun
85*4882a593Smuzhiyun value = value * 10000 / bat->adc_temp_divider;
86*4882a593Smuzhiyun
87*4882a593Smuzhiyun return value;
88*4882a593Smuzhiyun }
89*4882a593Smuzhiyun
tosa_bat_get_property(struct power_supply * psy,enum power_supply_property psp,union power_supply_propval * val)90*4882a593Smuzhiyun static int tosa_bat_get_property(struct power_supply *psy,
91*4882a593Smuzhiyun enum power_supply_property psp,
92*4882a593Smuzhiyun union power_supply_propval *val)
93*4882a593Smuzhiyun {
94*4882a593Smuzhiyun int ret = 0;
95*4882a593Smuzhiyun struct tosa_bat *bat = power_supply_get_drvdata(psy);
96*4882a593Smuzhiyun
97*4882a593Smuzhiyun if (bat->is_present && !bat->is_present(bat)
98*4882a593Smuzhiyun && psp != POWER_SUPPLY_PROP_PRESENT) {
99*4882a593Smuzhiyun return -ENODEV;
100*4882a593Smuzhiyun }
101*4882a593Smuzhiyun
102*4882a593Smuzhiyun switch (psp) {
103*4882a593Smuzhiyun case POWER_SUPPLY_PROP_STATUS:
104*4882a593Smuzhiyun val->intval = bat->status;
105*4882a593Smuzhiyun break;
106*4882a593Smuzhiyun case POWER_SUPPLY_PROP_TECHNOLOGY:
107*4882a593Smuzhiyun val->intval = bat->technology;
108*4882a593Smuzhiyun break;
109*4882a593Smuzhiyun case POWER_SUPPLY_PROP_VOLTAGE_NOW:
110*4882a593Smuzhiyun val->intval = tosa_read_bat(bat);
111*4882a593Smuzhiyun break;
112*4882a593Smuzhiyun case POWER_SUPPLY_PROP_VOLTAGE_MAX:
113*4882a593Smuzhiyun if (bat->full_chrg == -1)
114*4882a593Smuzhiyun val->intval = bat->bat_max;
115*4882a593Smuzhiyun else
116*4882a593Smuzhiyun val->intval = bat->full_chrg;
117*4882a593Smuzhiyun break;
118*4882a593Smuzhiyun case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
119*4882a593Smuzhiyun val->intval = bat->bat_max;
120*4882a593Smuzhiyun break;
121*4882a593Smuzhiyun case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
122*4882a593Smuzhiyun val->intval = bat->bat_min;
123*4882a593Smuzhiyun break;
124*4882a593Smuzhiyun case POWER_SUPPLY_PROP_TEMP:
125*4882a593Smuzhiyun val->intval = tosa_read_temp(bat);
126*4882a593Smuzhiyun break;
127*4882a593Smuzhiyun case POWER_SUPPLY_PROP_PRESENT:
128*4882a593Smuzhiyun val->intval = bat->is_present ? bat->is_present(bat) : 1;
129*4882a593Smuzhiyun break;
130*4882a593Smuzhiyun default:
131*4882a593Smuzhiyun ret = -EINVAL;
132*4882a593Smuzhiyun break;
133*4882a593Smuzhiyun }
134*4882a593Smuzhiyun return ret;
135*4882a593Smuzhiyun }
136*4882a593Smuzhiyun
tosa_jacket_bat_is_present(struct tosa_bat * bat)137*4882a593Smuzhiyun static bool tosa_jacket_bat_is_present(struct tosa_bat *bat)
138*4882a593Smuzhiyun {
139*4882a593Smuzhiyun return gpio_get_value(TOSA_GPIO_JACKET_DETECT) == 0;
140*4882a593Smuzhiyun }
141*4882a593Smuzhiyun
tosa_bat_external_power_changed(struct power_supply * psy)142*4882a593Smuzhiyun static void tosa_bat_external_power_changed(struct power_supply *psy)
143*4882a593Smuzhiyun {
144*4882a593Smuzhiyun schedule_work(&bat_work);
145*4882a593Smuzhiyun }
146*4882a593Smuzhiyun
tosa_bat_gpio_isr(int irq,void * data)147*4882a593Smuzhiyun static irqreturn_t tosa_bat_gpio_isr(int irq, void *data)
148*4882a593Smuzhiyun {
149*4882a593Smuzhiyun pr_info("tosa_bat_gpio irq\n");
150*4882a593Smuzhiyun schedule_work(&bat_work);
151*4882a593Smuzhiyun return IRQ_HANDLED;
152*4882a593Smuzhiyun }
153*4882a593Smuzhiyun
tosa_bat_update(struct tosa_bat * bat)154*4882a593Smuzhiyun static void tosa_bat_update(struct tosa_bat *bat)
155*4882a593Smuzhiyun {
156*4882a593Smuzhiyun int old;
157*4882a593Smuzhiyun struct power_supply *psy = bat->psy;
158*4882a593Smuzhiyun
159*4882a593Smuzhiyun mutex_lock(&bat->work_lock);
160*4882a593Smuzhiyun
161*4882a593Smuzhiyun old = bat->status;
162*4882a593Smuzhiyun
163*4882a593Smuzhiyun if (bat->is_present && !bat->is_present(bat)) {
164*4882a593Smuzhiyun printk(KERN_NOTICE "%s not present\n", psy->desc->name);
165*4882a593Smuzhiyun bat->status = POWER_SUPPLY_STATUS_UNKNOWN;
166*4882a593Smuzhiyun bat->full_chrg = -1;
167*4882a593Smuzhiyun } else if (power_supply_am_i_supplied(psy)) {
168*4882a593Smuzhiyun if (bat->status == POWER_SUPPLY_STATUS_DISCHARGING) {
169*4882a593Smuzhiyun gpio_set_value(bat->gpio_charge_off, 0);
170*4882a593Smuzhiyun mdelay(15);
171*4882a593Smuzhiyun }
172*4882a593Smuzhiyun
173*4882a593Smuzhiyun if (gpio_get_value(bat->gpio_full)) {
174*4882a593Smuzhiyun if (old == POWER_SUPPLY_STATUS_CHARGING ||
175*4882a593Smuzhiyun bat->full_chrg == -1)
176*4882a593Smuzhiyun bat->full_chrg = tosa_read_bat(bat);
177*4882a593Smuzhiyun
178*4882a593Smuzhiyun gpio_set_value(bat->gpio_charge_off, 1);
179*4882a593Smuzhiyun bat->status = POWER_SUPPLY_STATUS_FULL;
180*4882a593Smuzhiyun } else {
181*4882a593Smuzhiyun gpio_set_value(bat->gpio_charge_off, 0);
182*4882a593Smuzhiyun bat->status = POWER_SUPPLY_STATUS_CHARGING;
183*4882a593Smuzhiyun }
184*4882a593Smuzhiyun } else {
185*4882a593Smuzhiyun gpio_set_value(bat->gpio_charge_off, 1);
186*4882a593Smuzhiyun bat->status = POWER_SUPPLY_STATUS_DISCHARGING;
187*4882a593Smuzhiyun }
188*4882a593Smuzhiyun
189*4882a593Smuzhiyun if (old != bat->status)
190*4882a593Smuzhiyun power_supply_changed(psy);
191*4882a593Smuzhiyun
192*4882a593Smuzhiyun mutex_unlock(&bat->work_lock);
193*4882a593Smuzhiyun }
194*4882a593Smuzhiyun
tosa_bat_work(struct work_struct * work)195*4882a593Smuzhiyun static void tosa_bat_work(struct work_struct *work)
196*4882a593Smuzhiyun {
197*4882a593Smuzhiyun tosa_bat_update(&tosa_bat_main);
198*4882a593Smuzhiyun tosa_bat_update(&tosa_bat_jacket);
199*4882a593Smuzhiyun }
200*4882a593Smuzhiyun
201*4882a593Smuzhiyun
202*4882a593Smuzhiyun static enum power_supply_property tosa_bat_main_props[] = {
203*4882a593Smuzhiyun POWER_SUPPLY_PROP_STATUS,
204*4882a593Smuzhiyun POWER_SUPPLY_PROP_TECHNOLOGY,
205*4882a593Smuzhiyun POWER_SUPPLY_PROP_VOLTAGE_NOW,
206*4882a593Smuzhiyun POWER_SUPPLY_PROP_VOLTAGE_MAX,
207*4882a593Smuzhiyun POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
208*4882a593Smuzhiyun POWER_SUPPLY_PROP_TEMP,
209*4882a593Smuzhiyun POWER_SUPPLY_PROP_PRESENT,
210*4882a593Smuzhiyun };
211*4882a593Smuzhiyun
212*4882a593Smuzhiyun static enum power_supply_property tosa_bat_bu_props[] = {
213*4882a593Smuzhiyun POWER_SUPPLY_PROP_STATUS,
214*4882a593Smuzhiyun POWER_SUPPLY_PROP_TECHNOLOGY,
215*4882a593Smuzhiyun POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
216*4882a593Smuzhiyun POWER_SUPPLY_PROP_VOLTAGE_NOW,
217*4882a593Smuzhiyun POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
218*4882a593Smuzhiyun POWER_SUPPLY_PROP_PRESENT,
219*4882a593Smuzhiyun };
220*4882a593Smuzhiyun
221*4882a593Smuzhiyun static const struct power_supply_desc tosa_bat_main_desc = {
222*4882a593Smuzhiyun .name = "main-battery",
223*4882a593Smuzhiyun .type = POWER_SUPPLY_TYPE_BATTERY,
224*4882a593Smuzhiyun .properties = tosa_bat_main_props,
225*4882a593Smuzhiyun .num_properties = ARRAY_SIZE(tosa_bat_main_props),
226*4882a593Smuzhiyun .get_property = tosa_bat_get_property,
227*4882a593Smuzhiyun .external_power_changed = tosa_bat_external_power_changed,
228*4882a593Smuzhiyun .use_for_apm = 1,
229*4882a593Smuzhiyun };
230*4882a593Smuzhiyun
231*4882a593Smuzhiyun static const struct power_supply_desc tosa_bat_jacket_desc = {
232*4882a593Smuzhiyun .name = "jacket-battery",
233*4882a593Smuzhiyun .type = POWER_SUPPLY_TYPE_BATTERY,
234*4882a593Smuzhiyun .properties = tosa_bat_main_props,
235*4882a593Smuzhiyun .num_properties = ARRAY_SIZE(tosa_bat_main_props),
236*4882a593Smuzhiyun .get_property = tosa_bat_get_property,
237*4882a593Smuzhiyun .external_power_changed = tosa_bat_external_power_changed,
238*4882a593Smuzhiyun };
239*4882a593Smuzhiyun
240*4882a593Smuzhiyun static const struct power_supply_desc tosa_bat_bu_desc = {
241*4882a593Smuzhiyun .name = "backup-battery",
242*4882a593Smuzhiyun .type = POWER_SUPPLY_TYPE_BATTERY,
243*4882a593Smuzhiyun .properties = tosa_bat_bu_props,
244*4882a593Smuzhiyun .num_properties = ARRAY_SIZE(tosa_bat_bu_props),
245*4882a593Smuzhiyun .get_property = tosa_bat_get_property,
246*4882a593Smuzhiyun .external_power_changed = tosa_bat_external_power_changed,
247*4882a593Smuzhiyun };
248*4882a593Smuzhiyun
249*4882a593Smuzhiyun static struct tosa_bat tosa_bat_main = {
250*4882a593Smuzhiyun .status = POWER_SUPPLY_STATUS_DISCHARGING,
251*4882a593Smuzhiyun .full_chrg = -1,
252*4882a593Smuzhiyun .psy = NULL,
253*4882a593Smuzhiyun
254*4882a593Smuzhiyun .gpio_full = TOSA_GPIO_BAT0_CRG,
255*4882a593Smuzhiyun .gpio_charge_off = TOSA_GPIO_CHARGE_OFF,
256*4882a593Smuzhiyun
257*4882a593Smuzhiyun .technology = POWER_SUPPLY_TECHNOLOGY_LIPO,
258*4882a593Smuzhiyun
259*4882a593Smuzhiyun .gpio_bat = TOSA_GPIO_BAT0_V_ON,
260*4882a593Smuzhiyun .adc_bat = WM97XX_AUX_ID3,
261*4882a593Smuzhiyun .adc_bat_divider = 414,
262*4882a593Smuzhiyun .bat_max = 4310000,
263*4882a593Smuzhiyun .bat_min = 1551 * 1000000 / 414,
264*4882a593Smuzhiyun
265*4882a593Smuzhiyun .gpio_temp = TOSA_GPIO_BAT1_TH_ON,
266*4882a593Smuzhiyun .adc_temp = WM97XX_AUX_ID2,
267*4882a593Smuzhiyun .adc_temp_divider = 10000,
268*4882a593Smuzhiyun };
269*4882a593Smuzhiyun
270*4882a593Smuzhiyun static struct tosa_bat tosa_bat_jacket = {
271*4882a593Smuzhiyun .status = POWER_SUPPLY_STATUS_DISCHARGING,
272*4882a593Smuzhiyun .full_chrg = -1,
273*4882a593Smuzhiyun .psy = NULL,
274*4882a593Smuzhiyun
275*4882a593Smuzhiyun .is_present = tosa_jacket_bat_is_present,
276*4882a593Smuzhiyun .gpio_full = TOSA_GPIO_BAT1_CRG,
277*4882a593Smuzhiyun .gpio_charge_off = TOSA_GPIO_CHARGE_OFF_JC,
278*4882a593Smuzhiyun
279*4882a593Smuzhiyun .technology = POWER_SUPPLY_TECHNOLOGY_LIPO,
280*4882a593Smuzhiyun
281*4882a593Smuzhiyun .gpio_bat = TOSA_GPIO_BAT1_V_ON,
282*4882a593Smuzhiyun .adc_bat = WM97XX_AUX_ID3,
283*4882a593Smuzhiyun .adc_bat_divider = 414,
284*4882a593Smuzhiyun .bat_max = 4310000,
285*4882a593Smuzhiyun .bat_min = 1551 * 1000000 / 414,
286*4882a593Smuzhiyun
287*4882a593Smuzhiyun .gpio_temp = TOSA_GPIO_BAT0_TH_ON,
288*4882a593Smuzhiyun .adc_temp = WM97XX_AUX_ID2,
289*4882a593Smuzhiyun .adc_temp_divider = 10000,
290*4882a593Smuzhiyun };
291*4882a593Smuzhiyun
292*4882a593Smuzhiyun static struct tosa_bat tosa_bat_bu = {
293*4882a593Smuzhiyun .status = POWER_SUPPLY_STATUS_UNKNOWN,
294*4882a593Smuzhiyun .full_chrg = -1,
295*4882a593Smuzhiyun .psy = NULL,
296*4882a593Smuzhiyun
297*4882a593Smuzhiyun .gpio_full = -1,
298*4882a593Smuzhiyun .gpio_charge_off = -1,
299*4882a593Smuzhiyun
300*4882a593Smuzhiyun .technology = POWER_SUPPLY_TECHNOLOGY_LiMn,
301*4882a593Smuzhiyun
302*4882a593Smuzhiyun .gpio_bat = TOSA_GPIO_BU_CHRG_ON,
303*4882a593Smuzhiyun .adc_bat = WM97XX_AUX_ID4,
304*4882a593Smuzhiyun .adc_bat_divider = 1266,
305*4882a593Smuzhiyun
306*4882a593Smuzhiyun .gpio_temp = -1,
307*4882a593Smuzhiyun .adc_temp = -1,
308*4882a593Smuzhiyun .adc_temp_divider = -1,
309*4882a593Smuzhiyun };
310*4882a593Smuzhiyun
311*4882a593Smuzhiyun static struct gpio tosa_bat_gpios[] = {
312*4882a593Smuzhiyun { TOSA_GPIO_CHARGE_OFF, GPIOF_OUT_INIT_HIGH, "main charge off" },
313*4882a593Smuzhiyun { TOSA_GPIO_CHARGE_OFF_JC, GPIOF_OUT_INIT_HIGH, "jacket charge off" },
314*4882a593Smuzhiyun { TOSA_GPIO_BAT_SW_ON, GPIOF_OUT_INIT_LOW, "battery switch" },
315*4882a593Smuzhiyun { TOSA_GPIO_BAT0_V_ON, GPIOF_OUT_INIT_LOW, "main battery" },
316*4882a593Smuzhiyun { TOSA_GPIO_BAT1_V_ON, GPIOF_OUT_INIT_LOW, "jacket battery" },
317*4882a593Smuzhiyun { TOSA_GPIO_BAT1_TH_ON, GPIOF_OUT_INIT_LOW, "main battery temp" },
318*4882a593Smuzhiyun { TOSA_GPIO_BAT0_TH_ON, GPIOF_OUT_INIT_LOW, "jacket battery temp" },
319*4882a593Smuzhiyun { TOSA_GPIO_BU_CHRG_ON, GPIOF_OUT_INIT_LOW, "backup battery" },
320*4882a593Smuzhiyun { TOSA_GPIO_BAT0_CRG, GPIOF_IN, "main battery full" },
321*4882a593Smuzhiyun { TOSA_GPIO_BAT1_CRG, GPIOF_IN, "jacket battery full" },
322*4882a593Smuzhiyun { TOSA_GPIO_BAT0_LOW, GPIOF_IN, "main battery low" },
323*4882a593Smuzhiyun { TOSA_GPIO_BAT1_LOW, GPIOF_IN, "jacket battery low" },
324*4882a593Smuzhiyun { TOSA_GPIO_JACKET_DETECT, GPIOF_IN, "jacket detect" },
325*4882a593Smuzhiyun };
326*4882a593Smuzhiyun
327*4882a593Smuzhiyun #ifdef CONFIG_PM
tosa_bat_suspend(struct platform_device * dev,pm_message_t state)328*4882a593Smuzhiyun static int tosa_bat_suspend(struct platform_device *dev, pm_message_t state)
329*4882a593Smuzhiyun {
330*4882a593Smuzhiyun /* flush all pending status updates */
331*4882a593Smuzhiyun flush_work(&bat_work);
332*4882a593Smuzhiyun return 0;
333*4882a593Smuzhiyun }
334*4882a593Smuzhiyun
tosa_bat_resume(struct platform_device * dev)335*4882a593Smuzhiyun static int tosa_bat_resume(struct platform_device *dev)
336*4882a593Smuzhiyun {
337*4882a593Smuzhiyun /* things may have changed while we were away */
338*4882a593Smuzhiyun schedule_work(&bat_work);
339*4882a593Smuzhiyun return 0;
340*4882a593Smuzhiyun }
341*4882a593Smuzhiyun #else
342*4882a593Smuzhiyun #define tosa_bat_suspend NULL
343*4882a593Smuzhiyun #define tosa_bat_resume NULL
344*4882a593Smuzhiyun #endif
345*4882a593Smuzhiyun
tosa_bat_probe(struct platform_device * dev)346*4882a593Smuzhiyun static int tosa_bat_probe(struct platform_device *dev)
347*4882a593Smuzhiyun {
348*4882a593Smuzhiyun int ret;
349*4882a593Smuzhiyun struct power_supply_config main_psy_cfg = {},
350*4882a593Smuzhiyun jacket_psy_cfg = {},
351*4882a593Smuzhiyun bu_psy_cfg = {};
352*4882a593Smuzhiyun
353*4882a593Smuzhiyun if (!machine_is_tosa())
354*4882a593Smuzhiyun return -ENODEV;
355*4882a593Smuzhiyun
356*4882a593Smuzhiyun ret = gpio_request_array(tosa_bat_gpios, ARRAY_SIZE(tosa_bat_gpios));
357*4882a593Smuzhiyun if (ret)
358*4882a593Smuzhiyun return ret;
359*4882a593Smuzhiyun
360*4882a593Smuzhiyun mutex_init(&tosa_bat_main.work_lock);
361*4882a593Smuzhiyun mutex_init(&tosa_bat_jacket.work_lock);
362*4882a593Smuzhiyun
363*4882a593Smuzhiyun INIT_WORK(&bat_work, tosa_bat_work);
364*4882a593Smuzhiyun
365*4882a593Smuzhiyun main_psy_cfg.drv_data = &tosa_bat_main;
366*4882a593Smuzhiyun tosa_bat_main.psy = power_supply_register(&dev->dev,
367*4882a593Smuzhiyun &tosa_bat_main_desc,
368*4882a593Smuzhiyun &main_psy_cfg);
369*4882a593Smuzhiyun if (IS_ERR(tosa_bat_main.psy)) {
370*4882a593Smuzhiyun ret = PTR_ERR(tosa_bat_main.psy);
371*4882a593Smuzhiyun goto err_psy_reg_main;
372*4882a593Smuzhiyun }
373*4882a593Smuzhiyun
374*4882a593Smuzhiyun jacket_psy_cfg.drv_data = &tosa_bat_jacket;
375*4882a593Smuzhiyun tosa_bat_jacket.psy = power_supply_register(&dev->dev,
376*4882a593Smuzhiyun &tosa_bat_jacket_desc,
377*4882a593Smuzhiyun &jacket_psy_cfg);
378*4882a593Smuzhiyun if (IS_ERR(tosa_bat_jacket.psy)) {
379*4882a593Smuzhiyun ret = PTR_ERR(tosa_bat_jacket.psy);
380*4882a593Smuzhiyun goto err_psy_reg_jacket;
381*4882a593Smuzhiyun }
382*4882a593Smuzhiyun
383*4882a593Smuzhiyun bu_psy_cfg.drv_data = &tosa_bat_bu;
384*4882a593Smuzhiyun tosa_bat_bu.psy = power_supply_register(&dev->dev, &tosa_bat_bu_desc,
385*4882a593Smuzhiyun &bu_psy_cfg);
386*4882a593Smuzhiyun if (IS_ERR(tosa_bat_bu.psy)) {
387*4882a593Smuzhiyun ret = PTR_ERR(tosa_bat_bu.psy);
388*4882a593Smuzhiyun goto err_psy_reg_bu;
389*4882a593Smuzhiyun }
390*4882a593Smuzhiyun
391*4882a593Smuzhiyun ret = request_irq(gpio_to_irq(TOSA_GPIO_BAT0_CRG),
392*4882a593Smuzhiyun tosa_bat_gpio_isr,
393*4882a593Smuzhiyun IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
394*4882a593Smuzhiyun "main full", &tosa_bat_main);
395*4882a593Smuzhiyun if (ret)
396*4882a593Smuzhiyun goto err_req_main;
397*4882a593Smuzhiyun
398*4882a593Smuzhiyun ret = request_irq(gpio_to_irq(TOSA_GPIO_BAT1_CRG),
399*4882a593Smuzhiyun tosa_bat_gpio_isr,
400*4882a593Smuzhiyun IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
401*4882a593Smuzhiyun "jacket full", &tosa_bat_jacket);
402*4882a593Smuzhiyun if (ret)
403*4882a593Smuzhiyun goto err_req_jacket;
404*4882a593Smuzhiyun
405*4882a593Smuzhiyun ret = request_irq(gpio_to_irq(TOSA_GPIO_JACKET_DETECT),
406*4882a593Smuzhiyun tosa_bat_gpio_isr,
407*4882a593Smuzhiyun IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
408*4882a593Smuzhiyun "jacket detect", &tosa_bat_jacket);
409*4882a593Smuzhiyun if (!ret) {
410*4882a593Smuzhiyun schedule_work(&bat_work);
411*4882a593Smuzhiyun return 0;
412*4882a593Smuzhiyun }
413*4882a593Smuzhiyun
414*4882a593Smuzhiyun free_irq(gpio_to_irq(TOSA_GPIO_BAT1_CRG), &tosa_bat_jacket);
415*4882a593Smuzhiyun err_req_jacket:
416*4882a593Smuzhiyun free_irq(gpio_to_irq(TOSA_GPIO_BAT0_CRG), &tosa_bat_main);
417*4882a593Smuzhiyun err_req_main:
418*4882a593Smuzhiyun power_supply_unregister(tosa_bat_bu.psy);
419*4882a593Smuzhiyun err_psy_reg_bu:
420*4882a593Smuzhiyun power_supply_unregister(tosa_bat_jacket.psy);
421*4882a593Smuzhiyun err_psy_reg_jacket:
422*4882a593Smuzhiyun power_supply_unregister(tosa_bat_main.psy);
423*4882a593Smuzhiyun err_psy_reg_main:
424*4882a593Smuzhiyun
425*4882a593Smuzhiyun /* see comment in tosa_bat_remove */
426*4882a593Smuzhiyun cancel_work_sync(&bat_work);
427*4882a593Smuzhiyun
428*4882a593Smuzhiyun gpio_free_array(tosa_bat_gpios, ARRAY_SIZE(tosa_bat_gpios));
429*4882a593Smuzhiyun return ret;
430*4882a593Smuzhiyun }
431*4882a593Smuzhiyun
tosa_bat_remove(struct platform_device * dev)432*4882a593Smuzhiyun static int tosa_bat_remove(struct platform_device *dev)
433*4882a593Smuzhiyun {
434*4882a593Smuzhiyun free_irq(gpio_to_irq(TOSA_GPIO_JACKET_DETECT), &tosa_bat_jacket);
435*4882a593Smuzhiyun free_irq(gpio_to_irq(TOSA_GPIO_BAT1_CRG), &tosa_bat_jacket);
436*4882a593Smuzhiyun free_irq(gpio_to_irq(TOSA_GPIO_BAT0_CRG), &tosa_bat_main);
437*4882a593Smuzhiyun
438*4882a593Smuzhiyun power_supply_unregister(tosa_bat_bu.psy);
439*4882a593Smuzhiyun power_supply_unregister(tosa_bat_jacket.psy);
440*4882a593Smuzhiyun power_supply_unregister(tosa_bat_main.psy);
441*4882a593Smuzhiyun
442*4882a593Smuzhiyun /*
443*4882a593Smuzhiyun * Now cancel the bat_work. We won't get any more schedules,
444*4882a593Smuzhiyun * since all sources (isr and external_power_changed) are
445*4882a593Smuzhiyun * unregistered now.
446*4882a593Smuzhiyun */
447*4882a593Smuzhiyun cancel_work_sync(&bat_work);
448*4882a593Smuzhiyun gpio_free_array(tosa_bat_gpios, ARRAY_SIZE(tosa_bat_gpios));
449*4882a593Smuzhiyun return 0;
450*4882a593Smuzhiyun }
451*4882a593Smuzhiyun
452*4882a593Smuzhiyun static struct platform_driver tosa_bat_driver = {
453*4882a593Smuzhiyun .driver.name = "wm97xx-battery",
454*4882a593Smuzhiyun .driver.owner = THIS_MODULE,
455*4882a593Smuzhiyun .probe = tosa_bat_probe,
456*4882a593Smuzhiyun .remove = tosa_bat_remove,
457*4882a593Smuzhiyun .suspend = tosa_bat_suspend,
458*4882a593Smuzhiyun .resume = tosa_bat_resume,
459*4882a593Smuzhiyun };
460*4882a593Smuzhiyun
461*4882a593Smuzhiyun module_platform_driver(tosa_bat_driver);
462*4882a593Smuzhiyun
463*4882a593Smuzhiyun MODULE_LICENSE("GPL");
464*4882a593Smuzhiyun MODULE_AUTHOR("Dmitry Baryshkov");
465*4882a593Smuzhiyun MODULE_DESCRIPTION("Tosa battery driver");
466*4882a593Smuzhiyun MODULE_ALIAS("platform:wm97xx-battery");
467