xref: /openbmc/linux/drivers/power/supply/collie_battery.c (revision 8c0984e5a75337df513047ec92a6c09d78e3e5cd)
1*8c0984e5SSebastian Reichel /*
2*8c0984e5SSebastian Reichel  * Battery and Power Management code for the Sharp SL-5x00
3*8c0984e5SSebastian Reichel  *
4*8c0984e5SSebastian Reichel  * Copyright (C) 2009 Thomas Kunze
5*8c0984e5SSebastian Reichel  *
6*8c0984e5SSebastian Reichel  * based on tosa_battery.c
7*8c0984e5SSebastian Reichel  *
8*8c0984e5SSebastian Reichel  * This program is free software; you can redistribute it and/or modify
9*8c0984e5SSebastian Reichel  * it under the terms of the GNU General Public License version 2 as
10*8c0984e5SSebastian Reichel  * published by the Free Software Foundation.
11*8c0984e5SSebastian Reichel  *
12*8c0984e5SSebastian Reichel  */
13*8c0984e5SSebastian Reichel #include <linux/kernel.h>
14*8c0984e5SSebastian Reichel #include <linux/module.h>
15*8c0984e5SSebastian Reichel #include <linux/power_supply.h>
16*8c0984e5SSebastian Reichel #include <linux/delay.h>
17*8c0984e5SSebastian Reichel #include <linux/spinlock.h>
18*8c0984e5SSebastian Reichel #include <linux/interrupt.h>
19*8c0984e5SSebastian Reichel #include <linux/gpio.h>
20*8c0984e5SSebastian Reichel #include <linux/mfd/ucb1x00.h>
21*8c0984e5SSebastian Reichel 
22*8c0984e5SSebastian Reichel #include <asm/mach/sharpsl_param.h>
23*8c0984e5SSebastian Reichel #include <asm/mach-types.h>
24*8c0984e5SSebastian Reichel #include <mach/collie.h>
25*8c0984e5SSebastian Reichel 
26*8c0984e5SSebastian Reichel static DEFINE_MUTEX(bat_lock); /* protects gpio pins */
27*8c0984e5SSebastian Reichel static struct work_struct bat_work;
28*8c0984e5SSebastian Reichel static struct ucb1x00 *ucb;
29*8c0984e5SSebastian Reichel 
30*8c0984e5SSebastian Reichel struct collie_bat {
31*8c0984e5SSebastian Reichel 	int status;
32*8c0984e5SSebastian Reichel 	struct power_supply *psy;
33*8c0984e5SSebastian Reichel 	int full_chrg;
34*8c0984e5SSebastian Reichel 
35*8c0984e5SSebastian Reichel 	struct mutex work_lock; /* protects data */
36*8c0984e5SSebastian Reichel 
37*8c0984e5SSebastian Reichel 	bool (*is_present)(struct collie_bat *bat);
38*8c0984e5SSebastian Reichel 	int gpio_full;
39*8c0984e5SSebastian Reichel 	int gpio_charge_on;
40*8c0984e5SSebastian Reichel 
41*8c0984e5SSebastian Reichel 	int technology;
42*8c0984e5SSebastian Reichel 
43*8c0984e5SSebastian Reichel 	int gpio_bat;
44*8c0984e5SSebastian Reichel 	int adc_bat;
45*8c0984e5SSebastian Reichel 	int adc_bat_divider;
46*8c0984e5SSebastian Reichel 	int bat_max;
47*8c0984e5SSebastian Reichel 	int bat_min;
48*8c0984e5SSebastian Reichel 
49*8c0984e5SSebastian Reichel 	int gpio_temp;
50*8c0984e5SSebastian Reichel 	int adc_temp;
51*8c0984e5SSebastian Reichel 	int adc_temp_divider;
52*8c0984e5SSebastian Reichel };
53*8c0984e5SSebastian Reichel 
54*8c0984e5SSebastian Reichel static struct collie_bat collie_bat_main;
55*8c0984e5SSebastian Reichel 
56*8c0984e5SSebastian Reichel static unsigned long collie_read_bat(struct collie_bat *bat)
57*8c0984e5SSebastian Reichel {
58*8c0984e5SSebastian Reichel 	unsigned long value = 0;
59*8c0984e5SSebastian Reichel 
60*8c0984e5SSebastian Reichel 	if (bat->gpio_bat < 0 || bat->adc_bat < 0)
61*8c0984e5SSebastian Reichel 		return 0;
62*8c0984e5SSebastian Reichel 	mutex_lock(&bat_lock);
63*8c0984e5SSebastian Reichel 	gpio_set_value(bat->gpio_bat, 1);
64*8c0984e5SSebastian Reichel 	msleep(5);
65*8c0984e5SSebastian Reichel 	ucb1x00_adc_enable(ucb);
66*8c0984e5SSebastian Reichel 	value = ucb1x00_adc_read(ucb, bat->adc_bat, UCB_SYNC);
67*8c0984e5SSebastian Reichel 	ucb1x00_adc_disable(ucb);
68*8c0984e5SSebastian Reichel 	gpio_set_value(bat->gpio_bat, 0);
69*8c0984e5SSebastian Reichel 	mutex_unlock(&bat_lock);
70*8c0984e5SSebastian Reichel 	value = value * 1000000 / bat->adc_bat_divider;
71*8c0984e5SSebastian Reichel 
72*8c0984e5SSebastian Reichel 	return value;
73*8c0984e5SSebastian Reichel }
74*8c0984e5SSebastian Reichel 
75*8c0984e5SSebastian Reichel static unsigned long collie_read_temp(struct collie_bat *bat)
76*8c0984e5SSebastian Reichel {
77*8c0984e5SSebastian Reichel 	unsigned long value = 0;
78*8c0984e5SSebastian Reichel 	if (bat->gpio_temp < 0 || bat->adc_temp < 0)
79*8c0984e5SSebastian Reichel 		return 0;
80*8c0984e5SSebastian Reichel 
81*8c0984e5SSebastian Reichel 	mutex_lock(&bat_lock);
82*8c0984e5SSebastian Reichel 	gpio_set_value(bat->gpio_temp, 1);
83*8c0984e5SSebastian Reichel 	msleep(5);
84*8c0984e5SSebastian Reichel 	ucb1x00_adc_enable(ucb);
85*8c0984e5SSebastian Reichel 	value = ucb1x00_adc_read(ucb, bat->adc_temp, UCB_SYNC);
86*8c0984e5SSebastian Reichel 	ucb1x00_adc_disable(ucb);
87*8c0984e5SSebastian Reichel 	gpio_set_value(bat->gpio_temp, 0);
88*8c0984e5SSebastian Reichel 	mutex_unlock(&bat_lock);
89*8c0984e5SSebastian Reichel 
90*8c0984e5SSebastian Reichel 	value = value * 10000 / bat->adc_temp_divider;
91*8c0984e5SSebastian Reichel 
92*8c0984e5SSebastian Reichel 	return value;
93*8c0984e5SSebastian Reichel }
94*8c0984e5SSebastian Reichel 
95*8c0984e5SSebastian Reichel static int collie_bat_get_property(struct power_supply *psy,
96*8c0984e5SSebastian Reichel 			    enum power_supply_property psp,
97*8c0984e5SSebastian Reichel 			    union power_supply_propval *val)
98*8c0984e5SSebastian Reichel {
99*8c0984e5SSebastian Reichel 	int ret = 0;
100*8c0984e5SSebastian Reichel 	struct collie_bat *bat = power_supply_get_drvdata(psy);
101*8c0984e5SSebastian Reichel 
102*8c0984e5SSebastian Reichel 	if (bat->is_present && !bat->is_present(bat)
103*8c0984e5SSebastian Reichel 			&& psp != POWER_SUPPLY_PROP_PRESENT) {
104*8c0984e5SSebastian Reichel 		return -ENODEV;
105*8c0984e5SSebastian Reichel 	}
106*8c0984e5SSebastian Reichel 
107*8c0984e5SSebastian Reichel 	switch (psp) {
108*8c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_STATUS:
109*8c0984e5SSebastian Reichel 		val->intval = bat->status;
110*8c0984e5SSebastian Reichel 		break;
111*8c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_TECHNOLOGY:
112*8c0984e5SSebastian Reichel 		val->intval = bat->technology;
113*8c0984e5SSebastian Reichel 		break;
114*8c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
115*8c0984e5SSebastian Reichel 		val->intval = collie_read_bat(bat);
116*8c0984e5SSebastian Reichel 		break;
117*8c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_VOLTAGE_MAX:
118*8c0984e5SSebastian Reichel 		if (bat->full_chrg == -1)
119*8c0984e5SSebastian Reichel 			val->intval = bat->bat_max;
120*8c0984e5SSebastian Reichel 		else
121*8c0984e5SSebastian Reichel 			val->intval = bat->full_chrg;
122*8c0984e5SSebastian Reichel 		break;
123*8c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
124*8c0984e5SSebastian Reichel 		val->intval = bat->bat_max;
125*8c0984e5SSebastian Reichel 		break;
126*8c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
127*8c0984e5SSebastian Reichel 		val->intval = bat->bat_min;
128*8c0984e5SSebastian Reichel 		break;
129*8c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_TEMP:
130*8c0984e5SSebastian Reichel 		val->intval = collie_read_temp(bat);
131*8c0984e5SSebastian Reichel 		break;
132*8c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_PRESENT:
133*8c0984e5SSebastian Reichel 		val->intval = bat->is_present ? bat->is_present(bat) : 1;
134*8c0984e5SSebastian Reichel 		break;
135*8c0984e5SSebastian Reichel 	default:
136*8c0984e5SSebastian Reichel 		ret = -EINVAL;
137*8c0984e5SSebastian Reichel 		break;
138*8c0984e5SSebastian Reichel 	}
139*8c0984e5SSebastian Reichel 	return ret;
140*8c0984e5SSebastian Reichel }
141*8c0984e5SSebastian Reichel 
142*8c0984e5SSebastian Reichel static void collie_bat_external_power_changed(struct power_supply *psy)
143*8c0984e5SSebastian Reichel {
144*8c0984e5SSebastian Reichel 	schedule_work(&bat_work);
145*8c0984e5SSebastian Reichel }
146*8c0984e5SSebastian Reichel 
147*8c0984e5SSebastian Reichel static irqreturn_t collie_bat_gpio_isr(int irq, void *data)
148*8c0984e5SSebastian Reichel {
149*8c0984e5SSebastian Reichel 	pr_info("collie_bat_gpio irq\n");
150*8c0984e5SSebastian Reichel 	schedule_work(&bat_work);
151*8c0984e5SSebastian Reichel 	return IRQ_HANDLED;
152*8c0984e5SSebastian Reichel }
153*8c0984e5SSebastian Reichel 
154*8c0984e5SSebastian Reichel static void collie_bat_update(struct collie_bat *bat)
155*8c0984e5SSebastian Reichel {
156*8c0984e5SSebastian Reichel 	int old;
157*8c0984e5SSebastian Reichel 	struct power_supply *psy = bat->psy;
158*8c0984e5SSebastian Reichel 
159*8c0984e5SSebastian Reichel 	mutex_lock(&bat->work_lock);
160*8c0984e5SSebastian Reichel 
161*8c0984e5SSebastian Reichel 	old = bat->status;
162*8c0984e5SSebastian Reichel 
163*8c0984e5SSebastian Reichel 	if (bat->is_present && !bat->is_present(bat)) {
164*8c0984e5SSebastian Reichel 		printk(KERN_NOTICE "%s not present\n", psy->desc->name);
165*8c0984e5SSebastian Reichel 		bat->status = POWER_SUPPLY_STATUS_UNKNOWN;
166*8c0984e5SSebastian Reichel 		bat->full_chrg = -1;
167*8c0984e5SSebastian Reichel 	} else if (power_supply_am_i_supplied(psy)) {
168*8c0984e5SSebastian Reichel 		if (bat->status == POWER_SUPPLY_STATUS_DISCHARGING) {
169*8c0984e5SSebastian Reichel 			gpio_set_value(bat->gpio_charge_on, 1);
170*8c0984e5SSebastian Reichel 			mdelay(15);
171*8c0984e5SSebastian Reichel 		}
172*8c0984e5SSebastian Reichel 
173*8c0984e5SSebastian Reichel 		if (gpio_get_value(bat->gpio_full)) {
174*8c0984e5SSebastian Reichel 			if (old == POWER_SUPPLY_STATUS_CHARGING ||
175*8c0984e5SSebastian Reichel 					bat->full_chrg == -1)
176*8c0984e5SSebastian Reichel 				bat->full_chrg = collie_read_bat(bat);
177*8c0984e5SSebastian Reichel 
178*8c0984e5SSebastian Reichel 			gpio_set_value(bat->gpio_charge_on, 0);
179*8c0984e5SSebastian Reichel 			bat->status = POWER_SUPPLY_STATUS_FULL;
180*8c0984e5SSebastian Reichel 		} else {
181*8c0984e5SSebastian Reichel 			gpio_set_value(bat->gpio_charge_on, 1);
182*8c0984e5SSebastian Reichel 			bat->status = POWER_SUPPLY_STATUS_CHARGING;
183*8c0984e5SSebastian Reichel 		}
184*8c0984e5SSebastian Reichel 	} else {
185*8c0984e5SSebastian Reichel 		gpio_set_value(bat->gpio_charge_on, 0);
186*8c0984e5SSebastian Reichel 		bat->status = POWER_SUPPLY_STATUS_DISCHARGING;
187*8c0984e5SSebastian Reichel 	}
188*8c0984e5SSebastian Reichel 
189*8c0984e5SSebastian Reichel 	if (old != bat->status)
190*8c0984e5SSebastian Reichel 		power_supply_changed(psy);
191*8c0984e5SSebastian Reichel 
192*8c0984e5SSebastian Reichel 	mutex_unlock(&bat->work_lock);
193*8c0984e5SSebastian Reichel }
194*8c0984e5SSebastian Reichel 
195*8c0984e5SSebastian Reichel static void collie_bat_work(struct work_struct *work)
196*8c0984e5SSebastian Reichel {
197*8c0984e5SSebastian Reichel 	collie_bat_update(&collie_bat_main);
198*8c0984e5SSebastian Reichel }
199*8c0984e5SSebastian Reichel 
200*8c0984e5SSebastian Reichel 
201*8c0984e5SSebastian Reichel static enum power_supply_property collie_bat_main_props[] = {
202*8c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_STATUS,
203*8c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_TECHNOLOGY,
204*8c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
205*8c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_VOLTAGE_NOW,
206*8c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
207*8c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_VOLTAGE_MAX,
208*8c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_PRESENT,
209*8c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_TEMP,
210*8c0984e5SSebastian Reichel };
211*8c0984e5SSebastian Reichel 
212*8c0984e5SSebastian Reichel static enum power_supply_property collie_bat_bu_props[] = {
213*8c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_STATUS,
214*8c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_TECHNOLOGY,
215*8c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
216*8c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_VOLTAGE_NOW,
217*8c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
218*8c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_VOLTAGE_MAX,
219*8c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_PRESENT,
220*8c0984e5SSebastian Reichel };
221*8c0984e5SSebastian Reichel 
222*8c0984e5SSebastian Reichel static const struct power_supply_desc collie_bat_main_desc = {
223*8c0984e5SSebastian Reichel 	.name		= "main-battery",
224*8c0984e5SSebastian Reichel 	.type		= POWER_SUPPLY_TYPE_BATTERY,
225*8c0984e5SSebastian Reichel 	.properties	= collie_bat_main_props,
226*8c0984e5SSebastian Reichel 	.num_properties	= ARRAY_SIZE(collie_bat_main_props),
227*8c0984e5SSebastian Reichel 	.get_property	= collie_bat_get_property,
228*8c0984e5SSebastian Reichel 	.external_power_changed = collie_bat_external_power_changed,
229*8c0984e5SSebastian Reichel 	.use_for_apm	= 1,
230*8c0984e5SSebastian Reichel };
231*8c0984e5SSebastian Reichel 
232*8c0984e5SSebastian Reichel static struct collie_bat collie_bat_main = {
233*8c0984e5SSebastian Reichel 	.status = POWER_SUPPLY_STATUS_DISCHARGING,
234*8c0984e5SSebastian Reichel 	.full_chrg = -1,
235*8c0984e5SSebastian Reichel 	.psy = NULL,
236*8c0984e5SSebastian Reichel 
237*8c0984e5SSebastian Reichel 	.gpio_full = COLLIE_GPIO_CO,
238*8c0984e5SSebastian Reichel 	.gpio_charge_on = COLLIE_GPIO_CHARGE_ON,
239*8c0984e5SSebastian Reichel 
240*8c0984e5SSebastian Reichel 	.technology = POWER_SUPPLY_TECHNOLOGY_LIPO,
241*8c0984e5SSebastian Reichel 
242*8c0984e5SSebastian Reichel 	.gpio_bat = COLLIE_GPIO_MBAT_ON,
243*8c0984e5SSebastian Reichel 	.adc_bat = UCB_ADC_INP_AD1,
244*8c0984e5SSebastian Reichel 	.adc_bat_divider = 155,
245*8c0984e5SSebastian Reichel 	.bat_max = 4310000,
246*8c0984e5SSebastian Reichel 	.bat_min = 1551 * 1000000 / 414,
247*8c0984e5SSebastian Reichel 
248*8c0984e5SSebastian Reichel 	.gpio_temp = COLLIE_GPIO_TMP_ON,
249*8c0984e5SSebastian Reichel 	.adc_temp = UCB_ADC_INP_AD0,
250*8c0984e5SSebastian Reichel 	.adc_temp_divider = 10000,
251*8c0984e5SSebastian Reichel };
252*8c0984e5SSebastian Reichel 
253*8c0984e5SSebastian Reichel static const struct power_supply_desc collie_bat_bu_desc = {
254*8c0984e5SSebastian Reichel 	.name		= "backup-battery",
255*8c0984e5SSebastian Reichel 	.type		= POWER_SUPPLY_TYPE_BATTERY,
256*8c0984e5SSebastian Reichel 	.properties	= collie_bat_bu_props,
257*8c0984e5SSebastian Reichel 	.num_properties	= ARRAY_SIZE(collie_bat_bu_props),
258*8c0984e5SSebastian Reichel 	.get_property	= collie_bat_get_property,
259*8c0984e5SSebastian Reichel 	.external_power_changed = collie_bat_external_power_changed,
260*8c0984e5SSebastian Reichel };
261*8c0984e5SSebastian Reichel 
262*8c0984e5SSebastian Reichel static struct collie_bat collie_bat_bu = {
263*8c0984e5SSebastian Reichel 	.status = POWER_SUPPLY_STATUS_UNKNOWN,
264*8c0984e5SSebastian Reichel 	.full_chrg = -1,
265*8c0984e5SSebastian Reichel 	.psy = NULL,
266*8c0984e5SSebastian Reichel 
267*8c0984e5SSebastian Reichel 	.gpio_full = -1,
268*8c0984e5SSebastian Reichel 	.gpio_charge_on = -1,
269*8c0984e5SSebastian Reichel 
270*8c0984e5SSebastian Reichel 	.technology = POWER_SUPPLY_TECHNOLOGY_LiMn,
271*8c0984e5SSebastian Reichel 
272*8c0984e5SSebastian Reichel 	.gpio_bat = COLLIE_GPIO_BBAT_ON,
273*8c0984e5SSebastian Reichel 	.adc_bat = UCB_ADC_INP_AD1,
274*8c0984e5SSebastian Reichel 	.adc_bat_divider = 155,
275*8c0984e5SSebastian Reichel 	.bat_max = 3000000,
276*8c0984e5SSebastian Reichel 	.bat_min = 1900000,
277*8c0984e5SSebastian Reichel 
278*8c0984e5SSebastian Reichel 	.gpio_temp = -1,
279*8c0984e5SSebastian Reichel 	.adc_temp = -1,
280*8c0984e5SSebastian Reichel 	.adc_temp_divider = -1,
281*8c0984e5SSebastian Reichel };
282*8c0984e5SSebastian Reichel 
283*8c0984e5SSebastian Reichel static struct gpio collie_batt_gpios[] = {
284*8c0984e5SSebastian Reichel 	{ COLLIE_GPIO_CO,	    GPIOF_IN,		"main battery full" },
285*8c0984e5SSebastian Reichel 	{ COLLIE_GPIO_MAIN_BAT_LOW, GPIOF_IN,		"main battery low" },
286*8c0984e5SSebastian Reichel 	{ COLLIE_GPIO_CHARGE_ON,    GPIOF_OUT_INIT_LOW,	"main charge on" },
287*8c0984e5SSebastian Reichel 	{ COLLIE_GPIO_MBAT_ON,	    GPIOF_OUT_INIT_LOW,	"main battery" },
288*8c0984e5SSebastian Reichel 	{ COLLIE_GPIO_TMP_ON,	    GPIOF_OUT_INIT_LOW,	"main battery temp" },
289*8c0984e5SSebastian Reichel 	{ COLLIE_GPIO_BBAT_ON,	    GPIOF_OUT_INIT_LOW,	"backup battery" },
290*8c0984e5SSebastian Reichel };
291*8c0984e5SSebastian Reichel 
292*8c0984e5SSebastian Reichel #ifdef CONFIG_PM
293*8c0984e5SSebastian Reichel static int wakeup_enabled;
294*8c0984e5SSebastian Reichel 
295*8c0984e5SSebastian Reichel static int collie_bat_suspend(struct ucb1x00_dev *dev)
296*8c0984e5SSebastian Reichel {
297*8c0984e5SSebastian Reichel 	/* flush all pending status updates */
298*8c0984e5SSebastian Reichel 	flush_work(&bat_work);
299*8c0984e5SSebastian Reichel 
300*8c0984e5SSebastian Reichel 	if (device_may_wakeup(&dev->ucb->dev) &&
301*8c0984e5SSebastian Reichel 	    collie_bat_main.status == POWER_SUPPLY_STATUS_CHARGING)
302*8c0984e5SSebastian Reichel 		wakeup_enabled = !enable_irq_wake(gpio_to_irq(COLLIE_GPIO_CO));
303*8c0984e5SSebastian Reichel 	else
304*8c0984e5SSebastian Reichel 		wakeup_enabled = 0;
305*8c0984e5SSebastian Reichel 
306*8c0984e5SSebastian Reichel 	return 0;
307*8c0984e5SSebastian Reichel }
308*8c0984e5SSebastian Reichel 
309*8c0984e5SSebastian Reichel static int collie_bat_resume(struct ucb1x00_dev *dev)
310*8c0984e5SSebastian Reichel {
311*8c0984e5SSebastian Reichel 	if (wakeup_enabled)
312*8c0984e5SSebastian Reichel 		disable_irq_wake(gpio_to_irq(COLLIE_GPIO_CO));
313*8c0984e5SSebastian Reichel 
314*8c0984e5SSebastian Reichel 	/* things may have changed while we were away */
315*8c0984e5SSebastian Reichel 	schedule_work(&bat_work);
316*8c0984e5SSebastian Reichel 	return 0;
317*8c0984e5SSebastian Reichel }
318*8c0984e5SSebastian Reichel #else
319*8c0984e5SSebastian Reichel #define collie_bat_suspend NULL
320*8c0984e5SSebastian Reichel #define collie_bat_resume NULL
321*8c0984e5SSebastian Reichel #endif
322*8c0984e5SSebastian Reichel 
323*8c0984e5SSebastian Reichel static int collie_bat_probe(struct ucb1x00_dev *dev)
324*8c0984e5SSebastian Reichel {
325*8c0984e5SSebastian Reichel 	int ret;
326*8c0984e5SSebastian Reichel 	struct power_supply_config psy_main_cfg = {}, psy_bu_cfg = {};
327*8c0984e5SSebastian Reichel 
328*8c0984e5SSebastian Reichel 	if (!machine_is_collie())
329*8c0984e5SSebastian Reichel 		return -ENODEV;
330*8c0984e5SSebastian Reichel 
331*8c0984e5SSebastian Reichel 	ucb = dev->ucb;
332*8c0984e5SSebastian Reichel 
333*8c0984e5SSebastian Reichel 	ret = gpio_request_array(collie_batt_gpios,
334*8c0984e5SSebastian Reichel 				 ARRAY_SIZE(collie_batt_gpios));
335*8c0984e5SSebastian Reichel 	if (ret)
336*8c0984e5SSebastian Reichel 		return ret;
337*8c0984e5SSebastian Reichel 
338*8c0984e5SSebastian Reichel 	mutex_init(&collie_bat_main.work_lock);
339*8c0984e5SSebastian Reichel 
340*8c0984e5SSebastian Reichel 	INIT_WORK(&bat_work, collie_bat_work);
341*8c0984e5SSebastian Reichel 
342*8c0984e5SSebastian Reichel 	psy_main_cfg.drv_data = &collie_bat_main;
343*8c0984e5SSebastian Reichel 	collie_bat_main.psy = power_supply_register(&dev->ucb->dev,
344*8c0984e5SSebastian Reichel 						    &collie_bat_main_desc,
345*8c0984e5SSebastian Reichel 						    &psy_main_cfg);
346*8c0984e5SSebastian Reichel 	if (IS_ERR(collie_bat_main.psy)) {
347*8c0984e5SSebastian Reichel 		ret = PTR_ERR(collie_bat_main.psy);
348*8c0984e5SSebastian Reichel 		goto err_psy_reg_main;
349*8c0984e5SSebastian Reichel 	}
350*8c0984e5SSebastian Reichel 
351*8c0984e5SSebastian Reichel 	psy_bu_cfg.drv_data = &collie_bat_bu;
352*8c0984e5SSebastian Reichel 	collie_bat_bu.psy = power_supply_register(&dev->ucb->dev,
353*8c0984e5SSebastian Reichel 						  &collie_bat_bu_desc,
354*8c0984e5SSebastian Reichel 						  &psy_bu_cfg);
355*8c0984e5SSebastian Reichel 	if (IS_ERR(collie_bat_bu.psy)) {
356*8c0984e5SSebastian Reichel 		ret = PTR_ERR(collie_bat_bu.psy);
357*8c0984e5SSebastian Reichel 		goto err_psy_reg_bu;
358*8c0984e5SSebastian Reichel 	}
359*8c0984e5SSebastian Reichel 
360*8c0984e5SSebastian Reichel 	ret = request_irq(gpio_to_irq(COLLIE_GPIO_CO),
361*8c0984e5SSebastian Reichel 				collie_bat_gpio_isr,
362*8c0984e5SSebastian Reichel 				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
363*8c0984e5SSebastian Reichel 				"main full", &collie_bat_main);
364*8c0984e5SSebastian Reichel 	if (ret)
365*8c0984e5SSebastian Reichel 		goto err_irq;
366*8c0984e5SSebastian Reichel 
367*8c0984e5SSebastian Reichel 	device_init_wakeup(&ucb->dev, 1);
368*8c0984e5SSebastian Reichel 	schedule_work(&bat_work);
369*8c0984e5SSebastian Reichel 
370*8c0984e5SSebastian Reichel 	return 0;
371*8c0984e5SSebastian Reichel 
372*8c0984e5SSebastian Reichel err_irq:
373*8c0984e5SSebastian Reichel 	power_supply_unregister(collie_bat_bu.psy);
374*8c0984e5SSebastian Reichel err_psy_reg_bu:
375*8c0984e5SSebastian Reichel 	power_supply_unregister(collie_bat_main.psy);
376*8c0984e5SSebastian Reichel err_psy_reg_main:
377*8c0984e5SSebastian Reichel 
378*8c0984e5SSebastian Reichel 	/* see comment in collie_bat_remove */
379*8c0984e5SSebastian Reichel 	cancel_work_sync(&bat_work);
380*8c0984e5SSebastian Reichel 	gpio_free_array(collie_batt_gpios, ARRAY_SIZE(collie_batt_gpios));
381*8c0984e5SSebastian Reichel 	return ret;
382*8c0984e5SSebastian Reichel }
383*8c0984e5SSebastian Reichel 
384*8c0984e5SSebastian Reichel static void collie_bat_remove(struct ucb1x00_dev *dev)
385*8c0984e5SSebastian Reichel {
386*8c0984e5SSebastian Reichel 	free_irq(gpio_to_irq(COLLIE_GPIO_CO), &collie_bat_main);
387*8c0984e5SSebastian Reichel 
388*8c0984e5SSebastian Reichel 	power_supply_unregister(collie_bat_bu.psy);
389*8c0984e5SSebastian Reichel 	power_supply_unregister(collie_bat_main.psy);
390*8c0984e5SSebastian Reichel 
391*8c0984e5SSebastian Reichel 	/*
392*8c0984e5SSebastian Reichel 	 * Now cancel the bat_work.  We won't get any more schedules,
393*8c0984e5SSebastian Reichel 	 * since all sources (isr and external_power_changed) are
394*8c0984e5SSebastian Reichel 	 * unregistered now.
395*8c0984e5SSebastian Reichel 	 */
396*8c0984e5SSebastian Reichel 	cancel_work_sync(&bat_work);
397*8c0984e5SSebastian Reichel 	gpio_free_array(collie_batt_gpios, ARRAY_SIZE(collie_batt_gpios));
398*8c0984e5SSebastian Reichel }
399*8c0984e5SSebastian Reichel 
400*8c0984e5SSebastian Reichel static struct ucb1x00_driver collie_bat_driver = {
401*8c0984e5SSebastian Reichel 	.add		= collie_bat_probe,
402*8c0984e5SSebastian Reichel 	.remove		= collie_bat_remove,
403*8c0984e5SSebastian Reichel 	.suspend	= collie_bat_suspend,
404*8c0984e5SSebastian Reichel 	.resume		= collie_bat_resume,
405*8c0984e5SSebastian Reichel };
406*8c0984e5SSebastian Reichel 
407*8c0984e5SSebastian Reichel static int __init collie_bat_init(void)
408*8c0984e5SSebastian Reichel {
409*8c0984e5SSebastian Reichel 	return ucb1x00_register_driver(&collie_bat_driver);
410*8c0984e5SSebastian Reichel }
411*8c0984e5SSebastian Reichel 
412*8c0984e5SSebastian Reichel static void __exit collie_bat_exit(void)
413*8c0984e5SSebastian Reichel {
414*8c0984e5SSebastian Reichel 	ucb1x00_unregister_driver(&collie_bat_driver);
415*8c0984e5SSebastian Reichel }
416*8c0984e5SSebastian Reichel 
417*8c0984e5SSebastian Reichel module_init(collie_bat_init);
418*8c0984e5SSebastian Reichel module_exit(collie_bat_exit);
419*8c0984e5SSebastian Reichel 
420*8c0984e5SSebastian Reichel MODULE_LICENSE("GPL");
421*8c0984e5SSebastian Reichel MODULE_AUTHOR("Thomas Kunze");
422*8c0984e5SSebastian Reichel MODULE_DESCRIPTION("Collie battery driver");
423