xref: /openbmc/linux/drivers/power/supply/collie_battery.c (revision d2912cb15bdda8ba4a5dd73396ad62641af2f520)
1*d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
28c0984e5SSebastian Reichel /*
38c0984e5SSebastian Reichel  * Battery and Power Management code for the Sharp SL-5x00
48c0984e5SSebastian Reichel  *
58c0984e5SSebastian Reichel  * Copyright (C) 2009 Thomas Kunze
68c0984e5SSebastian Reichel  *
78c0984e5SSebastian Reichel  * based on tosa_battery.c
88c0984e5SSebastian Reichel  */
98c0984e5SSebastian Reichel #include <linux/kernel.h>
108c0984e5SSebastian Reichel #include <linux/module.h>
118c0984e5SSebastian Reichel #include <linux/power_supply.h>
128c0984e5SSebastian Reichel #include <linux/delay.h>
138c0984e5SSebastian Reichel #include <linux/spinlock.h>
148c0984e5SSebastian Reichel #include <linux/interrupt.h>
158c0984e5SSebastian Reichel #include <linux/gpio.h>
168c0984e5SSebastian Reichel #include <linux/mfd/ucb1x00.h>
178c0984e5SSebastian Reichel 
188c0984e5SSebastian Reichel #include <asm/mach/sharpsl_param.h>
198c0984e5SSebastian Reichel #include <asm/mach-types.h>
208c0984e5SSebastian Reichel #include <mach/collie.h>
218c0984e5SSebastian Reichel 
228c0984e5SSebastian Reichel static DEFINE_MUTEX(bat_lock); /* protects gpio pins */
238c0984e5SSebastian Reichel static struct work_struct bat_work;
248c0984e5SSebastian Reichel static struct ucb1x00 *ucb;
258c0984e5SSebastian Reichel 
268c0984e5SSebastian Reichel struct collie_bat {
278c0984e5SSebastian Reichel 	int status;
288c0984e5SSebastian Reichel 	struct power_supply *psy;
298c0984e5SSebastian Reichel 	int full_chrg;
308c0984e5SSebastian Reichel 
318c0984e5SSebastian Reichel 	struct mutex work_lock; /* protects data */
328c0984e5SSebastian Reichel 
338c0984e5SSebastian Reichel 	bool (*is_present)(struct collie_bat *bat);
348c0984e5SSebastian Reichel 	int gpio_full;
358c0984e5SSebastian Reichel 	int gpio_charge_on;
368c0984e5SSebastian Reichel 
378c0984e5SSebastian Reichel 	int technology;
388c0984e5SSebastian Reichel 
398c0984e5SSebastian Reichel 	int gpio_bat;
408c0984e5SSebastian Reichel 	int adc_bat;
418c0984e5SSebastian Reichel 	int adc_bat_divider;
428c0984e5SSebastian Reichel 	int bat_max;
438c0984e5SSebastian Reichel 	int bat_min;
448c0984e5SSebastian Reichel 
458c0984e5SSebastian Reichel 	int gpio_temp;
468c0984e5SSebastian Reichel 	int adc_temp;
478c0984e5SSebastian Reichel 	int adc_temp_divider;
488c0984e5SSebastian Reichel };
498c0984e5SSebastian Reichel 
508c0984e5SSebastian Reichel static struct collie_bat collie_bat_main;
518c0984e5SSebastian Reichel 
528c0984e5SSebastian Reichel static unsigned long collie_read_bat(struct collie_bat *bat)
538c0984e5SSebastian Reichel {
548c0984e5SSebastian Reichel 	unsigned long value = 0;
558c0984e5SSebastian Reichel 
568c0984e5SSebastian Reichel 	if (bat->gpio_bat < 0 || bat->adc_bat < 0)
578c0984e5SSebastian Reichel 		return 0;
588c0984e5SSebastian Reichel 	mutex_lock(&bat_lock);
598c0984e5SSebastian Reichel 	gpio_set_value(bat->gpio_bat, 1);
608c0984e5SSebastian Reichel 	msleep(5);
618c0984e5SSebastian Reichel 	ucb1x00_adc_enable(ucb);
628c0984e5SSebastian Reichel 	value = ucb1x00_adc_read(ucb, bat->adc_bat, UCB_SYNC);
638c0984e5SSebastian Reichel 	ucb1x00_adc_disable(ucb);
648c0984e5SSebastian Reichel 	gpio_set_value(bat->gpio_bat, 0);
658c0984e5SSebastian Reichel 	mutex_unlock(&bat_lock);
668c0984e5SSebastian Reichel 	value = value * 1000000 / bat->adc_bat_divider;
678c0984e5SSebastian Reichel 
688c0984e5SSebastian Reichel 	return value;
698c0984e5SSebastian Reichel }
708c0984e5SSebastian Reichel 
718c0984e5SSebastian Reichel static unsigned long collie_read_temp(struct collie_bat *bat)
728c0984e5SSebastian Reichel {
738c0984e5SSebastian Reichel 	unsigned long value = 0;
748c0984e5SSebastian Reichel 	if (bat->gpio_temp < 0 || bat->adc_temp < 0)
758c0984e5SSebastian Reichel 		return 0;
768c0984e5SSebastian Reichel 
778c0984e5SSebastian Reichel 	mutex_lock(&bat_lock);
788c0984e5SSebastian Reichel 	gpio_set_value(bat->gpio_temp, 1);
798c0984e5SSebastian Reichel 	msleep(5);
808c0984e5SSebastian Reichel 	ucb1x00_adc_enable(ucb);
818c0984e5SSebastian Reichel 	value = ucb1x00_adc_read(ucb, bat->adc_temp, UCB_SYNC);
828c0984e5SSebastian Reichel 	ucb1x00_adc_disable(ucb);
838c0984e5SSebastian Reichel 	gpio_set_value(bat->gpio_temp, 0);
848c0984e5SSebastian Reichel 	mutex_unlock(&bat_lock);
858c0984e5SSebastian Reichel 
868c0984e5SSebastian Reichel 	value = value * 10000 / bat->adc_temp_divider;
878c0984e5SSebastian Reichel 
888c0984e5SSebastian Reichel 	return value;
898c0984e5SSebastian Reichel }
908c0984e5SSebastian Reichel 
918c0984e5SSebastian Reichel static int collie_bat_get_property(struct power_supply *psy,
928c0984e5SSebastian Reichel 			    enum power_supply_property psp,
938c0984e5SSebastian Reichel 			    union power_supply_propval *val)
948c0984e5SSebastian Reichel {
958c0984e5SSebastian Reichel 	int ret = 0;
968c0984e5SSebastian Reichel 	struct collie_bat *bat = power_supply_get_drvdata(psy);
978c0984e5SSebastian Reichel 
988c0984e5SSebastian Reichel 	if (bat->is_present && !bat->is_present(bat)
998c0984e5SSebastian Reichel 			&& psp != POWER_SUPPLY_PROP_PRESENT) {
1008c0984e5SSebastian Reichel 		return -ENODEV;
1018c0984e5SSebastian Reichel 	}
1028c0984e5SSebastian Reichel 
1038c0984e5SSebastian Reichel 	switch (psp) {
1048c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_STATUS:
1058c0984e5SSebastian Reichel 		val->intval = bat->status;
1068c0984e5SSebastian Reichel 		break;
1078c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_TECHNOLOGY:
1088c0984e5SSebastian Reichel 		val->intval = bat->technology;
1098c0984e5SSebastian Reichel 		break;
1108c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
1118c0984e5SSebastian Reichel 		val->intval = collie_read_bat(bat);
1128c0984e5SSebastian Reichel 		break;
1138c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_VOLTAGE_MAX:
1148c0984e5SSebastian Reichel 		if (bat->full_chrg == -1)
1158c0984e5SSebastian Reichel 			val->intval = bat->bat_max;
1168c0984e5SSebastian Reichel 		else
1178c0984e5SSebastian Reichel 			val->intval = bat->full_chrg;
1188c0984e5SSebastian Reichel 		break;
1198c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
1208c0984e5SSebastian Reichel 		val->intval = bat->bat_max;
1218c0984e5SSebastian Reichel 		break;
1228c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
1238c0984e5SSebastian Reichel 		val->intval = bat->bat_min;
1248c0984e5SSebastian Reichel 		break;
1258c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_TEMP:
1268c0984e5SSebastian Reichel 		val->intval = collie_read_temp(bat);
1278c0984e5SSebastian Reichel 		break;
1288c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_PRESENT:
1298c0984e5SSebastian Reichel 		val->intval = bat->is_present ? bat->is_present(bat) : 1;
1308c0984e5SSebastian Reichel 		break;
1318c0984e5SSebastian Reichel 	default:
1328c0984e5SSebastian Reichel 		ret = -EINVAL;
1338c0984e5SSebastian Reichel 		break;
1348c0984e5SSebastian Reichel 	}
1358c0984e5SSebastian Reichel 	return ret;
1368c0984e5SSebastian Reichel }
1378c0984e5SSebastian Reichel 
1388c0984e5SSebastian Reichel static void collie_bat_external_power_changed(struct power_supply *psy)
1398c0984e5SSebastian Reichel {
1408c0984e5SSebastian Reichel 	schedule_work(&bat_work);
1418c0984e5SSebastian Reichel }
1428c0984e5SSebastian Reichel 
1438c0984e5SSebastian Reichel static irqreturn_t collie_bat_gpio_isr(int irq, void *data)
1448c0984e5SSebastian Reichel {
1458c0984e5SSebastian Reichel 	pr_info("collie_bat_gpio irq\n");
1468c0984e5SSebastian Reichel 	schedule_work(&bat_work);
1478c0984e5SSebastian Reichel 	return IRQ_HANDLED;
1488c0984e5SSebastian Reichel }
1498c0984e5SSebastian Reichel 
1508c0984e5SSebastian Reichel static void collie_bat_update(struct collie_bat *bat)
1518c0984e5SSebastian Reichel {
1528c0984e5SSebastian Reichel 	int old;
1538c0984e5SSebastian Reichel 	struct power_supply *psy = bat->psy;
1548c0984e5SSebastian Reichel 
1558c0984e5SSebastian Reichel 	mutex_lock(&bat->work_lock);
1568c0984e5SSebastian Reichel 
1578c0984e5SSebastian Reichel 	old = bat->status;
1588c0984e5SSebastian Reichel 
1598c0984e5SSebastian Reichel 	if (bat->is_present && !bat->is_present(bat)) {
1608c0984e5SSebastian Reichel 		printk(KERN_NOTICE "%s not present\n", psy->desc->name);
1618c0984e5SSebastian Reichel 		bat->status = POWER_SUPPLY_STATUS_UNKNOWN;
1628c0984e5SSebastian Reichel 		bat->full_chrg = -1;
1638c0984e5SSebastian Reichel 	} else if (power_supply_am_i_supplied(psy)) {
1648c0984e5SSebastian Reichel 		if (bat->status == POWER_SUPPLY_STATUS_DISCHARGING) {
1658c0984e5SSebastian Reichel 			gpio_set_value(bat->gpio_charge_on, 1);
1668c0984e5SSebastian Reichel 			mdelay(15);
1678c0984e5SSebastian Reichel 		}
1688c0984e5SSebastian Reichel 
1698c0984e5SSebastian Reichel 		if (gpio_get_value(bat->gpio_full)) {
1708c0984e5SSebastian Reichel 			if (old == POWER_SUPPLY_STATUS_CHARGING ||
1718c0984e5SSebastian Reichel 					bat->full_chrg == -1)
1728c0984e5SSebastian Reichel 				bat->full_chrg = collie_read_bat(bat);
1738c0984e5SSebastian Reichel 
1748c0984e5SSebastian Reichel 			gpio_set_value(bat->gpio_charge_on, 0);
1758c0984e5SSebastian Reichel 			bat->status = POWER_SUPPLY_STATUS_FULL;
1768c0984e5SSebastian Reichel 		} else {
1778c0984e5SSebastian Reichel 			gpio_set_value(bat->gpio_charge_on, 1);
1788c0984e5SSebastian Reichel 			bat->status = POWER_SUPPLY_STATUS_CHARGING;
1798c0984e5SSebastian Reichel 		}
1808c0984e5SSebastian Reichel 	} else {
1818c0984e5SSebastian Reichel 		gpio_set_value(bat->gpio_charge_on, 0);
1828c0984e5SSebastian Reichel 		bat->status = POWER_SUPPLY_STATUS_DISCHARGING;
1838c0984e5SSebastian Reichel 	}
1848c0984e5SSebastian Reichel 
1858c0984e5SSebastian Reichel 	if (old != bat->status)
1868c0984e5SSebastian Reichel 		power_supply_changed(psy);
1878c0984e5SSebastian Reichel 
1888c0984e5SSebastian Reichel 	mutex_unlock(&bat->work_lock);
1898c0984e5SSebastian Reichel }
1908c0984e5SSebastian Reichel 
1918c0984e5SSebastian Reichel static void collie_bat_work(struct work_struct *work)
1928c0984e5SSebastian Reichel {
1938c0984e5SSebastian Reichel 	collie_bat_update(&collie_bat_main);
1948c0984e5SSebastian Reichel }
1958c0984e5SSebastian Reichel 
1968c0984e5SSebastian Reichel 
1978c0984e5SSebastian Reichel static enum power_supply_property collie_bat_main_props[] = {
1988c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_STATUS,
1998c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_TECHNOLOGY,
2008c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
2018c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_VOLTAGE_NOW,
2028c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
2038c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_VOLTAGE_MAX,
2048c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_PRESENT,
2058c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_TEMP,
2068c0984e5SSebastian Reichel };
2078c0984e5SSebastian Reichel 
2088c0984e5SSebastian Reichel static enum power_supply_property collie_bat_bu_props[] = {
2098c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_STATUS,
2108c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_TECHNOLOGY,
2118c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
2128c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_VOLTAGE_NOW,
2138c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
2148c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_VOLTAGE_MAX,
2158c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_PRESENT,
2168c0984e5SSebastian Reichel };
2178c0984e5SSebastian Reichel 
2188c0984e5SSebastian Reichel static const struct power_supply_desc collie_bat_main_desc = {
2198c0984e5SSebastian Reichel 	.name		= "main-battery",
2208c0984e5SSebastian Reichel 	.type		= POWER_SUPPLY_TYPE_BATTERY,
2218c0984e5SSebastian Reichel 	.properties	= collie_bat_main_props,
2228c0984e5SSebastian Reichel 	.num_properties	= ARRAY_SIZE(collie_bat_main_props),
2238c0984e5SSebastian Reichel 	.get_property	= collie_bat_get_property,
2248c0984e5SSebastian Reichel 	.external_power_changed = collie_bat_external_power_changed,
2258c0984e5SSebastian Reichel 	.use_for_apm	= 1,
2268c0984e5SSebastian Reichel };
2278c0984e5SSebastian Reichel 
2288c0984e5SSebastian Reichel static struct collie_bat collie_bat_main = {
2298c0984e5SSebastian Reichel 	.status = POWER_SUPPLY_STATUS_DISCHARGING,
2308c0984e5SSebastian Reichel 	.full_chrg = -1,
2318c0984e5SSebastian Reichel 	.psy = NULL,
2328c0984e5SSebastian Reichel 
2338c0984e5SSebastian Reichel 	.gpio_full = COLLIE_GPIO_CO,
2348c0984e5SSebastian Reichel 	.gpio_charge_on = COLLIE_GPIO_CHARGE_ON,
2358c0984e5SSebastian Reichel 
2368c0984e5SSebastian Reichel 	.technology = POWER_SUPPLY_TECHNOLOGY_LIPO,
2378c0984e5SSebastian Reichel 
2388c0984e5SSebastian Reichel 	.gpio_bat = COLLIE_GPIO_MBAT_ON,
2398c0984e5SSebastian Reichel 	.adc_bat = UCB_ADC_INP_AD1,
2408c0984e5SSebastian Reichel 	.adc_bat_divider = 155,
2418c0984e5SSebastian Reichel 	.bat_max = 4310000,
2428c0984e5SSebastian Reichel 	.bat_min = 1551 * 1000000 / 414,
2438c0984e5SSebastian Reichel 
2448c0984e5SSebastian Reichel 	.gpio_temp = COLLIE_GPIO_TMP_ON,
2458c0984e5SSebastian Reichel 	.adc_temp = UCB_ADC_INP_AD0,
2468c0984e5SSebastian Reichel 	.adc_temp_divider = 10000,
2478c0984e5SSebastian Reichel };
2488c0984e5SSebastian Reichel 
2498c0984e5SSebastian Reichel static const struct power_supply_desc collie_bat_bu_desc = {
2508c0984e5SSebastian Reichel 	.name		= "backup-battery",
2518c0984e5SSebastian Reichel 	.type		= POWER_SUPPLY_TYPE_BATTERY,
2528c0984e5SSebastian Reichel 	.properties	= collie_bat_bu_props,
2538c0984e5SSebastian Reichel 	.num_properties	= ARRAY_SIZE(collie_bat_bu_props),
2548c0984e5SSebastian Reichel 	.get_property	= collie_bat_get_property,
2558c0984e5SSebastian Reichel 	.external_power_changed = collie_bat_external_power_changed,
2568c0984e5SSebastian Reichel };
2578c0984e5SSebastian Reichel 
2588c0984e5SSebastian Reichel static struct collie_bat collie_bat_bu = {
2598c0984e5SSebastian Reichel 	.status = POWER_SUPPLY_STATUS_UNKNOWN,
2608c0984e5SSebastian Reichel 	.full_chrg = -1,
2618c0984e5SSebastian Reichel 	.psy = NULL,
2628c0984e5SSebastian Reichel 
2638c0984e5SSebastian Reichel 	.gpio_full = -1,
2648c0984e5SSebastian Reichel 	.gpio_charge_on = -1,
2658c0984e5SSebastian Reichel 
2668c0984e5SSebastian Reichel 	.technology = POWER_SUPPLY_TECHNOLOGY_LiMn,
2678c0984e5SSebastian Reichel 
2688c0984e5SSebastian Reichel 	.gpio_bat = COLLIE_GPIO_BBAT_ON,
2698c0984e5SSebastian Reichel 	.adc_bat = UCB_ADC_INP_AD1,
2708c0984e5SSebastian Reichel 	.adc_bat_divider = 155,
2718c0984e5SSebastian Reichel 	.bat_max = 3000000,
2728c0984e5SSebastian Reichel 	.bat_min = 1900000,
2738c0984e5SSebastian Reichel 
2748c0984e5SSebastian Reichel 	.gpio_temp = -1,
2758c0984e5SSebastian Reichel 	.adc_temp = -1,
2768c0984e5SSebastian Reichel 	.adc_temp_divider = -1,
2778c0984e5SSebastian Reichel };
2788c0984e5SSebastian Reichel 
2798c0984e5SSebastian Reichel static struct gpio collie_batt_gpios[] = {
2808c0984e5SSebastian Reichel 	{ COLLIE_GPIO_CO,	    GPIOF_IN,		"main battery full" },
2818c0984e5SSebastian Reichel 	{ COLLIE_GPIO_MAIN_BAT_LOW, GPIOF_IN,		"main battery low" },
2828c0984e5SSebastian Reichel 	{ COLLIE_GPIO_CHARGE_ON,    GPIOF_OUT_INIT_LOW,	"main charge on" },
2838c0984e5SSebastian Reichel 	{ COLLIE_GPIO_MBAT_ON,	    GPIOF_OUT_INIT_LOW,	"main battery" },
2848c0984e5SSebastian Reichel 	{ COLLIE_GPIO_TMP_ON,	    GPIOF_OUT_INIT_LOW,	"main battery temp" },
2858c0984e5SSebastian Reichel 	{ COLLIE_GPIO_BBAT_ON,	    GPIOF_OUT_INIT_LOW,	"backup battery" },
2868c0984e5SSebastian Reichel };
2878c0984e5SSebastian Reichel 
2888c0984e5SSebastian Reichel #ifdef CONFIG_PM
2898c0984e5SSebastian Reichel static int wakeup_enabled;
2908c0984e5SSebastian Reichel 
2918c0984e5SSebastian Reichel static int collie_bat_suspend(struct ucb1x00_dev *dev)
2928c0984e5SSebastian Reichel {
2938c0984e5SSebastian Reichel 	/* flush all pending status updates */
2948c0984e5SSebastian Reichel 	flush_work(&bat_work);
2958c0984e5SSebastian Reichel 
2968c0984e5SSebastian Reichel 	if (device_may_wakeup(&dev->ucb->dev) &&
2978c0984e5SSebastian Reichel 	    collie_bat_main.status == POWER_SUPPLY_STATUS_CHARGING)
2988c0984e5SSebastian Reichel 		wakeup_enabled = !enable_irq_wake(gpio_to_irq(COLLIE_GPIO_CO));
2998c0984e5SSebastian Reichel 	else
3008c0984e5SSebastian Reichel 		wakeup_enabled = 0;
3018c0984e5SSebastian Reichel 
3028c0984e5SSebastian Reichel 	return 0;
3038c0984e5SSebastian Reichel }
3048c0984e5SSebastian Reichel 
3058c0984e5SSebastian Reichel static int collie_bat_resume(struct ucb1x00_dev *dev)
3068c0984e5SSebastian Reichel {
3078c0984e5SSebastian Reichel 	if (wakeup_enabled)
3088c0984e5SSebastian Reichel 		disable_irq_wake(gpio_to_irq(COLLIE_GPIO_CO));
3098c0984e5SSebastian Reichel 
3108c0984e5SSebastian Reichel 	/* things may have changed while we were away */
3118c0984e5SSebastian Reichel 	schedule_work(&bat_work);
3128c0984e5SSebastian Reichel 	return 0;
3138c0984e5SSebastian Reichel }
3148c0984e5SSebastian Reichel #else
3158c0984e5SSebastian Reichel #define collie_bat_suspend NULL
3168c0984e5SSebastian Reichel #define collie_bat_resume NULL
3178c0984e5SSebastian Reichel #endif
3188c0984e5SSebastian Reichel 
3198c0984e5SSebastian Reichel static int collie_bat_probe(struct ucb1x00_dev *dev)
3208c0984e5SSebastian Reichel {
3218c0984e5SSebastian Reichel 	int ret;
3228c0984e5SSebastian Reichel 	struct power_supply_config psy_main_cfg = {}, psy_bu_cfg = {};
3238c0984e5SSebastian Reichel 
3248c0984e5SSebastian Reichel 	if (!machine_is_collie())
3258c0984e5SSebastian Reichel 		return -ENODEV;
3268c0984e5SSebastian Reichel 
3278c0984e5SSebastian Reichel 	ucb = dev->ucb;
3288c0984e5SSebastian Reichel 
3298c0984e5SSebastian Reichel 	ret = gpio_request_array(collie_batt_gpios,
3308c0984e5SSebastian Reichel 				 ARRAY_SIZE(collie_batt_gpios));
3318c0984e5SSebastian Reichel 	if (ret)
3328c0984e5SSebastian Reichel 		return ret;
3338c0984e5SSebastian Reichel 
3348c0984e5SSebastian Reichel 	mutex_init(&collie_bat_main.work_lock);
3358c0984e5SSebastian Reichel 
3368c0984e5SSebastian Reichel 	INIT_WORK(&bat_work, collie_bat_work);
3378c0984e5SSebastian Reichel 
3388c0984e5SSebastian Reichel 	psy_main_cfg.drv_data = &collie_bat_main;
3398c0984e5SSebastian Reichel 	collie_bat_main.psy = power_supply_register(&dev->ucb->dev,
3408c0984e5SSebastian Reichel 						    &collie_bat_main_desc,
3418c0984e5SSebastian Reichel 						    &psy_main_cfg);
3428c0984e5SSebastian Reichel 	if (IS_ERR(collie_bat_main.psy)) {
3438c0984e5SSebastian Reichel 		ret = PTR_ERR(collie_bat_main.psy);
3448c0984e5SSebastian Reichel 		goto err_psy_reg_main;
3458c0984e5SSebastian Reichel 	}
3468c0984e5SSebastian Reichel 
3478c0984e5SSebastian Reichel 	psy_bu_cfg.drv_data = &collie_bat_bu;
3488c0984e5SSebastian Reichel 	collie_bat_bu.psy = power_supply_register(&dev->ucb->dev,
3498c0984e5SSebastian Reichel 						  &collie_bat_bu_desc,
3508c0984e5SSebastian Reichel 						  &psy_bu_cfg);
3518c0984e5SSebastian Reichel 	if (IS_ERR(collie_bat_bu.psy)) {
3528c0984e5SSebastian Reichel 		ret = PTR_ERR(collie_bat_bu.psy);
3538c0984e5SSebastian Reichel 		goto err_psy_reg_bu;
3548c0984e5SSebastian Reichel 	}
3558c0984e5SSebastian Reichel 
3568c0984e5SSebastian Reichel 	ret = request_irq(gpio_to_irq(COLLIE_GPIO_CO),
3578c0984e5SSebastian Reichel 				collie_bat_gpio_isr,
3588c0984e5SSebastian Reichel 				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
3598c0984e5SSebastian Reichel 				"main full", &collie_bat_main);
3608c0984e5SSebastian Reichel 	if (ret)
3618c0984e5SSebastian Reichel 		goto err_irq;
3628c0984e5SSebastian Reichel 
3638c0984e5SSebastian Reichel 	device_init_wakeup(&ucb->dev, 1);
3648c0984e5SSebastian Reichel 	schedule_work(&bat_work);
3658c0984e5SSebastian Reichel 
3668c0984e5SSebastian Reichel 	return 0;
3678c0984e5SSebastian Reichel 
3688c0984e5SSebastian Reichel err_irq:
3698c0984e5SSebastian Reichel 	power_supply_unregister(collie_bat_bu.psy);
3708c0984e5SSebastian Reichel err_psy_reg_bu:
3718c0984e5SSebastian Reichel 	power_supply_unregister(collie_bat_main.psy);
3728c0984e5SSebastian Reichel err_psy_reg_main:
3738c0984e5SSebastian Reichel 
3748c0984e5SSebastian Reichel 	/* see comment in collie_bat_remove */
3758c0984e5SSebastian Reichel 	cancel_work_sync(&bat_work);
3768c0984e5SSebastian Reichel 	gpio_free_array(collie_batt_gpios, ARRAY_SIZE(collie_batt_gpios));
3778c0984e5SSebastian Reichel 	return ret;
3788c0984e5SSebastian Reichel }
3798c0984e5SSebastian Reichel 
3808c0984e5SSebastian Reichel static void collie_bat_remove(struct ucb1x00_dev *dev)
3818c0984e5SSebastian Reichel {
3828c0984e5SSebastian Reichel 	free_irq(gpio_to_irq(COLLIE_GPIO_CO), &collie_bat_main);
3838c0984e5SSebastian Reichel 
3848c0984e5SSebastian Reichel 	power_supply_unregister(collie_bat_bu.psy);
3858c0984e5SSebastian Reichel 	power_supply_unregister(collie_bat_main.psy);
3868c0984e5SSebastian Reichel 
3878c0984e5SSebastian Reichel 	/*
3888c0984e5SSebastian Reichel 	 * Now cancel the bat_work.  We won't get any more schedules,
3898c0984e5SSebastian Reichel 	 * since all sources (isr and external_power_changed) are
3908c0984e5SSebastian Reichel 	 * unregistered now.
3918c0984e5SSebastian Reichel 	 */
3928c0984e5SSebastian Reichel 	cancel_work_sync(&bat_work);
3938c0984e5SSebastian Reichel 	gpio_free_array(collie_batt_gpios, ARRAY_SIZE(collie_batt_gpios));
3948c0984e5SSebastian Reichel }
3958c0984e5SSebastian Reichel 
3968c0984e5SSebastian Reichel static struct ucb1x00_driver collie_bat_driver = {
3978c0984e5SSebastian Reichel 	.add		= collie_bat_probe,
3988c0984e5SSebastian Reichel 	.remove		= collie_bat_remove,
3998c0984e5SSebastian Reichel 	.suspend	= collie_bat_suspend,
4008c0984e5SSebastian Reichel 	.resume		= collie_bat_resume,
4018c0984e5SSebastian Reichel };
4028c0984e5SSebastian Reichel 
4038c0984e5SSebastian Reichel static int __init collie_bat_init(void)
4048c0984e5SSebastian Reichel {
4058c0984e5SSebastian Reichel 	return ucb1x00_register_driver(&collie_bat_driver);
4068c0984e5SSebastian Reichel }
4078c0984e5SSebastian Reichel 
4088c0984e5SSebastian Reichel static void __exit collie_bat_exit(void)
4098c0984e5SSebastian Reichel {
4108c0984e5SSebastian Reichel 	ucb1x00_unregister_driver(&collie_bat_driver);
4118c0984e5SSebastian Reichel }
4128c0984e5SSebastian Reichel 
4138c0984e5SSebastian Reichel module_init(collie_bat_init);
4148c0984e5SSebastian Reichel module_exit(collie_bat_exit);
4158c0984e5SSebastian Reichel 
4168c0984e5SSebastian Reichel MODULE_LICENSE("GPL");
4178c0984e5SSebastian Reichel MODULE_AUTHOR("Thomas Kunze");
4188c0984e5SSebastian Reichel MODULE_DESCRIPTION("Collie battery driver");
419