152a8b820STom Warren /* 200a2749dSAllen Martin * NVIDIA Tegra20 GPIO handling. 352a8b820STom Warren * (C) Copyright 2010-2012 452a8b820STom Warren * NVIDIA Corporation <www.nvidia.com> 552a8b820STom Warren * 61a459660SWolfgang Denk * SPDX-License-Identifier: GPL-2.0+ 752a8b820STom Warren */ 852a8b820STom Warren 952a8b820STom Warren /* 1052a8b820STom Warren * Based on (mostly copied from) kw_gpio.c based Linux 2.6 kernel driver. 1152a8b820STom Warren * Tom Warren (twarren@nvidia.com) 1252a8b820STom Warren */ 1352a8b820STom Warren 1452a8b820STom Warren #include <common.h> 152fccd2d9SSimon Glass #include <dm.h> 162fccd2d9SSimon Glass #include <malloc.h> 172fccd2d9SSimon Glass #include <errno.h> 182fccd2d9SSimon Glass #include <fdtdec.h> 1952a8b820STom Warren #include <asm/io.h> 2052a8b820STom Warren #include <asm/bitops.h> 21150c2493STom Warren #include <asm/arch/tegra.h> 2252a8b820STom Warren #include <asm/gpio.h> 232fccd2d9SSimon Glass #include <dm/device-internal.h> 24838aa5c9SSimon Glass #include <dt-bindings/gpio/gpio.h> 252fccd2d9SSimon Glass 262fccd2d9SSimon Glass DECLARE_GLOBAL_DATA_PTR; 2752a8b820STom Warren 2852a8b820STom Warren enum { 2929f3e3f2STom Warren TEGRA_CMD_INFO, 3029f3e3f2STom Warren TEGRA_CMD_PORT, 3129f3e3f2STom Warren TEGRA_CMD_OUTPUT, 3229f3e3f2STom Warren TEGRA_CMD_INPUT, 3352a8b820STom Warren }; 3452a8b820STom Warren 352fccd2d9SSimon Glass struct tegra_gpio_platdata { 362fccd2d9SSimon Glass struct gpio_ctlr_bank *bank; 372fccd2d9SSimon Glass const char *port_name; /* Name of port, e.g. "B" */ 382fccd2d9SSimon Glass int base_gpio; /* Port number for this port (0, 1,.., n-1) */ 392fccd2d9SSimon Glass }; 4052a8b820STom Warren 412fccd2d9SSimon Glass /* Information about each port at run-time */ 422fccd2d9SSimon Glass struct tegra_port_info { 432fccd2d9SSimon Glass struct gpio_ctlr_bank *bank; 442fccd2d9SSimon Glass int base_gpio; /* Port number for this port (0, 1,.., n-1) */ 452fccd2d9SSimon Glass }; 4652a8b820STom Warren 4752a8b820STom Warren /* Return config of pin 'gpio' as GPIO (1) or SFPIO (0) */ 4852a8b820STom Warren static int get_config(unsigned gpio) 4952a8b820STom Warren { 5052a8b820STom Warren struct gpio_ctlr *ctlr = (struct gpio_ctlr *)NV_PA_GPIO_BASE; 5152a8b820STom Warren struct gpio_ctlr_bank *bank = &ctlr->gpio_bank[GPIO_BANK(gpio)]; 5252a8b820STom Warren u32 u; 5352a8b820STom Warren int type; 5452a8b820STom Warren 5552a8b820STom Warren u = readl(&bank->gpio_config[GPIO_PORT(gpio)]); 5652a8b820STom Warren type = (u >> GPIO_BIT(gpio)) & 1; 5752a8b820STom Warren 5852a8b820STom Warren debug("get_config: port = %d, bit = %d is %s\n", 5952a8b820STom Warren GPIO_FULLPORT(gpio), GPIO_BIT(gpio), type ? "GPIO" : "SFPIO"); 6052a8b820STom Warren 6152a8b820STom Warren return type; 6252a8b820STom Warren } 6352a8b820STom Warren 6452a8b820STom Warren /* Config pin 'gpio' as GPIO or SFPIO, based on 'type' */ 6552a8b820STom Warren static void set_config(unsigned gpio, int type) 6652a8b820STom Warren { 6752a8b820STom Warren struct gpio_ctlr *ctlr = (struct gpio_ctlr *)NV_PA_GPIO_BASE; 6852a8b820STom Warren struct gpio_ctlr_bank *bank = &ctlr->gpio_bank[GPIO_BANK(gpio)]; 6952a8b820STom Warren u32 u; 7052a8b820STom Warren 7152a8b820STom Warren debug("set_config: port = %d, bit = %d, %s\n", 7252a8b820STom Warren GPIO_FULLPORT(gpio), GPIO_BIT(gpio), type ? "GPIO" : "SFPIO"); 7352a8b820STom Warren 7452a8b820STom Warren u = readl(&bank->gpio_config[GPIO_PORT(gpio)]); 7552a8b820STom Warren if (type) /* GPIO */ 7652a8b820STom Warren u |= 1 << GPIO_BIT(gpio); 7752a8b820STom Warren else 7852a8b820STom Warren u &= ~(1 << GPIO_BIT(gpio)); 7952a8b820STom Warren writel(u, &bank->gpio_config[GPIO_PORT(gpio)]); 8052a8b820STom Warren } 8152a8b820STom Warren 8252a8b820STom Warren /* Return GPIO pin 'gpio' direction - 0 = input or 1 = output */ 8352a8b820STom Warren static int get_direction(unsigned gpio) 8452a8b820STom Warren { 8552a8b820STom Warren struct gpio_ctlr *ctlr = (struct gpio_ctlr *)NV_PA_GPIO_BASE; 8652a8b820STom Warren struct gpio_ctlr_bank *bank = &ctlr->gpio_bank[GPIO_BANK(gpio)]; 8752a8b820STom Warren u32 u; 8852a8b820STom Warren int dir; 8952a8b820STom Warren 9052a8b820STom Warren u = readl(&bank->gpio_dir_out[GPIO_PORT(gpio)]); 9152a8b820STom Warren dir = (u >> GPIO_BIT(gpio)) & 1; 9252a8b820STom Warren 9352a8b820STom Warren debug("get_direction: port = %d, bit = %d, %s\n", 9452a8b820STom Warren GPIO_FULLPORT(gpio), GPIO_BIT(gpio), dir ? "OUT" : "IN"); 9552a8b820STom Warren 9652a8b820STom Warren return dir; 9752a8b820STom Warren } 9852a8b820STom Warren 9952a8b820STom Warren /* Config GPIO pin 'gpio' as input or output (OE) as per 'output' */ 10052a8b820STom Warren static void set_direction(unsigned gpio, int output) 10152a8b820STom Warren { 10252a8b820STom Warren struct gpio_ctlr *ctlr = (struct gpio_ctlr *)NV_PA_GPIO_BASE; 10352a8b820STom Warren struct gpio_ctlr_bank *bank = &ctlr->gpio_bank[GPIO_BANK(gpio)]; 10452a8b820STom Warren u32 u; 10552a8b820STom Warren 10652a8b820STom Warren debug("set_direction: port = %d, bit = %d, %s\n", 10752a8b820STom Warren GPIO_FULLPORT(gpio), GPIO_BIT(gpio), output ? "OUT" : "IN"); 10852a8b820STom Warren 10952a8b820STom Warren u = readl(&bank->gpio_dir_out[GPIO_PORT(gpio)]); 11052a8b820STom Warren if (output) 11152a8b820STom Warren u |= 1 << GPIO_BIT(gpio); 11252a8b820STom Warren else 11352a8b820STom Warren u &= ~(1 << GPIO_BIT(gpio)); 11452a8b820STom Warren writel(u, &bank->gpio_dir_out[GPIO_PORT(gpio)]); 11552a8b820STom Warren } 11652a8b820STom Warren 11752a8b820STom Warren /* set GPIO pin 'gpio' output bit as 0 or 1 as per 'high' */ 11852a8b820STom Warren static void set_level(unsigned gpio, int high) 11952a8b820STom Warren { 12052a8b820STom Warren struct gpio_ctlr *ctlr = (struct gpio_ctlr *)NV_PA_GPIO_BASE; 12152a8b820STom Warren struct gpio_ctlr_bank *bank = &ctlr->gpio_bank[GPIO_BANK(gpio)]; 12252a8b820STom Warren u32 u; 12352a8b820STom Warren 12452a8b820STom Warren debug("set_level: port = %d, bit %d == %d\n", 12552a8b820STom Warren GPIO_FULLPORT(gpio), GPIO_BIT(gpio), high); 12652a8b820STom Warren 12752a8b820STom Warren u = readl(&bank->gpio_out[GPIO_PORT(gpio)]); 12852a8b820STom Warren if (high) 12952a8b820STom Warren u |= 1 << GPIO_BIT(gpio); 13052a8b820STom Warren else 13152a8b820STom Warren u &= ~(1 << GPIO_BIT(gpio)); 13252a8b820STom Warren writel(u, &bank->gpio_out[GPIO_PORT(gpio)]); 13352a8b820STom Warren } 13452a8b820STom Warren 13552a8b820STom Warren /* 13652a8b820STom Warren * Generic_GPIO primitives. 13752a8b820STom Warren */ 13852a8b820STom Warren 1392fccd2d9SSimon Glass static int tegra_gpio_request(struct udevice *dev, unsigned offset, 1402fccd2d9SSimon Glass const char *label) 14152a8b820STom Warren { 1422fccd2d9SSimon Glass struct tegra_port_info *state = dev_get_priv(dev); 14352a8b820STom Warren 14452a8b820STom Warren /* Configure as a GPIO */ 1452fccd2d9SSimon Glass set_config(state->base_gpio + offset, 1); 14652a8b820STom Warren 14752a8b820STom Warren return 0; 14852a8b820STom Warren } 14952a8b820STom Warren 15052a8b820STom Warren /* set GPIO pin 'gpio' as an input */ 1512fccd2d9SSimon Glass static int tegra_gpio_direction_input(struct udevice *dev, unsigned offset) 15252a8b820STom Warren { 1532fccd2d9SSimon Glass struct tegra_port_info *state = dev_get_priv(dev); 15452a8b820STom Warren 15552a8b820STom Warren /* Configure GPIO direction as input. */ 1562fccd2d9SSimon Glass set_direction(state->base_gpio + offset, 0); 15752a8b820STom Warren 15852a8b820STom Warren return 0; 15952a8b820STom Warren } 16052a8b820STom Warren 16152a8b820STom Warren /* set GPIO pin 'gpio' as an output, with polarity 'value' */ 1622fccd2d9SSimon Glass static int tegra_gpio_direction_output(struct udevice *dev, unsigned offset, 1632fccd2d9SSimon Glass int value) 16452a8b820STom Warren { 1652fccd2d9SSimon Glass struct tegra_port_info *state = dev_get_priv(dev); 1662fccd2d9SSimon Glass int gpio = state->base_gpio + offset; 16752a8b820STom Warren 16852a8b820STom Warren /* Configure GPIO output value. */ 16952a8b820STom Warren set_level(gpio, value); 17052a8b820STom Warren 17152a8b820STom Warren /* Configure GPIO direction as output. */ 17252a8b820STom Warren set_direction(gpio, 1); 17352a8b820STom Warren 17452a8b820STom Warren return 0; 17552a8b820STom Warren } 17652a8b820STom Warren 17752a8b820STom Warren /* read GPIO IN value of pin 'gpio' */ 1782fccd2d9SSimon Glass static int tegra_gpio_get_value(struct udevice *dev, unsigned offset) 17952a8b820STom Warren { 1802fccd2d9SSimon Glass struct tegra_port_info *state = dev_get_priv(dev); 1812fccd2d9SSimon Glass int gpio = state->base_gpio + offset; 18252a8b820STom Warren int val; 18352a8b820STom Warren 1842fccd2d9SSimon Glass debug("%s: pin = %d (port %d:bit %d)\n", __func__, 18552a8b820STom Warren gpio, GPIO_FULLPORT(gpio), GPIO_BIT(gpio)); 18652a8b820STom Warren 1872fccd2d9SSimon Glass val = readl(&state->bank->gpio_in[GPIO_PORT(gpio)]); 18852a8b820STom Warren 18952a8b820STom Warren return (val >> GPIO_BIT(gpio)) & 1; 19052a8b820STom Warren } 19152a8b820STom Warren 19252a8b820STom Warren /* write GPIO OUT value to pin 'gpio' */ 1932fccd2d9SSimon Glass static int tegra_gpio_set_value(struct udevice *dev, unsigned offset, int value) 19452a8b820STom Warren { 1952fccd2d9SSimon Glass struct tegra_port_info *state = dev_get_priv(dev); 1962fccd2d9SSimon Glass int gpio = state->base_gpio + offset; 1972fccd2d9SSimon Glass 19852a8b820STom Warren debug("gpio_set_value: pin = %d (port %d:bit %d), value = %d\n", 19952a8b820STom Warren gpio, GPIO_FULLPORT(gpio), GPIO_BIT(gpio), value); 20052a8b820STom Warren 20152a8b820STom Warren /* Configure GPIO output value. */ 20252a8b820STom Warren set_level(gpio, value); 20352a8b820STom Warren 20452a8b820STom Warren return 0; 20552a8b820STom Warren } 20652a8b820STom Warren 207eceb3f26SStephen Warren void gpio_config_table(const struct tegra_gpio_config *config, int len) 208eceb3f26SStephen Warren { 209eceb3f26SStephen Warren int i; 210eceb3f26SStephen Warren 211eceb3f26SStephen Warren for (i = 0; i < len; i++) { 212eceb3f26SStephen Warren switch (config[i].init) { 213eceb3f26SStephen Warren case TEGRA_GPIO_INIT_IN: 214eceb3f26SStephen Warren gpio_direction_input(config[i].gpio); 215eceb3f26SStephen Warren break; 216eceb3f26SStephen Warren case TEGRA_GPIO_INIT_OUT0: 217eceb3f26SStephen Warren gpio_direction_output(config[i].gpio, 0); 218eceb3f26SStephen Warren break; 219eceb3f26SStephen Warren case TEGRA_GPIO_INIT_OUT1: 220eceb3f26SStephen Warren gpio_direction_output(config[i].gpio, 1); 221eceb3f26SStephen Warren break; 222eceb3f26SStephen Warren } 223eceb3f26SStephen Warren set_config(config[i].gpio, 1); 224eceb3f26SStephen Warren } 225eceb3f26SStephen Warren } 226eceb3f26SStephen Warren 2272fccd2d9SSimon Glass static int tegra_gpio_get_function(struct udevice *dev, unsigned offset) 22852a8b820STom Warren { 2292fccd2d9SSimon Glass struct tegra_port_info *state = dev_get_priv(dev); 2302fccd2d9SSimon Glass int gpio = state->base_gpio + offset; 23152a8b820STom Warren 2322fccd2d9SSimon Glass if (!get_config(gpio)) 2332fccd2d9SSimon Glass return GPIOF_FUNC; 2342fccd2d9SSimon Glass else if (get_direction(gpio)) 2352fccd2d9SSimon Glass return GPIOF_OUTPUT; 23652a8b820STom Warren else 2372fccd2d9SSimon Glass return GPIOF_INPUT; 2382fccd2d9SSimon Glass } 2392fccd2d9SSimon Glass 240838aa5c9SSimon Glass static int tegra_gpio_xlate(struct udevice *dev, struct gpio_desc *desc, 241838aa5c9SSimon Glass struct fdtdec_phandle_args *args) 242838aa5c9SSimon Glass { 243838aa5c9SSimon Glass int gpio, port, ret; 244838aa5c9SSimon Glass 245838aa5c9SSimon Glass gpio = args->args[0]; 246838aa5c9SSimon Glass port = gpio / TEGRA_GPIOS_PER_PORT; 247838aa5c9SSimon Glass ret = device_get_child(dev, port, &desc->dev); 248838aa5c9SSimon Glass if (ret) 249838aa5c9SSimon Glass return ret; 250838aa5c9SSimon Glass desc->offset = gpio % TEGRA_GPIOS_PER_PORT; 251838aa5c9SSimon Glass desc->flags = args->args[1] & GPIO_ACTIVE_LOW ? GPIOD_ACTIVE_LOW : 0; 252838aa5c9SSimon Glass 253838aa5c9SSimon Glass return 0; 254838aa5c9SSimon Glass } 255838aa5c9SSimon Glass 2562fccd2d9SSimon Glass static const struct dm_gpio_ops gpio_tegra_ops = { 2572fccd2d9SSimon Glass .request = tegra_gpio_request, 2582fccd2d9SSimon Glass .direction_input = tegra_gpio_direction_input, 2592fccd2d9SSimon Glass .direction_output = tegra_gpio_direction_output, 2602fccd2d9SSimon Glass .get_value = tegra_gpio_get_value, 2612fccd2d9SSimon Glass .set_value = tegra_gpio_set_value, 2622fccd2d9SSimon Glass .get_function = tegra_gpio_get_function, 263838aa5c9SSimon Glass .xlate = tegra_gpio_xlate, 2642fccd2d9SSimon Glass }; 2652fccd2d9SSimon Glass 2662fccd2d9SSimon Glass /** 2672fccd2d9SSimon Glass * Returns the name of a GPIO port 2682fccd2d9SSimon Glass * 2692fccd2d9SSimon Glass * GPIOs are named A, B, C, ..., Z, AA, BB, CC, ... 2702fccd2d9SSimon Glass * 2712fccd2d9SSimon Glass * @base_port: Base port number (0, 1..n-1) 2722fccd2d9SSimon Glass * @return allocated string containing the name 2732fccd2d9SSimon Glass */ 2742fccd2d9SSimon Glass static char *gpio_port_name(int base_port) 2752fccd2d9SSimon Glass { 2762fccd2d9SSimon Glass char *name, *s; 2772fccd2d9SSimon Glass 2782fccd2d9SSimon Glass name = malloc(3); 2792fccd2d9SSimon Glass if (name) { 2802fccd2d9SSimon Glass s = name; 2812fccd2d9SSimon Glass *s++ = 'A' + (base_port % 26); 2822fccd2d9SSimon Glass if (base_port >= 26) 2832fccd2d9SSimon Glass *s++ = *name; 2842fccd2d9SSimon Glass *s = '\0'; 2852fccd2d9SSimon Glass } 2862fccd2d9SSimon Glass 2872fccd2d9SSimon Glass return name; 2882fccd2d9SSimon Glass } 2892fccd2d9SSimon Glass 2902fccd2d9SSimon Glass static const struct udevice_id tegra_gpio_ids[] = { 2912fccd2d9SSimon Glass { .compatible = "nvidia,tegra30-gpio" }, 2922fccd2d9SSimon Glass { .compatible = "nvidia,tegra20-gpio" }, 2932fccd2d9SSimon Glass { } 2942fccd2d9SSimon Glass }; 2952fccd2d9SSimon Glass 2962fccd2d9SSimon Glass static int gpio_tegra_probe(struct udevice *dev) 2972fccd2d9SSimon Glass { 2982fccd2d9SSimon Glass struct gpio_dev_priv *uc_priv = dev->uclass_priv; 2992fccd2d9SSimon Glass struct tegra_port_info *priv = dev->priv; 3002fccd2d9SSimon Glass struct tegra_gpio_platdata *plat = dev->platdata; 3012fccd2d9SSimon Glass 3022fccd2d9SSimon Glass /* Only child devices have ports */ 3032fccd2d9SSimon Glass if (!plat) 3042fccd2d9SSimon Glass return 0; 3052fccd2d9SSimon Glass 3062fccd2d9SSimon Glass priv->bank = plat->bank; 3072fccd2d9SSimon Glass priv->base_gpio = plat->base_gpio; 3082fccd2d9SSimon Glass 3092fccd2d9SSimon Glass uc_priv->gpio_count = TEGRA_GPIOS_PER_PORT; 3102fccd2d9SSimon Glass uc_priv->bank_name = plat->port_name; 3112fccd2d9SSimon Glass 3122fccd2d9SSimon Glass return 0; 3132fccd2d9SSimon Glass } 3142fccd2d9SSimon Glass 3152fccd2d9SSimon Glass /** 3162fccd2d9SSimon Glass * We have a top-level GPIO device with no actual GPIOs. It has a child 3172fccd2d9SSimon Glass * device for each Tegra port. 3182fccd2d9SSimon Glass */ 3192fccd2d9SSimon Glass static int gpio_tegra_bind(struct udevice *parent) 3202fccd2d9SSimon Glass { 3212fccd2d9SSimon Glass struct tegra_gpio_platdata *plat = parent->platdata; 3222fccd2d9SSimon Glass struct gpio_ctlr *ctlr; 3232fccd2d9SSimon Glass int bank_count; 3242fccd2d9SSimon Glass int bank; 3252fccd2d9SSimon Glass int ret; 3262fccd2d9SSimon Glass 3272fccd2d9SSimon Glass /* If this is a child device, there is nothing to do here */ 3282fccd2d9SSimon Glass if (plat) 3292fccd2d9SSimon Glass return 0; 3302fccd2d9SSimon Glass 331*bdfb3416SSimon Glass /* TODO(sjg@chromium.org): Remove once SPL supports device tree */ 332*bdfb3416SSimon Glass #ifdef CONFIG_SPL_BUILD 333*bdfb3416SSimon Glass ctlr = (struct gpio_ctlr *)NV_PA_GPIO_BASE; 334*bdfb3416SSimon Glass bank_count = TEGRA_GPIO_BANKS; 335*bdfb3416SSimon Glass #else 336*bdfb3416SSimon Glass { 337*bdfb3416SSimon Glass int len; 338*bdfb3416SSimon Glass 3392fccd2d9SSimon Glass /* 3402fccd2d9SSimon Glass * This driver does not make use of interrupts, other than to figure 3412fccd2d9SSimon Glass * out the number of GPIO banks 3422fccd2d9SSimon Glass */ 3432fccd2d9SSimon Glass if (!fdt_getprop(gd->fdt_blob, parent->of_offset, "interrupts", &len)) 3442fccd2d9SSimon Glass return -EINVAL; 3452fccd2d9SSimon Glass bank_count = len / 3 / sizeof(u32); 3462fccd2d9SSimon Glass ctlr = (struct gpio_ctlr *)fdtdec_get_addr(gd->fdt_blob, 3472fccd2d9SSimon Glass parent->of_offset, "reg"); 348*bdfb3416SSimon Glass } 349*bdfb3416SSimon Glass #endif 3502fccd2d9SSimon Glass for (bank = 0; bank < bank_count; bank++) { 3512fccd2d9SSimon Glass int port; 3522fccd2d9SSimon Glass 3532fccd2d9SSimon Glass for (port = 0; port < TEGRA_PORTS_PER_BANK; port++) { 3542fccd2d9SSimon Glass struct tegra_gpio_platdata *plat; 3552fccd2d9SSimon Glass struct udevice *dev; 3562fccd2d9SSimon Glass int base_port; 3572fccd2d9SSimon Glass 3582fccd2d9SSimon Glass plat = calloc(1, sizeof(*plat)); 3592fccd2d9SSimon Glass if (!plat) 3602fccd2d9SSimon Glass return -ENOMEM; 3612fccd2d9SSimon Glass plat->bank = &ctlr->gpio_bank[bank]; 3622fccd2d9SSimon Glass base_port = bank * TEGRA_PORTS_PER_BANK + port; 3632fccd2d9SSimon Glass plat->base_gpio = TEGRA_GPIOS_PER_PORT * base_port; 3642fccd2d9SSimon Glass plat->port_name = gpio_port_name(base_port); 3652fccd2d9SSimon Glass 3662fccd2d9SSimon Glass ret = device_bind(parent, parent->driver, 3672fccd2d9SSimon Glass plat->port_name, plat, -1, &dev); 3682fccd2d9SSimon Glass if (ret) 3692fccd2d9SSimon Glass return ret; 3702fccd2d9SSimon Glass dev->of_offset = parent->of_offset; 37152a8b820STom Warren } 37252a8b820STom Warren } 3732fccd2d9SSimon Glass 3742fccd2d9SSimon Glass return 0; 3752fccd2d9SSimon Glass } 3762fccd2d9SSimon Glass 3772fccd2d9SSimon Glass U_BOOT_DRIVER(gpio_tegra) = { 3782fccd2d9SSimon Glass .name = "gpio_tegra", 3792fccd2d9SSimon Glass .id = UCLASS_GPIO, 3802fccd2d9SSimon Glass .of_match = tegra_gpio_ids, 3812fccd2d9SSimon Glass .bind = gpio_tegra_bind, 3822fccd2d9SSimon Glass .probe = gpio_tegra_probe, 3832fccd2d9SSimon Glass .priv_auto_alloc_size = sizeof(struct tegra_port_info), 3842fccd2d9SSimon Glass .ops = &gpio_tegra_ops, 385*bdfb3416SSimon Glass .flags = DM_FLAG_PRE_RELOC, 3862fccd2d9SSimon Glass }; 387