Commit ba940ed8 authored by Linus Walleij's avatar Linus Walleij Committed by Sebastian Reichel
Browse files

power: supply: collie_battery: Convert to GPIO descriptors



This converts the Collie battery driver to use GPIO descriptors.
We use a mixture of 3 GPIOs defined in the machine and 3
GPIOs requested directly from the ucb1x00 chip.

Cc: Robert Jarzmik <robert.jarzmik@free.fr>
Cc: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
Signed-off-by: default avatarLinus Walleij <linus.walleij@linaro.org>
Signed-off-by: default avatarSebastian Reichel <sebastian.reichel@collabora.com>
parent b2f6cb78
Loading
Loading
Loading
Loading
+21 −0
Original line number Diff line number Diff line
@@ -98,6 +98,26 @@ static struct mcp_plat_data collie_mcp_data = {
	.codec_pdata	= &collie_ucb1x00_data,
};

/* Battery management GPIOs */
static struct gpiod_lookup_table collie_battery_gpiod_table = {
	/* the MCP codec mcp0 has the ucb1x00 as attached device */
	.dev_id = "ucb1x00",
	.table = {
		/* This is found on the main GPIO on the SA1100 */
		GPIO_LOOKUP("gpio", COLLIE_GPIO_CO,
			    "main battery full", GPIO_ACTIVE_HIGH),
		GPIO_LOOKUP("gpio", COLLIE_GPIO_MAIN_BAT_LOW,
			    "main battery low", GPIO_ACTIVE_HIGH),
		/*
		 * This is GPIO 0 on the Scoop expander, which is registered
		 * from common/scoop.c with this gpio chip label.
		 */
		GPIO_LOOKUP("sharp-scoop", 0,
			    "main charge on", GPIO_ACTIVE_HIGH),
		{ },
	},
};

static int collie_ir_startup(struct device *dev)
{
	int rc = gpio_request(COLLIE_GPIO_IR_ON, "IrDA");
@@ -395,6 +415,7 @@ static void __init collie_init(void)
	platform_scoop_config = &collie_pcmcia_config;

	gpiod_add_lookup_table(&collie_power_gpiod_table);
	gpiod_add_lookup_table(&collie_battery_gpiod_table);

	ret = platform_add_devices(devices, ARRAY_SIZE(devices));
	if (ret) {
+109 −42
Original line number Diff line number Diff line
@@ -12,7 +12,9 @@
#include <linux/delay.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/machine.h>
#include <linux/gpio/consumer.h>
#include <linux/mfd/ucb1x00.h>

#include <asm/mach/sharpsl_param.h>
@@ -31,18 +33,18 @@ struct collie_bat {
	struct mutex work_lock; /* protects data */

	bool (*is_present)(struct collie_bat *bat);
	int gpio_full;
	int gpio_charge_on;
	struct gpio_desc *gpio_full;
	struct gpio_desc *gpio_charge_on;

	int technology;

	int gpio_bat;
	struct gpio_desc *gpio_bat;
	int adc_bat;
	int adc_bat_divider;
	int bat_max;
	int bat_min;

	int gpio_temp;
	struct gpio_desc *gpio_temp;
	int adc_temp;
	int adc_temp_divider;
};
@@ -53,15 +55,15 @@ static unsigned long collie_read_bat(struct collie_bat *bat)
{
	unsigned long value = 0;

	if (bat->gpio_bat < 0 || bat->adc_bat < 0)
	if (!bat->gpio_bat || bat->adc_bat < 0)
		return 0;
	mutex_lock(&bat_lock);
	gpio_set_value(bat->gpio_bat, 1);
	gpiod_set_value(bat->gpio_bat, 1);
	msleep(5);
	ucb1x00_adc_enable(ucb);
	value = ucb1x00_adc_read(ucb, bat->adc_bat, UCB_SYNC);
	ucb1x00_adc_disable(ucb);
	gpio_set_value(bat->gpio_bat, 0);
	gpiod_set_value(bat->gpio_bat, 0);
	mutex_unlock(&bat_lock);
	value = value * 1000000 / bat->adc_bat_divider;

@@ -71,16 +73,16 @@ static unsigned long collie_read_bat(struct collie_bat *bat)
static unsigned long collie_read_temp(struct collie_bat *bat)
{
	unsigned long value = 0;
	if (bat->gpio_temp < 0 || bat->adc_temp < 0)
	if (!bat->gpio_temp || bat->adc_temp < 0)
		return 0;

	mutex_lock(&bat_lock);
	gpio_set_value(bat->gpio_temp, 1);
	gpiod_set_value(bat->gpio_temp, 1);
	msleep(5);
	ucb1x00_adc_enable(ucb);
	value = ucb1x00_adc_read(ucb, bat->adc_temp, UCB_SYNC);
	ucb1x00_adc_disable(ucb);
	gpio_set_value(bat->gpio_temp, 0);
	gpiod_set_value(bat->gpio_temp, 0);
	mutex_unlock(&bat_lock);

	value = value * 10000 / bat->adc_temp_divider;
@@ -162,23 +164,23 @@ static void collie_bat_update(struct collie_bat *bat)
		bat->full_chrg = -1;
	} else if (power_supply_am_i_supplied(psy)) {
		if (bat->status == POWER_SUPPLY_STATUS_DISCHARGING) {
			gpio_set_value(bat->gpio_charge_on, 1);
			gpiod_set_value(bat->gpio_charge_on, 1);
			mdelay(15);
		}

		if (gpio_get_value(bat->gpio_full)) {
		if (gpiod_get_value(bat->gpio_full)) {
			if (old == POWER_SUPPLY_STATUS_CHARGING ||
					bat->full_chrg == -1)
				bat->full_chrg = collie_read_bat(bat);

			gpio_set_value(bat->gpio_charge_on, 0);
			gpiod_set_value(bat->gpio_charge_on, 0);
			bat->status = POWER_SUPPLY_STATUS_FULL;
		} else {
			gpio_set_value(bat->gpio_charge_on, 1);
			gpiod_set_value(bat->gpio_charge_on, 1);
			bat->status = POWER_SUPPLY_STATUS_CHARGING;
		}
	} else {
		gpio_set_value(bat->gpio_charge_on, 0);
		gpiod_set_value(bat->gpio_charge_on, 0);
		bat->status = POWER_SUPPLY_STATUS_DISCHARGING;
	}

@@ -230,18 +232,18 @@ static struct collie_bat collie_bat_main = {
	.full_chrg = -1,
	.psy = NULL,

	.gpio_full = COLLIE_GPIO_CO,
	.gpio_charge_on = COLLIE_GPIO_CHARGE_ON,
	.gpio_full = NULL,
	.gpio_charge_on = NULL,

	.technology = POWER_SUPPLY_TECHNOLOGY_LIPO,

	.gpio_bat = COLLIE_GPIO_MBAT_ON,
	.gpio_bat = NULL,
	.adc_bat = UCB_ADC_INP_AD1,
	.adc_bat_divider = 155,
	.bat_max = 4310000,
	.bat_min = 1551 * 1000000 / 414,

	.gpio_temp = COLLIE_GPIO_TMP_ON,
	.gpio_temp = NULL,
	.adc_temp = UCB_ADC_INP_AD0,
	.adc_temp_divider = 10000,
};
@@ -260,30 +262,24 @@ static struct collie_bat collie_bat_bu = {
	.full_chrg = -1,
	.psy = NULL,

	.gpio_full = -1,
	.gpio_charge_on = -1,
	.gpio_full = NULL,
	.gpio_charge_on = NULL,

	.technology = POWER_SUPPLY_TECHNOLOGY_LiMn,

	.gpio_bat = COLLIE_GPIO_BBAT_ON,
	.gpio_bat = NULL,
	.adc_bat = UCB_ADC_INP_AD1,
	.adc_bat_divider = 155,
	.bat_max = 3000000,
	.bat_min = 1900000,

	.gpio_temp = -1,
	.gpio_temp = NULL,
	.adc_temp = -1,
	.adc_temp_divider = -1,
};

static struct gpio collie_batt_gpios[] = {
	{ COLLIE_GPIO_CO,	    GPIOF_IN,		"main battery full" },
	{ COLLIE_GPIO_MAIN_BAT_LOW, GPIOF_IN,		"main battery low" },
	{ COLLIE_GPIO_CHARGE_ON,    GPIOF_OUT_INIT_LOW,	"main charge on" },
	{ COLLIE_GPIO_MBAT_ON,	    GPIOF_OUT_INIT_LOW,	"main battery" },
	{ COLLIE_GPIO_TMP_ON,	    GPIOF_OUT_INIT_LOW,	"main battery temp" },
	{ COLLIE_GPIO_BBAT_ON,	    GPIOF_OUT_INIT_LOW,	"backup battery" },
};
/* Obtained but unused GPIO */
static struct gpio_desc *collie_mbat_low;

#ifdef CONFIG_PM
static int wakeup_enabled;
@@ -295,7 +291,7 @@ static int collie_bat_suspend(struct ucb1x00_dev *dev)

	if (device_may_wakeup(&dev->ucb->dev) &&
	    collie_bat_main.status == POWER_SUPPLY_STATUS_CHARGING)
		wakeup_enabled = !enable_irq_wake(gpio_to_irq(COLLIE_GPIO_CO));
		wakeup_enabled = !enable_irq_wake(gpiod_to_irq(collie_bat_main.gpio_full));
	else
		wakeup_enabled = 0;

@@ -305,7 +301,7 @@ static int collie_bat_suspend(struct ucb1x00_dev *dev)
static int collie_bat_resume(struct ucb1x00_dev *dev)
{
	if (wakeup_enabled)
		disable_irq_wake(gpio_to_irq(COLLIE_GPIO_CO));
		disable_irq_wake(gpiod_to_irq(collie_bat_main.gpio_full));

	/* things may have changed while we were away */
	schedule_work(&bat_work);
@@ -320,16 +316,71 @@ static int collie_bat_probe(struct ucb1x00_dev *dev)
{
	int ret;
	struct power_supply_config psy_main_cfg = {}, psy_bu_cfg = {};
	struct gpio_chip *gc = &dev->ucb->gpio;

	if (!machine_is_collie())
		return -ENODEV;

	ucb = dev->ucb;

	ret = gpio_request_array(collie_batt_gpios,
				 ARRAY_SIZE(collie_batt_gpios));
	if (ret)
		return ret;
	/* Obtain all the main battery GPIOs */
	collie_bat_main.gpio_full = gpiod_get(&dev->ucb->dev,
					      "main battery full",
					      GPIOD_IN);
	if (IS_ERR(collie_bat_main.gpio_full))
		return PTR_ERR(collie_bat_main.gpio_full);

	collie_mbat_low = gpiod_get(&dev->ucb->dev,
				    "main battery low",
				    GPIOD_IN);
	if (IS_ERR(collie_mbat_low)) {
		ret = PTR_ERR(collie_mbat_low);
		goto err_put_gpio_full;
	}

	collie_bat_main.gpio_charge_on = gpiod_get(&dev->ucb->dev,
						   "main charge on",
						   GPIOD_OUT_LOW);
	if (IS_ERR(collie_bat_main.gpio_charge_on)) {
		ret = PTR_ERR(collie_bat_main.gpio_charge_on);
		goto err_put_mbat_low;
	}

	/* COLLIE_GPIO_MBAT_ON = GPIO 7 on the UCB (TC35143) */
	collie_bat_main.gpio_bat = gpiochip_request_own_desc(gc,
						7,
						"main battery",
						GPIO_ACTIVE_HIGH,
						GPIOD_OUT_LOW);
	if (IS_ERR(collie_bat_main.gpio_bat)) {
		ret = PTR_ERR(collie_bat_main.gpio_bat);
		goto err_put_gpio_charge_on;
	}

	/* COLLIE_GPIO_TMP_ON = GPIO 9 on the UCB (TC35143) */
	collie_bat_main.gpio_temp = gpiochip_request_own_desc(gc,
						9,
						"main battery temp",
						GPIO_ACTIVE_HIGH,
						GPIOD_OUT_LOW);
	if (IS_ERR(collie_bat_main.gpio_temp)) {
		ret = PTR_ERR(collie_bat_main.gpio_temp);
		goto err_free_gpio_bat;
	}

	/*
	 * Obtain the backup battery COLLIE_GPIO_BBAT_ON which is
	 * GPIO 8 on the UCB (TC35143)
	 */
	collie_bat_bu.gpio_bat = gpiochip_request_own_desc(gc,
						8,
						"backup battery",
						GPIO_ACTIVE_HIGH,
						GPIOD_OUT_LOW);
	if (IS_ERR(collie_bat_bu.gpio_bat)) {
		ret = PTR_ERR(collie_bat_bu.gpio_bat);
		goto err_free_gpio_temp;
	}

	mutex_init(&collie_bat_main.work_lock);

@@ -370,27 +421,43 @@ static int collie_bat_probe(struct ucb1x00_dev *dev)
err_psy_reg_bu:
	power_supply_unregister(collie_bat_main.psy);
err_psy_reg_main:

	/* see comment in collie_bat_remove */
	cancel_work_sync(&bat_work);
	gpio_free_array(collie_batt_gpios, ARRAY_SIZE(collie_batt_gpios));
	gpiochip_free_own_desc(collie_bat_bu.gpio_bat);
err_free_gpio_temp:
	gpiochip_free_own_desc(collie_bat_main.gpio_temp);
err_free_gpio_bat:
	gpiochip_free_own_desc(collie_bat_main.gpio_bat);
err_put_gpio_charge_on:
	gpiod_put(collie_bat_main.gpio_charge_on);
err_put_mbat_low:
	gpiod_put(collie_mbat_low);
err_put_gpio_full:
	gpiod_put(collie_bat_main.gpio_full);

	return ret;
}

static void collie_bat_remove(struct ucb1x00_dev *dev)
{
	free_irq(gpio_to_irq(COLLIE_GPIO_CO), &collie_bat_main);

	power_supply_unregister(collie_bat_bu.psy);
	power_supply_unregister(collie_bat_main.psy);

	/* These are obtained from the machine */
	gpiod_put(collie_bat_main.gpio_full);
	gpiod_put(collie_mbat_low);
	gpiod_put(collie_bat_main.gpio_charge_on);
	/* These are directly from the UCB so let's free them */
	gpiochip_free_own_desc(collie_bat_main.gpio_bat);
	gpiochip_free_own_desc(collie_bat_main.gpio_temp);
	gpiochip_free_own_desc(collie_bat_bu.gpio_bat);
	/*
	 * Now cancel the bat_work.  We won't get any more schedules,
	 * since all sources (isr and external_power_changed) are
	 * unregistered now.
	 */
	cancel_work_sync(&bat_work);
	gpio_free_array(collie_batt_gpios, ARRAY_SIZE(collie_batt_gpios));
}

static struct ucb1x00_driver collie_bat_driver = {