X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=drivers%2Fgpio%2Fs5p_gpio.c;h=5c894a26d718b19995bfed3732b286760e3bdbcf;hb=640f4e35deccbea377f543737594753ff5a44d95;hp=656bf4a06c22072f2c228cb69cf174ee7b089b3b;hpb=b8a7c467960ffb4d5a5e1eef5f7783fb6f594542;p=u-boot diff --git a/drivers/gpio/s5p_gpio.c b/drivers/gpio/s5p_gpio.c index 656bf4a06c..5c894a26d7 100644 --- a/drivers/gpio/s5p_gpio.c +++ b/drivers/gpio/s5p_gpio.c @@ -2,41 +2,76 @@ * (C) Copyright 2009 Samsung Electronics * Minkyu Kang * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA + * SPDX-License-Identifier: GPL-2.0+ */ #include +#include +#include +#include +#include #include #include +#include + +DECLARE_GLOBAL_DATA_PTR; + +#define S5P_GPIO_GET_PIN(x) (x % GPIO_PER_BANK) + +#define CON_MASK(val) (0xf << ((val) << 2)) +#define CON_SFR(gpio, cfg) ((cfg) << ((gpio) << 2)) +#define CON_SFR_UNSHIFT(val, gpio) ((val) >> ((gpio) << 2)) + +#define DAT_MASK(gpio) (0x1 << (gpio)) +#define DAT_SET(gpio) (0x1 << (gpio)) + +#define PULL_MASK(gpio) (0x3 << ((gpio) << 1)) +#define PULL_MODE(gpio, pull) ((pull) << ((gpio) << 1)) + +#define DRV_MASK(gpio) (0x3 << ((gpio) << 1)) +#define DRV_SET(gpio, mode) ((mode) << ((gpio) << 1)) +#define RATE_MASK(gpio) (0x1 << (gpio + 16)) +#define RATE_SET(gpio) (0x1 << (gpio + 16)) -#define CON_MASK(x) (0xf << ((x) << 2)) -#define CON_SFR(x, v) ((v) << ((x) << 2)) +/* Platform data for each bank */ +struct exynos_gpio_platdata { + struct s5p_gpio_bank *bank; + const char *bank_name; /* Name of port, e.g. 'gpa0" */ +}; -#define DAT_MASK(x) (0x1 << (x)) -#define DAT_SET(x) (0x1 << (x)) +/* Information about each bank at run-time */ +struct exynos_bank_info { + struct s5p_gpio_bank *bank; +}; -#define PULL_MASK(x) (0x3 << ((x) << 1)) -#define PULL_MODE(x, v) ((v) << ((x) << 1)) +static struct s5p_gpio_bank *s5p_gpio_get_bank(unsigned int gpio) +{ + const struct gpio_info *data; + unsigned int upto; + int i, count; + + data = get_gpio_data(); + count = get_bank_num(); + upto = 0; + + for (i = 0; i < count; i++) { + debug("i=%d, upto=%d\n", i, upto); + if (gpio < data->max_gpio) { + struct s5p_gpio_bank *bank; + bank = (struct s5p_gpio_bank *)data->reg_addr; + bank += (gpio - upto) / GPIO_PER_BANK; + debug("gpio=%d, bank=%p\n", gpio, bank); + return bank; + } + + upto = data->max_gpio; + data++; + } -#define DRV_MASK(x) (0x3 << ((x) << 1)) -#define DRV_SET(x, m) ((m) << ((x) << 1)) -#define RATE_MASK(x) (0x1 << (x + 16)) -#define RATE_SET(x) (0x1 << (x + 16)) + return NULL; +} -void s5p_gpio_cfg_pin(struct s5p_gpio_bank *bank, int gpio, int cfg) +static void s5p_gpio_cfg_pin(struct s5p_gpio_bank *bank, int gpio, int cfg) { unsigned int value; @@ -46,12 +81,10 @@ void s5p_gpio_cfg_pin(struct s5p_gpio_bank *bank, int gpio, int cfg) writel(value, &bank->con); } -void s5p_gpio_direction_output(struct s5p_gpio_bank *bank, int gpio, int en) +static void s5p_gpio_set_value(struct s5p_gpio_bank *bank, int gpio, int en) { unsigned int value; - s5p_gpio_cfg_pin(bank, gpio, GPIO_OUTPUT); - value = readl(&bank->dat); value &= ~DAT_MASK(gpio); if (en) @@ -59,31 +92,35 @@ void s5p_gpio_direction_output(struct s5p_gpio_bank *bank, int gpio, int en) writel(value, &bank->dat); } -void s5p_gpio_direction_input(struct s5p_gpio_bank *bank, int gpio) +#ifdef CONFIG_SPL_BUILD +/* Common GPIO API - SPL does not support driver model yet */ +int gpio_set_value(unsigned gpio, int value) { - s5p_gpio_cfg_pin(bank, gpio, GPIO_INPUT); -} + s5p_gpio_set_value(s5p_gpio_get_bank(gpio), + s5p_gpio_get_pin(gpio), value); -void s5p_gpio_set_value(struct s5p_gpio_bank *bank, int gpio, int en) + return 0; +} +#else +static int s5p_gpio_get_cfg_pin(struct s5p_gpio_bank *bank, int gpio) { unsigned int value; - value = readl(&bank->dat); - value &= ~DAT_MASK(gpio); - if (en) - value |= DAT_SET(gpio); - writel(value, &bank->dat); + value = readl(&bank->con); + value &= CON_MASK(gpio); + return CON_SFR_UNSHIFT(value, gpio); } -unsigned int s5p_gpio_get_value(struct s5p_gpio_bank *bank, int gpio) +static unsigned int s5p_gpio_get_value(struct s5p_gpio_bank *bank, int gpio) { unsigned int value; value = readl(&bank->dat); return !!(value & DAT_MASK(gpio)); } +#endif /* CONFIG_SPL_BUILD */ -void s5p_gpio_set_pull(struct s5p_gpio_bank *bank, int gpio, int mode) +static void s5p_gpio_set_pull(struct s5p_gpio_bank *bank, int gpio, int mode) { unsigned int value; @@ -91,8 +128,8 @@ void s5p_gpio_set_pull(struct s5p_gpio_bank *bank, int gpio, int mode) value &= ~PULL_MASK(gpio); switch (mode) { - case GPIO_PULL_DOWN: - case GPIO_PULL_UP: + case S5P_GPIO_PULL_DOWN: + case S5P_GPIO_PULL_UP: value |= PULL_MODE(gpio, mode); break; default: @@ -102,7 +139,7 @@ void s5p_gpio_set_pull(struct s5p_gpio_bank *bank, int gpio, int mode) writel(value, &bank->pull); } -void s5p_gpio_set_drv(struct s5p_gpio_bank *bank, int gpio, int mode) +static void s5p_gpio_set_drv(struct s5p_gpio_bank *bank, int gpio, int mode) { unsigned int value; @@ -110,10 +147,10 @@ void s5p_gpio_set_drv(struct s5p_gpio_bank *bank, int gpio, int mode) value &= ~DRV_MASK(gpio); switch (mode) { - case GPIO_DRV_1X: - case GPIO_DRV_2X: - case GPIO_DRV_3X: - case GPIO_DRV_4X: + case S5P_GPIO_DRV_1X: + case S5P_GPIO_DRV_2X: + case S5P_GPIO_DRV_3X: + case S5P_GPIO_DRV_4X: value |= DRV_SET(gpio, mode); break; default: @@ -123,7 +160,7 @@ void s5p_gpio_set_drv(struct s5p_gpio_bank *bank, int gpio, int mode) writel(value, &bank->drv); } -void s5p_gpio_set_rate(struct s5p_gpio_bank *bank, int gpio, int mode) +static void s5p_gpio_set_rate(struct s5p_gpio_bank *bank, int gpio, int mode) { unsigned int value; @@ -131,8 +168,8 @@ void s5p_gpio_set_rate(struct s5p_gpio_bank *bank, int gpio, int mode) value &= ~RATE_MASK(gpio); switch (mode) { - case GPIO_DRV_FAST: - case GPIO_DRV_SLOW: + case S5P_GPIO_DRV_FAST: + case S5P_GPIO_DRV_SLOW: value |= RATE_SET(gpio); break; default: @@ -142,57 +179,195 @@ void s5p_gpio_set_rate(struct s5p_gpio_bank *bank, int gpio, int mode) writel(value, &bank->drv); } -struct s5p_gpio_bank *s5p_gpio_get_bank(unsigned gpio) +int s5p_gpio_get_pin(unsigned gpio) { - int bank; - unsigned g = gpio - s5p_gpio_part_max(gpio); + return S5P_GPIO_GET_PIN(gpio); +} + +/* Driver model interface */ +#ifndef CONFIG_SPL_BUILD +/* set GPIO pin 'gpio' as an input */ +static int exynos_gpio_direction_input(struct udevice *dev, unsigned offset) +{ + struct exynos_bank_info *state = dev_get_priv(dev); + + /* Configure GPIO direction as input. */ + s5p_gpio_cfg_pin(state->bank, offset, S5P_GPIO_INPUT); - bank = g / GPIO_PER_BANK; - bank *= sizeof(struct s5p_gpio_bank); - return (struct s5p_gpio_bank *) (s5p_gpio_base(gpio) + bank); + return 0; } -int s5p_gpio_get_pin(unsigned gpio) +/* set GPIO pin 'gpio' as an output, with polarity 'value' */ +static int exynos_gpio_direction_output(struct udevice *dev, unsigned offset, + int value) { - return gpio % GPIO_PER_BANK; + struct exynos_bank_info *state = dev_get_priv(dev); + + /* Configure GPIO output value. */ + s5p_gpio_set_value(state->bank, offset, value); + + /* Configure GPIO direction as output. */ + s5p_gpio_cfg_pin(state->bank, offset, S5P_GPIO_OUTPUT); + + return 0; } -/* Common GPIO API */ +/* read GPIO IN value of pin 'gpio' */ +static int exynos_gpio_get_value(struct udevice *dev, unsigned offset) +{ + struct exynos_bank_info *state = dev_get_priv(dev); + + return s5p_gpio_get_value(state->bank, offset); +} -int gpio_request(unsigned gpio, const char *label) +/* write GPIO OUT value to pin 'gpio' */ +static int exynos_gpio_set_value(struct udevice *dev, unsigned offset, + int value) { + struct exynos_bank_info *state = dev_get_priv(dev); + + s5p_gpio_set_value(state->bank, offset, value); + return 0; } +#endif /* nCONFIG_SPL_BUILD */ -int gpio_free(unsigned gpio) +/* + * There is no common GPIO API for pull, drv, pin, rate (yet). These + * functions are kept here to preserve function ordering for review. + */ +void gpio_set_pull(int gpio, int mode) { - return 0; + s5p_gpio_set_pull(s5p_gpio_get_bank(gpio), + s5p_gpio_get_pin(gpio), mode); } -int gpio_direction_input(unsigned gpio) +void gpio_set_drv(int gpio, int mode) { - s5p_gpio_direction_input(s5p_gpio_get_bank(gpio), - s5p_gpio_get_pin(gpio)); - return 0; + s5p_gpio_set_drv(s5p_gpio_get_bank(gpio), + s5p_gpio_get_pin(gpio), mode); } -int gpio_direction_output(unsigned gpio, int value) +void gpio_cfg_pin(int gpio, int cfg) { - s5p_gpio_direction_output(s5p_gpio_get_bank(gpio), - s5p_gpio_get_pin(gpio), value); - return 0; + s5p_gpio_cfg_pin(s5p_gpio_get_bank(gpio), + s5p_gpio_get_pin(gpio), cfg); } -int gpio_get_value(unsigned gpio) +void gpio_set_rate(int gpio, int mode) { - return (int) s5p_gpio_get_value(s5p_gpio_get_bank(gpio), - s5p_gpio_get_pin(gpio)); + s5p_gpio_set_rate(s5p_gpio_get_bank(gpio), + s5p_gpio_get_pin(gpio), mode); } -int gpio_set_value(unsigned gpio, int value) +#ifndef CONFIG_SPL_BUILD +static int exynos_gpio_get_function(struct udevice *dev, unsigned offset) { - s5p_gpio_set_value(s5p_gpio_get_bank(gpio), - s5p_gpio_get_pin(gpio), value); + struct exynos_bank_info *state = dev_get_priv(dev); + int cfg; + + cfg = s5p_gpio_get_cfg_pin(state->bank, offset); + if (cfg == S5P_GPIO_OUTPUT) + return GPIOF_OUTPUT; + else if (cfg == S5P_GPIO_INPUT) + return GPIOF_INPUT; + else + return GPIOF_FUNC; +} + +static const struct dm_gpio_ops gpio_exynos_ops = { + .direction_input = exynos_gpio_direction_input, + .direction_output = exynos_gpio_direction_output, + .get_value = exynos_gpio_get_value, + .set_value = exynos_gpio_set_value, + .get_function = exynos_gpio_get_function, +}; + +static int gpio_exynos_probe(struct udevice *dev) +{ + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); + struct exynos_bank_info *priv = dev->priv; + struct exynos_gpio_platdata *plat = dev->platdata; + + /* Only child devices have ports */ + if (!plat) + return 0; + + priv->bank = plat->bank; + + uc_priv->gpio_count = GPIO_PER_BANK; + 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 Exynos GPIO bank. + */ +static int gpio_exynos_bind(struct udevice *parent) +{ + struct exynos_gpio_platdata *plat = parent->platdata; + struct s5p_gpio_bank *bank, *base; + const void *blob = gd->fdt_blob; + int node; + + /* If this is a child device, there is nothing to do here */ + if (plat) + return 0; + + base = (struct s5p_gpio_bank *)devfdt_get_addr(parent); + for (node = fdt_first_subnode(blob, dev_of_offset(parent)), bank = base; + node > 0; + node = fdt_next_subnode(blob, node), bank++) { + struct exynos_gpio_platdata *plat; + struct udevice *dev; + fdt_addr_t reg; + int ret; + + if (!fdtdec_get_bool(blob, node, "gpio-controller")) + continue; + plat = calloc(1, sizeof(*plat)); + if (!plat) + return -ENOMEM; + + plat->bank_name = fdt_get_name(blob, node, NULL); + ret = device_bind(parent, parent->driver, + plat->bank_name, plat, -1, &dev); + if (ret) + return ret; + + dev_set_of_offset(dev, node); + + reg = devfdt_get_addr(dev); + if (reg != FDT_ADDR_T_NONE) + bank = (struct s5p_gpio_bank *)((ulong)base + reg); + + plat->bank = bank; + + debug("dev at %p: %s\n", bank, plat->bank_name); + } + + return 0; +} + +static const struct udevice_id exynos_gpio_ids[] = { + { .compatible = "samsung,s5pc100-pinctrl" }, + { .compatible = "samsung,s5pc110-pinctrl" }, + { .compatible = "samsung,exynos4210-pinctrl" }, + { .compatible = "samsung,exynos4x12-pinctrl" }, + { .compatible = "samsung,exynos5250-pinctrl" }, + { .compatible = "samsung,exynos5420-pinctrl" }, + { } +}; + +U_BOOT_DRIVER(gpio_exynos) = { + .name = "gpio_exynos", + .id = UCLASS_GPIO, + .of_match = exynos_gpio_ids, + .bind = gpio_exynos_bind, + .probe = gpio_exynos_probe, + .priv_auto_alloc_size = sizeof(struct exynos_bank_info), + .ops = &gpio_exynos_ops, +}; +#endif