xref: /openbmc/linux/drivers/power/supply/collie_battery.c (revision 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e)
1d2912cb1SThomas 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>
15ba940ed8SLinus Walleij #include <linux/gpio/driver.h>
16ba940ed8SLinus Walleij #include <linux/gpio/machine.h>
17ba940ed8SLinus Walleij #include <linux/gpio/consumer.h>
188c0984e5SSebastian Reichel #include <linux/mfd/ucb1x00.h>
198c0984e5SSebastian Reichel 
208c0984e5SSebastian Reichel #include <asm/mach/sharpsl_param.h>
218c0984e5SSebastian Reichel #include <asm/mach-types.h>
228c0984e5SSebastian Reichel #include <mach/collie.h>
238c0984e5SSebastian Reichel 
248c0984e5SSebastian Reichel static DEFINE_MUTEX(bat_lock); /* protects gpio pins */
258c0984e5SSebastian Reichel static struct work_struct bat_work;
268c0984e5SSebastian Reichel static struct ucb1x00 *ucb;
278c0984e5SSebastian Reichel 
288c0984e5SSebastian Reichel struct collie_bat {
298c0984e5SSebastian Reichel 	int status;
308c0984e5SSebastian Reichel 	struct power_supply *psy;
318c0984e5SSebastian Reichel 	int full_chrg;
328c0984e5SSebastian Reichel 
338c0984e5SSebastian Reichel 	struct mutex work_lock; /* protects data */
348c0984e5SSebastian Reichel 
358c0984e5SSebastian Reichel 	bool (*is_present)(struct collie_bat *bat);
36ba940ed8SLinus Walleij 	struct gpio_desc *gpio_full;
37ba940ed8SLinus Walleij 	struct gpio_desc *gpio_charge_on;
388c0984e5SSebastian Reichel 
398c0984e5SSebastian Reichel 	int technology;
408c0984e5SSebastian Reichel 
41ba940ed8SLinus Walleij 	struct gpio_desc *gpio_bat;
428c0984e5SSebastian Reichel 	int adc_bat;
438c0984e5SSebastian Reichel 	int adc_bat_divider;
448c0984e5SSebastian Reichel 	int bat_max;
458c0984e5SSebastian Reichel 	int bat_min;
468c0984e5SSebastian Reichel 
47ba940ed8SLinus Walleij 	struct gpio_desc *gpio_temp;
488c0984e5SSebastian Reichel 	int adc_temp;
498c0984e5SSebastian Reichel 	int adc_temp_divider;
508c0984e5SSebastian Reichel };
518c0984e5SSebastian Reichel 
528c0984e5SSebastian Reichel static struct collie_bat collie_bat_main;
538c0984e5SSebastian Reichel 
collie_read_bat(struct collie_bat * bat)548c0984e5SSebastian Reichel static unsigned long collie_read_bat(struct collie_bat *bat)
558c0984e5SSebastian Reichel {
568c0984e5SSebastian Reichel 	unsigned long value = 0;
578c0984e5SSebastian Reichel 
58ba940ed8SLinus Walleij 	if (!bat->gpio_bat || bat->adc_bat < 0)
598c0984e5SSebastian Reichel 		return 0;
608c0984e5SSebastian Reichel 	mutex_lock(&bat_lock);
61ba940ed8SLinus Walleij 	gpiod_set_value(bat->gpio_bat, 1);
628c0984e5SSebastian Reichel 	msleep(5);
638c0984e5SSebastian Reichel 	ucb1x00_adc_enable(ucb);
648c0984e5SSebastian Reichel 	value = ucb1x00_adc_read(ucb, bat->adc_bat, UCB_SYNC);
658c0984e5SSebastian Reichel 	ucb1x00_adc_disable(ucb);
66ba940ed8SLinus Walleij 	gpiod_set_value(bat->gpio_bat, 0);
678c0984e5SSebastian Reichel 	mutex_unlock(&bat_lock);
688c0984e5SSebastian Reichel 	value = value * 1000000 / bat->adc_bat_divider;
698c0984e5SSebastian Reichel 
708c0984e5SSebastian Reichel 	return value;
718c0984e5SSebastian Reichel }
728c0984e5SSebastian Reichel 
collie_read_temp(struct collie_bat * bat)738c0984e5SSebastian Reichel static unsigned long collie_read_temp(struct collie_bat *bat)
748c0984e5SSebastian Reichel {
758c0984e5SSebastian Reichel 	unsigned long value = 0;
76ba940ed8SLinus Walleij 	if (!bat->gpio_temp || bat->adc_temp < 0)
778c0984e5SSebastian Reichel 		return 0;
788c0984e5SSebastian Reichel 
798c0984e5SSebastian Reichel 	mutex_lock(&bat_lock);
80ba940ed8SLinus Walleij 	gpiod_set_value(bat->gpio_temp, 1);
818c0984e5SSebastian Reichel 	msleep(5);
828c0984e5SSebastian Reichel 	ucb1x00_adc_enable(ucb);
838c0984e5SSebastian Reichel 	value = ucb1x00_adc_read(ucb, bat->adc_temp, UCB_SYNC);
848c0984e5SSebastian Reichel 	ucb1x00_adc_disable(ucb);
85ba940ed8SLinus Walleij 	gpiod_set_value(bat->gpio_temp, 0);
868c0984e5SSebastian Reichel 	mutex_unlock(&bat_lock);
878c0984e5SSebastian Reichel 
888c0984e5SSebastian Reichel 	value = value * 10000 / bat->adc_temp_divider;
898c0984e5SSebastian Reichel 
908c0984e5SSebastian Reichel 	return value;
918c0984e5SSebastian Reichel }
928c0984e5SSebastian Reichel 
collie_bat_get_property(struct power_supply * psy,enum power_supply_property psp,union power_supply_propval * val)938c0984e5SSebastian Reichel static int collie_bat_get_property(struct power_supply *psy,
948c0984e5SSebastian Reichel 			    enum power_supply_property psp,
958c0984e5SSebastian Reichel 			    union power_supply_propval *val)
968c0984e5SSebastian Reichel {
978c0984e5SSebastian Reichel 	int ret = 0;
988c0984e5SSebastian Reichel 	struct collie_bat *bat = power_supply_get_drvdata(psy);
998c0984e5SSebastian Reichel 
1008c0984e5SSebastian Reichel 	if (bat->is_present && !bat->is_present(bat)
1018c0984e5SSebastian Reichel 			&& psp != POWER_SUPPLY_PROP_PRESENT) {
1028c0984e5SSebastian Reichel 		return -ENODEV;
1038c0984e5SSebastian Reichel 	}
1048c0984e5SSebastian Reichel 
1058c0984e5SSebastian Reichel 	switch (psp) {
1068c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_STATUS:
1078c0984e5SSebastian Reichel 		val->intval = bat->status;
1088c0984e5SSebastian Reichel 		break;
1098c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_TECHNOLOGY:
1108c0984e5SSebastian Reichel 		val->intval = bat->technology;
1118c0984e5SSebastian Reichel 		break;
1128c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
1138c0984e5SSebastian Reichel 		val->intval = collie_read_bat(bat);
1148c0984e5SSebastian Reichel 		break;
1158c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_VOLTAGE_MAX:
1168c0984e5SSebastian Reichel 		if (bat->full_chrg == -1)
1178c0984e5SSebastian Reichel 			val->intval = bat->bat_max;
1188c0984e5SSebastian Reichel 		else
1198c0984e5SSebastian Reichel 			val->intval = bat->full_chrg;
1208c0984e5SSebastian Reichel 		break;
1218c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
1228c0984e5SSebastian Reichel 		val->intval = bat->bat_max;
1238c0984e5SSebastian Reichel 		break;
1248c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
1258c0984e5SSebastian Reichel 		val->intval = bat->bat_min;
1268c0984e5SSebastian Reichel 		break;
1278c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_TEMP:
1288c0984e5SSebastian Reichel 		val->intval = collie_read_temp(bat);
1298c0984e5SSebastian Reichel 		break;
1308c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_PRESENT:
1318c0984e5SSebastian Reichel 		val->intval = bat->is_present ? bat->is_present(bat) : 1;
1328c0984e5SSebastian Reichel 		break;
1338c0984e5SSebastian Reichel 	default:
1348c0984e5SSebastian Reichel 		ret = -EINVAL;
1358c0984e5SSebastian Reichel 		break;
1368c0984e5SSebastian Reichel 	}
1378c0984e5SSebastian Reichel 	return ret;
1388c0984e5SSebastian Reichel }
1398c0984e5SSebastian Reichel 
collie_bat_external_power_changed(struct power_supply * psy)1408c0984e5SSebastian Reichel static void collie_bat_external_power_changed(struct power_supply *psy)
1418c0984e5SSebastian Reichel {
1428c0984e5SSebastian Reichel 	schedule_work(&bat_work);
1438c0984e5SSebastian Reichel }
1448c0984e5SSebastian Reichel 
collie_bat_gpio_isr(int irq,void * data)1458c0984e5SSebastian Reichel static irqreturn_t collie_bat_gpio_isr(int irq, void *data)
1468c0984e5SSebastian Reichel {
1478c0984e5SSebastian Reichel 	pr_info("collie_bat_gpio irq\n");
1488c0984e5SSebastian Reichel 	schedule_work(&bat_work);
1498c0984e5SSebastian Reichel 	return IRQ_HANDLED;
1508c0984e5SSebastian Reichel }
1518c0984e5SSebastian Reichel 
collie_bat_update(struct collie_bat * bat)1528c0984e5SSebastian Reichel static void collie_bat_update(struct collie_bat *bat)
1538c0984e5SSebastian Reichel {
1548c0984e5SSebastian Reichel 	int old;
1558c0984e5SSebastian Reichel 	struct power_supply *psy = bat->psy;
1568c0984e5SSebastian Reichel 
1578c0984e5SSebastian Reichel 	mutex_lock(&bat->work_lock);
1588c0984e5SSebastian Reichel 
1598c0984e5SSebastian Reichel 	old = bat->status;
1608c0984e5SSebastian Reichel 
1618c0984e5SSebastian Reichel 	if (bat->is_present && !bat->is_present(bat)) {
1628c0984e5SSebastian Reichel 		printk(KERN_NOTICE "%s not present\n", psy->desc->name);
1638c0984e5SSebastian Reichel 		bat->status = POWER_SUPPLY_STATUS_UNKNOWN;
1648c0984e5SSebastian Reichel 		bat->full_chrg = -1;
1658c0984e5SSebastian Reichel 	} else if (power_supply_am_i_supplied(psy)) {
1668c0984e5SSebastian Reichel 		if (bat->status == POWER_SUPPLY_STATUS_DISCHARGING) {
167ba940ed8SLinus Walleij 			gpiod_set_value(bat->gpio_charge_on, 1);
1688c0984e5SSebastian Reichel 			mdelay(15);
1698c0984e5SSebastian Reichel 		}
1708c0984e5SSebastian Reichel 
171ba940ed8SLinus Walleij 		if (gpiod_get_value(bat->gpio_full)) {
1728c0984e5SSebastian Reichel 			if (old == POWER_SUPPLY_STATUS_CHARGING ||
1738c0984e5SSebastian Reichel 					bat->full_chrg == -1)
1748c0984e5SSebastian Reichel 				bat->full_chrg = collie_read_bat(bat);
1758c0984e5SSebastian Reichel 
176ba940ed8SLinus Walleij 			gpiod_set_value(bat->gpio_charge_on, 0);
1778c0984e5SSebastian Reichel 			bat->status = POWER_SUPPLY_STATUS_FULL;
1788c0984e5SSebastian Reichel 		} else {
179ba940ed8SLinus Walleij 			gpiod_set_value(bat->gpio_charge_on, 1);
1808c0984e5SSebastian Reichel 			bat->status = POWER_SUPPLY_STATUS_CHARGING;
1818c0984e5SSebastian Reichel 		}
1828c0984e5SSebastian Reichel 	} else {
183ba940ed8SLinus Walleij 		gpiod_set_value(bat->gpio_charge_on, 0);
1848c0984e5SSebastian Reichel 		bat->status = POWER_SUPPLY_STATUS_DISCHARGING;
1858c0984e5SSebastian Reichel 	}
1868c0984e5SSebastian Reichel 
1878c0984e5SSebastian Reichel 	if (old != bat->status)
1888c0984e5SSebastian Reichel 		power_supply_changed(psy);
1898c0984e5SSebastian Reichel 
1908c0984e5SSebastian Reichel 	mutex_unlock(&bat->work_lock);
1918c0984e5SSebastian Reichel }
1928c0984e5SSebastian Reichel 
collie_bat_work(struct work_struct * work)1938c0984e5SSebastian Reichel static void collie_bat_work(struct work_struct *work)
1948c0984e5SSebastian Reichel {
1958c0984e5SSebastian Reichel 	collie_bat_update(&collie_bat_main);
1968c0984e5SSebastian Reichel }
1978c0984e5SSebastian Reichel 
1988c0984e5SSebastian Reichel 
1998c0984e5SSebastian Reichel static enum power_supply_property collie_bat_main_props[] = {
2008c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_STATUS,
2018c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_TECHNOLOGY,
2028c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
2038c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_VOLTAGE_NOW,
2048c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
2058c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_VOLTAGE_MAX,
2068c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_PRESENT,
2078c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_TEMP,
2088c0984e5SSebastian Reichel };
2098c0984e5SSebastian Reichel 
2108c0984e5SSebastian Reichel static enum power_supply_property collie_bat_bu_props[] = {
2118c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_STATUS,
2128c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_TECHNOLOGY,
2138c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
2148c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_VOLTAGE_NOW,
2158c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
2168c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_VOLTAGE_MAX,
2178c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_PRESENT,
2188c0984e5SSebastian Reichel };
2198c0984e5SSebastian Reichel 
2208c0984e5SSebastian Reichel static const struct power_supply_desc collie_bat_main_desc = {
2218c0984e5SSebastian Reichel 	.name		= "main-battery",
2228c0984e5SSebastian Reichel 	.type		= POWER_SUPPLY_TYPE_BATTERY,
2238c0984e5SSebastian Reichel 	.properties	= collie_bat_main_props,
2248c0984e5SSebastian Reichel 	.num_properties	= ARRAY_SIZE(collie_bat_main_props),
2258c0984e5SSebastian Reichel 	.get_property	= collie_bat_get_property,
2268c0984e5SSebastian Reichel 	.external_power_changed = collie_bat_external_power_changed,
2278c0984e5SSebastian Reichel 	.use_for_apm	= 1,
2288c0984e5SSebastian Reichel };
2298c0984e5SSebastian Reichel 
2308c0984e5SSebastian Reichel static struct collie_bat collie_bat_main = {
2318c0984e5SSebastian Reichel 	.status = POWER_SUPPLY_STATUS_DISCHARGING,
2328c0984e5SSebastian Reichel 	.full_chrg = -1,
2338c0984e5SSebastian Reichel 	.psy = NULL,
2348c0984e5SSebastian Reichel 
235ba940ed8SLinus Walleij 	.gpio_full = NULL,
236ba940ed8SLinus Walleij 	.gpio_charge_on = NULL,
2378c0984e5SSebastian Reichel 
2388c0984e5SSebastian Reichel 	.technology = POWER_SUPPLY_TECHNOLOGY_LIPO,
2398c0984e5SSebastian Reichel 
240ba940ed8SLinus Walleij 	.gpio_bat = NULL,
2418c0984e5SSebastian Reichel 	.adc_bat = UCB_ADC_INP_AD1,
2428c0984e5SSebastian Reichel 	.adc_bat_divider = 155,
2438c0984e5SSebastian Reichel 	.bat_max = 4310000,
2448c0984e5SSebastian Reichel 	.bat_min = 1551 * 1000000 / 414,
2458c0984e5SSebastian Reichel 
246ba940ed8SLinus Walleij 	.gpio_temp = NULL,
2478c0984e5SSebastian Reichel 	.adc_temp = UCB_ADC_INP_AD0,
2488c0984e5SSebastian Reichel 	.adc_temp_divider = 10000,
2498c0984e5SSebastian Reichel };
2508c0984e5SSebastian Reichel 
2518c0984e5SSebastian Reichel static const struct power_supply_desc collie_bat_bu_desc = {
2528c0984e5SSebastian Reichel 	.name		= "backup-battery",
2538c0984e5SSebastian Reichel 	.type		= POWER_SUPPLY_TYPE_BATTERY,
2548c0984e5SSebastian Reichel 	.properties	= collie_bat_bu_props,
2558c0984e5SSebastian Reichel 	.num_properties	= ARRAY_SIZE(collie_bat_bu_props),
2568c0984e5SSebastian Reichel 	.get_property	= collie_bat_get_property,
2578c0984e5SSebastian Reichel 	.external_power_changed = collie_bat_external_power_changed,
2588c0984e5SSebastian Reichel };
2598c0984e5SSebastian Reichel 
2608c0984e5SSebastian Reichel static struct collie_bat collie_bat_bu = {
2618c0984e5SSebastian Reichel 	.status = POWER_SUPPLY_STATUS_UNKNOWN,
2628c0984e5SSebastian Reichel 	.full_chrg = -1,
2638c0984e5SSebastian Reichel 	.psy = NULL,
2648c0984e5SSebastian Reichel 
265ba940ed8SLinus Walleij 	.gpio_full = NULL,
266ba940ed8SLinus Walleij 	.gpio_charge_on = NULL,
2678c0984e5SSebastian Reichel 
2688c0984e5SSebastian Reichel 	.technology = POWER_SUPPLY_TECHNOLOGY_LiMn,
2698c0984e5SSebastian Reichel 
270ba940ed8SLinus Walleij 	.gpio_bat = NULL,
2718c0984e5SSebastian Reichel 	.adc_bat = UCB_ADC_INP_AD1,
2728c0984e5SSebastian Reichel 	.adc_bat_divider = 155,
2738c0984e5SSebastian Reichel 	.bat_max = 3000000,
2748c0984e5SSebastian Reichel 	.bat_min = 1900000,
2758c0984e5SSebastian Reichel 
276ba940ed8SLinus Walleij 	.gpio_temp = NULL,
2778c0984e5SSebastian Reichel 	.adc_temp = -1,
2788c0984e5SSebastian Reichel 	.adc_temp_divider = -1,
2798c0984e5SSebastian Reichel };
2808c0984e5SSebastian Reichel 
281ba940ed8SLinus Walleij /* Obtained but unused GPIO */
282ba940ed8SLinus Walleij static struct gpio_desc *collie_mbat_low;
2838c0984e5SSebastian Reichel 
2848c0984e5SSebastian Reichel #ifdef CONFIG_PM
2858c0984e5SSebastian Reichel static int wakeup_enabled;
2868c0984e5SSebastian Reichel 
collie_bat_suspend(struct ucb1x00_dev * dev)2878c0984e5SSebastian Reichel static int collie_bat_suspend(struct ucb1x00_dev *dev)
2888c0984e5SSebastian Reichel {
2898c0984e5SSebastian Reichel 	/* flush all pending status updates */
2908c0984e5SSebastian Reichel 	flush_work(&bat_work);
2918c0984e5SSebastian Reichel 
2928c0984e5SSebastian Reichel 	if (device_may_wakeup(&dev->ucb->dev) &&
2938c0984e5SSebastian Reichel 	    collie_bat_main.status == POWER_SUPPLY_STATUS_CHARGING)
294ba940ed8SLinus Walleij 		wakeup_enabled = !enable_irq_wake(gpiod_to_irq(collie_bat_main.gpio_full));
2958c0984e5SSebastian Reichel 	else
2968c0984e5SSebastian Reichel 		wakeup_enabled = 0;
2978c0984e5SSebastian Reichel 
2988c0984e5SSebastian Reichel 	return 0;
2998c0984e5SSebastian Reichel }
3008c0984e5SSebastian Reichel 
collie_bat_resume(struct ucb1x00_dev * dev)3018c0984e5SSebastian Reichel static int collie_bat_resume(struct ucb1x00_dev *dev)
3028c0984e5SSebastian Reichel {
3038c0984e5SSebastian Reichel 	if (wakeup_enabled)
304ba940ed8SLinus Walleij 		disable_irq_wake(gpiod_to_irq(collie_bat_main.gpio_full));
3058c0984e5SSebastian Reichel 
3068c0984e5SSebastian Reichel 	/* things may have changed while we were away */
3078c0984e5SSebastian Reichel 	schedule_work(&bat_work);
3088c0984e5SSebastian Reichel 	return 0;
3098c0984e5SSebastian Reichel }
3108c0984e5SSebastian Reichel #else
3118c0984e5SSebastian Reichel #define collie_bat_suspend NULL
3128c0984e5SSebastian Reichel #define collie_bat_resume NULL
3138c0984e5SSebastian Reichel #endif
3148c0984e5SSebastian Reichel 
collie_bat_probe(struct ucb1x00_dev * dev)3158c0984e5SSebastian Reichel static int collie_bat_probe(struct ucb1x00_dev *dev)
3168c0984e5SSebastian Reichel {
3178c0984e5SSebastian Reichel 	int ret;
3188c0984e5SSebastian Reichel 	struct power_supply_config psy_main_cfg = {}, psy_bu_cfg = {};
319ba940ed8SLinus Walleij 	struct gpio_chip *gc = &dev->ucb->gpio;
3208c0984e5SSebastian Reichel 
3218c0984e5SSebastian Reichel 	if (!machine_is_collie())
3228c0984e5SSebastian Reichel 		return -ENODEV;
3238c0984e5SSebastian Reichel 
3248c0984e5SSebastian Reichel 	ucb = dev->ucb;
3258c0984e5SSebastian Reichel 
326ba940ed8SLinus Walleij 	/* Obtain all the main battery GPIOs */
327ba940ed8SLinus Walleij 	collie_bat_main.gpio_full = gpiod_get(&dev->ucb->dev,
328ba940ed8SLinus Walleij 					      "main battery full",
329ba940ed8SLinus Walleij 					      GPIOD_IN);
330ba940ed8SLinus Walleij 	if (IS_ERR(collie_bat_main.gpio_full))
331ba940ed8SLinus Walleij 		return PTR_ERR(collie_bat_main.gpio_full);
332ba940ed8SLinus Walleij 
333ba940ed8SLinus Walleij 	collie_mbat_low = gpiod_get(&dev->ucb->dev,
334ba940ed8SLinus Walleij 				    "main battery low",
335ba940ed8SLinus Walleij 				    GPIOD_IN);
336ba940ed8SLinus Walleij 	if (IS_ERR(collie_mbat_low)) {
337ba940ed8SLinus Walleij 		ret = PTR_ERR(collie_mbat_low);
338ba940ed8SLinus Walleij 		goto err_put_gpio_full;
339ba940ed8SLinus Walleij 	}
340ba940ed8SLinus Walleij 
341ba940ed8SLinus Walleij 	collie_bat_main.gpio_charge_on = gpiod_get(&dev->ucb->dev,
342ba940ed8SLinus Walleij 						   "main charge on",
343ba940ed8SLinus Walleij 						   GPIOD_OUT_LOW);
344ba940ed8SLinus Walleij 	if (IS_ERR(collie_bat_main.gpio_charge_on)) {
345ba940ed8SLinus Walleij 		ret = PTR_ERR(collie_bat_main.gpio_charge_on);
346ba940ed8SLinus Walleij 		goto err_put_mbat_low;
347ba940ed8SLinus Walleij 	}
348ba940ed8SLinus Walleij 
349ba940ed8SLinus Walleij 	/* COLLIE_GPIO_MBAT_ON = GPIO 7 on the UCB (TC35143) */
350ba940ed8SLinus Walleij 	collie_bat_main.gpio_bat = gpiochip_request_own_desc(gc,
351ba940ed8SLinus Walleij 						7,
352ba940ed8SLinus Walleij 						"main battery",
353ba940ed8SLinus Walleij 						GPIO_ACTIVE_HIGH,
354ba940ed8SLinus Walleij 						GPIOD_OUT_LOW);
355ba940ed8SLinus Walleij 	if (IS_ERR(collie_bat_main.gpio_bat)) {
356ba940ed8SLinus Walleij 		ret = PTR_ERR(collie_bat_main.gpio_bat);
357ba940ed8SLinus Walleij 		goto err_put_gpio_charge_on;
358ba940ed8SLinus Walleij 	}
359ba940ed8SLinus Walleij 
360ba940ed8SLinus Walleij 	/* COLLIE_GPIO_TMP_ON = GPIO 9 on the UCB (TC35143) */
361ba940ed8SLinus Walleij 	collie_bat_main.gpio_temp = gpiochip_request_own_desc(gc,
362ba940ed8SLinus Walleij 						9,
363ba940ed8SLinus Walleij 						"main battery temp",
364ba940ed8SLinus Walleij 						GPIO_ACTIVE_HIGH,
365ba940ed8SLinus Walleij 						GPIOD_OUT_LOW);
366ba940ed8SLinus Walleij 	if (IS_ERR(collie_bat_main.gpio_temp)) {
367ba940ed8SLinus Walleij 		ret = PTR_ERR(collie_bat_main.gpio_temp);
368ba940ed8SLinus Walleij 		goto err_free_gpio_bat;
369ba940ed8SLinus Walleij 	}
370ba940ed8SLinus Walleij 
371ba940ed8SLinus Walleij 	/*
372ba940ed8SLinus Walleij 	 * Obtain the backup battery COLLIE_GPIO_BBAT_ON which is
373ba940ed8SLinus Walleij 	 * GPIO 8 on the UCB (TC35143)
374ba940ed8SLinus Walleij 	 */
375ba940ed8SLinus Walleij 	collie_bat_bu.gpio_bat = gpiochip_request_own_desc(gc,
376ba940ed8SLinus Walleij 						8,
377ba940ed8SLinus Walleij 						"backup battery",
378ba940ed8SLinus Walleij 						GPIO_ACTIVE_HIGH,
379ba940ed8SLinus Walleij 						GPIOD_OUT_LOW);
380ba940ed8SLinus Walleij 	if (IS_ERR(collie_bat_bu.gpio_bat)) {
381ba940ed8SLinus Walleij 		ret = PTR_ERR(collie_bat_bu.gpio_bat);
382ba940ed8SLinus Walleij 		goto err_free_gpio_temp;
383ba940ed8SLinus Walleij 	}
3848c0984e5SSebastian Reichel 
3858c0984e5SSebastian Reichel 	mutex_init(&collie_bat_main.work_lock);
3868c0984e5SSebastian Reichel 
3878c0984e5SSebastian Reichel 	INIT_WORK(&bat_work, collie_bat_work);
3888c0984e5SSebastian Reichel 
3898c0984e5SSebastian Reichel 	psy_main_cfg.drv_data = &collie_bat_main;
3908c0984e5SSebastian Reichel 	collie_bat_main.psy = power_supply_register(&dev->ucb->dev,
3918c0984e5SSebastian Reichel 						    &collie_bat_main_desc,
3928c0984e5SSebastian Reichel 						    &psy_main_cfg);
3938c0984e5SSebastian Reichel 	if (IS_ERR(collie_bat_main.psy)) {
3948c0984e5SSebastian Reichel 		ret = PTR_ERR(collie_bat_main.psy);
3958c0984e5SSebastian Reichel 		goto err_psy_reg_main;
3968c0984e5SSebastian Reichel 	}
3978c0984e5SSebastian Reichel 
3988c0984e5SSebastian Reichel 	psy_bu_cfg.drv_data = &collie_bat_bu;
3998c0984e5SSebastian Reichel 	collie_bat_bu.psy = power_supply_register(&dev->ucb->dev,
4008c0984e5SSebastian Reichel 						  &collie_bat_bu_desc,
4018c0984e5SSebastian Reichel 						  &psy_bu_cfg);
4028c0984e5SSebastian Reichel 	if (IS_ERR(collie_bat_bu.psy)) {
4038c0984e5SSebastian Reichel 		ret = PTR_ERR(collie_bat_bu.psy);
4048c0984e5SSebastian Reichel 		goto err_psy_reg_bu;
4058c0984e5SSebastian Reichel 	}
4068c0984e5SSebastian Reichel 
407*fccd2b76SAndy Shevchenko 	ret = request_irq(gpiod_to_irq(collie_bat_main.gpio_full),
4088c0984e5SSebastian Reichel 				collie_bat_gpio_isr,
4098c0984e5SSebastian Reichel 				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
4108c0984e5SSebastian Reichel 				"main full", &collie_bat_main);
4118c0984e5SSebastian Reichel 	if (ret)
4128c0984e5SSebastian Reichel 		goto err_irq;
4138c0984e5SSebastian Reichel 
4148c0984e5SSebastian Reichel 	device_init_wakeup(&ucb->dev, 1);
4158c0984e5SSebastian Reichel 	schedule_work(&bat_work);
4168c0984e5SSebastian Reichel 
4178c0984e5SSebastian Reichel 	return 0;
4188c0984e5SSebastian Reichel 
4198c0984e5SSebastian Reichel err_irq:
4208c0984e5SSebastian Reichel 	power_supply_unregister(collie_bat_bu.psy);
4218c0984e5SSebastian Reichel err_psy_reg_bu:
4228c0984e5SSebastian Reichel 	power_supply_unregister(collie_bat_main.psy);
4238c0984e5SSebastian Reichel err_psy_reg_main:
4248c0984e5SSebastian Reichel 	/* see comment in collie_bat_remove */
4258c0984e5SSebastian Reichel 	cancel_work_sync(&bat_work);
426ba940ed8SLinus Walleij 	gpiochip_free_own_desc(collie_bat_bu.gpio_bat);
427ba940ed8SLinus Walleij err_free_gpio_temp:
428ba940ed8SLinus Walleij 	gpiochip_free_own_desc(collie_bat_main.gpio_temp);
429ba940ed8SLinus Walleij err_free_gpio_bat:
430ba940ed8SLinus Walleij 	gpiochip_free_own_desc(collie_bat_main.gpio_bat);
431ba940ed8SLinus Walleij err_put_gpio_charge_on:
432ba940ed8SLinus Walleij 	gpiod_put(collie_bat_main.gpio_charge_on);
433ba940ed8SLinus Walleij err_put_mbat_low:
434ba940ed8SLinus Walleij 	gpiod_put(collie_mbat_low);
435ba940ed8SLinus Walleij err_put_gpio_full:
436ba940ed8SLinus Walleij 	gpiod_put(collie_bat_main.gpio_full);
437ba940ed8SLinus Walleij 
4388c0984e5SSebastian Reichel 	return ret;
4398c0984e5SSebastian Reichel }
4408c0984e5SSebastian Reichel 
collie_bat_remove(struct ucb1x00_dev * dev)4418c0984e5SSebastian Reichel static void collie_bat_remove(struct ucb1x00_dev *dev)
4428c0984e5SSebastian Reichel {
443*fccd2b76SAndy Shevchenko 	free_irq(gpiod_to_irq(collie_bat_main.gpio_full), &collie_bat_main);
4448c0984e5SSebastian Reichel 	power_supply_unregister(collie_bat_bu.psy);
4458c0984e5SSebastian Reichel 	power_supply_unregister(collie_bat_main.psy);
4468c0984e5SSebastian Reichel 
447ba940ed8SLinus Walleij 	/* These are obtained from the machine */
448ba940ed8SLinus Walleij 	gpiod_put(collie_bat_main.gpio_full);
449ba940ed8SLinus Walleij 	gpiod_put(collie_mbat_low);
450ba940ed8SLinus Walleij 	gpiod_put(collie_bat_main.gpio_charge_on);
451ba940ed8SLinus Walleij 	/* These are directly from the UCB so let's free them */
452ba940ed8SLinus Walleij 	gpiochip_free_own_desc(collie_bat_main.gpio_bat);
453ba940ed8SLinus Walleij 	gpiochip_free_own_desc(collie_bat_main.gpio_temp);
454ba940ed8SLinus Walleij 	gpiochip_free_own_desc(collie_bat_bu.gpio_bat);
4558c0984e5SSebastian Reichel 	/*
4568c0984e5SSebastian Reichel 	 * Now cancel the bat_work.  We won't get any more schedules,
4578c0984e5SSebastian Reichel 	 * since all sources (isr and external_power_changed) are
4588c0984e5SSebastian Reichel 	 * unregistered now.
4598c0984e5SSebastian Reichel 	 */
4608c0984e5SSebastian Reichel 	cancel_work_sync(&bat_work);
4618c0984e5SSebastian Reichel }
4628c0984e5SSebastian Reichel 
4638c0984e5SSebastian Reichel static struct ucb1x00_driver collie_bat_driver = {
4648c0984e5SSebastian Reichel 	.add		= collie_bat_probe,
4658c0984e5SSebastian Reichel 	.remove		= collie_bat_remove,
4668c0984e5SSebastian Reichel 	.suspend	= collie_bat_suspend,
4678c0984e5SSebastian Reichel 	.resume		= collie_bat_resume,
4688c0984e5SSebastian Reichel };
4698c0984e5SSebastian Reichel 
collie_bat_init(void)4708c0984e5SSebastian Reichel static int __init collie_bat_init(void)
4718c0984e5SSebastian Reichel {
4728c0984e5SSebastian Reichel 	return ucb1x00_register_driver(&collie_bat_driver);
4738c0984e5SSebastian Reichel }
4748c0984e5SSebastian Reichel 
collie_bat_exit(void)4758c0984e5SSebastian Reichel static void __exit collie_bat_exit(void)
4768c0984e5SSebastian Reichel {
4778c0984e5SSebastian Reichel 	ucb1x00_unregister_driver(&collie_bat_driver);
4788c0984e5SSebastian Reichel }
4798c0984e5SSebastian Reichel 
4808c0984e5SSebastian Reichel module_init(collie_bat_init);
4818c0984e5SSebastian Reichel module_exit(collie_bat_exit);
4828c0984e5SSebastian Reichel 
4838c0984e5SSebastian Reichel MODULE_LICENSE("GPL");
4848c0984e5SSebastian Reichel MODULE_AUTHOR("Thomas Kunze");
4858c0984e5SSebastian Reichel MODULE_DESCRIPTION("Collie battery driver");
486