summaryrefslogtreecommitdiff
path: root/drivers/gpio
diff options
context:
space:
mode:
authorAlbert ARIBAUD <albert.u.boot@aribaud.net>2015-07-07 11:38:44 +0200
committerAlbert ARIBAUD <albert.u.boot@aribaud.net>2015-07-07 11:38:44 +0200
commit6f43ba70d15e15a08c25b3d956c70addb6740737 (patch)
treee5ddc8498043c0c47559737ea60e4d7fc866e20a /drivers/gpio
parent003b09dad492ebc385b28067b8028a0c0ff9323f (diff)
parent9c6b05cb724e18d1db3f9e1a75b2272572f06fbd (diff)
Merge branch 'u-boot/master' into 'u-boot-arm/master'
Diffstat (limited to 'drivers/gpio')
-rw-r--r--drivers/gpio/Kconfig7
-rw-r--r--drivers/gpio/Makefile1
-rw-r--r--drivers/gpio/gpio-uclass.c1
-rw-r--r--drivers/gpio/intel_ich6_gpio.c257
-rw-r--r--drivers/gpio/sunxi_gpio.c1
-rw-r--r--drivers/gpio/vybrid_gpio.c169
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,
+};