]> git.ipfire.org Git - thirdparty/u-boot.git/commitdiff
gpio: Add support for microblaze xilinx GPIO
authorMichal Simek <michal.simek@xilinx.com>
Wed, 24 Apr 2013 08:01:20 +0000 (10:01 +0200)
committerMichal Simek <michal.simek@xilinx.com>
Fri, 10 May 2013 05:29:15 +0000 (07:29 +0200)
Microblaze uses gpio which is connected to the system reset.
Currently gpio subsystem wasn't used for it.

Add gpio driver and change Microblaze reset logic to be done
via gpio subsystem.

There are various configurations which Microblaze can have
that's why gpio_alloc/gpio_alloc_dual(for dual channel)
function has been introduced and gpio can be allocated
dynamically.

Adding several gpios IP is also possible and supported.

For listing gpio configuration please use "gpio status" command

This patch also remove one compilation warning:
microblaze-generic.c: In function 'do_reset':
microblaze-generic.c:38:47: warning: operation on '*1073741824u'
 may be undefined [-Wsequence-point]

Signed-off-by: Michal Simek <michal.simek@xilinx.com>
arch/microblaze/include/asm/gpio.h
board/xilinx/microblaze-generic/microblaze-generic.c
drivers/gpio/Makefile
drivers/gpio/xilinx_gpio.c [new file with mode: 0644]
include/configs/microblaze-generic.h
include/configs/petalinux-auto-board.h.template

index 883f4d466509a398bddc2852e8fea0ad6eb674ba..f5cad566861798b495704bcad14e6cc87af57664 100644 (file)
@@ -1,41 +1,15 @@
 #ifndef _ASM_MICROBLAZE_GPIO_H_
 #define _ASM_MICROBLAZE_GPIO_H_
 
-#include <asm/io.h>
+#include <asm-generic/gpio.h>
 
-static inline int gpio_request(unsigned gpio, const char *label)
-{
-       return 0;
-}
+/* Allocation functions */
+extern int gpio_alloc_dual(u32 baseaddr, const char *name, u32 gpio_no0,
+                          u32 gpio_no1);
+extern int gpio_alloc(u32 baseaddr, const char *name, u32 gpio_no);
 
-static inline int gpio_free(unsigned gpio)
-{
-       return 0;
-}
+#define gpio_status()  gpio_info()
+extern void gpio_info(void);
 
-static inline int gpio_direction_input(unsigned gpio)
-{
-       return 0;
-}
-
-static inline int gpio_direction_output(unsigned gpio, int value)
-{
-       return 0;
-}
-
-static inline int gpio_get_value(unsigned gpio)
-{
-       return 0;
-}
-
-static inline int gpio_set_value(unsigned gpio, int value)
-{
-       return 0;
-}
-
-static inline int gpio_is_valid(int number)
-{
-       return 0;
-}
 #endif
 
index 81b2881d6e541db3400b2f7243f3a5dfeee14e50..f7a028e4f6287350bfad6bcebf2a5d290f22eb67 100644 (file)
 #include <asm/processor.h>
 #include <asm/microblaze_intc.h>
 #include <asm/asm.h>
+#include <asm/gpio.h>
+
+#ifdef CONFIG_XILINX_GPIO
+static int reset_pin = -1;
+#endif
 
 int do_reset(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 {
-#ifdef CONFIG_SYS_GPIO_0
-       *((u32 volatile *)(CONFIG_SYS_GPIO_0_ADDR)) =
-           ++(*((u32 volatile *)(CONFIG_SYS_GPIO_0_ADDR)));
+#ifdef CONFIG_XILINX_GPIO
+       if (reset_pin != -1)
+               gpio_direction_output(reset_pin, 1);
 #endif
 
 #ifdef CONFIG_XILINX_TB_WATCHDOG
@@ -52,8 +57,10 @@ int do_reset(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 
 int gpio_init (void)
 {
-#ifdef CONFIG_SYS_GPIO_0
-       *((unsigned long *)(CONFIG_SYS_GPIO_0_ADDR)) = 0xFFFFFFFF;
+#ifdef CONFIG_XILINX_GPIO
+       reset_pin = gpio_alloc(CONFIG_SYS_GPIO_0_ADDR, "reset", 1);
+       if (reset_pin != -1)
+               gpio_request(reset_pin, "reset_pin");
 #endif
        return 0;
 }
index 9df1e2632f774805b635edd317292cb3196fa6cf..830e8e691a4e00ae3d8d649f69133ed0e649738b 100644 (file)
@@ -47,6 +47,7 @@ COBJS-$(CONFIG_OMAP_GPIO)     += omap_gpio.o
 COBJS-$(CONFIG_DB8500_GPIO)    += db8500_gpio.o
 COBJS-$(CONFIG_BCM2835_GPIO)   += bcm2835_gpio.o
 COBJS-$(CONFIG_S3C2440_GPIO)   += s3c2440_gpio.o
+COBJS-$(CONFIG_XILINX_GPIO)    += xilinx_gpio.o
 
 COBJS  := $(COBJS-y)
 SRCS   := $(COBJS:.o=.c)
diff --git a/drivers/gpio/xilinx_gpio.c b/drivers/gpio/xilinx_gpio.c
new file mode 100644 (file)
index 0000000..37fb0c5
--- /dev/null
@@ -0,0 +1,364 @@
+/*
+ * Copyright (c) 2013 Xilinx, Michal Simek
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <malloc.h>
+#include <linux/list.h>
+#include <asm/io.h>
+#include <asm/gpio.h>
+
+static LIST_HEAD(gpio_list);
+
+enum gpio_direction {
+       GPIO_DIRECTION_OUT = 0,
+       GPIO_DIRECTION_IN = 1,
+};
+
+/* Gpio simple map */
+struct gpio_regs {
+       u32 gpiodata;
+       u32 gpiodir;
+};
+
+#define GPIO_NAME_SIZE 10
+
+struct gpio_names {
+       char name[GPIO_NAME_SIZE];
+};
+
+/* Initialized, rxbd_current, rx_first_buf must be 0 after init */
+struct xilinx_gpio_priv {
+       struct gpio_regs *regs;
+       u32 gpio_min;
+       u32 gpio_max;
+       u32 gpiodata_store;
+       char name[GPIO_NAME_SIZE];
+       struct list_head list;
+       struct gpio_names *gpio_name;
+};
+
+/* Store number of allocated gpio pins */
+static u32 xilinx_gpio_max;
+
+/* Get associated gpio controller */
+static struct xilinx_gpio_priv *gpio_get_controller(unsigned gpio)
+{
+       struct list_head *entry;
+       struct xilinx_gpio_priv *priv = NULL;
+
+       list_for_each(entry, &gpio_list) {
+               priv = list_entry(entry, struct xilinx_gpio_priv, list);
+               if (gpio >= priv->gpio_min && gpio <= priv->gpio_max) {
+                       debug("%s: reg: %x, min-max: %d-%d\n", __func__,
+                             (u32)priv->regs, priv->gpio_min, priv->gpio_max);
+                       return priv;
+               }
+       }
+       puts("!!!Can't get gpio controller!!!\n");
+       return NULL;
+}
+
+/* Get gpio pin name if used/setup */
+static char *get_name(unsigned gpio)
+{
+       u32 gpio_priv;
+       struct xilinx_gpio_priv *priv;
+
+       debug("%s\n", __func__);
+
+       priv = gpio_get_controller(gpio);
+       if (priv) {
+               gpio_priv = gpio - priv->gpio_min;
+
+               return *priv->gpio_name[gpio_priv].name ?
+                       priv->gpio_name[gpio_priv].name : "UNKNOWN";
+       }
+       return "UNKNOWN";
+}
+
+/* Get output value */
+static int gpio_get_output_value(unsigned gpio)
+{
+       u32 val, gpio_priv;
+       struct xilinx_gpio_priv *priv = gpio_get_controller(gpio);
+
+       if (priv) {
+               gpio_priv = gpio - priv->gpio_min;
+               val = !!(priv->gpiodata_store & (1 << gpio_priv));
+               debug("%s: reg: %x, gpio_no: %d, dir: %d\n", __func__,
+                     (u32)priv->regs, gpio_priv, val);
+
+               return val;
+       }
+       return -1;
+}
+
+/* Get input value */
+static int gpio_get_input_value(unsigned gpio)
+{
+       u32 val, gpio_priv;
+       struct gpio_regs *regs;
+       struct xilinx_gpio_priv *priv = gpio_get_controller(gpio);
+
+       if (priv) {
+               regs = priv->regs;
+               gpio_priv = gpio - priv->gpio_min;
+               val = readl(&regs->gpiodata);
+               val = !!(val & (1 << gpio_priv));
+               debug("%s: reg: %x, gpio_no: %d, dir: %d\n", __func__,
+                     (u32)priv->regs, gpio_priv, val);
+
+               return val;
+       }
+       return -1;
+}
+
+/* Set gpio direction */
+static int gpio_set_direction(unsigned gpio, enum gpio_direction direction)
+{
+       u32 val, gpio_priv;
+       struct gpio_regs *regs;
+       struct xilinx_gpio_priv *priv = gpio_get_controller(gpio);
+
+       if (priv) {
+               regs = priv->regs;
+               val = readl(&regs->gpiodir);
+
+               gpio_priv = gpio - priv->gpio_min;
+               if (direction == GPIO_DIRECTION_OUT)
+                       val &= ~(1 << gpio_priv);
+               else
+                       val |= 1 << gpio_priv;
+
+               writel(val, &regs->gpiodir);
+               debug("%s: reg: %x, gpio_no: %d, dir: %d\n", __func__,
+                     (u32)priv->regs, gpio_priv, val);
+
+               return 0;
+       }
+
+       return -1;
+}
+
+/* Get gpio direction */
+static int gpio_get_direction(unsigned gpio)
+{
+       u32 val, gpio_priv;
+       struct gpio_regs *regs;
+       struct xilinx_gpio_priv *priv = gpio_get_controller(gpio);
+
+       if (priv) {
+               regs = priv->regs;
+               gpio_priv = gpio - priv->gpio_min;
+               val = readl(&regs->gpiodir);
+               val = !!(val & (1 << gpio_priv));
+               debug("%s: reg: %x, gpio_no: %d, dir: %d\n", __func__,
+                     (u32)priv->regs, gpio_priv, val);
+
+               return val;
+       }
+
+       return -1;
+}
+
+/*
+ * Get input value
+ * for example gpio setup to output only can't get input value
+ * which is breaking gpio toggle command
+ */
+int gpio_get_value(unsigned gpio)
+{
+       u32 val;
+
+       if (gpio_get_direction(gpio) == GPIO_DIRECTION_OUT)
+               val = gpio_get_output_value(gpio);
+       else
+               val = gpio_get_input_value(gpio);
+
+       return val;
+}
+
+/* Set output value */
+static int gpio_set_output_value(unsigned gpio, int value)
+{
+       u32 val, gpio_priv;
+       struct gpio_regs *regs;
+       struct xilinx_gpio_priv *priv = gpio_get_controller(gpio);
+
+       if (priv) {
+               regs = priv->regs;
+               gpio_priv = gpio - priv->gpio_min;
+               val = priv->gpiodata_store;
+               if (value)
+                       val |= 1 << gpio_priv;
+               else
+                       val &= ~(1 << gpio_priv);
+
+               writel(val, &regs->gpiodata);
+               debug("%s: reg: %x, gpio_no: %d, output_val: %d\n", __func__,
+                     (u32)priv->regs, gpio_priv, val);
+               priv->gpiodata_store = val;
+
+               return 0;
+       }
+
+       return -1;
+}
+
+int gpio_set_value(unsigned gpio, int value)
+{
+       if (gpio_get_direction(gpio) == GPIO_DIRECTION_OUT)
+               return gpio_set_output_value(gpio, value);
+
+       return -1;
+}
+
+/* Set GPIO as input */
+int gpio_direction_input(unsigned gpio)
+{
+       debug("%s\n", __func__);
+       return gpio_set_direction(gpio, GPIO_DIRECTION_IN);
+}
+
+/* Setup GPIO as output and set output value */
+int gpio_direction_output(unsigned gpio, int value)
+{
+       int ret = gpio_set_direction(gpio, GPIO_DIRECTION_OUT);
+
+       debug("%s\n", __func__);
+
+       if (ret < 0)
+               return ret;
+
+       return gpio_set_output_value(gpio, value);
+}
+
+/* Show gpio status */
+void gpio_info(void)
+{
+       unsigned gpio;
+
+       struct list_head *entry;
+       struct xilinx_gpio_priv *priv = NULL;
+
+       list_for_each(entry, &gpio_list) {
+               priv = list_entry(entry, struct xilinx_gpio_priv, list);
+               printf("\n%s: %s/%x (%d-%d)\n", __func__, priv->name,
+                      (u32)priv->regs, priv->gpio_min, priv->gpio_max);
+
+               for (gpio = priv->gpio_min; gpio <= priv->gpio_max; gpio++) {
+                       printf("GPIO_%d:\t%s is an ", gpio, get_name(gpio));
+                       if (gpio_get_direction(gpio) == GPIO_DIRECTION_OUT)
+                               printf("OUTPUT value = %d\n",
+                                      gpio_get_output_value(gpio));
+                       else
+                               printf("INPUT value = %d\n",
+                                      gpio_get_input_value(gpio));
+               }
+       }
+}
+
+int gpio_request(unsigned gpio, const char *label)
+{
+       u32 gpio_priv;
+       struct xilinx_gpio_priv *priv;
+
+       if (gpio >= xilinx_gpio_max)
+               return -EINVAL;
+
+       priv = gpio_get_controller(gpio);
+       if (priv) {
+               gpio_priv = gpio - priv->gpio_min;
+
+               if (label != NULL) {
+                       strncpy(priv->gpio_name[gpio_priv].name, label,
+                               GPIO_NAME_SIZE);
+                       priv->gpio_name[gpio_priv].name[GPIO_NAME_SIZE - 1] =
+                                       '\0';
+               }
+               return 0;
+       }
+
+       return -1;
+}
+
+int gpio_free(unsigned gpio)
+{
+       u32 gpio_priv;
+       struct xilinx_gpio_priv *priv;
+
+       if (gpio >= xilinx_gpio_max)
+               return -EINVAL;
+
+       priv = gpio_get_controller(gpio);
+       if (priv) {
+               gpio_priv = gpio - priv->gpio_min;
+               priv->gpio_name[gpio_priv].name[0] = '\0';
+
+               /* Do nothing here */
+               return 0;
+       }
+
+       return -1;
+}
+
+int gpio_alloc(u32 baseaddr, const char *name, u32 gpio_no)
+{
+       struct xilinx_gpio_priv *priv;
+
+       priv = calloc(1, sizeof(struct xilinx_gpio_priv));
+
+       /* Setup gpio name */
+       if (name != NULL) {
+               strncpy(priv->name, name, GPIO_NAME_SIZE);
+               priv->name[GPIO_NAME_SIZE - 1] = '\0';
+       }
+       priv->regs = (struct gpio_regs *)baseaddr;
+
+       priv->gpio_min = xilinx_gpio_max;
+       xilinx_gpio_max = priv->gpio_min + gpio_no;
+       priv->gpio_max = xilinx_gpio_max - 1;
+
+       priv->gpio_name = calloc(gpio_no, sizeof(struct gpio_names));
+
+       INIT_LIST_HEAD(&priv->list);
+       list_add_tail(&priv->list, &gpio_list);
+
+       printf("%s: Add %s (%d-%d)\n", __func__, name,
+              priv->gpio_min, priv->gpio_max);
+
+       /* Return the first gpio allocated for this device */
+       return priv->gpio_min;
+}
+
+/* Dual channel gpio is one IP with two independent channels */
+int gpio_alloc_dual(u32 baseaddr, const char *name, u32 gpio_no0, u32 gpio_no1)
+{
+       int ret;
+
+       ret = gpio_alloc(baseaddr, name, gpio_no0);
+       gpio_alloc(baseaddr + 8, strcat((char *)name, "_1"), gpio_no1);
+
+       /* Return the first gpio allocated for this device */
+       return ret;
+}
index 5657582ead02d795261e55aa30e5379fbab57076..738b258da295b1e1a4cf2554d8fb9678acb6f2c3 100644 (file)
 
 #undef ET_DEBUG
 
+/* gpio */
+#ifdef XILINX_GPIO_BASEADDR
+# define CONFIG_XILINX_GPIO
+# define CONFIG_SYS_GPIO_0_ADDR                XILINX_GPIO_BASEADDR
+#endif
 
 /* interrupt controller */
 #ifdef XILINX_INTC_BASEADDR
 /* FIXME: hack for zynq */
 #define CONFIG_CMD_IRQ
 #define CONFIG_CMD_ECHO
+#define CONFIG_CMD_GPIO
 
 #undef CONFIG_CMD_NFS
 #undef CONFIG_CMD_JFFS2
index 9e15ad07091fe9bfdf3c708840a98af817a14b2d..491d27925b92b114ba69e7d9f2688d8088e863a0 100644 (file)
 
 /* gpio */
 #ifdef XILINX_GPIO_BASEADDR
+       #define CONFIG_XILINX_GPIO
        #define CONFIG_SYS_GPIO_0               1
        #define CONFIG_SYS_GPIO_0_ADDR          XILINX_GPIO_BASEADDR
 #endif