X-Git-Url: https://git.kernelconcepts.de/?p=karo-tx-uboot.git;a=blobdiff_plain;f=drivers%2Fgpio%2Fs5p_gpio.c;h=13d74eb951bc406a6c3aea51f2548201c16ac258;hp=11a0472c695eeb4c9aeae1e663a7a62e186ff411;hb=b8809e60cdb5ab09fb28a0903f25b2878483ec04;hpb=4c89a369c7cd6e7ad3adec4601cfa69fec476164 diff --git a/drivers/gpio/s5p_gpio.c b/drivers/gpio/s5p_gpio.c index 11a0472c69..13d74eb951 100644 --- a/drivers/gpio/s5p_gpio.c +++ b/drivers/gpio/s5p_gpio.c @@ -6,29 +6,75 @@ */ #include +#include +#include +#include +#include #include #include +#include -#define S5P_GPIO_GET_BANK(x) ((x >> S5P_GPIO_BANK_SHIFT) \ - & S5P_GPIO_BANK_MASK) +DECLARE_GLOBAL_DATA_PTR; -#define S5P_GPIO_GET_PIN(x) (x & S5P_GPIO_PIN_MASK) +#define S5P_GPIO_GET_PIN(x) (x % GPIO_PER_BANK) -#define CON_MASK(x) (0xf << ((x) << 2)) -#define CON_SFR(x, v) ((v) << ((x) << 2)) +#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(x) (0x1 << (x)) -#define DAT_SET(x) (0x1 << (x)) +#define DAT_MASK(gpio) (0x1 << (gpio)) +#define DAT_SET(gpio) (0x1 << (gpio)) -#define PULL_MASK(x) (0x3 << ((x) << 1)) -#define PULL_MODE(x, v) ((v) << ((x) << 1)) +#define PULL_MASK(gpio) (0x3 << ((gpio) << 1)) +#define PULL_MODE(gpio, pull) ((pull) << ((gpio) << 1)) -#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)) +#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)) -void s5p_gpio_cfg_pin(struct s5p_gpio_bank *bank, int gpio, int cfg) +#define GPIO_NAME_SIZE 20 + +/* Platform data for each bank */ +struct exynos_gpio_platdata { + struct s5p_gpio_bank *bank; + const char *bank_name; /* Name of port, e.g. 'gpa0" */ +}; + +/* Information about each bank at run-time */ +struct exynos_bank_info { + char label[GPIO_PER_BANK][GPIO_NAME_SIZE]; + struct s5p_gpio_bank *bank; +}; + +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++; + } + + return NULL; +} + +static void s5p_gpio_cfg_pin(struct s5p_gpio_bank *bank, int gpio, int cfg) { unsigned int value; @@ -38,18 +84,7 @@ 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) -{ - s5p_gpio_cfg_pin(bank, gpio, GPIO_OUTPUT); - s5p_gpio_set_value(bank, gpio, en); -} - -void s5p_gpio_direction_input(struct s5p_gpio_bank *bank, int gpio) -{ - s5p_gpio_cfg_pin(bank, gpio, GPIO_INPUT); -} - -void s5p_gpio_set_value(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; @@ -60,15 +95,35 @@ void s5p_gpio_set_value(struct s5p_gpio_bank *bank, int gpio, int en) writel(value, &bank->dat); } -unsigned int s5p_gpio_get_value(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_set_value(s5p_gpio_get_bank(gpio), + s5p_gpio_get_pin(gpio), value); + + return 0; +} +#else +static int s5p_gpio_get_cfg_pin(struct s5p_gpio_bank *bank, int gpio) +{ + unsigned int value; + + value = readl(&bank->con); + value &= CON_MASK(gpio); + return CON_SFR_UNSHIFT(value, 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; @@ -76,8 +131,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: @@ -87,7 +142,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; @@ -95,10 +150,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: @@ -108,7 +163,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; @@ -116,8 +171,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: @@ -127,55 +182,290 @@ 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) { - unsigned bank = S5P_GPIO_GET_BANK(gpio); - unsigned base = s5p_gpio_base(gpio); + return S5P_GPIO_GET_PIN(gpio); +} - return (struct s5p_gpio_bank *)(base + bank); +/* Driver model interface */ +#ifndef CONFIG_SPL_BUILD +static int exynos_gpio_get_state(struct udevice *dev, unsigned int offset, + char *buf, int bufsize) +{ + struct gpio_dev_priv *uc_priv = dev->uclass_priv; + struct exynos_bank_info *state = dev_get_priv(dev); + const char *label; + bool is_output; + int size; + int cfg; + + label = state->label[offset]; + cfg = s5p_gpio_get_cfg_pin(state->bank, offset); + is_output = cfg == S5P_GPIO_OUTPUT; + size = snprintf(buf, bufsize, "%s%d: ", + uc_priv->bank_name ? uc_priv->bank_name : "", offset); + buf += size; + bufsize -= size; + if (is_output || cfg == S5P_GPIO_INPUT) { + snprintf(buf, bufsize, "%s: %d [%c]%s%s", + is_output ? "out" : " in", + s5p_gpio_get_value(state->bank, offset), + *label ? 'x' : ' ', + *label ? " " : "", + label); + } else { + snprintf(buf, bufsize, "sfpio"); + } + + return 0; } -int s5p_gpio_get_pin(unsigned gpio) +static int check_reserved(struct udevice *dev, unsigned offset, + const char *func) { - return S5P_GPIO_GET_PIN(gpio); + struct exynos_bank_info *state = dev_get_priv(dev); + struct gpio_dev_priv *uc_priv = dev->uclass_priv; + + if (!*state->label[offset]) { + printf("exynos_gpio: %s: error: gpio %s%d not reserved\n", + func, uc_priv->bank_name, offset); + return -EPERM; + } + + return 0; } -/* Common GPIO API */ +/* 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); + int ret; + + ret = check_reserved(dev, offset, __func__); + if (ret) + return ret; + + /* Configure GPIO direction as input. */ + s5p_gpio_cfg_pin(state->bank, offset, S5P_GPIO_INPUT); + + return 0; +} -int gpio_request(unsigned gpio, const char *label) +/* set GPIO pin 'gpio' as an output, with polarity 'value' */ +static int exynos_gpio_direction_output(struct udevice *dev, unsigned offset, + int value) { + struct exynos_bank_info *state = dev_get_priv(dev); + int ret; + + ret = check_reserved(dev, offset, __func__); + if (ret) + return ret; + + /* 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; } -int gpio_free(unsigned gpio) +/* 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); + int ret; + + ret = check_reserved(dev, offset, __func__); + if (ret) + return ret; + + return s5p_gpio_get_value(state->bank, offset); +} + +/* 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); + int ret; + + ret = check_reserved(dev, offset, __func__); + if (ret) + return ret; + + s5p_gpio_set_value(state->bank, offset, value); + return 0; } -int gpio_direction_input(unsigned gpio) +static int exynos_gpio_request(struct udevice *dev, unsigned offset, + const char *label) { - s5p_gpio_direction_input(s5p_gpio_get_bank(gpio), - s5p_gpio_get_pin(gpio)); + struct exynos_bank_info *state = dev_get_priv(dev); + + if (*state->label[offset]) + return -EBUSY; + + strncpy(state->label[offset], label, GPIO_NAME_SIZE); + state->label[offset][GPIO_NAME_SIZE - 1] = '\0'; + return 0; } -int gpio_direction_output(unsigned gpio, int value) +static int exynos_gpio_free(struct udevice *dev, unsigned offset) { - s5p_gpio_direction_output(s5p_gpio_get_bank(gpio), - s5p_gpio_get_pin(gpio), value); + struct exynos_bank_info *state = dev_get_priv(dev); + int ret; + + ret = check_reserved(dev, offset, __func__); + if (ret) + return ret; + state->label[offset][0] = '\0'; + return 0; } +#endif /* nCONFIG_SPL_BUILD */ -int gpio_get_value(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 (int) s5p_gpio_get_value(s5p_gpio_get_bank(gpio), - s5p_gpio_get_pin(gpio)); + s5p_gpio_set_pull(s5p_gpio_get_bank(gpio), + s5p_gpio_get_pin(gpio), mode); } -int gpio_set_value(unsigned gpio, int value) +void gpio_set_drv(int gpio, int mode) { - s5p_gpio_set_value(s5p_gpio_get_bank(gpio), - s5p_gpio_get_pin(gpio), value); + s5p_gpio_set_drv(s5p_gpio_get_bank(gpio), + s5p_gpio_get_pin(gpio), mode); +} + +void gpio_cfg_pin(int gpio, int cfg) +{ + s5p_gpio_cfg_pin(s5p_gpio_get_bank(gpio), + s5p_gpio_get_pin(gpio), cfg); +} + +void gpio_set_rate(int gpio, int mode) +{ + s5p_gpio_set_rate(s5p_gpio_get_bank(gpio), + s5p_gpio_get_pin(gpio), mode); +} + +#ifndef CONFIG_SPL_BUILD +static int exynos_gpio_get_function(struct udevice *dev, unsigned offset) +{ + struct exynos_bank_info *state = dev_get_priv(dev); + int cfg; + + if (!*state->label[offset]) + return GPIOF_UNUSED; + 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 = { + .request = exynos_gpio_request, + .free = exynos_gpio_free, + .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, + .get_state = exynos_gpio_get_state, +}; + +static int gpio_exynos_probe(struct udevice *dev) +{ + struct gpio_dev_priv *uc_priv = dev->uclass_priv; + 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 *)fdtdec_get_addr(gd->fdt_blob, + parent->of_offset, "reg"); + for (node = fdt_first_subnode(blob, parent->of_offset), 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; + reg = fdtdec_get_addr(blob, node, "reg"); + if (reg != FDT_ADDR_T_NONE) + bank = (struct s5p_gpio_bank *)((ulong)base + reg); + plat->bank = bank; + plat->bank_name = fdt_get_name(blob, node, NULL); + debug("dev at %p: %s\n", bank, plat->bank_name); + + 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 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