summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormario.six@gdsys.cc <mario.six@gdsys.cc>2016-05-25 15:15:20 +0200
committerYork Sun <york.sun@nxp.com>2016-06-03 22:13:24 -0700
commit07d31f8f98fb350776c78a681ef27fd8ee288acd (patch)
tree724a919f480470a53e8cd03c42561a19e3844172
parentb7707b043ebbf88fe0fb49442db9316ded3a0740 (diff)
dm: gpio: Add driver for MPC85XX GPIO controller
This patch adds a driver for the built-in GPIO controller of the MPC85XX SoC (probably supporting other PowerQUICC III SoCs as well). 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 in configurable to match the actual GPIO count of the SoC (e.g. the 32/32/23 banks of the P1022 SoC). The usual functions of GPIO drivers (setting input/output mode and output value setting) are supported. The driver has been tested on MPC85XX, but it is likely that other PowerQUICC III devices will work as well. Signed-off-by: Mario Six <mario.six@gdsys.cc> Reviewed-by: Simon Glass <sjg@chromium.org> Reviewed-by: York Sun <york.sun@nxp.com>
-rw-r--r--arch/powerpc/include/asm/arch-mpc85xx/gpio.h2
-rw-r--r--arch/powerpc/include/asm/immap_85xx.h2
-rw-r--r--drivers/gpio/Kconfig26
-rw-r--r--drivers/gpio/Makefile1
-rw-r--r--drivers/gpio/mpc85xx_gpio.c187
5 files changed, 218 insertions, 0 deletions
diff --git a/arch/powerpc/include/asm/arch-mpc85xx/gpio.h b/arch/powerpc/include/asm/arch-mpc85xx/gpio.h
index da7352abb27..41b6677bba3 100644
--- a/arch/powerpc/include/asm/arch-mpc85xx/gpio.h
+++ b/arch/powerpc/include/asm/arch-mpc85xx/gpio.h
@@ -14,6 +14,8 @@
#ifndef __ASM_ARCH_MX85XX_GPIO_H
#define __ASM_ARCH_MX85XX_GPIO_H
+#ifndef CONFIG_MPC85XX_GPIO
#include <asm/mpc85xx_gpio.h>
+#endif
#endif
diff --git a/arch/powerpc/include/asm/immap_85xx.h b/arch/powerpc/include/asm/immap_85xx.h
index 07d2adf71f1..c045a24d1ab 100644
--- a/arch/powerpc/include/asm/immap_85xx.h
+++ b/arch/powerpc/include/asm/immap_85xx.h
@@ -265,6 +265,7 @@ typedef struct ccsr_pcix {
#define PIWAR_WRITE_SNOOP 0x00005000
#define PIWAR_MEM_2G 0x0000001e
+#ifndef CONFIG_MPC85XX_GPIO
typedef struct ccsr_gpio {
u32 gpdir;
u32 gpodr;
@@ -273,6 +274,7 @@ typedef struct ccsr_gpio {
u32 gpimr;
u32 gpicr;
} ccsr_gpio_t;
+#endif
/* L2 Cache Registers */
typedef struct ccsr_l2cache {
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 93a7e8c6c23..b674a478248 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -173,4 +173,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).
+
+ The standard functions of input/output mode, and output value setting
+ are supported; the open-drain capability of the controller is not
+ supported yet.
+
+ 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 ddec1ef8dee..21b2cc26776 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -35,6 +35,7 @@ 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/mpc85xx_gpio.c b/drivers/gpio/mpc85xx_gpio.c
new file mode 100644
index 00000000000..17755dfb924
--- /dev/null
+++ b/drivers/gpio/mpc85xx_gpio.c
@@ -0,0 +1,187 @@
+/*
+ * (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 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_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_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),
+};