diff options
author | Albert ARIBAUD <albert.u.boot@aribaud.net> | 2015-07-07 11:38:44 +0200 |
---|---|---|
committer | Albert ARIBAUD <albert.u.boot@aribaud.net> | 2015-07-07 11:38:44 +0200 |
commit | 6f43ba70d15e15a08c25b3d956c70addb6740737 (patch) | |
tree | e5ddc8498043c0c47559737ea60e4d7fc866e20a /drivers/gpio | |
parent | 003b09dad492ebc385b28067b8028a0c0ff9323f (diff) | |
parent | 9c6b05cb724e18d1db3f9e1a75b2272572f06fbd (diff) |
Merge branch 'u-boot/master' into 'u-boot-arm/master'
Diffstat (limited to 'drivers/gpio')
-rw-r--r-- | drivers/gpio/Kconfig | 7 | ||||
-rw-r--r-- | drivers/gpio/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpio/gpio-uclass.c | 1 | ||||
-rw-r--r-- | drivers/gpio/intel_ich6_gpio.c | 257 | ||||
-rw-r--r-- | drivers/gpio/sunxi_gpio.c | 1 | ||||
-rw-r--r-- | drivers/gpio/vybrid_gpio.c | 169 |
6 files changed, 408 insertions, 28 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 0840a30fba..0c43777cef 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -35,3 +35,10 @@ config SANDBOX_GPIO_COUNT are specified using the device tree. But you can also have a number of 'anonymous' GPIOs that do not belong to any device or bank. Select a suitable value depending on your needs. + +config VYBRID_GPIO + bool "Vybrid GPIO driver" + depends on DM + default n + help + Say yes here to support Vybrid vf610 GPIOs. diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index ba9efe8d54..586485055d 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -45,3 +45,4 @@ obj-$(CONFIG_SUNXI_GPIO) += sunxi_gpio.o obj-$(CONFIG_LPC32XX_GPIO) += lpc32xx_gpio.o obj-$(CONFIG_STM32_GPIO) += stm32_gpio.o obj-$(CONFIG_ZYNQ_GPIO) += zynq_gpio.o +obj-$(CONFIG_VYBRID_GPIO) += vybrid_gpio.o diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c index 530bb3e128..bf982b9d19 100644 --- a/drivers/gpio/gpio-uclass.c +++ b/drivers/gpio/gpio-uclass.c @@ -757,6 +757,7 @@ static int gpio_pre_remove(struct udevice *dev) UCLASS_DRIVER(gpio) = { .id = UCLASS_GPIO, .name = "gpio", + .flags = DM_UC_FLAG_SEQ_ALIAS, .post_probe = gpio_post_probe, .pre_remove = gpio_pre_remove, .per_device_auto_alloc_size = sizeof(struct gpio_dev_priv), diff --git a/drivers/gpio/intel_ich6_gpio.c b/drivers/gpio/intel_ich6_gpio.c index 7e679a086e..8a108f3805 100644 --- a/drivers/gpio/intel_ich6_gpio.c +++ b/drivers/gpio/intel_ich6_gpio.c @@ -44,21 +44,28 @@ struct ich6_bank_priv { uint16_t lvl; }; +#define GPIO_USESEL_OFFSET(x) (x) +#define GPIO_IOSEL_OFFSET(x) (x + 4) +#define GPIO_LVL_OFFSET(x) (x + 8) + +#define IOPAD_MODE_MASK 0x7 +#define IOPAD_PULL_ASSIGN_SHIFT 7 +#define IOPAD_PULL_ASSIGN_MASK (0x3 << IOPAD_PULL_ASSIGN_SHIFT) +#define IOPAD_PULL_STRENGTH_SHIFT 9 +#define IOPAD_PULL_STRENGTH_MASK (0x3 << IOPAD_PULL_STRENGTH_SHIFT) + /* TODO: Move this to device tree, or platform data */ void ich_gpio_set_gpio_map(const struct pch_gpio_map *map) { gd->arch.gpio_map = map; } -static int gpio_ich6_ofdata_to_platdata(struct udevice *dev) +static int gpio_ich6_get_base(unsigned long base) { - struct ich6_bank_platdata *plat = dev_get_platdata(dev); pci_dev_t pci_dev; /* handle for 0:1f:0 */ u8 tmpbyte; u16 tmpword; u32 tmplong; - u16 gpiobase; - int offset; /* Where should it be? */ pci_dev = PCI_BDF(0, 0x1f, 0); @@ -123,9 +130,9 @@ static int gpio_ich6_ofdata_to_platdata(struct udevice *dev) * while on the Ivybridge the bit0 is used to indicate it is an * I/O space. */ - tmplong = x86_pci_read_config32(pci_dev, PCI_CFG_GPIOBASE); + tmplong = x86_pci_read_config32(pci_dev, base); if (tmplong == 0x00000000 || tmplong == 0xffffffff) { - debug("%s: unexpected GPIOBASE value\n", __func__); + debug("%s: unexpected BASE value\n", __func__); return -ENODEV; } @@ -135,7 +142,215 @@ static int gpio_ich6_ofdata_to_platdata(struct udevice *dev) * at the offset that we just read. Bit 0 indicates that it's * an I/O address, not a memory address, so mask that off. */ - gpiobase = tmplong & 0xfffe; + return tmplong & 0xfffc; +} + +static int _ich6_gpio_set_value(uint16_t base, unsigned offset, int value) +{ + u32 val; + + val = inl(base); + if (value) + val |= (1UL << offset); + else + val &= ~(1UL << offset); + outl(val, base); + + return 0; +} + +static int _ich6_gpio_set_function(uint16_t base, unsigned offset, int func) +{ + u32 val; + + if (func) { + val = inl(base); + val |= (1UL << offset); + outl(val, base); + } else { + val = inl(base); + val &= ~(1UL << offset); + outl(val, base); + } + + return 0; +} + +static int _ich6_gpio_set_direction(uint16_t base, unsigned offset, int dir) +{ + u32 val; + + if (!dir) { + val = inl(base); + val |= (1UL << offset); + outl(val, base); + } else { + val = inl(base); + val &= ~(1UL << offset); + outl(val, base); + } + + return 0; +} + +static int _gpio_ich6_pinctrl_cfg_pin(s32 gpiobase, s32 iobase, int pin_node) +{ + u32 gpio_offset[2]; + int pad_offset; + int val; + int ret; + const void *prop; + + /* + * GPIO node is not mandatory, so we only do the + * pinmuxing if the node exist. + */ + ret = fdtdec_get_int_array(gd->fdt_blob, pin_node, "gpio-offset", + gpio_offset, 2); + if (!ret) { + /* Do we want to force the GPIO mode? */ + prop = fdt_getprop(gd->fdt_blob, pin_node, "mode-gpio", + NULL); + if (prop) + _ich6_gpio_set_function(GPIO_USESEL_OFFSET + (gpiobase) + + gpio_offset[0], + gpio_offset[1], 1); + + val = + fdtdec_get_int(gd->fdt_blob, pin_node, "direction", -1); + if (val != -1) + _ich6_gpio_set_direction(GPIO_IOSEL_OFFSET + (gpiobase) + + gpio_offset[0], + gpio_offset[1], val); + + val = + fdtdec_get_int(gd->fdt_blob, pin_node, "output-value", -1); + if (val != -1) + _ich6_gpio_set_value(GPIO_LVL_OFFSET(gpiobase) + + gpio_offset[0], + gpio_offset[1], val); + } + + /* if iobase is present, let's configure the pad */ + if (iobase != -1) { + int iobase_addr; + + /* + * The offset for the same pin for the IOBASE and GPIOBASE are + * different, so instead of maintaining a lookup table, + * the device tree should provide directly the correct + * value for both mapping. + */ + pad_offset = + fdtdec_get_int(gd->fdt_blob, pin_node, "pad-offset", -1); + if (pad_offset == -1) { + debug("%s: Invalid register io offset %d\n", + __func__, pad_offset); + return -EINVAL; + } + + /* compute the absolute pad address */ + iobase_addr = iobase + pad_offset; + + /* + * Do we need to set a specific function mode? + * If someone put also 'mode-gpio', this option will + * be just ignored by the controller + */ + val = fdtdec_get_int(gd->fdt_blob, pin_node, "mode-func", -1); + if (val != -1) + clrsetbits_le32(iobase_addr, IOPAD_MODE_MASK, val); + + /* Configure the pull-up/down if needed */ + val = fdtdec_get_int(gd->fdt_blob, pin_node, "pull-assign", -1); + if (val != -1) + clrsetbits_le32(iobase_addr, + IOPAD_PULL_ASSIGN_MASK, + val << IOPAD_PULL_ASSIGN_SHIFT); + + val = + fdtdec_get_int(gd->fdt_blob, pin_node, "pull-strength", -1); + if (val != -1) + clrsetbits_le32(iobase_addr, + IOPAD_PULL_STRENGTH_MASK, + val << IOPAD_PULL_STRENGTH_SHIFT); + + debug("%s: pad cfg [0x%x]: %08x\n", __func__, pad_offset, + readl(iobase_addr)); + } + + return 0; +} + +int gpio_ich6_pinctrl_init(void) +{ + int pin_node; + int node; + int ret; + int gpiobase; + int iobase_offset; + int iobase = -1; + + /* + * Get the memory/io base address to configure every pins. + * IOBASE is used to configure the mode/pads + * GPIOBASE is used to configure the direction and default value + */ + gpiobase = gpio_ich6_get_base(PCI_CFG_GPIOBASE); + if (gpiobase < 0) { + debug("%s: invalid GPIOBASE address (%08x)\n", __func__, + gpiobase); + return -EINVAL; + } + + /* This is not an error to not have a pinctrl node */ + node = + fdtdec_next_compatible(gd->fdt_blob, 0, COMPAT_INTEL_X86_PINCTRL); + if (node <= 0) { + debug("%s: no pinctrl node\n", __func__); + return 0; + } + + /* + * Get the IOBASE, this is not mandatory as this is not + * supported by all the CPU + */ + iobase_offset = fdtdec_get_int(gd->fdt_blob, node, "io-base", -1); + if (iobase_offset == -1) { + debug("%s: io-base offset not present\n", __func__); + } else { + iobase = gpio_ich6_get_base(iobase_offset); + if (iobase < 0) { + debug("%s: invalid IOBASE address (%08x)\n", __func__, + iobase); + return -EINVAL; + } + } + + for (pin_node = fdt_first_subnode(gd->fdt_blob, node); + pin_node > 0; + pin_node = fdt_next_subnode(gd->fdt_blob, pin_node)) { + /* Configure the pin */ + ret = _gpio_ich6_pinctrl_cfg_pin(gpiobase, iobase, pin_node); + if (ret != 0) { + debug("%s: invalid configuration for the pin %d\n", + __func__, pin_node); + return ret; + } + } + + return 0; +} + +static int gpio_ich6_ofdata_to_platdata(struct udevice *dev) +{ + struct ich6_bank_platdata *plat = dev_get_platdata(dev); + u16 gpiobase; + int offset; + + gpiobase = gpio_ich6_get_base(PCI_CFG_GPIOBASE); offset = fdtdec_get_int(gd->fdt_blob, dev->of_offset, "reg", -1); if (offset == -1) { debug("%s: Invalid register offset %d\n", __func__, offset); @@ -192,30 +407,24 @@ static int ich6_gpio_request(struct udevice *dev, unsigned offset, static int ich6_gpio_direction_input(struct udevice *dev, unsigned offset) { struct ich6_bank_priv *bank = dev_get_priv(dev); - u32 tmplong; - tmplong = inl(bank->io_sel); - tmplong |= (1UL << offset); - outl(bank->io_sel, tmplong); - return 0; + return _ich6_gpio_set_direction(inl(bank->io_sel), offset, 0); } static int ich6_gpio_direction_output(struct udevice *dev, unsigned offset, int value) { + int ret; struct ich6_bank_priv *bank = dev_get_priv(dev); - u32 tmplong; - gpio_set_value(offset, value); + ret = _ich6_gpio_set_direction(inl(bank->io_sel), offset, 1); + if (ret) + return ret; - tmplong = inl(bank->io_sel); - tmplong &= ~(1UL << offset); - outl(bank->io_sel, tmplong); - return 0; + return _ich6_gpio_set_value(bank->lvl, offset, value); } static int ich6_gpio_get_value(struct udevice *dev, unsigned offset) - { struct ich6_bank_priv *bank = dev_get_priv(dev); u32 tmplong; @@ -230,15 +439,7 @@ static int ich6_gpio_set_value(struct udevice *dev, unsigned offset, int value) { struct ich6_bank_priv *bank = dev_get_priv(dev); - u32 tmplong; - - tmplong = inl(bank->lvl); - if (value) - tmplong |= (1UL << offset); - else - tmplong &= ~(1UL << offset); - outl(bank->lvl, tmplong); - return 0; + return _ich6_gpio_set_value(bank->lvl, offset, value); } static int ich6_gpio_get_function(struct udevice *dev, unsigned offset) diff --git a/drivers/gpio/sunxi_gpio.c b/drivers/gpio/sunxi_gpio.c index f9881308f4..afa165ab78 100644 --- a/drivers/gpio/sunxi_gpio.c +++ b/drivers/gpio/sunxi_gpio.c @@ -304,6 +304,7 @@ static const struct udevice_id sunxi_gpio_ids[] = { { .compatible = "allwinner,sun6i-a31s-pinctrl" }, { .compatible = "allwinner,sun7i-a20-pinctrl" }, { .compatible = "allwinner,sun8i-a23-pinctrl" }, + { .compatible = "allwinner,sun8i-a33-pinctrl" }, { .compatible = "allwinner,sun9i-a80-pinctrl" }, { } }; diff --git a/drivers/gpio/vybrid_gpio.c b/drivers/gpio/vybrid_gpio.c new file mode 100644 index 0000000000..6eaf0a9ad4 --- /dev/null +++ b/drivers/gpio/vybrid_gpio.c @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2015 + * Bhuvanchandra DV, Toradex, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <fdtdec.h> +#include <asm/gpio.h> +#include <asm/imx-common/iomux-v3.h> +#include <asm/io.h> +#include <malloc.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct vybrid_gpios { + unsigned int chip; + struct vybrid_gpio_regs *reg; +}; + +static int vybrid_gpio_direction_input(struct udevice *dev, unsigned gpio) +{ + const struct vybrid_gpios *gpios = dev_get_priv(dev); + + gpio = gpio + (gpios->chip * VYBRID_GPIO_COUNT); + imx_iomux_gpio_set_direction(gpio, VF610_GPIO_DIRECTION_IN); + + return 0; +} + +static int vybrid_gpio_direction_output(struct udevice *dev, unsigned gpio, + int value) +{ + const struct vybrid_gpios *gpios = dev_get_priv(dev); + + gpio = gpio + (gpios->chip * VYBRID_GPIO_COUNT); + gpio_set_value(gpio, value); + imx_iomux_gpio_set_direction(gpio, VF610_GPIO_DIRECTION_OUT); + + return 0; +} + +static int vybrid_gpio_get_value(struct udevice *dev, unsigned gpio) +{ + const struct vybrid_gpios *gpios = dev_get_priv(dev); + + return ((readl(&gpios->reg->gpio_pdir) & (1 << gpio))) ? 1 : 0; +} + +static int vybrid_gpio_set_value(struct udevice *dev, unsigned gpio, + int value) +{ + const struct vybrid_gpios *gpios = dev_get_priv(dev); + if (value) + writel((1 << gpio), &gpios->reg->gpio_psor); + else + writel((1 << gpio), &gpios->reg->gpio_pcor); + + return 0; +} + +static int vybrid_gpio_get_function(struct udevice *dev, unsigned gpio) +{ + const struct vybrid_gpios *gpios = dev_get_priv(dev); + u32 g_state = 0; + + gpio = gpio + (gpios->chip * VYBRID_GPIO_COUNT); + + imx_iomux_gpio_get_function(gpio, &g_state); + + if (((g_state & (0x07 << PAD_MUX_MODE_SHIFT)) >> PAD_MUX_MODE_SHIFT) > 0) + return GPIOF_FUNC; + if (g_state & PAD_CTL_OBE_ENABLE) + return GPIOF_OUTPUT; + if (g_state & PAD_CTL_IBE_ENABLE) + return GPIOF_INPUT; + if (!(g_state & PAD_CTL_OBE_IBE_ENABLE)) + return GPIOF_UNUSED; + + return GPIOF_UNKNOWN; +} + +static const struct dm_gpio_ops gpio_vybrid_ops = { + .direction_input = vybrid_gpio_direction_input, + .direction_output = vybrid_gpio_direction_output, + .get_value = vybrid_gpio_get_value, + .set_value = vybrid_gpio_set_value, + .get_function = vybrid_gpio_get_function, +}; + +static int vybrid_gpio_probe(struct udevice *dev) +{ + struct vybrid_gpios *gpios = dev_get_priv(dev); + struct vybrid_gpio_platdata *plat = dev_get_platdata(dev); + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); + + uc_priv->bank_name = plat->port_name; + uc_priv->gpio_count = VYBRID_GPIO_COUNT; + gpios->reg = (struct vybrid_gpio_regs *)plat->base; + gpios->chip = plat->chip; + + return 0; +} + +static int vybrid_gpio_bind(struct udevice *dev) +{ + struct vybrid_gpio_platdata *plat = dev->platdata; + fdt_addr_t base_addr; + + if (plat) + return 0; + + base_addr = dev_get_addr(dev); + if (base_addr == FDT_ADDR_T_NONE) + return -ENODEV; + + /* + * TODO: + * When every board is converted to driver model and DT is + * supported, this can be done by auto-alloc feature, but + * not using calloc to alloc memory for platdata. + */ + plat = calloc(1, sizeof(*plat)); + if (!plat) + return -ENOMEM; + + plat->base = base_addr; + plat->chip = dev->req_seq; + plat->port_name = fdt_get_name(gd->fdt_blob, dev->of_offset, NULL); + dev->platdata = plat; + + return 0; +} + +#ifndef CONFIG_OF_CONTROL +static const struct vybrid_gpio_platdata vybrid_gpio[] = { + {0, GPIO0_BASE_ADDR, "GPIO0 "}, + {1, GPIO1_BASE_ADDR, "GPIO1 "}, + {2, GPIO2_BASE_ADDR, "GPIO2 "}, + {3, GPIO3_BASE_ADDR, "GPIO3 "}, + {4, GPIO4_BASE_ADDR, "GPIO4 "}, +}; + +U_BOOT_DEVICES(vybrid_gpio) = { + { "gpio_vybrid", &vybrid_gpio[0] }, + { "gpio_vybrid", &vybrid_gpio[1] }, + { "gpio_vybrid", &vybrid_gpio[2] }, + { "gpio_vybrid", &vybrid_gpio[3] }, + { "gpio_vybrid", &vybrid_gpio[4] }, +}; +#endif + +static const struct udevice_id vybrid_gpio_ids[] = { + { .compatible = "fsl,vf610-gpio" }, + { } +}; + +U_BOOT_DRIVER(gpio_vybrid) = { + .name = "gpio_vybrid", + .id = UCLASS_GPIO, + .ops = &gpio_vybrid_ops, + .probe = vybrid_gpio_probe, + .priv_auto_alloc_size = sizeof(struct vybrid_gpios), + .of_match = vybrid_gpio_ids, + .bind = vybrid_gpio_bind, +}; |