u-boot/drivers/gpio/sunxi_gpio.c
Paul Kocialkowski f7c7ab636a power: axp221: Virtual VBUS detect and enable GPIOs to replace separate logic
This converts the VBUS detection and enable logic to GPIO instead of separate
axp functions and checks that have to be used aside usual GPIO functions.

Signed-off-by: Paul Kocialkowski <contact@paulk.fr>
Acked-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
2015-04-15 16:17:17 +02:00

311 lines
6.6 KiB
C

/*
* (C) Copyright 2012 Henrik Nordstrom <henrik@henriknordstrom.net>
*
* Based on earlier arch/arm/cpu/armv7/sunxi/gpio.c:
*
* (C) Copyright 2007-2011
* Allwinner Technology Co., Ltd. <www.allwinnertech.com>
* Tom Cubie <tangliang@allwinnertech.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <fdtdec.h>
#include <malloc.h>
#include <asm/io.h>
#include <asm/gpio.h>
#include <dm/device-internal.h>
#ifdef CONFIG_AXP209_POWER
#include <axp209.h>
#endif
#ifdef CONFIG_AXP221_POWER
#include <axp221.h>
#endif
DECLARE_GLOBAL_DATA_PTR;
#define SUNXI_GPIOS_PER_BANK SUNXI_GPIO_A_NR
struct sunxi_gpio_platdata {
struct sunxi_gpio *regs;
const char *bank_name; /* Name of bank, e.g. "B" */
int gpio_count;
};
#ifndef CONFIG_DM_GPIO
static int sunxi_gpio_output(u32 pin, u32 val)
{
u32 dat;
u32 bank = GPIO_BANK(pin);
u32 num = GPIO_NUM(pin);
struct sunxi_gpio *pio = BANK_TO_GPIO(bank);
dat = readl(&pio->dat);
if (val)
dat |= 0x1 << num;
else
dat &= ~(0x1 << num);
writel(dat, &pio->dat);
return 0;
}
static int sunxi_gpio_input(u32 pin)
{
u32 dat;
u32 bank = GPIO_BANK(pin);
u32 num = GPIO_NUM(pin);
struct sunxi_gpio *pio = BANK_TO_GPIO(bank);
dat = readl(&pio->dat);
dat >>= num;
return dat & 0x1;
}
int gpio_request(unsigned gpio, const char *label)
{
return 0;
}
int gpio_free(unsigned gpio)
{
return 0;
}
int gpio_direction_input(unsigned gpio)
{
#ifdef AXP_GPIO
if (gpio >= SUNXI_GPIO_AXP0_START)
return axp_gpio_direction_input(gpio - SUNXI_GPIO_AXP0_START);
#endif
sunxi_gpio_set_cfgpin(gpio, SUNXI_GPIO_INPUT);
return 0;
}
int gpio_direction_output(unsigned gpio, int value)
{
#ifdef AXP_GPIO
if (gpio >= SUNXI_GPIO_AXP0_START)
return axp_gpio_direction_output(gpio - SUNXI_GPIO_AXP0_START,
value);
#endif
sunxi_gpio_set_cfgpin(gpio, SUNXI_GPIO_OUTPUT);
return sunxi_gpio_output(gpio, value);
}
int gpio_get_value(unsigned gpio)
{
#ifdef AXP_GPIO
if (gpio >= SUNXI_GPIO_AXP0_START)
return axp_gpio_get_value(gpio - SUNXI_GPIO_AXP0_START);
#endif
return sunxi_gpio_input(gpio);
}
int gpio_set_value(unsigned gpio, int value)
{
#ifdef AXP_GPIO
if (gpio >= SUNXI_GPIO_AXP0_START)
return axp_gpio_set_value(gpio - SUNXI_GPIO_AXP0_START, value);
#endif
return sunxi_gpio_output(gpio, value);
}
int sunxi_name_to_gpio(const char *name)
{
int group = 0;
int groupsize = 9 * 32;
long pin;
char *eptr;
#ifdef AXP_GPIO
if (strncasecmp(name, "AXP0-", 5) == 0) {
name += 5;
if (strcmp(name, "VBUS-DETECT") == 0)
return SUNXI_GPIO_AXP0_START +
SUNXI_GPIO_AXP0_VBUS_DETECT;
if (strcmp(name, "VBUS-ENABLE") == 0)
return SUNXI_GPIO_AXP0_START +
SUNXI_GPIO_AXP0_VBUS_ENABLE;
pin = simple_strtol(name, &eptr, 10);
if (!*name || *eptr)
return -1;
return SUNXI_GPIO_AXP0_START + pin;
}
#endif
if (*name == 'P' || *name == 'p')
name++;
if (*name >= 'A') {
group = *name - (*name > 'a' ? 'a' : 'A');
groupsize = 32;
name++;
}
pin = simple_strtol(name, &eptr, 10);
if (!*name || *eptr)
return -1;
if (pin < 0 || pin > groupsize || group >= 9)
return -1;
return group * 32 + pin;
}
#endif
#ifdef CONFIG_DM_GPIO
static int sunxi_gpio_direction_input(struct udevice *dev, unsigned offset)
{
struct sunxi_gpio_platdata *plat = dev_get_platdata(dev);
sunxi_gpio_set_cfgbank(plat->regs, offset, SUNXI_GPIO_INPUT);
return 0;
}
static int sunxi_gpio_direction_output(struct udevice *dev, unsigned offset,
int value)
{
struct sunxi_gpio_platdata *plat = dev_get_platdata(dev);
u32 num = GPIO_NUM(offset);
sunxi_gpio_set_cfgbank(plat->regs, offset, SUNXI_GPIO_OUTPUT);
clrsetbits_le32(&plat->regs->dat, 1 << num, value ? (1 << num) : 0);
return 0;
}
static int sunxi_gpio_get_value(struct udevice *dev, unsigned offset)
{
struct sunxi_gpio_platdata *plat = dev_get_platdata(dev);
u32 num = GPIO_NUM(offset);
unsigned dat;
dat = readl(&plat->regs->dat);
dat >>= num;
return dat & 0x1;
}
static int sunxi_gpio_set_value(struct udevice *dev, unsigned offset,
int value)
{
struct sunxi_gpio_platdata *plat = dev_get_platdata(dev);
u32 num = GPIO_NUM(offset);
clrsetbits_le32(&plat->regs->dat, 1 << num, value ? (1 << num) : 0);
return 0;
}
static int sunxi_gpio_get_function(struct udevice *dev, unsigned offset)
{
struct sunxi_gpio_platdata *plat = dev_get_platdata(dev);
int func;
func = sunxi_gpio_get_cfgbank(plat->regs, offset);
if (func == SUNXI_GPIO_OUTPUT)
return GPIOF_OUTPUT;
else if (func == SUNXI_GPIO_INPUT)
return GPIOF_INPUT;
else
return GPIOF_FUNC;
}
static const struct dm_gpio_ops gpio_sunxi_ops = {
.direction_input = sunxi_gpio_direction_input,
.direction_output = sunxi_gpio_direction_output,
.get_value = sunxi_gpio_get_value,
.set_value = sunxi_gpio_set_value,
.get_function = sunxi_gpio_get_function,
};
/**
* Returns the name of a GPIO bank
*
* GPIO banks are named A, B, C, ...
*
* @bank: Bank number (0, 1..n-1)
* @return allocated string containing the name
*/
static char *gpio_bank_name(int bank)
{
char *name;
name = malloc(2);
if (name) {
name[0] = 'A' + bank;
name[1] = '\0';
}
return name;
}
static int gpio_sunxi_probe(struct udevice *dev)
{
struct sunxi_gpio_platdata *plat = dev_get_platdata(dev);
struct gpio_dev_priv *uc_priv = dev->uclass_priv;
/* Tell the uclass how many GPIOs we have */
if (plat) {
uc_priv->gpio_count = plat->gpio_count;
uc_priv->bank_name = plat->bank_name;
}
return 0;
}
/**
* We have a top-level GPIO device with no actual GPIOs. It has a child
* device for each Sunxi bank.
*/
static int gpio_sunxi_bind(struct udevice *parent)
{
struct sunxi_gpio_platdata *plat = parent->platdata;
struct sunxi_gpio_reg *ctlr;
int bank;
int ret;
/* If this is a child device, there is nothing to do here */
if (plat)
return 0;
ctlr = (struct sunxi_gpio_reg *)fdtdec_get_addr(gd->fdt_blob,
parent->of_offset, "reg");
for (bank = 0; bank < SUNXI_GPIO_BANKS; bank++) {
struct sunxi_gpio_platdata *plat;
struct udevice *dev;
plat = calloc(1, sizeof(*plat));
if (!plat)
return -ENOMEM;
plat->regs = &ctlr->gpio_bank[bank];
plat->bank_name = gpio_bank_name(bank);
plat->gpio_count = SUNXI_GPIOS_PER_BANK;
ret = device_bind(parent, parent->driver,
plat->bank_name, plat, -1, &dev);
if (ret)
return ret;
dev->of_offset = parent->of_offset;
}
return 0;
}
static const struct udevice_id sunxi_gpio_ids[] = {
{ .compatible = "allwinner,sun7i-a20-pinctrl" },
{ }
};
U_BOOT_DRIVER(gpio_sunxi) = {
.name = "gpio_sunxi",
.id = UCLASS_GPIO,
.ops = &gpio_sunxi_ops,
.of_match = sunxi_gpio_ids,
.bind = gpio_sunxi_bind,
.probe = gpio_sunxi_probe,
};
#endif