diff options
Diffstat (limited to 'drivers/gpio/gpio-uniphier.c')
-rw-r--r-- | drivers/gpio/gpio-uniphier.c | 147 |
1 files changed, 147 insertions, 0 deletions
diff --git a/drivers/gpio/gpio-uniphier.c b/drivers/gpio/gpio-uniphier.c new file mode 100644 index 00000000000..80bb16ec064 --- /dev/null +++ b/drivers/gpio/gpio-uniphier.c @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2016 Masahiro Yamada <yamada.masahiro@socionext.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm/device.h> +#include <mapmem.h> +#include <linux/bitops.h> +#include <linux/io.h> +#include <asm/errno.h> +#include <asm/gpio.h> + +#define UNIPHIER_GPIO_PORTS_PER_BANK 8 + +#define UNIPHIER_GPIO_REG_DATA 0 /* data */ +#define UNIPHIER_GPIO_REG_DIR 4 /* direction (1:in, 0:out) */ + +struct uniphier_gpio_priv { + void __iomem *base; + char bank_name[16]; +}; + +static void uniphier_gpio_offset_write(struct udevice *dev, unsigned offset, + unsigned reg, int value) +{ + struct uniphier_gpio_priv *priv = dev_get_priv(dev); + u32 tmp; + + tmp = readl(priv->base + reg); + if (value) + tmp |= BIT(offset); + else + tmp &= ~BIT(offset); + writel(tmp, priv->base + reg); +} + +static int uniphier_gpio_offset_read(struct udevice *dev, unsigned offset, + unsigned reg) +{ + struct uniphier_gpio_priv *priv = dev_get_priv(dev); + + return !!(readl(priv->base + reg) & BIT(offset)); +} + +static int uniphier_gpio_direction_input(struct udevice *dev, unsigned offset) +{ + uniphier_gpio_offset_write(dev, offset, UNIPHIER_GPIO_REG_DIR, 1); + + return 0; +} + +static int uniphier_gpio_direction_output(struct udevice *dev, unsigned offset, + int value) +{ + uniphier_gpio_offset_write(dev, offset, UNIPHIER_GPIO_REG_DATA, value); + uniphier_gpio_offset_write(dev, offset, UNIPHIER_GPIO_REG_DIR, 0); + + return 0; +} + +static int uniphier_gpio_get_value(struct udevice *dev, unsigned offset) +{ + return uniphier_gpio_offset_read(dev, offset, UNIPHIER_GPIO_REG_DATA); +} + +static int uniphier_gpio_set_value(struct udevice *dev, unsigned offset, + int value) +{ + uniphier_gpio_offset_write(dev, offset, UNIPHIER_GPIO_REG_DATA, value); + + return 0; +} + +static int uniphier_gpio_get_function(struct udevice *dev, unsigned offset) +{ + return uniphier_gpio_offset_read(dev, offset, UNIPHIER_GPIO_REG_DIR) ? + GPIOF_INPUT : GPIOF_OUTPUT; +} + +static const struct dm_gpio_ops uniphier_gpio_ops = { + .direction_input = uniphier_gpio_direction_input, + .direction_output = uniphier_gpio_direction_output, + .get_value = uniphier_gpio_get_value, + .set_value = uniphier_gpio_set_value, + .get_function = uniphier_gpio_get_function, +}; + +static int uniphier_gpio_probe(struct udevice *dev) +{ + struct uniphier_gpio_priv *priv = dev_get_priv(dev); + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); + DECLARE_GLOBAL_DATA_PTR; + fdt_addr_t addr; + fdt_size_t size; + unsigned int tmp; + + addr = fdtdec_get_addr_size(gd->fdt_blob, dev->of_offset, "reg", + &size); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + + priv->base = map_sysmem(addr, size); + if (!priv->base) + return -ENOMEM; + + uc_priv->gpio_count = UNIPHIER_GPIO_PORTS_PER_BANK; + + tmp = (addr & 0xfff); + + /* Unfortunately, there is a register hole at offset 0x90-0x9f. */ + if (tmp > 0x90) + tmp -= 0x10; + + snprintf(priv->bank_name, sizeof(priv->bank_name) - 1, + "port%d-", (tmp - 8) / 8); + + uc_priv->bank_name = priv->bank_name; + + return 0; +} + +static int uniphier_gpio_remove(struct udevice *dev) +{ + struct uniphier_gpio_priv *priv = dev_get_priv(dev); + + unmap_sysmem(priv->base); + + return 0; +} + +/* .data = the number of GPIO banks */ +static const struct udevice_id uniphier_gpio_match[] = { + { .compatible = "socionext,uniphier-gpio" }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(uniphier_gpio) = { + .name = "uniphier_gpio", + .id = UCLASS_GPIO, + .of_match = uniphier_gpio_match, + .probe = uniphier_gpio_probe, + .remove = uniphier_gpio_remove, + .priv_auto_alloc_size = sizeof(struct uniphier_gpio_priv), + .ops = &uniphier_gpio_ops, +}; |