Blackfin: bf538: add support for extended GPIO banks
authorMichael Hennerich <michael.hennerich@analog.com>
Mon, 28 Sep 2009 12:23:41 +0000 (12:23 +0000)
committerMike Frysinger <vapier@gentoo.org>
Tue, 15 Dec 2009 05:14:05 +0000 (00:14 -0500)
The GPIOs on ports C/D/E on the BF538/BF539 do not behave the same way as
the other ports on the part and the same way as all other Blackfin parts.
The MMRs are programmed slightly different and they cannot be used to
generate interrupts or wakeup a sleeping system.  Since these guys don't
fit into the existing code, create a simple gpiolib driver for them.

Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
arch/blackfin/include/asm/gpio.h
arch/blackfin/kernel/bfin_gpio.c
arch/blackfin/mach-bf538/Makefile
arch/blackfin/mach-bf538/ext-gpio.c [new file with mode: 0644]
arch/blackfin/mach-bf538/include/mach/defBF539.h
arch/blackfin/mach-bf538/include/mach/gpio.h
arch/blackfin/mach-bf538/include/mach/portmux.h
arch/blackfin/mach-common/dpmc_modes.S

index 5b44d05..539468a 100644 (file)
@@ -159,6 +159,11 @@ struct gpio_port_t {
 };
 #endif
 
+#ifdef BFIN_SPECIAL_GPIO_BANKS
+void bfin_special_gpio_free(unsigned gpio);
+int bfin_special_gpio_request(unsigned gpio, const char *label);
+#endif
+
 #ifdef CONFIG_PM
 
 unsigned int bfin_pm_standby_setup(void);
index c4161e0..a174596 100644 (file)
@@ -100,6 +100,12 @@ u8 pmux_offset[][16] = {
 };
 # endif
 
+#elif defined(BF538_FAMILY)
+static unsigned short * const port_fer[] = {
+       (unsigned short *) PORTCIO_FER,
+       (unsigned short *) PORTDIO_FER,
+       (unsigned short *) PORTEIO_FER,
+};
 #endif
 
 static unsigned short reserved_gpio_map[GPIO_BANK_NUM];
@@ -163,6 +169,27 @@ static int cmp_label(unsigned short ident, const char *label)
 
 static void port_setup(unsigned gpio, unsigned short usage)
 {
+#if defined(BF538_FAMILY)
+       /*
+        * BF538/9 Port C,D and E are special.
+        * Inverted PORT_FER polarity on CDE and no PORF_FER on F
+        * Regular PORT F GPIOs are handled here, CDE are exclusively
+        * managed by GPIOLIB
+        */
+
+       if (gpio < MAX_BLACKFIN_GPIOS || gpio >= MAX_RESOURCES)
+               return;
+
+       gpio -= MAX_BLACKFIN_GPIOS;
+
+       if (usage == GPIO_USAGE)
+               *port_fer[gpio_bank(gpio)] |= gpio_bit(gpio);
+       else
+               *port_fer[gpio_bank(gpio)] &= ~gpio_bit(gpio);
+       SSYNC();
+       return;
+#endif
+
        if (check_gpio(gpio))
                return;
 
@@ -981,6 +1008,76 @@ void bfin_gpio_free(unsigned gpio)
 }
 EXPORT_SYMBOL(bfin_gpio_free);
 
+#ifdef BFIN_SPECIAL_GPIO_BANKS
+static unsigned short reserved_special_gpio_map[gpio_bank(MAX_RESOURCES)];
+
+int bfin_special_gpio_request(unsigned gpio, const char *label)
+{
+       unsigned long flags;
+
+       local_irq_save_hw(flags);
+
+       /*
+        * Allow that the identical GPIO can
+        * be requested from the same driver twice
+        * Do nothing and return -
+        */
+
+       if (cmp_label(gpio, label) == 0) {
+               local_irq_restore_hw(flags);
+               return 0;
+       }
+
+       if (unlikely(reserved_special_gpio_map[gpio_bank(gpio)] & gpio_bit(gpio))) {
+               local_irq_restore_hw(flags);
+               printk(KERN_ERR "bfin-gpio: GPIO %d is already reserved by %s !\n",
+                      gpio, get_label(gpio));
+
+               return -EBUSY;
+       }
+       if (unlikely(reserved_peri_map[gpio_bank(gpio)] & gpio_bit(gpio))) {
+               local_irq_restore_hw(flags);
+               printk(KERN_ERR
+                      "bfin-gpio: GPIO %d is already reserved as Peripheral by %s !\n",
+                      gpio, get_label(gpio));
+
+               return -EBUSY;
+       }
+
+       reserved_special_gpio_map[gpio_bank(gpio)] |= gpio_bit(gpio);
+       reserved_peri_map[gpio_bank(gpio)] |= gpio_bit(gpio);
+
+       set_label(gpio, label);
+       local_irq_restore_hw(flags);
+       port_setup(gpio, GPIO_USAGE);
+
+       return 0;
+}
+EXPORT_SYMBOL(bfin_special_gpio_request);
+
+void bfin_special_gpio_free(unsigned gpio)
+{
+       unsigned long flags;
+
+       might_sleep();
+
+       local_irq_save_hw(flags);
+
+       if (unlikely(!(reserved_special_gpio_map[gpio_bank(gpio)] & gpio_bit(gpio)))) {
+               gpio_error(gpio);
+               local_irq_restore_hw(flags);
+               return;
+       }
+
+       reserved_special_gpio_map[gpio_bank(gpio)] &= ~gpio_bit(gpio);
+       reserved_peri_map[gpio_bank(gpio)] &= ~gpio_bit(gpio);
+       set_label(gpio, "free");
+       local_irq_restore_hw(flags);
+}
+EXPORT_SYMBOL(bfin_special_gpio_free);
+#endif
+
+
 int bfin_gpio_irq_request(unsigned gpio, const char *label)
 {
        unsigned long flags;
index 8cd2719..c0be54f 100644 (file)
@@ -3,3 +3,4 @@
 #
 
 obj-y := ints-priority.o dma.o
+obj-$(CONFIG_GPIOLIB)  += ext-gpio.o
diff --git a/arch/blackfin/mach-bf538/ext-gpio.c b/arch/blackfin/mach-bf538/ext-gpio.c
new file mode 100644 (file)
index 0000000..180b125
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * GPIOLIB interface for BF538/9 PORT C, D, and E GPIOs
+ *
+ * Copyright 2009 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <asm/blackfin.h>
+#include <asm/gpio.h>
+#include <asm/portmux.h>
+
+#define DEFINE_REG(reg, off) \
+static inline u16 read_##reg(void __iomem *port) \
+       { return bfin_read16(port + off); } \
+static inline void write_##reg(void __iomem *port, u16 v) \
+       { bfin_write16(port + off, v); }
+
+DEFINE_REG(PORTIO, 0x00)
+DEFINE_REG(PORTIO_CLEAR, 0x10)
+DEFINE_REG(PORTIO_SET, 0x20)
+DEFINE_REG(PORTIO_DIR, 0x40)
+DEFINE_REG(PORTIO_INEN, 0x50)
+
+static void __iomem *gpio_chip_to_mmr(struct gpio_chip *chip)
+{
+       switch (chip->base) {
+       default: /* not really needed, but keeps gcc happy */
+       case GPIO_PC0: return (void __iomem *)PORTCIO;
+       case GPIO_PD0: return (void __iomem *)PORTDIO;
+       case GPIO_PE0: return (void __iomem *)PORTEIO;
+       }
+}
+
+static int bf538_gpio_get_value(struct gpio_chip *chip, unsigned gpio)
+{
+       void __iomem *port = gpio_chip_to_mmr(chip);
+       return !!(read_PORTIO(port) & (1u << gpio));
+}
+
+static void bf538_gpio_set_value(struct gpio_chip *chip, unsigned gpio, int value)
+{
+       void __iomem *port = gpio_chip_to_mmr(chip);
+       if (value)
+               write_PORTIO_SET(port, (1u << gpio));
+       else
+               write_PORTIO_CLEAR(port, (1u << gpio));
+}
+
+static int bf538_gpio_direction_input(struct gpio_chip *chip, unsigned gpio)
+{
+       void __iomem *port = gpio_chip_to_mmr(chip);
+       write_PORTIO_DIR(port, read_PORTIO_DIR(port) & ~(1u << gpio));
+       write_PORTIO_INEN(port, read_PORTIO_INEN(port) | (1u << gpio));
+       return 0;
+}
+
+static int bf538_gpio_direction_output(struct gpio_chip *chip, unsigned gpio, int value)
+{
+       void __iomem *port = gpio_chip_to_mmr(chip);
+       write_PORTIO_INEN(port, read_PORTIO_INEN(port) & ~(1u << gpio));
+       bf538_gpio_set_value(port, gpio, value);
+       write_PORTIO_DIR(port, read_PORTIO_DIR(port) | (1u << gpio));
+       return 0;
+}
+
+static int bf538_gpio_request(struct gpio_chip *chip, unsigned gpio)
+{
+       return bfin_special_gpio_request(chip->base + gpio, chip->label);
+}
+
+static void bf538_gpio_free(struct gpio_chip *chip, unsigned gpio)
+{
+       return bfin_special_gpio_free(chip->base + gpio);
+}
+
+/* We don't set the irq fields as these banks cannot generate interrupts */
+
+static struct gpio_chip bf538_portc_chip = {
+       .label = "GPIO-PC",
+       .direction_input = bf538_gpio_direction_input,
+       .get = bf538_gpio_get_value,
+       .direction_output = bf538_gpio_direction_output,
+       .set = bf538_gpio_set_value,
+       .request = bf538_gpio_request,
+       .free = bf538_gpio_free,
+       .base = GPIO_PC0,
+       .ngpio = GPIO_PC9 - GPIO_PC0 + 1,
+};
+
+static struct gpio_chip bf538_portd_chip = {
+       .label = "GPIO-PD",
+       .direction_input = bf538_gpio_direction_input,
+       .get = bf538_gpio_get_value,
+       .direction_output = bf538_gpio_direction_output,
+       .set = bf538_gpio_set_value,
+       .request = bf538_gpio_request,
+       .free = bf538_gpio_free,
+       .base = GPIO_PD0,
+       .ngpio = GPIO_PD13 - GPIO_PD0 + 1,
+};
+
+static struct gpio_chip bf538_porte_chip = {
+       .label = "GPIO-PE",
+       .direction_input = bf538_gpio_direction_input,
+       .get = bf538_gpio_get_value,
+       .direction_output = bf538_gpio_direction_output,
+       .set = bf538_gpio_set_value,
+       .request = bf538_gpio_request,
+       .free = bf538_gpio_free,
+       .base = GPIO_PE0,
+       .ngpio = GPIO_PE15 - GPIO_PE0 + 1,
+};
+
+static int __init bf538_extgpio_setup(void)
+{
+       return gpiochip_add(&bf538_portc_chip) |
+               gpiochip_add(&bf538_portd_chip) |
+               gpiochip_add(&bf538_porte_chip);
+}
+arch_initcall(bf538_extgpio_setup);
index 5f6c34d..1f1aeab 100644 (file)
 /* General-Purpose Ports  (0xFFC01500 -        0xFFC015FF)      */
 
 /* GPIO        Port C Register Names */
-#define        GPIO_C_CNFG                     0xFFC01500      /* GPIO Pin Port C Configuration Register */
-#define        GPIO_C_D                        0xFFC01510      /* GPIO Pin Port C Data Register */
-#define        GPIO_C_C                        0xFFC01520      /* Clear GPIO Pin Port C Register */
-#define        GPIO_C_S                        0xFFC01530      /* Set GPIO Pin Port C Register */
-#define        GPIO_C_T                        0xFFC01540      /* Toggle GPIO Pin Port C Register */
-#define        GPIO_C_DIR                      0xFFC01550      /* GPIO Pin Port C Direction Register */
-#define        GPIO_C_INEN                     0xFFC01560      /* GPIO Pin Port C Input Enable Register */
+#define PORTCIO_FER                    0xFFC01500      /* GPIO Pin Port C Configuration Register */
+#define PORTCIO                                0xFFC01510      /* GPIO Pin Port C Data Register */
+#define PORTCIO_CLEAR                  0xFFC01520      /* Clear GPIO Pin Port C Register */
+#define PORTCIO_SET                    0xFFC01530      /* Set GPIO Pin Port C Register */
+#define PORTCIO_TOGGLE                 0xFFC01540      /* Toggle GPIO Pin Port C Register */
+#define PORTCIO_DIR                    0xFFC01550      /* GPIO Pin Port C Direction Register */
+#define PORTCIO_INEN                   0xFFC01560      /* GPIO Pin Port C Input Enable Register */
 
 /* GPIO        Port D Register Names */
-#define        GPIO_D_CNFG                     0xFFC01504      /* GPIO Pin Port D Configuration Register */
-#define        GPIO_D_D                        0xFFC01514      /* GPIO Pin Port D Data Register */
-#define        GPIO_D_C                        0xFFC01524      /* Clear GPIO Pin Port D Register */
-#define        GPIO_D_S                        0xFFC01534      /* Set GPIO Pin Port D Register */
-#define        GPIO_D_T                        0xFFC01544      /* Toggle GPIO Pin Port D Register */
-#define        GPIO_D_DIR                      0xFFC01554      /* GPIO Pin Port D Direction Register */
-#define        GPIO_D_INEN                     0xFFC01564      /* GPIO Pin Port D Input Enable Register */
+#define PORTDIO_FER                    0xFFC01504      /* GPIO Pin Port D Configuration Register */
+#define PORTDIO                                0xFFC01514      /* GPIO Pin Port D Data Register */
+#define PORTDIO_CLEAR                  0xFFC01524      /* Clear GPIO Pin Port D Register */
+#define PORTDIO_SET                    0xFFC01534      /* Set GPIO Pin Port D Register */
+#define PORTDIO_TOGGLE                 0xFFC01544      /* Toggle GPIO Pin Port D Register */
+#define PORTDIO_DIR                    0xFFC01554      /* GPIO Pin Port D Direction Register */
+#define PORTDIO_INEN                   0xFFC01564      /* GPIO Pin Port D Input Enable Register */
 
 /* GPIO        Port E Register Names */
-#define        GPIO_E_CNFG                     0xFFC01508      /* GPIO Pin Port E Configuration Register */
-#define        GPIO_E_D                        0xFFC01518      /* GPIO Pin Port E Data Register */
-#define        GPIO_E_C                        0xFFC01528      /* Clear GPIO Pin Port E Register */
-#define        GPIO_E_S                        0xFFC01538      /* Set GPIO Pin Port E Register */
-#define        GPIO_E_T                        0xFFC01548      /* Toggle GPIO Pin Port E Register */
-#define        GPIO_E_DIR                      0xFFC01558      /* GPIO Pin Port E Direction Register */
-#define        GPIO_E_INEN                     0xFFC01568      /* GPIO Pin Port E Input Enable Register */
+#define PORTEIO_FER                    0xFFC01508      /* GPIO Pin Port E Configuration Register */
+#define PORTEIO                                0xFFC01518      /* GPIO Pin Port E Data Register */
+#define PORTEIO_CLEAR                  0xFFC01528      /* Clear GPIO Pin Port E Register */
+#define PORTEIO_SET                    0xFFC01538      /* Set GPIO Pin Port E Register */
+#define PORTEIO_TOGGLE                 0xFFC01548      /* Toggle GPIO Pin Port E Register */
+#define PORTEIO_DIR                    0xFFC01558      /* GPIO Pin Port E Direction Register */
+#define PORTEIO_INEN                   0xFFC01568      /* GPIO Pin Port E Input Enable Register */
 
 /* DMA Controller 1 Traffic Control Registers (0xFFC01B00 - 0xFFC01BFF) */
 
index 295c78a..0c346fb 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 Analog Devices Inc.
+ * Copyright (C) 2008-2009 Analog Devices Inc.
  * Licensed under the GPL-2 or later.
  */
 
@@ -7,11 +7,8 @@
 #ifndef _MACH_GPIO_H_
 #define _MACH_GPIO_H_
 
-       /* FIXME:
-        * For now only support PORTF GPIOs.
-        * PORT C,D and E are for peripheral usage only
-        */
 #define MAX_BLACKFIN_GPIOS 16
+#define BFIN_SPECIAL_GPIO_BANKS 3
 
 #define        GPIO_PF0        0       /* PF */
 #define        GPIO_PF1        1
index 6121cf8..0083ba1 100644 (file)
@@ -7,7 +7,7 @@
 #ifndef _MACH_PORTMUX_H_
 #define _MACH_PORTMUX_H_
 
-#define MAX_RESOURCES  MAX_BLACKFIN_GPIOS
+#define MAX_RESOURCES  64
 
 #define P_TMR2         (P_DONTCARE)
 #define P_TMR1         (P_DONTCARE)
index 8009a51..b037168 100644 (file)
@@ -404,6 +404,21 @@ ENTRY(_do_hibernate)
        PM_SYS_PUSH(EBIU_FCTL)
 #endif
 
+#ifdef PORTCIO_FER
+       PM_SYS_PUSH16(PORTCIO_DIR)
+       PM_SYS_PUSH16(PORTCIO_INEN)
+       PM_SYS_PUSH16(PORTCIO)
+       PM_SYS_PUSH16(PORTCIO_FER)
+       PM_SYS_PUSH16(PORTDIO_DIR)
+       PM_SYS_PUSH16(PORTDIO_INEN)
+       PM_SYS_PUSH16(PORTDIO)
+       PM_SYS_PUSH16(PORTDIO_FER)
+       PM_SYS_PUSH16(PORTEIO_DIR)
+       PM_SYS_PUSH16(PORTEIO_INEN)
+       PM_SYS_PUSH16(PORTEIO)
+       PM_SYS_PUSH16(PORTEIO_FER)
+#endif
+
        PM_SYS_PUSH16(SYSCR)
 
        /* Save Core MMRs */
@@ -716,6 +731,21 @@ ENTRY(_do_hibernate)
        P0.L = lo(PLL_CTL);
        PM_SYS_POP16(SYSCR)
 
+#ifdef PORTCIO_FER
+       PM_SYS_POP16(PORTEIO_FER)
+       PM_SYS_POP16(PORTEIO)
+       PM_SYS_POP16(PORTEIO_INEN)
+       PM_SYS_POP16(PORTEIO_DIR)
+       PM_SYS_POP16(PORTDIO_FER)
+       PM_SYS_POP16(PORTDIO)
+       PM_SYS_POP16(PORTDIO_INEN)
+       PM_SYS_POP16(PORTDIO_DIR)
+       PM_SYS_POP16(PORTCIO_FER)
+       PM_SYS_POP16(PORTCIO)
+       PM_SYS_POP16(PORTCIO_INEN)
+       PM_SYS_POP16(PORTCIO_DIR)
+#endif
+
 #ifdef EBIU_FCTL
        PM_SYS_POP(EBIU_FCTL)
        PM_SYS_POP(EBIU_MODE)