summaryrefslogtreecommitdiffstats
path: root/drivers/gpio
diff options
context:
space:
mode:
authorStefano Babic <sbabic@denx.de>2016-06-18 10:24:54 +0200
committerStefano Babic <sbabic@denx.de>2016-06-18 10:25:13 +0200
commitdc557e9a1fe00ca9d884bd88feef5bebf23fede4 (patch)
treeec09fdf8f7c4c44e30f4b38b7459a2cbbb71d094 /drivers/gpio
parentd2ba7a6adcef6e6f8c4418c7b0caf9d7ab98a6d4 (diff)
parent6b3943f1b04be60f147ee540fbd72c4c7ea89f80 (diff)
downloadtalos-obmc-uboot-dc557e9a1fe00ca9d884bd88feef5bebf23fede4.tar.gz
talos-obmc-uboot-dc557e9a1fe00ca9d884bd88feef5bebf23fede4.zip
Merge branch 'master' of git://git.denx.de/u-boot
Signed-off-by: Stefano Babic <sbabic@denx.de>
Diffstat (limited to 'drivers/gpio')
-rw-r--r--drivers/gpio/Kconfig41
-rw-r--r--drivers/gpio/Makefile2
-rw-r--r--drivers/gpio/gpio-uclass.c32
-rw-r--r--drivers/gpio/mpc85xx_gpio.c228
-rw-r--r--drivers/gpio/mxs_gpio.c1
-rw-r--r--drivers/gpio/pca953x_gpio.c9
-rw-r--r--drivers/gpio/rk_gpio.c1
-rw-r--r--drivers/gpio/sandbox.c35
-rw-r--r--drivers/gpio/sunxi_gpio.c90
-rw-r--r--drivers/gpio/tegra186_gpio.c288
-rw-r--r--drivers/gpio/tegra186_gpio_priv.h61
-rw-r--r--drivers/gpio/zynq_gpio.c22
12 files changed, 765 insertions, 45 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 93a7e8c6c2..73b862dc0b 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -109,6 +109,21 @@ config SANDBOX_GPIO_COUNT
of 'anonymous' GPIOs that do not belong to any device or bank.
Select a suitable value depending on your needs.
+config TEGRA_GPIO
+ bool "Tegra20..210 GPIO driver"
+ depends on DM_GPIO
+ help
+ Support for the GPIO controller contained in NVIDIA Tegra20 through
+ Tegra210.
+
+config TEGRA186_GPIO
+ bool "Tegra186 GPIO driver"
+ depends on DM_GPIO
+ help
+ Support for the GPIO controller contained in NVIDIA Tegra186. This
+ covers both the "main" and "AON" controller instances, even though
+ they have slightly different register layout.
+
config GPIO_UNIPHIER
bool "UniPhier GPIO"
depends on ARCH_UNIPHIER
@@ -173,4 +188,30 @@ config DM_PCA953X
Now, max 24 bits chips and PCA953X compatible chips are
supported
+
+config MPC85XX_GPIO
+ bool "Freescale MPC85XX GPIO driver"
+ depends on DM_GPIO
+ help
+ This driver supports the built-in GPIO controller of MPC85XX CPUs.
+ Each GPIO bank is identified by its own entry in the device tree,
+ i.e.
+
+ gpio-controller@fc00 {
+ #gpio-cells = <2>;
+ compatible = "fsl,pq3-gpio";
+ reg = <0xfc00 0x100>
+ }
+
+ By default, each bank is assumed to have 32 GPIOs, but the ngpios
+ setting is honored, so the number of GPIOs for each bank is
+ configurable to match the actual GPIO count of the SoC (e.g. the
+ 32/32/23 banks of the P1022 SoC).
+
+ Aside from the standard functions of input/output mode, and output
+ value setting, the open-drain feature, which can configure individual
+ GPIOs to work as open-drain outputs, is supported.
+
+ The driver has been tested on MPC85XX, but it is likely that other
+ PowerQUICC III devices will work as well.
endmenu
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index ddec1ef8de..792d19186a 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -31,10 +31,12 @@ obj-$(CONFIG_S5P) += s5p_gpio.o
obj-$(CONFIG_SANDBOX_GPIO) += sandbox.o
obj-$(CONFIG_SPEAR_GPIO) += spear_gpio.o
obj-$(CONFIG_TEGRA_GPIO) += tegra_gpio.o
+obj-$(CONFIG_TEGRA186_GPIO) += tegra186_gpio.o
obj-$(CONFIG_DA8XX_GPIO) += da8xx_gpio.o
obj-$(CONFIG_DM644X_GPIO) += da8xx_gpio.o
obj-$(CONFIG_ALTERA_PIO) += altera_pio.o
obj-$(CONFIG_MPC83XX_GPIO) += mpc83xx_gpio.o
+obj-$(CONFIG_MPC85XX_GPIO) += mpc85xx_gpio.o
obj-$(CONFIG_SH_GPIO_PFC) += sh_pfc.o
obj-$(CONFIG_OMAP_GPIO) += omap_gpio.o
obj-$(CONFIG_DB8500_GPIO) += db8500_gpio.o
diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c
index 732b6c2afa..4559739d61 100644
--- a/drivers/gpio/gpio-uclass.c
+++ b/drivers/gpio/gpio-uclass.c
@@ -367,6 +367,38 @@ int dm_gpio_set_value(const struct gpio_desc *desc, int value)
return 0;
}
+int dm_gpio_get_open_drain(struct gpio_desc *desc)
+{
+ struct dm_gpio_ops *ops = gpio_get_ops(desc->dev);
+ int ret;
+
+ ret = check_reserved(desc, "get_open_drain");
+ if (ret)
+ return ret;
+
+ if (ops->set_open_drain)
+ return ops->get_open_drain(desc->dev, desc->offset);
+ else
+ return -ENOSYS;
+}
+
+int dm_gpio_set_open_drain(struct gpio_desc *desc, int value)
+{
+ struct dm_gpio_ops *ops = gpio_get_ops(desc->dev);
+ int ret;
+
+ ret = check_reserved(desc, "set_open_drain");
+ if (ret)
+ return ret;
+
+ if (ops->set_open_drain)
+ ret = ops->set_open_drain(desc->dev, desc->offset, value);
+ else
+ return 0; /* feature not supported -> ignore setting */
+
+ return ret;
+}
+
int dm_gpio_set_dir_flags(struct gpio_desc *desc, ulong flags)
{
struct udevice *dev = desc->dev;
diff --git a/drivers/gpio/mpc85xx_gpio.c b/drivers/gpio/mpc85xx_gpio.c
new file mode 100644
index 0000000000..04773e2b31
--- /dev/null
+++ b/drivers/gpio/mpc85xx_gpio.c
@@ -0,0 +1,228 @@
+/*
+ * (C) Copyright 2016
+ * Mario Six, Guntermann & Drunck GmbH, six@gdsys.de
+ *
+ * based on arch/powerpc/include/asm/mpc85xx_gpio.h, which is
+ *
+ * Copyright 2010 eXMeritus, A Boeing Company
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <asm/gpio.h>
+#include <mapmem.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct ccsr_gpio {
+ u32 gpdir;
+ u32 gpodr;
+ u32 gpdat;
+ u32 gpier;
+ u32 gpimr;
+ u32 gpicr;
+};
+
+struct mpc85xx_gpio_data {
+ /* The bank's register base in memory */
+ struct ccsr_gpio __iomem *base;
+ /* The address of the registers; used to identify the bank */
+ ulong addr;
+ /* The GPIO count of the bank */
+ uint gpio_count;
+ /* The GPDAT register cannot be used to determine the value of output
+ * pins on MPC8572/MPC8536, so we shadow it and use the shadowed value
+ * for output pins */
+ u32 dat_shadow;
+};
+
+inline u32 gpio_mask(unsigned gpio) {
+ return (1U << (31 - (gpio)));
+}
+
+static inline u32 mpc85xx_gpio_get_val(struct ccsr_gpio *base, u32 mask)
+{
+ return in_be32(&base->gpdat) & mask;
+}
+
+static inline u32 mpc85xx_gpio_get_dir(struct ccsr_gpio *base, u32 mask)
+{
+ return in_be32(&base->gpdir) & mask;
+}
+
+static inline void mpc85xx_gpio_set_in(struct ccsr_gpio *base, u32 gpios)
+{
+ clrbits_be32(&base->gpdat, gpios);
+ /* GPDIR register 0 -> input */
+ clrbits_be32(&base->gpdir, gpios);
+}
+
+static inline void mpc85xx_gpio_set_low(struct ccsr_gpio *base, u32 gpios)
+{
+ clrbits_be32(&base->gpdat, gpios);
+ /* GPDIR register 1 -> output */
+ setbits_be32(&base->gpdir, gpios);
+}
+
+static inline void mpc85xx_gpio_set_high(struct ccsr_gpio *base, u32 gpios)
+{
+ setbits_be32(&base->gpdat, gpios);
+ /* GPDIR register 1 -> output */
+ setbits_be32(&base->gpdir, gpios);
+}
+
+static inline int mpc85xx_gpio_open_drain_val(struct ccsr_gpio *base, u32 mask)
+{
+ return in_be32(&base->gpodr) & mask;
+}
+
+static inline void mpc85xx_gpio_open_drain_on(struct ccsr_gpio *base, u32
+ gpios)
+{
+ /* GPODR register 1 -> open drain on */
+ setbits_be32(&base->gpodr, gpios);
+}
+
+static inline void mpc85xx_gpio_open_drain_off(struct ccsr_gpio *base,
+ u32 gpios)
+{
+ /* GPODR register 0 -> open drain off (actively driven) */
+ clrbits_be32(&base->gpodr, gpios);
+}
+
+static int mpc85xx_gpio_direction_input(struct udevice *dev, unsigned gpio)
+{
+ struct mpc85xx_gpio_data *data = dev_get_priv(dev);
+
+ mpc85xx_gpio_set_in(data->base, gpio_mask(gpio));
+ return 0;
+}
+
+static int mpc85xx_gpio_set_value(struct udevice *dev, unsigned gpio,
+ int value)
+{
+ struct mpc85xx_gpio_data *data = dev_get_priv(dev);
+
+ if (value) {
+ data->dat_shadow |= gpio_mask(gpio);
+ mpc85xx_gpio_set_high(data->base, gpio_mask(gpio));
+ } else {
+ data->dat_shadow &= ~gpio_mask(gpio);
+ mpc85xx_gpio_set_low(data->base, gpio_mask(gpio));
+ }
+ return 0;
+}
+
+static int mpc85xx_gpio_direction_output(struct udevice *dev, unsigned gpio,
+ int value)
+{
+ return mpc85xx_gpio_set_value(dev, gpio, value);
+}
+
+static int mpc85xx_gpio_get_value(struct udevice *dev, unsigned gpio)
+{
+ struct mpc85xx_gpio_data *data = dev_get_priv(dev);
+
+ if (!!mpc85xx_gpio_get_dir(data->base, gpio_mask(gpio))) {
+ /* Output -> use shadowed value */
+ return !!(data->dat_shadow & gpio_mask(gpio));
+ } else {
+ /* Input -> read value from GPDAT register */
+ return !!mpc85xx_gpio_get_val(data->base, gpio_mask(gpio));
+ }
+}
+
+static int mpc85xx_gpio_get_open_drain(struct udevice *dev, unsigned gpio)
+{
+ struct mpc85xx_gpio_data *data = dev_get_priv(dev);
+
+ return !!mpc85xx_gpio_open_drain_val(data->base, gpio_mask(gpio));
+}
+
+static int mpc85xx_gpio_set_open_drain(struct udevice *dev, unsigned gpio,
+ int value)
+{
+ struct mpc85xx_gpio_data *data = dev_get_priv(dev);
+
+ if (value) {
+ mpc85xx_gpio_open_drain_on(data->base, gpio_mask(gpio));
+ } else {
+ mpc85xx_gpio_open_drain_off(data->base, gpio_mask(gpio));
+ }
+ return 0;
+}
+
+static int mpc85xx_gpio_get_function(struct udevice *dev, unsigned gpio)
+{
+ struct mpc85xx_gpio_data *data = dev_get_priv(dev);
+ int dir;
+
+ dir = !!mpc85xx_gpio_get_dir(data->base, gpio_mask(gpio));
+ return dir ? GPIOF_OUTPUT : GPIOF_INPUT;
+}
+
+static int mpc85xx_gpio_ofdata_to_platdata(struct udevice *dev) {
+ struct mpc85xx_gpio_data *data = dev_get_priv(dev);
+ fdt_addr_t addr;
+ fdt_size_t size;
+
+ addr = fdtdec_get_addr_size_auto_noparent(gd->fdt_blob, dev->of_offset,
+ "reg", 0, &size);
+
+ data->addr = addr;
+ data->base = map_sysmem(CONFIG_SYS_IMMR + addr, size);
+
+ if (!data->base)
+ return -ENOMEM;
+
+ data->gpio_count = fdtdec_get_int(gd->fdt_blob, dev->of_offset,
+ "ngpios", 32);
+ data->dat_shadow = 0;
+
+ return 0;
+}
+
+static int mpc85xx_gpio_probe(struct udevice *dev)
+{
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct mpc85xx_gpio_data *data = dev_get_priv(dev);
+ char name[32], *str;
+
+ snprintf(name, sizeof(name), "MPC@%lx_", data->addr);
+ str = strdup(name);
+
+ if (!str)
+ return -ENOMEM;
+
+ uc_priv->bank_name = str;
+ uc_priv->gpio_count = data->gpio_count;
+
+ return 0;
+}
+
+static const struct dm_gpio_ops gpio_mpc85xx_ops = {
+ .direction_input = mpc85xx_gpio_direction_input,
+ .direction_output = mpc85xx_gpio_direction_output,
+ .get_value = mpc85xx_gpio_get_value,
+ .set_value = mpc85xx_gpio_set_value,
+ .get_open_drain = mpc85xx_gpio_get_open_drain,
+ .set_open_drain = mpc85xx_gpio_set_open_drain,
+ .get_function = mpc85xx_gpio_get_function,
+};
+
+static const struct udevice_id mpc85xx_gpio_ids[] = {
+ { .compatible = "fsl,pq3-gpio" },
+ { /* sentinel */ }
+};
+
+U_BOOT_DRIVER(gpio_mpc85xx) = {
+ .name = "gpio_mpc85xx",
+ .id = UCLASS_GPIO,
+ .ops = &gpio_mpc85xx_ops,
+ .ofdata_to_platdata = mpc85xx_gpio_ofdata_to_platdata,
+ .of_match = mpc85xx_gpio_ids,
+ .probe = mpc85xx_gpio_probe,
+ .priv_auto_alloc_size = sizeof(struct mpc85xx_gpio_data),
+};
diff --git a/drivers/gpio/mxs_gpio.c b/drivers/gpio/mxs_gpio.c
index b54a10b493..c25b4c1c2e 100644
--- a/drivers/gpio/mxs_gpio.c
+++ b/drivers/gpio/mxs_gpio.c
@@ -8,7 +8,6 @@
*/
#include <common.h>
-#include <netdev.h>
#include <asm/errno.h>
#include <asm/io.h>
#include <asm/arch/iomux.h>
diff --git a/drivers/gpio/pca953x_gpio.c b/drivers/gpio/pca953x_gpio.c
index 987d10e967..065b181bd2 100644
--- a/drivers/gpio/pca953x_gpio.c
+++ b/drivers/gpio/pca953x_gpio.c
@@ -16,8 +16,8 @@
*
* TODO:
* 1. Support PCA957X_TYPE
- * 2. Support max 40 gpio pins
- * 3. Support Plolarity Inversion
+ * 2. Support 24 gpio pins
+ * 3. Support Polarity Inversion
*/
#include <common.h>
@@ -47,7 +47,7 @@ enum {
PCA953X_DIRECTION_OUT,
};
-#define MAX_BANK 3
+#define MAX_BANK 5
#define BANK_SZ 8
DECLARE_GLOBAL_DATA_PTR;
@@ -121,6 +121,9 @@ static int pca953x_read_regs(struct udevice *dev, int reg, u8 *val)
ret = dm_i2c_read(dev, reg, val, 1);
} else if (info->gpio_count <= 16) {
ret = dm_i2c_read(dev, reg << 1, val, info->bank_count);
+ } else if (info->gpio_count == 40) {
+ /* Auto increment */
+ ret = dm_i2c_read(dev, (reg << 3) | 0x80, val, info->bank_count);
} else {
dev_err(dev, "Unsupported now\n");
return -EINVAL;
diff --git a/drivers/gpio/rk_gpio.c b/drivers/gpio/rk_gpio.c
index fefe3ca203..64abcbaa0a 100644
--- a/drivers/gpio/rk_gpio.c
+++ b/drivers/gpio/rk_gpio.c
@@ -8,7 +8,6 @@
*/
#include <common.h>
-#include <clk.h>
#include <dm.h>
#include <syscon.h>
#include <asm/errno.h>
diff --git a/drivers/gpio/sandbox.c b/drivers/gpio/sandbox.c
index a9b1efcd06..f6435a0543 100644
--- a/drivers/gpio/sandbox.c
+++ b/drivers/gpio/sandbox.c
@@ -15,6 +15,7 @@ DECLARE_GLOBAL_DATA_PTR;
/* Flags for each GPIO */
#define GPIOF_OUTPUT (1 << 0) /* Currently set as an output */
#define GPIOF_HIGH (1 << 1) /* Currently set high */
+#define GPIOF_ODR (1 << 2) /* Currently set to open drain mode */
struct gpio_state {
const char *label; /* label given by requester */
@@ -70,6 +71,16 @@ int sandbox_gpio_set_value(struct udevice *dev, unsigned offset, int value)
return set_gpio_flag(dev, offset, GPIOF_HIGH, value);
}
+int sandbox_gpio_get_open_drain(struct udevice *dev, unsigned offset)
+{
+ return get_gpio_flag(dev, offset, GPIOF_ODR);
+}
+
+int sandbox_gpio_set_open_drain(struct udevice *dev, unsigned offset, int value)
+{
+ return set_gpio_flag(dev, offset, GPIOF_ODR, value);
+}
+
int sandbox_gpio_get_direction(struct udevice *dev, unsigned offset)
{
return get_gpio_flag(dev, offset, GPIOF_OUTPUT);
@@ -124,6 +135,28 @@ static int sb_gpio_set_value(struct udevice *dev, unsigned offset, int value)
return sandbox_gpio_set_value(dev, offset, value);
}
+/* read GPIO ODR value of port 'offset' */
+static int sb_gpio_get_open_drain(struct udevice *dev, unsigned offset)
+{
+ debug("%s: offset:%u\n", __func__, offset);
+
+ return sandbox_gpio_get_open_drain(dev, offset);
+}
+
+/* write GPIO ODR value to port 'offset' */
+static int sb_gpio_set_open_drain(struct udevice *dev, unsigned offset, int value)
+{
+ debug("%s: offset:%u, value = %d\n", __func__, offset, value);
+
+ if (!sandbox_gpio_get_direction(dev, offset)) {
+ printf("sandbox_gpio: error: set_open_drain on input gpio %u\n",
+ offset);
+ return -1;
+ }
+
+ return sandbox_gpio_set_open_drain(dev, offset, value);
+}
+
static int sb_gpio_get_function(struct udevice *dev, unsigned offset)
{
if (get_gpio_flag(dev, offset, GPIOF_OUTPUT))
@@ -154,6 +187,8 @@ static const struct dm_gpio_ops gpio_sandbox_ops = {
.direction_output = sb_gpio_direction_output,
.get_value = sb_gpio_get_value,
.set_value = sb_gpio_set_value,
+ .get_open_drain = sb_gpio_get_open_drain,
+ .set_open_drain = sb_gpio_set_open_drain,
.get_function = sb_gpio_get_function,
.xlate = sb_gpio_xlate,
};
diff --git a/drivers/gpio/sunxi_gpio.c b/drivers/gpio/sunxi_gpio.c
index a7cec18d57..94abbeb39a 100644
--- a/drivers/gpio/sunxi_gpio.c
+++ b/drivers/gpio/sunxi_gpio.c
@@ -258,43 +258,30 @@ static int gpio_sunxi_probe(struct udevice *dev)
return 0;
}
+
+struct sunxi_gpio_soc_data {
+ int start;
+ int no_banks;
+};
+
/**
* We have a top-level GPIO device with no actual GPIOs. It has a child
* device for each Sunxi bank.
*/
static int gpio_sunxi_bind(struct udevice *parent)
{
+ struct sunxi_gpio_soc_data *soc_data =
+ (struct sunxi_gpio_soc_data *)dev_get_driver_data(parent);
struct sunxi_gpio_platdata *plat = parent->platdata;
struct sunxi_gpio_reg *ctlr;
- int bank, no_banks, ret, start;
+ int bank, ret;
/* If this is a child device, there is nothing to do here */
if (plat)
return 0;
- if (fdt_node_check_compatible(gd->fdt_blob, parent->of_offset,
- "allwinner,sun6i-a31-r-pinctrl") == 0) {
- start = 'L' - 'A';
- no_banks = 2; /* L & M */
- } else if (fdt_node_check_compatible(gd->fdt_blob, parent->of_offset,
- "allwinner,sun8i-a23-r-pinctrl") == 0 ||
- fdt_node_check_compatible(gd->fdt_blob, parent->of_offset,
- "allwinner,sun8i-a83t-r-pinctrl") == 0 ||
- fdt_node_check_compatible(gd->fdt_blob, parent->of_offset,
- "allwinner,sun8i-h3-r-pinctrl") == 0) {
- start = 'L' - 'A';
- no_banks = 1; /* L only */
- } else if (fdt_node_check_compatible(gd->fdt_blob, parent->of_offset,
- "allwinner,sun9i-a80-r-pinctrl") == 0) {
- start = 'L' - 'A';
- no_banks = 3; /* L, M & N */
- } else {
- start = 0;
- no_banks = SUNXI_GPIO_BANKS;
- }
-
ctlr = (struct sunxi_gpio_reg *)dev_get_addr(parent);
- for (bank = 0; bank < no_banks; bank++) {
+ for (bank = 0; bank < soc_data->no_banks; bank++) {
struct sunxi_gpio_platdata *plat;
struct udevice *dev;
@@ -302,7 +289,7 @@ static int gpio_sunxi_bind(struct udevice *parent)
if (!plat)
return -ENOMEM;
plat->regs = &ctlr->gpio_bank[bank];
- plat->bank_name = gpio_bank_name(start + bank);
+ plat->bank_name = gpio_bank_name(soc_data->start + bank);
plat->gpio_count = SUNXI_GPIOS_PER_BANK;
ret = device_bind(parent, parent->driver,
@@ -315,23 +302,46 @@ static int gpio_sunxi_bind(struct udevice *parent)
return 0;
}
+static const struct sunxi_gpio_soc_data soc_data_a_all = {
+ .start = 0,
+ .no_banks = SUNXI_GPIO_BANKS,
+};
+
+static const struct sunxi_gpio_soc_data soc_data_l_1 = {
+ .start = 'L' - 'A',
+ .no_banks = 1,
+};
+
+static const struct sunxi_gpio_soc_data soc_data_l_2 = {
+ .start = 'L' - 'A',
+ .no_banks = 2,
+};
+
+static const struct sunxi_gpio_soc_data soc_data_l_3 = {
+ .start = 'L' - 'A',
+ .no_banks = 3,
+};
+
+#define ID(_compat_, _soc_data_) \
+ { .compatible = _compat_, .data = (ulong)&soc_data_##_soc_data_ }
+
static const struct udevice_id sunxi_gpio_ids[] = {
- { .compatible = "allwinner,sun4i-a10-pinctrl" },
- { .compatible = "allwinner,sun5i-a10s-pinctrl" },
- { .compatible = "allwinner,sun5i-a13-pinctrl" },
- { .compatible = "allwinner,sun6i-a31-pinctrl" },
- { .compatible = "allwinner,sun6i-a31s-pinctrl" },
- { .compatible = "allwinner,sun7i-a20-pinctrl" },
- { .compatible = "allwinner,sun8i-a23-pinctrl" },
- { .compatible = "allwinner,sun8i-a33-pinctrl" },
- { .compatible = "allwinner,sun8i-a83t-pinctrl", },
- { .compatible = "allwinner,sun8i-h3-pinctrl" },
- { .compatible = "allwinner,sun9i-a80-pinctrl" },
- { .compatible = "allwinner,sun6i-a31-r-pinctrl" },
- { .compatible = "allwinner,sun8i-a23-r-pinctrl" },
- { .compatible = "allwinner,sun8i-a83t-r-pinctrl" },
- { .compatible = "allwinner,sun8i-h3-r-pinctrl", },
- { .compatible = "allwinner,sun9i-a80-r-pinctrl", },
+ ID("allwinner,sun4i-a10-pinctrl", a_all),
+ ID("allwinner,sun5i-a10s-pinctrl", a_all),
+ ID("allwinner,sun5i-a13-pinctrl", a_all),
+ ID("allwinner,sun6i-a31-pinctrl", a_all),
+ ID("allwinner,sun6i-a31s-pinctrl", a_all),
+ ID("allwinner,sun7i-a20-pinctrl", a_all),
+ ID("allwinner,sun8i-a23-pinctrl", a_all),
+ ID("allwinner,sun8i-a33-pinctrl", a_all),
+ ID("allwinner,sun8i-a83t-pinctrl", a_all),
+ ID("allwinner,sun8i-h3-pinctrl", a_all),
+ ID("allwinner,sun9i-a80-pinctrl", a_all),
+ ID("allwinner,sun6i-a31-r-pinctrl", l_2),
+ ID("allwinner,sun8i-a23-r-pinctrl", l_1),
+ ID("allwinner,sun8i-a83t-r-pinctrl", l_1),
+ ID("allwinner,sun8i-h3-r-pinctrl", l_1),
+ ID("allwinner,sun9i-a80-r-pinctrl", l_3),
{ }
};
diff --git a/drivers/gpio/tegra186_gpio.c b/drivers/gpio/tegra186_gpio.c
new file mode 100644
index 0000000000..1c681514db
--- /dev/null
+++ b/drivers/gpio/tegra186_gpio.c
@@ -0,0 +1,288 @@
+/*
+ * Copyright (c) 2010-2016, NVIDIA CORPORATION.
+ * (based on tegra_gpio.c)
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <malloc.h>
+#include <errno.h>
+#include <fdtdec.h>
+#include <asm/io.h>
+#include <asm/bitops.h>
+#include <asm/gpio.h>
+#include <dm/device-internal.h>
+#include <dt-bindings/gpio/gpio.h>
+#include "tegra186_gpio_priv.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct tegra186_gpio_port_data {
+ const char *name;
+ uint32_t offset;
+};
+
+struct tegra186_gpio_ctlr_data {
+ const struct tegra186_gpio_port_data *ports;
+ uint32_t port_count;
+};
+
+struct tegra186_gpio_platdata {
+ const char *name;
+ uint32_t *regs;
+};
+
+static uint32_t *tegra186_gpio_reg(struct udevice *dev, uint32_t reg,
+ uint32_t gpio)
+{
+ struct tegra186_gpio_platdata *plat = dev->platdata;
+ uint32_t index = (reg + (gpio * TEGRA186_GPIO_PER_GPIO_STRIDE)) / 4;
+
+ return &(plat->regs[index]);
+}
+
+static int tegra186_gpio_set_out(struct udevice *dev, unsigned offset,
+ bool output)
+{
+ uint32_t *reg;
+ uint32_t rval;
+
+ reg = tegra186_gpio_reg(dev, TEGRA186_GPIO_OUTPUT_CONTROL, offset);
+ rval = readl(reg);
+ if (output)
+ rval &= ~TEGRA186_GPIO_OUTPUT_CONTROL_FLOATED;
+ else
+ rval |= TEGRA186_GPIO_OUTPUT_CONTROL_FLOATED;
+ writel(rval, reg);
+
+ reg = tegra186_gpio_reg(dev, TEGRA186_GPIO_ENABLE_CONFIG, offset);
+ rval = readl(reg);
+ if (output)
+ rval |= TEGRA186_GPIO_ENABLE_CONFIG_OUT;
+ else
+ rval &= ~TEGRA186_GPIO_ENABLE_CONFIG_OUT;
+ rval |= TEGRA186_GPIO_ENABLE_CONFIG_ENABLE;
+ writel(rval, reg);
+
+ return 0;
+}
+
+static int tegra186_gpio_set_val(struct udevice *dev, unsigned offset, bool val)
+{
+ uint32_t *reg;
+ uint32_t rval;
+
+ reg = tegra186_gpio_reg(dev, TEGRA186_GPIO_OUTPUT_VALUE, offset);
+ rval = readl(reg);
+ if (val)
+ rval |= TEGRA186_GPIO_OUTPUT_VALUE_HIGH;
+ else
+ rval &= ~TEGRA186_GPIO_OUTPUT_VALUE_HIGH;
+ writel(rval, reg);
+
+ return 0;
+}
+
+static int tegra186_gpio_direction_input(struct udevice *dev, unsigned offset)
+{
+ return tegra186_gpio_set_out(dev, offset, false);
+}
+
+static int tegra186_gpio_direction_output(struct udevice *dev, unsigned offset,
+ int value)
+{
+ int ret;
+
+ ret = tegra186_gpio_set_val(dev, offset, value != 0);
+ if (ret)
+ return ret;
+ return tegra186_gpio_set_out(dev, offset, true);
+}
+
+static int tegra186_gpio_get_value(struct udevice *dev, unsigned offset)
+{
+ uint32_t *reg;
+ uint32_t rval;
+
+ reg = tegra186_gpio_reg(dev, TEGRA186_GPIO_ENABLE_CONFIG, offset);
+ rval = readl(reg);
+
+ if (rval & TEGRA186_GPIO_ENABLE_CONFIG_OUT)
+ reg = tegra186_gpio_reg(dev, TEGRA186_GPIO_OUTPUT_VALUE,
+ offset);
+ else
+ reg = tegra186_gpio_reg(dev, TEGRA186_GPIO_INPUT, offset);
+
+ rval = readl(reg);
+ return !!rval;
+}
+
+static int tegra186_gpio_set_value(struct udevice *dev, unsigned offset,
+ int value)
+{
+ return tegra186_gpio_set_val(dev, offset, value != 0);
+}
+
+static int tegra186_gpio_get_function(struct udevice *dev, unsigned offset)
+{
+ uint32_t *reg;
+ uint32_t rval;
+
+ reg = tegra186_gpio_reg(dev, TEGRA186_GPIO_ENABLE_CONFIG, offset);
+ rval = readl(reg);
+ if (rval & TEGRA186_GPIO_ENABLE_CONFIG_OUT)
+ return GPIOF_OUTPUT;
+ else
+ return GPIOF_INPUT;
+}
+
+static int tegra186_gpio_xlate(struct udevice *dev, struct gpio_desc *desc,
+ struct fdtdec_phandle_args *args)
+{
+ int gpio, port, ret;
+
+ gpio = args->args[0];
+ port = gpio / TEGRA186_GPIO_PER_GPIO_COUNT;
+ ret = device_get_child(dev, port, &desc->dev);
+ if (ret)
+ return ret;
+ desc->offset = gpio % TEGRA186_GPIO_PER_GPIO_COUNT;
+ desc->flags = args->args[1] & GPIO_ACTIVE_LOW ? GPIOD_ACTIVE_LOW : 0;
+
+ return 0;
+}
+
+static const struct dm_gpio_ops tegra186_gpio_ops = {
+ .direction_input = tegra186_gpio_direction_input,
+ .direction_output = tegra186_gpio_direction_output,
+ .get_value = tegra186_gpio_get_value,
+ .set_value = tegra186_gpio_set_value,
+ .get_function = tegra186_gpio_get_function,
+ .xlate = tegra186_gpio_xlate,
+};
+
+/**
+ * We have a top-level GPIO device with no actual GPIOs. It has a child device
+ * for each port within the controller.
+ */
+static int tegra186_gpio_bind(struct udevice *parent)
+{
+ struct tegra186_gpio_platdata *parent_plat = parent->platdata;
+ struct tegra186_gpio_ctlr_data *ctlr_data =
+ (struct tegra186_gpio_ctlr_data *)dev_get_driver_data(parent);
+ uint32_t *regs;
+ int port, ret;
+
+ /* If this is a child device, there is nothing to do here */
+ if (parent_plat)
+ return 0;
+
+ regs = (uint32_t *)dev_get_addr_name(parent, "gpio");
+ if (regs == (uint32_t *)FDT_ADDR_T_NONE)
+ return -ENODEV;
+
+ for (port = 0; port < ctlr_data->port_count; port++) {
+ struct tegra186_gpio_platdata *plat;
+ struct udevice *dev;
+
+ plat = calloc(1, sizeof(*plat));
+ if (!plat)
+ return -ENOMEM;
+ plat->name = ctlr_data->ports[port].name;
+ plat->regs = &(regs[ctlr_data->ports[port].offset / 4]);
+
+ ret = device_bind(parent, parent->driver, plat->name, plat,
+ -1, &dev);
+ if (ret)
+ return ret;
+ dev->of_offset = parent->of_offset;
+ }
+
+ return 0;
+}
+
+static int tegra186_gpio_probe(struct udevice *dev)
+{
+ struct tegra186_gpio_platdata *plat = dev->platdata;
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+
+ /* Only child devices have ports */
+ if (!plat)
+ return 0;
+
+ uc_priv->gpio_count = TEGRA186_GPIO_PER_GPIO_COUNT;
+ uc_priv->bank_name = plat->name;
+
+ return 0;
+}
+
+static const struct tegra186_gpio_port_data tegra186_gpio_main_ports[] = {
+ {"A", 0x2000},
+ {"B", 0x3000},
+ {"C", 0x3200},
+ {"D", 0x3400},
+ {"E", 0x2200},
+ {"F", 0x2400},
+ {"G", 0x4200},
+ {"H", 0x1000},
+ {"I", 0x0800},
+ {"J", 0x5000},
+ {"K", 0x5200},
+ {"L", 0x1200},
+ {"M", 0x5600},
+ {"N", 0x0000},
+ {"O", 0x0200},
+ {"P", 0x4000},
+ {"Q", 0x0400},
+ {"R", 0x0a00},
+ {"T", 0x0600},
+ {"X", 0x1400},
+ {"Y", 0x1600},
+ {"BB", 0x2600},
+ {"CC", 0x5400},
+};
+
+static const struct tegra186_gpio_ctlr_data tegra186_gpio_main_data = {
+ .ports = tegra186_gpio_main_ports,
+ .port_count = ARRAY_SIZE(tegra186_gpio_main_ports),
+};
+
+static const struct tegra186_gpio_port_data tegra186_gpio_aon_ports[] = {
+ {"S", 0x0200},
+ {"U", 0x0400},
+ {"V", 0x0800},
+ {"W", 0x0a00},
+ {"Z", 0x0e00},
+ {"AA", 0x0c00},
+ {"EE", 0x0600},
+ {"FF", 0x0000},
+};
+
+static const struct tegra186_gpio_ctlr_data tegra186_gpio_aon_data = {
+ .ports = tegra186_gpio_aon_ports,
+ .port_count = ARRAY_SIZE(tegra186_gpio_aon_ports),
+};
+
+static const struct udevice_id tegra186_gpio_ids[] = {
+ {
+ .compatible = "nvidia,tegra186-gpio",
+ .data = (ulong)&tegra186_gpio_main_data,
+ },
+ {
+ .compatible = "nvidia,tegra186-gpio-aon",
+ .data = (ulong)&tegra186_gpio_aon_data,
+ },
+ { }
+};
+
+U_BOOT_DRIVER(tegra186_gpio) = {
+ .name = "tegra186_gpio",
+ .id = UCLASS_GPIO,
+ .of_match = tegra186_gpio_ids,
+ .bind = tegra186_gpio_bind,
+ .probe = tegra186_gpio_probe,
+ .ops = &tegra186_gpio_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/gpio/tegra186_gpio_priv.h b/drivers/gpio/tegra186_gpio_priv.h
new file mode 100644
index 0000000000..9e85a4343b
--- /dev/null
+++ b/drivers/gpio/tegra186_gpio_priv.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2016, NVIDIA CORPORATION.
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#ifndef _TEGRA186_GPIO_PRIV_H_
+#define _TEGRA186_GPIO_PRIV_H_
+
+/*
+ * For each GPIO, there are a set of registers than affect it, all packed
+ * back-to-back.
+ */
+#define TEGRA186_GPIO_ENABLE_CONFIG 0x00
+#define TEGRA186_GPIO_ENABLE_CONFIG_ENABLE BIT(0)
+#define TEGRA186_GPIO_ENABLE_CONFIG_OUT BIT(1)
+#define TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_TYPE_SHIFT 2
+#define TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_TYPE_MASK 3
+#define TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_TYPE_NONE 0
+#define TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_TYPE_LEVEL 1
+#define TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_TYPE_SINGLE_EDGE 2
+#define TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_TYPE_DOUBLE_EDGE 3
+#define TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_LEVEL_HIGH_RISING BIT(4)
+#define TEGRA186_GPIO_ENABLE_CONFIG_DEBOUNCE_ENABLE BIT(5)
+#define TEGRA186_GPIO_ENABLE_CONFIG_INTERRUPT_ENABLE BIT(6)
+#define TEGRA186_GPIO_ENABLE_CONFIG_TIMESTAMPING_ENABLE BIT(7)
+
+#define TEGRA186_GPIO_DEBOUNCE_THRESHOLD 0x04
+
+#define TEGRA186_GPIO_INPUT 0x08
+
+#define TEGRA186_GPIO_OUTPUT_CONTROL 0x0c
+#define TEGRA186_GPIO_OUTPUT_CONTROL_FLOATED BIT(0)
+
+#define TEGRA186_GPIO_OUTPUT_VALUE 0x10
+#define TEGRA186_GPIO_OUTPUT_VALUE_HIGH 1
+
+#define TEGRA186_GPIO_INTERRUPT_CLEAR 0x14
+
+/*
+ * 8 GPIOs are packed into a port. Their registers appear back-to-back in the
+ * port's address space.
+ */
+#define TEGRA186_GPIO_PER_GPIO_STRIDE 0x20
+#define TEGRA186_GPIO_PER_GPIO_COUNT 8
+
+/*
+ * Per-port registers are packed immediately following all of a port's
+ * per-GPIO registers.
+ */
+#define TEGRA186_GPIO_INTERRUPT_STATUS_G 0x100
+#define TEGRA186_GPIO_INTERRUPT_STATUS_G_STRIDE 4
+#define TEGRA186_GPIO_INTERRUPT_STATUS_G_COUNT 8
+
+/*
+ * The registers for multiple ports are packed together back-to-back to form
+ * the overall controller.
+ */
+#define TEGRA186_GPIO_PER_PORT_STRIDE 0x200
+
+#endif
diff --git a/drivers/gpio/zynq_gpio.c b/drivers/gpio/zynq_gpio.c
index 3a995f610c..4ab2356081 100644
--- a/drivers/gpio/zynq_gpio.c
+++ b/drivers/gpio/zynq_gpio.c
@@ -299,11 +299,33 @@ static int zynq_gpio_direction_output(struct udevice *dev, unsigned gpio,
return 0;
}
+static int zynq_gpio_get_function(struct udevice *dev, unsigned offset)
+{
+ u32 reg;
+ unsigned int bank_num, bank_pin_num;
+ struct zynq_gpio_privdata *priv = dev_get_priv(dev);
+
+ if (check_gpio(offset, dev) < 0)
+ return -1;
+
+ zynq_gpio_get_bank_pin(offset, &bank_num, &bank_pin_num, dev);
+
+ /* set the GPIO pin as output */
+ reg = readl(priv->base + ZYNQ_GPIO_DIRM_OFFSET(bank_num));
+ reg &= BIT(bank_pin_num);
+ if (reg)
+ return GPIOF_OUTPUT;
+ else
+ return GPIOF_INPUT;
+}
+
static const struct dm_gpio_ops gpio_zynq_ops = {
.direction_input = zynq_gpio_direction_input,
.direction_output = zynq_gpio_direction_output,
.get_value = zynq_gpio_get_value,
.set_value = zynq_gpio_set_value,
+ .get_function = zynq_gpio_get_function,
+
};
static const struct udevice_id zynq_gpio_ids[] = {
OpenPOWER on IntegriCloud