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