gpio: Add driver for Altera's PIO core
authorJoachim Foerster <joachim.foerster@missinglinkelectronics.com>
Fri, 21 Oct 2011 13:48:50 +0000 (15:48 +0200)
committerThomas Chou <thomas@wytron.com.tw>
Fri, 28 Oct 2011 01:50:49 +0000 (09:50 +0800)
This driver may handle multiple PIO cores and thus needs to be
setup by calling the altera_pio_init() function within the early
board setup routine.

The driver comes with some extras, see below the copyleft header.

Signed-off-by: Joachim Foerster <joachim.foerster@missinglinkelectronics.com>
Tested-by: Thomas Chou <thomas@wytron.com.tw>
Signed-off-by: Thomas Chou <thomas@wytron.com.tw>
arch/nios2/include/asm/gpio.h
board/altera/nios2-generic/custom_fpga.h
board/altera/nios2-generic/nios2-generic.c
drivers/gpio/Makefile
drivers/gpio/altera_pio.c [new file with mode: 0644]

index 4b21c8f..908381f 100644 (file)
@@ -5,8 +5,8 @@
  * bit[0] data
  * bit[1] output enable
  *
- * when CONFIG_SYS_GPIO_BASE is not defined, board may provide
- * its own driver.
+ * When CONFIG_SYS_GPIO_BASE is not defined, the board may either
+ * provide its own driver or the altera_pio driver may be used.
  *
  * Copyright (C) 2010 Thomas Chou <thomas@wytron.com.tw>
  *
@@ -58,6 +58,15 @@ static inline int gpio_is_valid(int number)
        return ((unsigned)number) < CONFIG_SYS_GPIO_WIDTH;
 }
 #else
+#ifdef CONFIG_ALTERA_PIO
+extern int altera_pio_init(u32 base, u8 width, char iot,
+                          u32 rstval, u32 negmask,
+                          const char *label);
+
+extern void altera_pio_info(void);
+#define gpio_status() altera_pio_info()
+#endif
+
 extern int gpio_request(unsigned gpio, const char *label);
 extern int gpio_free(unsigned gpio);
 extern int gpio_direction_input(unsigned gpio);
index f7f3853..fd3ec9a 100644 (file)
@@ -51,6 +51,7 @@
 /* led_pio.s1 is a altera_avalon_pio */
 #define LED_PIO_BASE 0x82120870
 #define LED_PIO_WIDTH 8
+#define LED_PIO_RSTVAL 0x0
 
 /* high_res_timer.s1 is a altera_avalon_timer */
 #define CONFIG_SYS_TIMER_BASE 0x82120820
index 49ef80d..0f88275 100644 (file)
@@ -26,6 +26,7 @@
 #include <netdev.h>
 #include <mtd/cfi_flash.h>
 #include <asm/io.h>
+#include <asm/gpio.h>
 
 void text_base_hook(void); /* nop hook for text_base.S */
 
@@ -43,6 +44,13 @@ void early_flash_cmd_reset(void)
 int board_early_init_f(void)
 {
        text_base_hook();
+#ifdef CONFIG_ALTERA_PIO
+#ifdef LED_PIO_BASE
+       altera_pio_init(LED_PIO_BASE, LED_PIO_WIDTH, 'o',
+                       LED_PIO_RSTVAL, (1 << LED_PIO_WIDTH) - 1,
+                       "led");
+#endif
+#endif
 #if defined(CONFIG_ENV_IS_IN_FLASH) && defined(CONFIG_ENV_ADDR)
        early_flash_cmd_reset();
 #endif
index f505813..b5264d1 100644 (file)
@@ -35,6 +35,7 @@ COBJS-$(CONFIG_PCA9698)               += pca9698.o
 COBJS-$(CONFIG_S5P)            += s5p_gpio.o
 COBJS-$(CONFIG_TEGRA2_GPIO)    += tegra2_gpio.o
 COBJS-$(CONFIG_DA8XX_GPIO)     += da8xx_gpio.o
+COBJS-$(CONFIG_ALTERA_PIO)     += altera_pio.o
 
 COBJS  := $(COBJS-y)
 SRCS   := $(COBJS:.o=.c)
diff --git a/drivers/gpio/altera_pio.c b/drivers/gpio/altera_pio.c
new file mode 100644 (file)
index 0000000..fb03760
--- /dev/null
@@ -0,0 +1,299 @@
+/*
+ * Driver for Altera's PIO ip core
+ *
+ * Copyright (C) 2011  Missing Link Electronics
+ *                     Joachim Foerster <joachim@missinglinkelectronics.com>
+ *
+ * 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
+ *
+ * To use this driver, in your board's config. header:
+ * #define CONFIG_ALTERA_PIO
+ * #define CONFIG_SYS_ALTERA_PIO_NUM <number-of-pio-cores>
+ * #define CONFIG_SYS_ALTERA_PIO_GPIO_NUM <total-number-of-gpios>
+ * And in your board's early setup routine:
+ * altera_pio_init(<baseaddr>, <width>, 'i'|'o'|'t',
+ *                 <reset-value>, <neg-mask>, "label");
+ *  - 'i'|'o'|'t': PIO is input-only/output-only/tri-state
+ *  - <reset-value>: for correct initial status display, output-only
+ *  - <neg-mask> is meant to be used to in cases of active-low
+ *    GPIOs, such as LEDs and buttons (on/pressed == 0). Each bit
+ *    which is 1 in <neg-mask> inverts the corresponding GPIO's value
+ *    before set/after get. So: gpio_set_value(gpio, 1) => LED on .
+ *
+ * Do NOT define CONFIG_SYS_GPIO_BASE !
+ *
+ * Optionally, in your board's config. header:
+ * - To force a GPIO numbering scheme like in Linux ...
+ * #define CONFIG_GPIO_DOWNTO_NUMBERING
+ * ... starting with 255 (default)
+ * #define CONFIG_GPIO_DOWNTO_MAX 255
+ */
+#include <common.h>
+#include <asm/io.h>
+#include <asm/gpio.h>
+
+#ifdef CONFIG_GPIO_DOWNTO_NUMBERING
+#ifndef CONFIG_GPIO_DOWNTO_MAX
+#define CONFIG_GPIO_DOWNTO_MAX 255
+#endif
+#endif
+
+#define ALTERA_PIO_DATA                0x0
+#define ALTERA_PIO_DIR         0x4
+
+#define GPIO_LABEL_SIZE                9
+
+
+static struct altera_pio {
+       u32 base;
+       u8 width;
+       char iot;
+       u32 negmask;
+       u32 sh_data;
+       u32 sh_dir;
+       int gidx;
+       char label[GPIO_LABEL_SIZE];
+} pios[CONFIG_SYS_ALTERA_PIO_NUM];
+
+static int pio_num;
+
+static struct altera_pio_gpio {
+       unsigned num;
+       struct altera_pio *pio;
+       char reqlabel[GPIO_LABEL_SIZE];
+} gpios[CONFIG_SYS_ALTERA_PIO_GPIO_NUM];
+
+static int pio_gpio_num;
+
+
+static int altera_pio_gidx(unsigned gpio)
+{
+       int i;
+
+       for (i = 0; i < pio_gpio_num; ++i) {
+               if (gpio == gpios[i].num)
+                       break;
+       }
+       if (i >= pio_gpio_num)
+               return -1;
+       return i;
+}
+
+static struct altera_pio *altera_pio_get_and_mask(unsigned gpio, u32 *mask)
+{
+       int gidx = altera_pio_gidx(gpio);
+       if (gidx < 0)
+               return NULL;
+       if (mask)
+               *mask = 1 << (gidx - gpios[gidx].pio->gidx);
+       return gpios[gidx].pio;
+}
+
+#define altera_pio_use_gidx(_gidx, _reqlabel) \
+       { strncpy(gpios[_gidx].reqlabel, _reqlabel, GPIO_LABEL_SIZE); }
+#define altera_pio_unuse_gidx(_gidx) { gpios[_gidx].reqlabel[0] = '\0'; }
+#define altera_pio_is_gidx_used(_gidx) (gpios[_gidx].reqlabel[0] != '\0')
+
+static int altera_pio_gpio_init(struct altera_pio *pio, u8 width)
+{
+       u8 gidx = pio_gpio_num;
+       int i;
+
+       if (!width)
+               return -1;
+       if ((pio_gpio_num + width) > CONFIG_SYS_ALTERA_PIO_GPIO_NUM)
+               return -1;
+
+       for (i = 0; i < width; ++i) {
+#ifdef CONFIG_GPIO_DOWNTO_NUMBERING
+               gpios[pio_gpio_num + i].num = \
+                       CONFIG_GPIO_DOWNTO_MAX + 1 - gidx - width + i;
+#else
+               gpios[pio_gpio_num + i].num = pio_gpio_num + i;
+#endif
+               gpios[pio_gpio_num + i].pio = pio;
+               altera_pio_unuse_gidx(pio_gpio_num + i);
+       }
+       pio_gpio_num += width;
+       return gidx;
+}
+
+int altera_pio_init(u32 base, u8 width, char iot, u32 rstval, u32 negmask,
+                const char *label)
+{
+       if (pio_num >= CONFIG_SYS_ALTERA_PIO_NUM)
+               return -1;
+
+       pios[pio_num].base = base;
+       pios[pio_num].width = width;
+       pios[pio_num].iot = iot;
+       switch (iot) {
+       case 'i':
+               /* input only */
+               pios[pio_num].sh_dir = 0;
+               pios[pio_num].sh_data = readl(base + ALTERA_PIO_DATA);
+               break;
+       case 'o':
+               /* output only */
+               pios[pio_num].sh_dir = 0xffffffff & ((1 << width) - 1);
+               pios[pio_num].sh_data = rstval;
+               break;
+       case 't':
+               /* bidir, tri-state */
+               pios[pio_num].sh_dir = readl(base + ALTERA_PIO_DIR);
+               pios[pio_num].sh_data = readl(base + ALTERA_PIO_DATA);
+               break;
+       default:
+               return -1;
+       }
+       pios[pio_num].negmask = negmask & ((1 << width) - 1);
+       pios[pio_num].gidx = altera_pio_gpio_init(&pios[pio_num], width);
+       if (pios[pio_num].gidx < 0)
+               return -1;
+       strncpy(pios[pio_num].label, label, GPIO_LABEL_SIZE);
+       return pio_num++;
+}
+
+void altera_pio_info(void)
+{
+       int i;
+       int j;
+       int gidx;
+       u32 mask;
+
+       for (i = 0; i < pio_num; ++i) {
+               printf("Altera PIO % 2d, @0x%08x, "
+                       "width: %u, label: %s\n",
+                      i, pios[i].base, pios[i].width, pios[i].label);
+               gidx = pios[i].gidx;
+               for (j = gidx; j < (gidx + pios[i].width); ++j) {
+                       mask = 1 << (j - gidx);
+                       printf("\tGPIO % 4d: %s %s [%c] %s\n",
+                               gpios[j].num,
+                               gpios[j].pio->sh_dir & mask ? "out" : " in",
+                               gpio_get_value(gpios[j].num) ? "set" : "clr",
+                               altera_pio_is_gidx_used(j) ? 'x' : ' ',
+                               gpios[j].reqlabel);
+               }
+       }
+}
+
+
+int gpio_request(unsigned gpio, const char *label)
+{
+       int gidx = altera_pio_gidx(gpio);
+       if (gidx < 0)
+               return gidx;
+       if (altera_pio_is_gidx_used(gidx))
+               return -1;
+
+       altera_pio_use_gidx(gidx, label);
+       return 0;
+}
+
+int gpio_free(unsigned gpio)
+{
+       int gidx = altera_pio_gidx(gpio);
+       if (gidx < 0)
+               return gidx;
+       if (!altera_pio_is_gidx_used(gidx))
+               return -1;
+
+       altera_pio_unuse_gidx(gidx);
+       return 0;
+}
+
+int gpio_direction_input(unsigned gpio)
+{
+       u32 mask;
+       struct altera_pio *pio;
+
+       pio = altera_pio_get_and_mask(gpio, &mask);
+       if (!pio)
+               return -1;
+       if (pio->iot == 'o')
+               return -1;
+
+       writel(pio->sh_dir &= ~mask, pio->base + ALTERA_PIO_DIR);
+       return 0;
+}
+
+int gpio_direction_output(unsigned gpio, int value)
+{
+       u32 mask;
+       struct altera_pio *pio;
+
+       pio = altera_pio_get_and_mask(gpio, &mask);
+       if (!pio)
+               return -1;
+       if (pio->iot == 'i')
+               return -1;
+
+       value = (pio->negmask & mask) ? !value : value;
+       if (value)
+               pio->sh_data |= mask;
+       else
+               pio->sh_data &= ~mask;
+       writel(pio->sh_data, pio->base + ALTERA_PIO_DATA);
+       writel(pio->sh_dir |= mask, pio->base + ALTERA_PIO_DIR);
+       return 0;
+}
+
+int gpio_get_value(unsigned gpio)
+{
+       u32 mask;
+       struct altera_pio *pio;
+       u32 val;
+
+       pio = altera_pio_get_and_mask(gpio, &mask);
+       if (!pio)
+               return -1;
+
+       if ((pio->sh_dir & mask) || (pio->iot == 'o'))
+               val = pio->sh_data & mask;
+       else
+               val = readl(pio->base + ALTERA_PIO_DATA) & mask;
+       return (pio->negmask & mask) ? !val : val;
+}
+
+void gpio_set_value(unsigned gpio, int value)
+{
+       u32 mask;
+       struct altera_pio *pio;
+
+       pio = altera_pio_get_and_mask(gpio, &mask);
+       if (!pio)
+               return;
+       if (pio->iot == 'i')
+               return;
+
+       value = (pio->negmask & mask) ? !value : value;
+       if (value)
+               pio->sh_data |= mask;
+       else
+               pio->sh_data &= ~mask;
+       writel(pio->sh_data, pio->base + ALTERA_PIO_DATA);
+       return;
+}
+
+int gpio_is_valid(int number)
+{
+       int gidx = altera_pio_gidx(number);
+
+       if (gidx < 0)
+               return 1;
+       return 0;
+}