X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=drivers%2Fgpio%2Ftegra_gpio.c;h=302efddc2729aa00b3598d35342e1a8adb77994c;hb=83d290c56fab2d38cd1ab4c4cc7099559c1d5046;hp=82b30d5ab682eb77ad93d5ee3b6caafbb214c7f3;hpb=326ea986ac150acdc7656d57fca647db80b50158;p=platform%2Fkernel%2Fu-boot.git diff --git a/drivers/gpio/tegra_gpio.c b/drivers/gpio/tegra_gpio.c index 82b30d5..302efdd 100644 --- a/drivers/gpio/tegra_gpio.c +++ b/drivers/gpio/tegra_gpio.c @@ -1,9 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * NVIDIA Tegra20 GPIO handling. - * (C) Copyright 2010-2012 + * (C) Copyright 2010-2012,2015 * NVIDIA Corporation - * - * SPDX-License-Identifier: GPL-2.0+ */ /* @@ -12,28 +11,35 @@ */ #include +#include +#include +#include +#include #include #include #include #include - -enum { - TEGRA_CMD_INFO, - TEGRA_CMD_PORT, - TEGRA_CMD_OUTPUT, - TEGRA_CMD_INPUT, +#include +#include + +static const int CONFIG_SFIO = 0; +static const int CONFIG_GPIO = 1; +static const int DIRECTION_INPUT = 0; +static const int DIRECTION_OUTPUT = 1; + +struct tegra_gpio_platdata { + struct gpio_ctlr_bank *bank; + const char *port_name; /* Name of port, e.g. "B" */ + int base_gpio; /* Port number for this port (0, 1,.., n-1) */ }; -static struct gpio_names { - char name[GPIO_NAME_SIZE]; -} gpio_names[MAX_NUM_GPIOS]; - -static char *get_name(int i) -{ - return *gpio_names[i].name ? gpio_names[i].name : "UNKNOWN"; -} +/* Information about each port at run-time */ +struct tegra_port_info { + struct gpio_ctlr_bank *bank; + int base_gpio; /* Port number for this port (0, 1,.., n-1) */ +}; -/* Return config of pin 'gpio' as GPIO (1) or SFPIO (0) */ +/* Return config of pin 'gpio' as GPIO (1) or SFIO (0) */ static int get_config(unsigned gpio) { struct gpio_ctlr *ctlr = (struct gpio_ctlr *)NV_PA_GPIO_BASE; @@ -42,15 +48,15 @@ static int get_config(unsigned gpio) int type; u = readl(&bank->gpio_config[GPIO_PORT(gpio)]); - type = (u >> GPIO_BIT(gpio)) & 1; + type = (u >> GPIO_BIT(gpio)) & 1; debug("get_config: port = %d, bit = %d is %s\n", GPIO_FULLPORT(gpio), GPIO_BIT(gpio), type ? "GPIO" : "SFPIO"); - return type; + return type ? CONFIG_GPIO : CONFIG_SFIO; } -/* Config pin 'gpio' as GPIO or SFPIO, based on 'type' */ +/* Config pin 'gpio' as GPIO or SFIO, based on 'type' */ static void set_config(unsigned gpio, int type) { struct gpio_ctlr *ctlr = (struct gpio_ctlr *)NV_PA_GPIO_BASE; @@ -61,7 +67,7 @@ static void set_config(unsigned gpio, int type) GPIO_FULLPORT(gpio), GPIO_BIT(gpio), type ? "GPIO" : "SFPIO"); u = readl(&bank->gpio_config[GPIO_PORT(gpio)]); - if (type) /* GPIO */ + if (type != CONFIG_SFIO) u |= 1 << GPIO_BIT(gpio); else u &= ~(1 << GPIO_BIT(gpio)); @@ -82,7 +88,7 @@ static int get_direction(unsigned gpio) debug("get_direction: port = %d, bit = %d, %s\n", GPIO_FULLPORT(gpio), GPIO_BIT(gpio), dir ? "OUT" : "IN"); - return dir; + return dir ? DIRECTION_OUTPUT : DIRECTION_INPUT; } /* Config GPIO pin 'gpio' as input or output (OE) as per 'output' */ @@ -96,7 +102,7 @@ static void set_direction(unsigned gpio, int output) GPIO_FULLPORT(gpio), GPIO_BIT(gpio), output ? "OUT" : "IN"); u = readl(&bank->gpio_dir_out[GPIO_PORT(gpio)]); - if (output) + if (output != DIRECTION_INPUT) u |= 1 << GPIO_BIT(gpio); else u &= ~(1 << GPIO_BIT(gpio)); @@ -125,122 +131,252 @@ static void set_level(unsigned gpio, int high) * Generic_GPIO primitives. */ -int gpio_request(unsigned gpio, const char *label) +/* set GPIO pin 'gpio' as an input */ +static int tegra_gpio_direction_input(struct udevice *dev, unsigned offset) { - if (gpio >= MAX_NUM_GPIOS) - return -1; + struct tegra_port_info *state = dev_get_priv(dev); - if (label != NULL) { - strncpy(gpio_names[gpio].name, label, GPIO_NAME_SIZE); - gpio_names[gpio].name[GPIO_NAME_SIZE - 1] = '\0'; - } + /* Configure GPIO direction as input. */ + set_direction(state->base_gpio + offset, DIRECTION_INPUT); - /* Configure as a GPIO */ - set_config(gpio, 1); + /* Enable the pin as a GPIO */ + set_config(state->base_gpio + offset, 1); return 0; } -int gpio_free(unsigned gpio) +/* set GPIO pin 'gpio' as an output, with polarity 'value' */ +static int tegra_gpio_direction_output(struct udevice *dev, unsigned offset, + int value) { - if (gpio >= MAX_NUM_GPIOS) - return -1; + struct tegra_port_info *state = dev_get_priv(dev); + int gpio = state->base_gpio + offset; + + /* Configure GPIO output value. */ + set_level(gpio, value); + + /* Configure GPIO direction as output. */ + set_direction(gpio, DIRECTION_OUTPUT); + + /* Enable the pin as a GPIO */ + set_config(state->base_gpio + offset, 1); - gpio_names[gpio].name[0] = '\0'; - /* Do not configure as input or change pin mux here */ return 0; } -/* read GPIO OUT value of pin 'gpio' */ -static int gpio_get_output_value(unsigned gpio) +/* read GPIO IN value of pin 'gpio' */ +static int tegra_gpio_get_value(struct udevice *dev, unsigned offset) { - struct gpio_ctlr *ctlr = (struct gpio_ctlr *)NV_PA_GPIO_BASE; - struct gpio_ctlr_bank *bank = &ctlr->gpio_bank[GPIO_BANK(gpio)]; + struct tegra_port_info *state = dev_get_priv(dev); + int gpio = state->base_gpio + offset; int val; - debug("gpio_get_output_value: pin = %d (port %d:bit %d)\n", - gpio, GPIO_FULLPORT(gpio), GPIO_BIT(gpio)); + debug("%s: pin = %d (port %d:bit %d)\n", __func__, + gpio, GPIO_FULLPORT(gpio), GPIO_BIT(gpio)); - val = readl(&bank->gpio_out[GPIO_PORT(gpio)]); + if (get_direction(gpio) == DIRECTION_INPUT) + val = readl(&state->bank->gpio_in[GPIO_PORT(gpio)]); + else + val = readl(&state->bank->gpio_out[GPIO_PORT(gpio)]); return (val >> GPIO_BIT(gpio)) & 1; } -/* set GPIO pin 'gpio' as an input */ -int gpio_direction_input(unsigned gpio) +/* write GPIO OUT value to pin 'gpio' */ +static int tegra_gpio_set_value(struct udevice *dev, unsigned offset, int value) { - debug("gpio_direction_input: pin = %d (port %d:bit %d)\n", - gpio, GPIO_FULLPORT(gpio), GPIO_BIT(gpio)); + struct tegra_port_info *state = dev_get_priv(dev); + int gpio = state->base_gpio + offset; - /* Configure GPIO direction as input. */ - set_direction(gpio, 0); + debug("gpio_set_value: pin = %d (port %d:bit %d), value = %d\n", + gpio, GPIO_FULLPORT(gpio), GPIO_BIT(gpio), value); + + /* Configure GPIO output value. */ + set_level(gpio, value); return 0; } -/* set GPIO pin 'gpio' as an output, with polarity 'value' */ -int gpio_direction_output(unsigned gpio, int value) +void gpio_config_table(const struct tegra_gpio_config *config, int len) { - debug("gpio_direction_output: pin = %d (port %d:bit %d) = %s\n", - gpio, GPIO_FULLPORT(gpio), GPIO_BIT(gpio), - value ? "HIGH" : "LOW"); - - /* Configure GPIO output value. */ - set_level(gpio, value); + int i; + + for (i = 0; i < len; i++) { + switch (config[i].init) { + case TEGRA_GPIO_INIT_IN: + set_direction(config[i].gpio, DIRECTION_INPUT); + break; + case TEGRA_GPIO_INIT_OUT0: + set_level(config[i].gpio, 0); + set_direction(config[i].gpio, DIRECTION_OUTPUT); + break; + case TEGRA_GPIO_INIT_OUT1: + set_level(config[i].gpio, 1); + set_direction(config[i].gpio, DIRECTION_OUTPUT); + break; + } + set_config(config[i].gpio, CONFIG_GPIO); + } +} - /* Configure GPIO direction as output. */ - set_direction(gpio, 1); +static int tegra_gpio_get_function(struct udevice *dev, unsigned offset) +{ + struct tegra_port_info *state = dev_get_priv(dev); + int gpio = state->base_gpio + offset; - return 0; + if (!get_config(gpio)) + return GPIOF_FUNC; + else if (get_direction(gpio)) + return GPIOF_OUTPUT; + else + return GPIOF_INPUT; } -/* read GPIO IN value of pin 'gpio' */ -int gpio_get_value(unsigned gpio) +static int tegra_gpio_xlate(struct udevice *dev, struct gpio_desc *desc, + struct ofnode_phandle_args *args) { - struct gpio_ctlr *ctlr = (struct gpio_ctlr *)NV_PA_GPIO_BASE; - struct gpio_ctlr_bank *bank = &ctlr->gpio_bank[GPIO_BANK(gpio)]; - int val; + int gpio, port, ret; - debug("gpio_get_value: pin = %d (port %d:bit %d)\n", - gpio, GPIO_FULLPORT(gpio), GPIO_BIT(gpio)); + gpio = args->args[0]; + port = gpio / TEGRA_GPIOS_PER_PORT; + ret = device_get_child(dev, port, &desc->dev); + if (ret) + return ret; + desc->offset = gpio % TEGRA_GPIOS_PER_PORT; + desc->flags = args->args[1] & GPIO_ACTIVE_LOW ? GPIOD_ACTIVE_LOW : 0; - val = readl(&bank->gpio_in[GPIO_PORT(gpio)]); + return 0; +} - return (val >> GPIO_BIT(gpio)) & 1; +static const struct dm_gpio_ops gpio_tegra_ops = { + .direction_input = tegra_gpio_direction_input, + .direction_output = tegra_gpio_direction_output, + .get_value = tegra_gpio_get_value, + .set_value = tegra_gpio_set_value, + .get_function = tegra_gpio_get_function, + .xlate = tegra_gpio_xlate, +}; + +/** + * Returns the name of a GPIO port + * + * GPIOs are named A, B, C, ..., Z, AA, BB, CC, ... + * + * @base_port: Base port number (0, 1..n-1) + * @return allocated string containing the name + */ +static char *gpio_port_name(int base_port) +{ + char *name, *s; + + name = malloc(3); + if (name) { + s = name; + *s++ = 'A' + (base_port % 26); + if (base_port >= 26) + *s++ = *name; + *s = '\0'; + } + + return name; } -/* write GPIO OUT value to pin 'gpio' */ -int gpio_set_value(unsigned gpio, int value) +static const struct udevice_id tegra_gpio_ids[] = { + { .compatible = "nvidia,tegra30-gpio" }, + { .compatible = "nvidia,tegra20-gpio" }, + { } +}; + +static int gpio_tegra_probe(struct udevice *dev) { - debug("gpio_set_value: pin = %d (port %d:bit %d), value = %d\n", - gpio, GPIO_FULLPORT(gpio), GPIO_BIT(gpio), value); + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); + struct tegra_port_info *priv = dev->priv; + struct tegra_gpio_platdata *plat = dev->platdata; - /* Configure GPIO output value. */ - set_level(gpio, value); + /* Only child devices have ports */ + if (!plat) + return 0; + + priv->bank = plat->bank; + priv->base_gpio = plat->base_gpio; + + uc_priv->gpio_count = TEGRA_GPIOS_PER_PORT; + uc_priv->bank_name = plat->port_name; return 0; } -/* - * Display Tegra GPIO information +/** + * We have a top-level GPIO device with no actual GPIOs. It has a child + * device for each Tegra port. */ -void gpio_info(void) +static int gpio_tegra_bind(struct udevice *parent) { - unsigned c; - int type; - - for (c = 0; c < MAX_NUM_GPIOS; c++) { - type = get_config(c); /* GPIO, not SFPIO */ - if (type) { - printf("GPIO_%d:\t%s is an %s, ", c, - get_name(c), - get_direction(c) ? "OUTPUT" : "INPUT"); - if (get_direction(c)) - printf("value = %d", gpio_get_output_value(c)); - else - printf("value = %d", gpio_get_value(c)); - printf("\n"); - } else - continue; + struct tegra_gpio_platdata *plat = parent->platdata; + struct gpio_ctlr *ctlr; + int bank_count; + int bank; + int ret; + + /* If this is a child device, there is nothing to do here */ + if (plat) + return 0; + + /* TODO(sjg@chromium.org): Remove once SPL supports device tree */ +#ifdef CONFIG_SPL_BUILD + ctlr = (struct gpio_ctlr *)NV_PA_GPIO_BASE; + bank_count = TEGRA_GPIO_BANKS; +#else + { + int len; + + /* + * This driver does not make use of interrupts, other than to figure + * out the number of GPIO banks + */ + len = dev_read_size(parent, "interrupts"); + if (len < 0) + return len; + bank_count = len / 3 / sizeof(u32); + ctlr = (struct gpio_ctlr *)dev_read_addr(parent); + if ((ulong)ctlr == FDT_ADDR_T_NONE) + return -EINVAL; + } +#endif + for (bank = 0; bank < bank_count; bank++) { + int port; + + for (port = 0; port < TEGRA_PORTS_PER_BANK; port++) { + struct tegra_gpio_platdata *plat; + struct udevice *dev; + int base_port; + + plat = calloc(1, sizeof(*plat)); + if (!plat) + return -ENOMEM; + plat->bank = &ctlr->gpio_bank[bank]; + base_port = bank * TEGRA_PORTS_PER_BANK + port; + plat->base_gpio = TEGRA_GPIOS_PER_PORT * base_port; + plat->port_name = gpio_port_name(base_port); + + ret = device_bind(parent, parent->driver, + plat->port_name, plat, -1, &dev); + if (ret) + return ret; + dev_set_of_offset(dev, dev_of_offset(parent)); + } } + + return 0; } + +U_BOOT_DRIVER(gpio_tegra) = { + .name = "gpio_tegra", + .id = UCLASS_GPIO, + .of_match = tegra_gpio_ids, + .bind = gpio_tegra_bind, + .probe = gpio_tegra_probe, + .priv_auto_alloc_size = sizeof(struct tegra_port_info), + .ops = &gpio_tegra_ops, + .flags = DM_FLAG_PRE_RELOC, +};