dm: gpio: Add driver for MPC85XX GPIO controller
authormario.six@gdsys.cc <mario.six@gdsys.cc>
Wed, 25 May 2016 13:15:20 +0000 (15:15 +0200)
committerYork Sun <york.sun@nxp.com>
Sat, 4 Jun 2016 05:13:24 +0000 (22:13 -0700)
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>
arch/powerpc/include/asm/arch-mpc85xx/gpio.h
arch/powerpc/include/asm/immap_85xx.h
drivers/gpio/Kconfig
drivers/gpio/Makefile
drivers/gpio/mpc85xx_gpio.c [new file with mode: 0644]

index da7352a..41b6677 100644 (file)
@@ -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
index 07d2adf..c045a24 100644 (file)
@@ -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 {
index 93a7e8c..b674a47 100644 (file)
@@ -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
index ddec1ef..21b2cc2 100644 (file)
@@ -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 (file)
index 0000000..17755df
--- /dev/null
@@ -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),
+};