1abce2c62SIan Campbell /*
2abce2c62SIan Campbell * (C) Copyright 2012 Henrik Nordstrom <henrik@henriknordstrom.net>
3abce2c62SIan Campbell *
4abce2c62SIan Campbell * Based on earlier arch/arm/cpu/armv7/sunxi/gpio.c:
5abce2c62SIan Campbell *
6abce2c62SIan Campbell * (C) Copyright 2007-2011
7abce2c62SIan Campbell * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
8abce2c62SIan Campbell * Tom Cubie <tangliang@allwinnertech.com>
9abce2c62SIan Campbell *
10abce2c62SIan Campbell * SPDX-License-Identifier: GPL-2.0+
11abce2c62SIan Campbell */
12abce2c62SIan Campbell
13abce2c62SIan Campbell #include <common.h>
147aa97485SSimon Glass #include <dm.h>
157aa97485SSimon Glass #include <errno.h>
167aa97485SSimon Glass #include <fdtdec.h>
177aa97485SSimon Glass #include <malloc.h>
182fcf033dSHans de Goede #include <asm/arch/gpio.h>
19abce2c62SIan Campbell #include <asm/io.h>
20abce2c62SIan Campbell #include <asm/gpio.h>
217aa97485SSimon Glass #include <dm/device-internal.h>
224694dc56SChen-Yu Tsai #include <dt-bindings/gpio/gpio.h>
23abce2c62SIan Campbell
247aa97485SSimon Glass DECLARE_GLOBAL_DATA_PTR;
257aa97485SSimon Glass
267aa97485SSimon Glass #define SUNXI_GPIOS_PER_BANK SUNXI_GPIO_A_NR
277aa97485SSimon Glass
287aa97485SSimon Glass struct sunxi_gpio_platdata {
297aa97485SSimon Glass struct sunxi_gpio *regs;
307aa97485SSimon Glass const char *bank_name; /* Name of bank, e.g. "B" */
317aa97485SSimon Glass int gpio_count;
327aa97485SSimon Glass };
337aa97485SSimon Glass
347aa97485SSimon Glass #ifndef CONFIG_DM_GPIO
sunxi_gpio_output(u32 pin,u32 val)35abce2c62SIan Campbell static int sunxi_gpio_output(u32 pin, u32 val)
36abce2c62SIan Campbell {
37abce2c62SIan Campbell u32 dat;
38abce2c62SIan Campbell u32 bank = GPIO_BANK(pin);
39abce2c62SIan Campbell u32 num = GPIO_NUM(pin);
40abce2c62SIan Campbell struct sunxi_gpio *pio = BANK_TO_GPIO(bank);
41abce2c62SIan Campbell
42abce2c62SIan Campbell dat = readl(&pio->dat);
43abce2c62SIan Campbell if (val)
44abce2c62SIan Campbell dat |= 0x1 << num;
45abce2c62SIan Campbell else
46abce2c62SIan Campbell dat &= ~(0x1 << num);
47abce2c62SIan Campbell
48abce2c62SIan Campbell writel(dat, &pio->dat);
49abce2c62SIan Campbell
50abce2c62SIan Campbell return 0;
51abce2c62SIan Campbell }
52abce2c62SIan Campbell
sunxi_gpio_input(u32 pin)53abce2c62SIan Campbell static int sunxi_gpio_input(u32 pin)
54abce2c62SIan Campbell {
55abce2c62SIan Campbell u32 dat;
56abce2c62SIan Campbell u32 bank = GPIO_BANK(pin);
57abce2c62SIan Campbell u32 num = GPIO_NUM(pin);
58abce2c62SIan Campbell struct sunxi_gpio *pio = BANK_TO_GPIO(bank);
59abce2c62SIan Campbell
60abce2c62SIan Campbell dat = readl(&pio->dat);
61abce2c62SIan Campbell dat >>= num;
62abce2c62SIan Campbell
63abce2c62SIan Campbell return dat & 0x1;
64abce2c62SIan Campbell }
65abce2c62SIan Campbell
gpio_request(unsigned gpio,const char * label)66abce2c62SIan Campbell int gpio_request(unsigned gpio, const char *label)
67abce2c62SIan Campbell {
68abce2c62SIan Campbell return 0;
69abce2c62SIan Campbell }
70abce2c62SIan Campbell
gpio_free(unsigned gpio)71abce2c62SIan Campbell int gpio_free(unsigned gpio)
72abce2c62SIan Campbell {
73abce2c62SIan Campbell return 0;
74abce2c62SIan Campbell }
75abce2c62SIan Campbell
gpio_direction_input(unsigned gpio)76abce2c62SIan Campbell int gpio_direction_input(unsigned gpio)
77abce2c62SIan Campbell {
78abce2c62SIan Campbell sunxi_gpio_set_cfgpin(gpio, SUNXI_GPIO_INPUT);
79abce2c62SIan Campbell
80b0c4ae1aSAxel Lin return 0;
81abce2c62SIan Campbell }
82abce2c62SIan Campbell
gpio_direction_output(unsigned gpio,int value)83abce2c62SIan Campbell int gpio_direction_output(unsigned gpio, int value)
84abce2c62SIan Campbell {
85abce2c62SIan Campbell sunxi_gpio_set_cfgpin(gpio, SUNXI_GPIO_OUTPUT);
86abce2c62SIan Campbell
87abce2c62SIan Campbell return sunxi_gpio_output(gpio, value);
88abce2c62SIan Campbell }
89abce2c62SIan Campbell
gpio_get_value(unsigned gpio)90abce2c62SIan Campbell int gpio_get_value(unsigned gpio)
91abce2c62SIan Campbell {
92abce2c62SIan Campbell return sunxi_gpio_input(gpio);
93abce2c62SIan Campbell }
94abce2c62SIan Campbell
gpio_set_value(unsigned gpio,int value)95abce2c62SIan Campbell int gpio_set_value(unsigned gpio, int value)
96abce2c62SIan Campbell {
97abce2c62SIan Campbell return sunxi_gpio_output(gpio, value);
98abce2c62SIan Campbell }
99abce2c62SIan Campbell
sunxi_name_to_gpio(const char * name)100abce2c62SIan Campbell int sunxi_name_to_gpio(const char *name)
101abce2c62SIan Campbell {
102abce2c62SIan Campbell int group = 0;
103abce2c62SIan Campbell int groupsize = 9 * 32;
104abce2c62SIan Campbell long pin;
105abce2c62SIan Campbell char *eptr;
1066c727e09SHans de Goede
107abce2c62SIan Campbell if (*name == 'P' || *name == 'p')
108abce2c62SIan Campbell name++;
109abce2c62SIan Campbell if (*name >= 'A') {
110abce2c62SIan Campbell group = *name - (*name > 'a' ? 'a' : 'A');
111abce2c62SIan Campbell groupsize = 32;
112abce2c62SIan Campbell name++;
113abce2c62SIan Campbell }
114abce2c62SIan Campbell
115abce2c62SIan Campbell pin = simple_strtol(name, &eptr, 10);
116abce2c62SIan Campbell if (!*name || *eptr)
117abce2c62SIan Campbell return -1;
118abce2c62SIan Campbell if (pin < 0 || pin > groupsize || group >= 9)
119abce2c62SIan Campbell return -1;
120abce2c62SIan Campbell return group * 32 + pin;
121abce2c62SIan Campbell }
1227aa97485SSimon Glass #endif
1237aa97485SSimon Glass
sunxi_name_to_gpio_bank(const char * name)124746c087bSHans de Goede int sunxi_name_to_gpio_bank(const char *name)
125746c087bSHans de Goede {
126746c087bSHans de Goede int group = 0;
127746c087bSHans de Goede
128746c087bSHans de Goede if (*name == 'P' || *name == 'p')
129746c087bSHans de Goede name++;
130746c087bSHans de Goede if (*name >= 'A') {
131746c087bSHans de Goede group = *name - (*name > 'a' ? 'a' : 'A');
132746c087bSHans de Goede return group;
133746c087bSHans de Goede }
134746c087bSHans de Goede
135746c087bSHans de Goede return -1;
136746c087bSHans de Goede }
137746c087bSHans de Goede
1387aa97485SSimon Glass #ifdef CONFIG_DM_GPIO
139a5ab8838SSimon Glass /* TODO(sjg@chromium.org): Remove this function and use device tree */
sunxi_name_to_gpio(const char * name)140a5ab8838SSimon Glass int sunxi_name_to_gpio(const char *name)
141a5ab8838SSimon Glass {
142a5ab8838SSimon Glass unsigned int gpio;
143a5ab8838SSimon Glass int ret;
144f9b7a04bSHans de Goede #if !defined CONFIG_SPL_BUILD && defined CONFIG_AXP_GPIO
145f9b7a04bSHans de Goede char lookup[8];
146a5ab8838SSimon Glass
147f9b7a04bSHans de Goede if (strcasecmp(name, "AXP0-VBUS-DETECT") == 0) {
148f9b7a04bSHans de Goede sprintf(lookup, SUNXI_GPIO_AXP0_PREFIX "%d",
149f9b7a04bSHans de Goede SUNXI_GPIO_AXP0_VBUS_DETECT);
150f9b7a04bSHans de Goede name = lookup;
151f9b7a04bSHans de Goede } else if (strcasecmp(name, "AXP0-VBUS-ENABLE") == 0) {
152f9b7a04bSHans de Goede sprintf(lookup, SUNXI_GPIO_AXP0_PREFIX "%d",
153f9b7a04bSHans de Goede SUNXI_GPIO_AXP0_VBUS_ENABLE);
154f9b7a04bSHans de Goede name = lookup;
155f9b7a04bSHans de Goede }
156f9b7a04bSHans de Goede #endif
157a5ab8838SSimon Glass ret = gpio_lookup_name(name, NULL, NULL, &gpio);
158a5ab8838SSimon Glass
159a5ab8838SSimon Glass return ret ? ret : gpio;
160a5ab8838SSimon Glass }
161a5ab8838SSimon Glass
sunxi_gpio_direction_input(struct udevice * dev,unsigned offset)1627aa97485SSimon Glass static int sunxi_gpio_direction_input(struct udevice *dev, unsigned offset)
1637aa97485SSimon Glass {
1647aa97485SSimon Glass struct sunxi_gpio_platdata *plat = dev_get_platdata(dev);
1657aa97485SSimon Glass
1667aa97485SSimon Glass sunxi_gpio_set_cfgbank(plat->regs, offset, SUNXI_GPIO_INPUT);
1677aa97485SSimon Glass
1687aa97485SSimon Glass return 0;
1697aa97485SSimon Glass }
1707aa97485SSimon Glass
sunxi_gpio_direction_output(struct udevice * dev,unsigned offset,int value)1717aa97485SSimon Glass static int sunxi_gpio_direction_output(struct udevice *dev, unsigned offset,
1727aa97485SSimon Glass int value)
1737aa97485SSimon Glass {
1747aa97485SSimon Glass struct sunxi_gpio_platdata *plat = dev_get_platdata(dev);
1757aa97485SSimon Glass u32 num = GPIO_NUM(offset);
1767aa97485SSimon Glass
1777aa97485SSimon Glass sunxi_gpio_set_cfgbank(plat->regs, offset, SUNXI_GPIO_OUTPUT);
1787aa97485SSimon Glass clrsetbits_le32(&plat->regs->dat, 1 << num, value ? (1 << num) : 0);
1797aa97485SSimon Glass
1807aa97485SSimon Glass return 0;
1817aa97485SSimon Glass }
1827aa97485SSimon Glass
sunxi_gpio_get_value(struct udevice * dev,unsigned offset)1837aa97485SSimon Glass static int sunxi_gpio_get_value(struct udevice *dev, unsigned offset)
1847aa97485SSimon Glass {
1857aa97485SSimon Glass struct sunxi_gpio_platdata *plat = dev_get_platdata(dev);
1867aa97485SSimon Glass u32 num = GPIO_NUM(offset);
1877aa97485SSimon Glass unsigned dat;
1887aa97485SSimon Glass
1897aa97485SSimon Glass dat = readl(&plat->regs->dat);
1907aa97485SSimon Glass dat >>= num;
1917aa97485SSimon Glass
1927aa97485SSimon Glass return dat & 0x1;
1937aa97485SSimon Glass }
1947aa97485SSimon Glass
sunxi_gpio_set_value(struct udevice * dev,unsigned offset,int value)1957aa97485SSimon Glass static int sunxi_gpio_set_value(struct udevice *dev, unsigned offset,
1967aa97485SSimon Glass int value)
1977aa97485SSimon Glass {
1987aa97485SSimon Glass struct sunxi_gpio_platdata *plat = dev_get_platdata(dev);
1997aa97485SSimon Glass u32 num = GPIO_NUM(offset);
2007aa97485SSimon Glass
2017aa97485SSimon Glass clrsetbits_le32(&plat->regs->dat, 1 << num, value ? (1 << num) : 0);
2027aa97485SSimon Glass return 0;
2037aa97485SSimon Glass }
2047aa97485SSimon Glass
sunxi_gpio_get_function(struct udevice * dev,unsigned offset)2057aa97485SSimon Glass static int sunxi_gpio_get_function(struct udevice *dev, unsigned offset)
2067aa97485SSimon Glass {
2077aa97485SSimon Glass struct sunxi_gpio_platdata *plat = dev_get_platdata(dev);
2087aa97485SSimon Glass int func;
2097aa97485SSimon Glass
2107aa97485SSimon Glass func = sunxi_gpio_get_cfgbank(plat->regs, offset);
2117aa97485SSimon Glass if (func == SUNXI_GPIO_OUTPUT)
2127aa97485SSimon Glass return GPIOF_OUTPUT;
2137aa97485SSimon Glass else if (func == SUNXI_GPIO_INPUT)
2147aa97485SSimon Glass return GPIOF_INPUT;
2157aa97485SSimon Glass else
2167aa97485SSimon Glass return GPIOF_FUNC;
2177aa97485SSimon Glass }
2187aa97485SSimon Glass
sunxi_gpio_xlate(struct udevice * dev,struct gpio_desc * desc,struct ofnode_phandle_args * args)2194694dc56SChen-Yu Tsai static int sunxi_gpio_xlate(struct udevice *dev, struct gpio_desc *desc,
220*3a57123eSSimon Glass struct ofnode_phandle_args *args)
2214694dc56SChen-Yu Tsai {
2224694dc56SChen-Yu Tsai int ret;
2234694dc56SChen-Yu Tsai
2244694dc56SChen-Yu Tsai ret = device_get_child(dev, args->args[0], &desc->dev);
2254694dc56SChen-Yu Tsai if (ret)
2264694dc56SChen-Yu Tsai return ret;
2274694dc56SChen-Yu Tsai desc->offset = args->args[1];
2284694dc56SChen-Yu Tsai desc->flags = args->args[2] & GPIO_ACTIVE_LOW ? GPIOD_ACTIVE_LOW : 0;
2294694dc56SChen-Yu Tsai
2304694dc56SChen-Yu Tsai return 0;
2314694dc56SChen-Yu Tsai }
2324694dc56SChen-Yu Tsai
2337aa97485SSimon Glass static const struct dm_gpio_ops gpio_sunxi_ops = {
2347aa97485SSimon Glass .direction_input = sunxi_gpio_direction_input,
2357aa97485SSimon Glass .direction_output = sunxi_gpio_direction_output,
2367aa97485SSimon Glass .get_value = sunxi_gpio_get_value,
2377aa97485SSimon Glass .set_value = sunxi_gpio_set_value,
2387aa97485SSimon Glass .get_function = sunxi_gpio_get_function,
2394694dc56SChen-Yu Tsai .xlate = sunxi_gpio_xlate,
2407aa97485SSimon Glass };
2417aa97485SSimon Glass
2427aa97485SSimon Glass /**
2437aa97485SSimon Glass * Returns the name of a GPIO bank
2447aa97485SSimon Glass *
2457aa97485SSimon Glass * GPIO banks are named A, B, C, ...
2467aa97485SSimon Glass *
2477aa97485SSimon Glass * @bank: Bank number (0, 1..n-1)
2487aa97485SSimon Glass * @return allocated string containing the name
2497aa97485SSimon Glass */
gpio_bank_name(int bank)2507aa97485SSimon Glass static char *gpio_bank_name(int bank)
2517aa97485SSimon Glass {
2527aa97485SSimon Glass char *name;
2537aa97485SSimon Glass
25407ce60f3SSimon Glass name = malloc(3);
2557aa97485SSimon Glass if (name) {
25607ce60f3SSimon Glass name[0] = 'P';
25707ce60f3SSimon Glass name[1] = 'A' + bank;
25807ce60f3SSimon Glass name[2] = '\0';
2597aa97485SSimon Glass }
2607aa97485SSimon Glass
2617aa97485SSimon Glass return name;
2627aa97485SSimon Glass }
2637aa97485SSimon Glass
gpio_sunxi_probe(struct udevice * dev)2647aa97485SSimon Glass static int gpio_sunxi_probe(struct udevice *dev)
2657aa97485SSimon Glass {
2667aa97485SSimon Glass struct sunxi_gpio_platdata *plat = dev_get_platdata(dev);
267e564f054SSimon Glass struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
2687aa97485SSimon Glass
2697aa97485SSimon Glass /* Tell the uclass how many GPIOs we have */
2707aa97485SSimon Glass if (plat) {
2717aa97485SSimon Glass uc_priv->gpio_count = plat->gpio_count;
2727aa97485SSimon Glass uc_priv->bank_name = plat->bank_name;
2737aa97485SSimon Glass }
2747aa97485SSimon Glass
2757aa97485SSimon Glass return 0;
2767aa97485SSimon Glass }
2776f82fac2SStephen Warren
2786f82fac2SStephen Warren struct sunxi_gpio_soc_data {
2796f82fac2SStephen Warren int start;
2806f82fac2SStephen Warren int no_banks;
2816f82fac2SStephen Warren };
2826f82fac2SStephen Warren
2837aa97485SSimon Glass /**
2847aa97485SSimon Glass * We have a top-level GPIO device with no actual GPIOs. It has a child
2857aa97485SSimon Glass * device for each Sunxi bank.
2867aa97485SSimon Glass */
gpio_sunxi_bind(struct udevice * parent)2877aa97485SSimon Glass static int gpio_sunxi_bind(struct udevice *parent)
2887aa97485SSimon Glass {
2896f82fac2SStephen Warren struct sunxi_gpio_soc_data *soc_data =
2906f82fac2SStephen Warren (struct sunxi_gpio_soc_data *)dev_get_driver_data(parent);
2917aa97485SSimon Glass struct sunxi_gpio_platdata *plat = parent->platdata;
2927aa97485SSimon Glass struct sunxi_gpio_reg *ctlr;
2936f82fac2SStephen Warren int bank, ret;
2947aa97485SSimon Glass
2957aa97485SSimon Glass /* If this is a child device, there is nothing to do here */
2967aa97485SSimon Glass if (plat)
2977aa97485SSimon Glass return 0;
2987aa97485SSimon Glass
299a821c4afSSimon Glass ctlr = (struct sunxi_gpio_reg *)devfdt_get_addr(parent);
3006f82fac2SStephen Warren for (bank = 0; bank < soc_data->no_banks; bank++) {
3017aa97485SSimon Glass struct sunxi_gpio_platdata *plat;
3027aa97485SSimon Glass struct udevice *dev;
3037aa97485SSimon Glass
3047aa97485SSimon Glass plat = calloc(1, sizeof(*plat));
3057aa97485SSimon Glass if (!plat)
3067aa97485SSimon Glass return -ENOMEM;
3077aa97485SSimon Glass plat->regs = &ctlr->gpio_bank[bank];
3086f82fac2SStephen Warren plat->bank_name = gpio_bank_name(soc_data->start + bank);
3097aa97485SSimon Glass plat->gpio_count = SUNXI_GPIOS_PER_BANK;
3107aa97485SSimon Glass
3117aa97485SSimon Glass ret = device_bind(parent, parent->driver,
3127aa97485SSimon Glass plat->bank_name, plat, -1, &dev);
3137aa97485SSimon Glass if (ret)
3147aa97485SSimon Glass return ret;
315e160f7d4SSimon Glass dev_set_of_offset(dev, dev_of_offset(parent));
3167aa97485SSimon Glass }
3177aa97485SSimon Glass
3187aa97485SSimon Glass return 0;
3197aa97485SSimon Glass }
3207aa97485SSimon Glass
3216f82fac2SStephen Warren static const struct sunxi_gpio_soc_data soc_data_a_all = {
3226f82fac2SStephen Warren .start = 0,
3236f82fac2SStephen Warren .no_banks = SUNXI_GPIO_BANKS,
3246f82fac2SStephen Warren };
3256f82fac2SStephen Warren
3266f82fac2SStephen Warren static const struct sunxi_gpio_soc_data soc_data_l_1 = {
3276f82fac2SStephen Warren .start = 'L' - 'A',
3286f82fac2SStephen Warren .no_banks = 1,
3296f82fac2SStephen Warren };
3306f82fac2SStephen Warren
3316f82fac2SStephen Warren static const struct sunxi_gpio_soc_data soc_data_l_2 = {
3326f82fac2SStephen Warren .start = 'L' - 'A',
3336f82fac2SStephen Warren .no_banks = 2,
3346f82fac2SStephen Warren };
3356f82fac2SStephen Warren
3366f82fac2SStephen Warren static const struct sunxi_gpio_soc_data soc_data_l_3 = {
3376f82fac2SStephen Warren .start = 'L' - 'A',
3386f82fac2SStephen Warren .no_banks = 3,
3396f82fac2SStephen Warren };
3406f82fac2SStephen Warren
3416f82fac2SStephen Warren #define ID(_compat_, _soc_data_) \
3426f82fac2SStephen Warren { .compatible = _compat_, .data = (ulong)&soc_data_##_soc_data_ }
3436f82fac2SStephen Warren
3447aa97485SSimon Glass static const struct udevice_id sunxi_gpio_ids[] = {
3456f82fac2SStephen Warren ID("allwinner,sun4i-a10-pinctrl", a_all),
3466f82fac2SStephen Warren ID("allwinner,sun5i-a10s-pinctrl", a_all),
3476f82fac2SStephen Warren ID("allwinner,sun5i-a13-pinctrl", a_all),
3486f82fac2SStephen Warren ID("allwinner,sun6i-a31-pinctrl", a_all),
3496f82fac2SStephen Warren ID("allwinner,sun6i-a31s-pinctrl", a_all),
3506f82fac2SStephen Warren ID("allwinner,sun7i-a20-pinctrl", a_all),
3516f82fac2SStephen Warren ID("allwinner,sun8i-a23-pinctrl", a_all),
3526f82fac2SStephen Warren ID("allwinner,sun8i-a33-pinctrl", a_all),
3536f82fac2SStephen Warren ID("allwinner,sun8i-a83t-pinctrl", a_all),
3546f82fac2SStephen Warren ID("allwinner,sun8i-h3-pinctrl", a_all),
35533559ffeSChen-Yu Tsai ID("allwinner,sun8i-r40-pinctrl", a_all),
3566f82fac2SStephen Warren ID("allwinner,sun9i-a80-pinctrl", a_all),
3576f82fac2SStephen Warren ID("allwinner,sun6i-a31-r-pinctrl", l_2),
3586f82fac2SStephen Warren ID("allwinner,sun8i-a23-r-pinctrl", l_1),
3596f82fac2SStephen Warren ID("allwinner,sun8i-a83t-r-pinctrl", l_1),
3606f82fac2SStephen Warren ID("allwinner,sun8i-h3-r-pinctrl", l_1),
3616f82fac2SStephen Warren ID("allwinner,sun9i-a80-r-pinctrl", l_3),
3627aa97485SSimon Glass { }
3637aa97485SSimon Glass };
3647aa97485SSimon Glass
3657aa97485SSimon Glass U_BOOT_DRIVER(gpio_sunxi) = {
3667aa97485SSimon Glass .name = "gpio_sunxi",
3677aa97485SSimon Glass .id = UCLASS_GPIO,
3687aa97485SSimon Glass .ops = &gpio_sunxi_ops,
3697aa97485SSimon Glass .of_match = sunxi_gpio_ids,
3707aa97485SSimon Glass .bind = gpio_sunxi_bind,
3717aa97485SSimon Glass .probe = gpio_sunxi_probe,
3727aa97485SSimon Glass };
3737aa97485SSimon Glass #endif
374