[ARM] S3C64XX: GPIO library support
authorBen Dooks <ben-linux@fluff.org>
Fri, 31 Oct 2008 16:14:33 +0000 (16:14 +0000)
committerBen Dooks <ben-linux@fluff.org>
Mon, 15 Dec 2008 23:33:59 +0000 (23:33 +0000)
Add gpiolib registration for the GPIOs available on the
S3C64XX platform

Signed-off-by: Ben Dooks <ben-linux@fluff.org>
arch/arm/plat-s3c64xx/Makefile
arch/arm/plat-s3c64xx/gpiolib.c [new file with mode: 0644]
arch/arm/plat-s3c64xx/include/plat/regs-gpio.h [new file with mode: 0644]

index 9c09b081980538d12cd040f5deea70f318abfbc4..a5b7c388351e36d37adec30bb897b85482c45191 100644 (file)
@@ -17,6 +17,7 @@ obj-y                         += cpu.o
 obj-y                          += irq.o
 obj-y                          += irq-eint.o
 obj-y                          += clock.o
+obj-y                          += gpiolib.o
 
 # CPU support
 
diff --git a/arch/arm/plat-s3c64xx/gpiolib.c b/arch/arm/plat-s3c64xx/gpiolib.c
new file mode 100644 (file)
index 0000000..28ba235
--- /dev/null
@@ -0,0 +1,347 @@
+/* arch/arm/plat-s3c64xx/gpiolib.c
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ *      Ben Dooks <ben@simtec.co.uk>
+ *      http://armlinux.simtec.co.uk/
+ *
+ * S3C64XX - GPIOlib support 
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+
+#include <mach/map.h>
+#include <mach/gpio.h>
+
+#include <plat/gpio-core.h>
+#include <plat/regs-gpio.h>
+
+/* GPIO bank summary:
+ *
+ * Bank        GPIOs   Style   SlpCon  ExtInt Group
+ * A   8       4Bit    Yes     1
+ * B   7       4Bit    Yes     1
+ * C   8       4Bit    Yes     2
+ * D   5       4Bit    Yes     3
+ * E   5       4Bit    Yes     None
+ * F   16      2Bit    Yes     4 [1]
+ * G   7       4Bit    Yes     5
+ * H   10      4Bit[2] Yes     6
+ * I   16      2Bit    Yes     None
+ * J   12      2Bit    Yes     None
+ * K   16      4Bit[2] No      None
+ * L   15      4Bit[2] No      None
+ * M   6       4Bit    No      IRQ_EINT
+ * N   16      2Bit    No      IRQ_EINT
+ * O   16      2Bit    Yes     7
+ * P   15      2Bit    Yes     8
+ * Q   9       2Bit    Yes     9
+ *
+ * [1] BANKF pins 14,15 do not form part of the external interrupt sources
+ * [2] BANK has two control registers, GPxCON0 and GPxCON1
+ */
+
+#define OFF_GPCON      (0x00)
+#define OFF_GPDAT      (0x04)
+
+#define con_4bit_shift(__off) ((__off) * 4)
+
+/* The s3c64xx_gpiolib_4bit routines are to control the gpio banks where
+ * the gpio configuration register (GPxCON) has 4 bits per GPIO, as the
+ * following example:
+ *
+ * base + 0x00: Control register, 4 bits per gpio
+ *             gpio n: 4 bits starting at (4*n)
+ *             0000 = input, 0001 = output, others mean special-function
+ * base + 0x04: Data register, 1 bit per gpio
+ *             bit n: data bit n
+ *
+ * Note, since the data register is one bit per gpio and is at base + 0x4
+ * we can use s3c_gpiolib_get and s3c_gpiolib_set to change the state of
+ * the output.
+*/
+
+static int s3c64xx_gpiolib_4bit_input(struct gpio_chip *chip, unsigned offset)
+{
+       struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip);
+       void __iomem *base = ourchip->base;
+       unsigned long con;
+
+       con = __raw_readl(base + OFF_GPCON);
+       con &= ~(0xf << con_4bit_shift(offset));
+       __raw_writel(con, base + OFF_GPCON);
+
+       return 0;
+}
+
+static int s3c64xx_gpiolib_4bit_output(struct gpio_chip *chip,
+                                      unsigned offset, int value)
+{
+       struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip);
+       void __iomem *base = ourchip->base;
+       unsigned long con;
+       unsigned long dat;
+
+       con = __raw_readl(base + OFF_GPCON);
+       con &= ~(0xf << con_4bit_shift(offset));
+       con |= 0x1 << con_4bit_shift(offset);
+
+       dat = __raw_readl(base + OFF_GPDAT);
+       if (value)
+               dat |= 1 << offset;
+       else
+               dat &= ~(1 << offset);
+
+       __raw_writel(dat, base + OFF_GPDAT);
+       __raw_writel(con, base + OFF_GPCON);
+       __raw_writel(dat, base + OFF_GPDAT);
+
+       return 0;
+}
+
+/* The next set of routines are for the case where the GPIO configuration
+ * registers are 4 bits per GPIO but there is more than one register (the
+ * bank has more than 8 GPIOs.
+ *
+ * This case is the similar to the 4 bit case, but the registers are as
+ * follows:
+ *
+ * base + 0x00: Control register, 4 bits per gpio (lower 8 GPIOs)
+ *             gpio n: 4 bits starting at (4*n)
+ *             0000 = input, 0001 = output, others mean special-function
+ * base + 0x04: Control register, 4 bits per gpio (up to 8 additions GPIOs)
+ *             gpio n: 4 bits starting at (4*n)
+ *             0000 = input, 0001 = output, others mean special-function
+ * base + 0x08: Data register, 1 bit per gpio
+ *             bit n: data bit n
+ *
+ * To allow us to use the s3c_gpiolib_get and s3c_gpiolib_set routines we
+ * store the 'base + 0x4' address so that these routines see the data
+ * register at ourchip->base + 0x04.
+*/
+
+static int s3c64xx_gpiolib_4bit2_input(struct gpio_chip *chip, unsigned offset)
+{
+       struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip);
+       void __iomem *base = ourchip->base;
+       void __iomem *regcon = base;
+       unsigned long con;
+
+       if (offset > 7)
+               offset -= 8;
+       else
+               regcon -= 4;
+
+       con = __raw_readl(regcon);
+       con &= ~(0xf << con_4bit_shift(offset));
+       __raw_writel(con, regcon);
+
+       return 0;
+
+}
+
+static int s3c64xx_gpiolib_4bit2_output(struct gpio_chip *chip,
+                                      unsigned offset, int value)
+{
+       struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip);
+       void __iomem *base = ourchip->base;
+       void __iomem *regcon = base;
+       unsigned long con;
+       unsigned long dat;
+
+       if (offset > 7)
+               offset -= 8;
+       else
+               regcon -= 4;
+
+       con = __raw_readl(regcon);
+       con &= ~(0xf << con_4bit_shift(offset));
+       con |= 0x1 << con_4bit_shift(offset);
+
+       dat = __raw_readl(base + OFF_GPDAT);
+       if (value)
+               dat |= 1 << offset;
+       else
+               dat &= ~(1 << offset);
+
+       __raw_writel(dat, base + OFF_GPDAT);
+       __raw_writel(con, regcon);
+       __raw_writel(dat, base + OFF_GPDAT);
+
+       return 0;
+}
+
+static struct s3c_gpio_chip gpio_4bit[] = {
+       {
+               .base   = S3C64XX_GPA_BASE,
+               .chip   = {
+                       .base   = S3C64XX_GPA(0),
+                       .ngpio  = S3C64XX_GPIO_A_NR,
+                       .label  = "GPA",
+               },
+       }, {
+               .base   = S3C64XX_GPB_BASE,
+               .chip   = {
+                       .base   = S3C64XX_GPB(0),
+                       .ngpio  = S3C64XX_GPIO_B_NR,
+                       .label  = "GPB",
+               },
+       }, {
+               .base   = S3C64XX_GPC_BASE,
+               .chip   = {
+                       .base   = S3C64XX_GPC(0),
+                       .ngpio  = S3C64XX_GPIO_C_NR,
+                       .label  = "GPC",
+               },
+       }, {
+               .base   = S3C64XX_GPD_BASE,
+               .chip   = {
+                       .base   = S3C64XX_GPD(0),
+                       .ngpio  = S3C64XX_GPIO_D_NR,
+                       .label  = "GPD",
+               },
+       }, {
+               .base   = S3C64XX_GPE_BASE,
+               .chip   = {
+                       .base   = S3C64XX_GPE(0),
+                       .ngpio  = S3C64XX_GPIO_E_NR,
+                       .label  = "GPE",
+               },
+       }, {
+               .base   = S3C64XX_GPG_BASE,
+               .chip   = {
+                       .base   = S3C64XX_GPG(0),
+                       .ngpio  = S3C64XX_GPIO_G_NR,
+                       .label  = "GPG",
+               },
+       }, {
+               .base   = S3C64XX_GPM_BASE,
+               .chip   = {
+                       .base   = S3C64XX_GPM(0),
+                       .ngpio  = S3C64XX_GPIO_M_NR,
+                       .label  = "GPM",
+               },
+       },
+};
+
+static struct s3c_gpio_chip gpio_4bit2[] = {
+       {
+               .base   = S3C64XX_GPH_BASE + 0x4,
+               .chip   = {
+                       .base   = S3C64XX_GPH(0),
+                       .ngpio  = S3C64XX_GPIO_H_NR,
+                       .label  = "GPH",
+               },
+       }, {
+               .base   = S3C64XX_GPK_BASE + 0x4,
+               .chip   = {
+                       .base   = S3C64XX_GPK(0),
+                       .ngpio  = S3C64XX_GPIO_K_NR,
+                       .label  = "GPK",
+               },
+       }, {
+               .base   = S3C64XX_GPL_BASE + 0x4,
+               .chip   = {
+                       .base   = S3C64XX_GPL(0),
+                       .ngpio  = S3C64XX_GPIO_L_NR,
+                       .label  = "GPL",
+               },
+       },
+};
+
+static struct s3c_gpio_chip gpio_2bit[] = {
+       {
+               .base   = S3C64XX_GPF_BASE,
+               .chip   = {
+                       .base   = S3C64XX_GPF(0),
+                       .ngpio  = S3C64XX_GPIO_F_NR,
+                       .label  = "GPF",
+               },
+       }, {
+               .base   = S3C64XX_GPI_BASE,
+               .chip   = {
+                       .base   = S3C64XX_GPI(0),
+                       .ngpio  = S3C64XX_GPIO_I_NR,
+                       .label  = "GPI",
+               },
+       }, {
+               .base   = S3C64XX_GPJ_BASE,
+               .chip   = {
+                       .base   = S3C64XX_GPJ(0),
+                       .ngpio  = S3C64XX_GPIO_J_NR,
+                       .label  = "GPJ",
+               },
+       }, {
+               .base   = S3C64XX_GPN_BASE,
+               .chip   = {
+                       .base   = S3C64XX_GPN(0),
+                       .ngpio  = S3C64XX_GPIO_N_NR,
+                       .label  = "GPN",
+               },
+       }, {
+               .base   = S3C64XX_GPO_BASE,
+               .chip   = {
+                       .base   = S3C64XX_GPO(0),
+                       .ngpio  = S3C64XX_GPIO_O_NR,
+                       .label  = "GPO",
+               },
+       }, {
+               .base   = S3C64XX_GPP_BASE,
+               .chip   = {
+                       .base   = S3C64XX_GPP(0),
+                       .ngpio  = S3C64XX_GPIO_P_NR,
+                       .label  = "GPP",
+               },
+       }, {
+               .base   = S3C64XX_GPQ_BASE,
+               .chip   = {
+                       .base   = S3C64XX_GPQ(0),
+                       .ngpio  = S3C64XX_GPIO_Q_NR,
+                       .label  = "GPQ",
+               },
+       },
+};
+
+static __init void s3c64xx_gpiolib_add_4bit(struct s3c_gpio_chip *chip)
+{
+       chip->chip.direction_input = s3c64xx_gpiolib_4bit_input;
+       chip->chip.direction_output = s3c64xx_gpiolib_4bit_output;
+}
+
+static __init void s3c64xx_gpiolib_add_4bit2(struct s3c_gpio_chip *chip)
+{
+       chip->chip.direction_input = s3c64xx_gpiolib_4bit2_input;
+       chip->chip.direction_output = s3c64xx_gpiolib_4bit2_output;
+}
+
+static __init void s3c64xx_gpiolib_add(struct s3c_gpio_chip *chips,
+                                      int nr_chips,
+                                      void (*fn)(struct s3c_gpio_chip *))
+{
+       for (; nr_chips > 0; nr_chips--, chips++) {
+               if (fn)
+                       (fn)(chips);
+               s3c_gpiolib_add(chips);
+       }
+}
+
+static __init int s3c64xx_gpiolib_init(void)
+{
+       s3c64xx_gpiolib_add(gpio_4bit, ARRAY_SIZE(gpio_4bit),
+                           s3c64xx_gpiolib_add_4bit);
+
+       s3c64xx_gpiolib_add(gpio_4bit2, ARRAY_SIZE(gpio_4bit2),
+                           s3c64xx_gpiolib_add_4bit2);
+
+       s3c64xx_gpiolib_add(gpio_2bit, ARRAY_SIZE(gpio_2bit), NULL);
+
+       return 0;
+}
+
+arch_initcall(s3c64xx_gpiolib_init);
diff --git a/arch/arm/plat-s3c64xx/include/plat/regs-gpio.h b/arch/arm/plat-s3c64xx/include/plat/regs-gpio.h
new file mode 100644 (file)
index 0000000..75b873d
--- /dev/null
@@ -0,0 +1,35 @@
+/* linux/arch/arm/plat-s3c64xx/include/mach/regs-gpio.h
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ *      Ben Dooks <ben@simtec.co.uk>
+ *      http://armlinux.simtec.co.uk/
+ *
+ * S3C64XX - GPIO register definitions
+ */
+
+#ifndef __ASM_PLAT_S3C64XX_REGS_GPIO_H
+#define __ASM_PLAT_S3C64XX_REGS_GPIO_H __FILE__
+
+/* Base addresses for each of the banks */
+
+#define S3C64XX_GPA_BASE       (S3C64XX_VA_GPIO + 0x0000)
+#define S3C64XX_GPB_BASE       (S3C64XX_VA_GPIO + 0x0020)
+#define S3C64XX_GPC_BASE       (S3C64XX_VA_GPIO + 0x0040)
+#define S3C64XX_GPD_BASE       (S3C64XX_VA_GPIO + 0x0060)
+#define S3C64XX_GPE_BASE       (S3C64XX_VA_GPIO + 0x0080)
+#define S3C64XX_GPF_BASE       (S3C64XX_VA_GPIO + 0x00A0)
+#define S3C64XX_GPG_BASE       (S3C64XX_VA_GPIO + 0x00C0)
+#define S3C64XX_GPH_BASE       (S3C64XX_VA_GPIO + 0x00E0)
+#define S3C64XX_GPI_BASE       (S3C64XX_VA_GPIO + 0x0100)
+#define S3C64XX_GPJ_BASE       (S3C64XX_VA_GPIO + 0x0120)
+#define S3C64XX_GPK_BASE       (S3C64XX_VA_GPIO + 0x0800)
+#define S3C64XX_GPL_BASE       (S3C64XX_VA_GPIO + 0x0810)
+#define S3C64XX_GPM_BASE       (S3C64XX_VA_GPIO + 0x0820)
+#define S3C64XX_GPN_BASE       (S3C64XX_VA_GPIO + 0x0830)
+#define S3C64XX_GPO_BASE       (S3C64XX_VA_GPIO + 0x0140)
+#define S3C64XX_GPP_BASE       (S3C64XX_VA_GPIO + 0x0160)
+#define S3C64XX_GPQ_BASE       (S3C64XX_VA_GPIO + 0x0180)
+
+#endif /* __ASM_PLAT_S3C64XX_REGS_GPIO_H */
+