powerpc: implement GPIO LIB API on CPM1 Freescale SoC.
authorJochen Friedrich <jochen@scram.de>
Wed, 2 Jul 2008 16:18:23 +0000 (02:18 +1000)
committerKumar Gala <galak@kernel.crashing.org>
Mon, 28 Jul 2008 12:42:09 +0000 (07:42 -0500)
This patch implement GPIO LIB support for the CPM1 GPIOs.

Signed-off-by: Jochen Friedrich <jochen@scram.de>
Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
arch/powerpc/platforms/8xx/Kconfig
arch/powerpc/sysdev/cpm1.c

index 6fc849e51e480a5646d4db23b1b7bd2cb6a85828..71d7562e190be1120090b04d1ac547173812af73 100644 (file)
@@ -105,6 +105,16 @@ config 8xx_COPYBACK
 
          If in doubt, say Y here.
 
+config 8xx_GPIO
+       bool "GPIO API Support"
+       select GENERIC_GPIO
+       select ARCH_REQUIRE_GPIOLIB
+       help
+         Saying Y here will cause the ports on an MPC8xx processor to be used
+         with the GPIO API.  If you say N here, the kernel needs less memory.
+
+         If in doubt, say Y here.
+
 config 8xx_CPU6
        bool "CPU6 Silicon Errata (860 Pre Rev. C)"
        help
index 661df42830b912fc304783ac2b0aea135b9daaa3..4a04823e8423b9f8a24098a2fec81acf917598a9 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/interrupt.h>
 #include <linux/irq.h>
 #include <linux/module.h>
+#include <linux/spinlock.h>
 #include <asm/page.h>
 #include <asm/pgtable.h>
 #include <asm/8xx_immap.h>
 
 #include <asm/fs_pd.h>
 
+#ifdef CONFIG_8xx_GPIO
+#include <linux/of_gpio.h>
+#endif
+
 #define CPM_MAP_SIZE    (0x4000)
 
 cpm8xx_t __iomem *cpmp;  /* Pointer to comm processor space */
@@ -290,20 +295,24 @@ struct cpm_ioport16 {
        __be16 res[3];
 };
 
-struct cpm_ioport32 {
-       __be32 dir, par, sor;
+struct cpm_ioport32b {
+       __be32 dir, par, odr, dat;
+};
+
+struct cpm_ioport32e {
+       __be32 dir, par, sor, odr, dat;
 };
 
 static void cpm1_set_pin32(int port, int pin, int flags)
 {
-       struct cpm_ioport32 __iomem *iop;
+       struct cpm_ioport32e __iomem *iop;
        pin = 1 << (31 - pin);
 
        if (port == CPM_PORTB)
-               iop = (struct cpm_ioport32 __iomem *)
+               iop = (struct cpm_ioport32e __iomem *)
                      &mpc8xx_immr->im_cpm.cp_pbdir;
        else
-               iop = (struct cpm_ioport32 __iomem *)
+               iop = (struct cpm_ioport32e __iomem *)
                      &mpc8xx_immr->im_cpm.cp_pedir;
 
        if (flags & CPM_PIN_OUTPUT)
@@ -498,3 +507,251 @@ int cpm1_clk_setup(enum cpm_clk_target target, int clock, int mode)
 
        return 0;
 }
+
+/*
+ * GPIO LIB API implementation
+ */
+#ifdef CONFIG_8xx_GPIO
+
+struct cpm1_gpio16_chip {
+       struct of_mm_gpio_chip mm_gc;
+       spinlock_t lock;
+
+       /* shadowed data register to clear/set bits safely */
+       u16 cpdata;
+};
+
+static inline struct cpm1_gpio16_chip *
+to_cpm1_gpio16_chip(struct of_mm_gpio_chip *mm_gc)
+{
+       return container_of(mm_gc, struct cpm1_gpio16_chip, mm_gc);
+}
+
+static void cpm1_gpio16_save_regs(struct of_mm_gpio_chip *mm_gc)
+{
+       struct cpm1_gpio16_chip *cpm1_gc = to_cpm1_gpio16_chip(mm_gc);
+       struct cpm_ioport16 __iomem *iop = mm_gc->regs;
+
+       cpm1_gc->cpdata = in_be16(&iop->dat);
+}
+
+static int cpm1_gpio16_get(struct gpio_chip *gc, unsigned int gpio)
+{
+       struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+       struct cpm_ioport16 __iomem *iop = mm_gc->regs;
+       u16 pin_mask;
+
+       pin_mask = 1 << (15 - gpio);
+
+       return !!(in_be16(&iop->dat) & pin_mask);
+}
+
+static void cpm1_gpio16_set(struct gpio_chip *gc, unsigned int gpio, int value)
+{
+       struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+       struct cpm1_gpio16_chip *cpm1_gc = to_cpm1_gpio16_chip(mm_gc);
+       struct cpm_ioport16 __iomem *iop = mm_gc->regs;
+       unsigned long flags;
+       u16 pin_mask = 1 << (15 - gpio);
+
+       spin_lock_irqsave(&cpm1_gc->lock, flags);
+
+       if (value)
+               cpm1_gc->cpdata |= pin_mask;
+       else
+               cpm1_gc->cpdata &= ~pin_mask;
+
+       out_be16(&iop->dat, cpm1_gc->cpdata);
+
+       spin_unlock_irqrestore(&cpm1_gc->lock, flags);
+}
+
+static int cpm1_gpio16_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+       struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+       struct cpm_ioport16 __iomem *iop = mm_gc->regs;
+       u16 pin_mask;
+
+       pin_mask = 1 << (15 - gpio);
+
+       setbits16(&iop->dir, pin_mask);
+
+       cpm1_gpio16_set(gc, gpio, val);
+
+       return 0;
+}
+
+static int cpm1_gpio16_dir_in(struct gpio_chip *gc, unsigned int gpio)
+{
+       struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+       struct cpm_ioport16 __iomem *iop = mm_gc->regs;
+       u16 pin_mask;
+
+       pin_mask = 1 << (15 - gpio);
+
+       clrbits16(&iop->dir, pin_mask);
+
+       return 0;
+}
+
+int cpm1_gpiochip_add16(struct device_node *np)
+{
+       struct cpm1_gpio16_chip *cpm1_gc;
+       struct of_mm_gpio_chip *mm_gc;
+       struct of_gpio_chip *of_gc;
+       struct gpio_chip *gc;
+
+       cpm1_gc = kzalloc(sizeof(*cpm1_gc), GFP_KERNEL);
+       if (!cpm1_gc)
+               return -ENOMEM;
+
+       spin_lock_init(&cpm1_gc->lock);
+
+       mm_gc = &cpm1_gc->mm_gc;
+       of_gc = &mm_gc->of_gc;
+       gc = &of_gc->gc;
+
+       mm_gc->save_regs = cpm1_gpio16_save_regs;
+       of_gc->gpio_cells = 2;
+       gc->ngpio = 16;
+       gc->direction_input = cpm1_gpio16_dir_in;
+       gc->direction_output = cpm1_gpio16_dir_out;
+       gc->get = cpm1_gpio16_get;
+       gc->set = cpm1_gpio16_set;
+
+       return of_mm_gpiochip_add(np, mm_gc);
+}
+
+struct cpm1_gpio32_chip {
+       struct of_mm_gpio_chip mm_gc;
+       spinlock_t lock;
+
+       /* shadowed data register to clear/set bits safely */
+       u32 cpdata;
+};
+
+static inline struct cpm1_gpio32_chip *
+to_cpm1_gpio32_chip(struct of_mm_gpio_chip *mm_gc)
+{
+       return container_of(mm_gc, struct cpm1_gpio32_chip, mm_gc);
+}
+
+static void cpm1_gpio32_save_regs(struct of_mm_gpio_chip *mm_gc)
+{
+       struct cpm1_gpio32_chip *cpm1_gc = to_cpm1_gpio32_chip(mm_gc);
+       struct cpm_ioport32b __iomem *iop = mm_gc->regs;
+
+       cpm1_gc->cpdata = in_be32(&iop->dat);
+}
+
+static int cpm1_gpio32_get(struct gpio_chip *gc, unsigned int gpio)
+{
+       struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+       struct cpm_ioport32b __iomem *iop = mm_gc->regs;
+       u32 pin_mask;
+
+       pin_mask = 1 << (31 - gpio);
+
+       return !!(in_be32(&iop->dat) & pin_mask);
+}
+
+static void cpm1_gpio32_set(struct gpio_chip *gc, unsigned int gpio, int value)
+{
+       struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+       struct cpm1_gpio32_chip *cpm1_gc = to_cpm1_gpio32_chip(mm_gc);
+       struct cpm_ioport32b __iomem *iop = mm_gc->regs;
+       unsigned long flags;
+       u32 pin_mask = 1 << (31 - gpio);
+
+       spin_lock_irqsave(&cpm1_gc->lock, flags);
+
+       if (value)
+               cpm1_gc->cpdata |= pin_mask;
+       else
+               cpm1_gc->cpdata &= ~pin_mask;
+
+       out_be32(&iop->dat, cpm1_gc->cpdata);
+
+       spin_unlock_irqrestore(&cpm1_gc->lock, flags);
+}
+
+static int cpm1_gpio32_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+       struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+       struct cpm_ioport32b __iomem *iop = mm_gc->regs;
+       u32 pin_mask;
+
+       pin_mask = 1 << (31 - gpio);
+
+       setbits32(&iop->dir, pin_mask);
+
+       cpm1_gpio32_set(gc, gpio, val);
+
+       return 0;
+}
+
+static int cpm1_gpio32_dir_in(struct gpio_chip *gc, unsigned int gpio)
+{
+       struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+       struct cpm_ioport32b __iomem *iop = mm_gc->regs;
+       u32 pin_mask;
+
+       pin_mask = 1 << (31 - gpio);
+
+       clrbits32(&iop->dir, pin_mask);
+
+       return 0;
+}
+
+int cpm1_gpiochip_add32(struct device_node *np)
+{
+       struct cpm1_gpio32_chip *cpm1_gc;
+       struct of_mm_gpio_chip *mm_gc;
+       struct of_gpio_chip *of_gc;
+       struct gpio_chip *gc;
+
+       cpm1_gc = kzalloc(sizeof(*cpm1_gc), GFP_KERNEL);
+       if (!cpm1_gc)
+               return -ENOMEM;
+
+       spin_lock_init(&cpm1_gc->lock);
+
+       mm_gc = &cpm1_gc->mm_gc;
+       of_gc = &mm_gc->of_gc;
+       gc = &of_gc->gc;
+
+       mm_gc->save_regs = cpm1_gpio32_save_regs;
+       of_gc->gpio_cells = 2;
+       gc->ngpio = 32;
+       gc->direction_input = cpm1_gpio32_dir_in;
+       gc->direction_output = cpm1_gpio32_dir_out;
+       gc->get = cpm1_gpio32_get;
+       gc->set = cpm1_gpio32_set;
+
+       return of_mm_gpiochip_add(np, mm_gc);
+}
+
+static int cpm_init_par_io(void)
+{
+       struct device_node *np;
+
+       for_each_compatible_node(np, NULL, "fsl,cpm1-pario-bank-a")
+               cpm1_gpiochip_add16(np);
+
+       for_each_compatible_node(np, NULL, "fsl,cpm1-pario-bank-b")
+               cpm1_gpiochip_add32(np);
+
+       for_each_compatible_node(np, NULL, "fsl,cpm1-pario-bank-c")
+               cpm1_gpiochip_add16(np);
+
+       for_each_compatible_node(np, NULL, "fsl,cpm1-pario-bank-d")
+               cpm1_gpiochip_add16(np);
+
+       /* Port E uses CPM2 layout */
+       for_each_compatible_node(np, NULL, "fsl,cpm1-pario-bank-e")
+               cpm2_gpiochip_add32(np);
+       return 0;
+}
+arch_initcall(cpm_init_par_io);
+
+#endif /* CONFIG_8xx_GPIO */