powerpc/QE: implement support for the GPIO LIB API
authorAnton Vorontsov <avorontsov@ru.mvista.com>
Mon, 19 May 2008 17:47:05 +0000 (21:47 +0400)
committerKumar Gala <galak@kernel.crashing.org>
Tue, 10 Jun 2008 16:11:10 +0000 (11:11 -0500)
This is needed to access QE GPIOs via Linux GPIO API.

Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
Acked-By: Timur Tabi <timur@freescale.com>
Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
Documentation/powerpc/booting-without-of.txt
arch/powerpc/sysdev/qe_lib/Kconfig
arch/powerpc/sysdev/qe_lib/Makefile
arch/powerpc/sysdev/qe_lib/gpio.c [new file with mode: 0644]
include/asm-powerpc/qe.h

index 8675ebca2cfd417f24930439a56cec110f21d256..b68684d39f966931c521b8402548079a2d532ccd 100644 (file)
@@ -1736,6 +1736,33 @@ platforms are moved over to use the flattened-device-tree model.
                        ......
                };
 
+   Note that "par_io" nodes are obsolete, and should not be used for
+   the new device trees. Instead, each Par I/O bank should be represented
+   via its own gpio-controller node:
+
+   Required properties:
+   - #gpio-cells : should be "2".
+   - compatible : should be "fsl,<chip>-qe-pario-bank",
+     "fsl,mpc8323-qe-pario-bank".
+   - reg : offset to the register set and its length.
+   - gpio-controller : node to identify gpio controllers.
+
+   Example:
+       qe_pio_a: gpio-controller@1400 {
+               #gpio-cells = <2>;
+               compatible = "fsl,mpc8360-qe-pario-bank",
+                            "fsl,mpc8323-qe-pario-bank";
+               reg = <0x1400 0x18>;
+               gpio-controller;
+       };
+
+       qe_pio_e: gpio-controller@1460 {
+               #gpio-cells = <2>;
+               compatible = "fsl,mpc8360-qe-pario-bank",
+                            "fsl,mpc8323-qe-pario-bank";
+               reg = <0x1460 0x18>;
+               gpio-controller;
+       };
 
    vi) Pin configuration nodes
 
index 76ffbc48d4b949f410e611b15d6550a647bdb30b..4bb18f57901e5c3585bc5e9bd60d18704239c96c 100644 (file)
@@ -24,3 +24,12 @@ config QE_USB
        bool
        help
          QE USB Host Controller support
+
+config QE_GPIO
+       bool "QE GPIO support"
+       depends on QUICC_ENGINE
+       select GENERIC_GPIO
+       select HAVE_GPIO_LIB
+       help
+         Say Y here if you're going to use hardware that connects to the
+         QE GPIOs.
index e9ff8884f74a0243d704ebb62ba9874c58cc0e20..f1855c185291aa923b02dff0b668ae2e061c8f34 100644 (file)
@@ -7,3 +7,4 @@ obj-$(CONFIG_UCC)       += ucc.o
 obj-$(CONFIG_UCC_SLOW) += ucc_slow.o
 obj-$(CONFIG_UCC_FAST) += ucc_fast.o
 obj-$(CONFIG_QE_USB)   += usb.o
+obj-$(CONFIG_QE_GPIO)  += gpio.o
diff --git a/arch/powerpc/sysdev/qe_lib/gpio.c b/arch/powerpc/sysdev/qe_lib/gpio.c
new file mode 100644 (file)
index 0000000..c712e24
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * QUICC Engine GPIOs
+ *
+ * Copyright (c) MontaVista Software, Inc. 2008.
+ *
+ * Author: Anton Vorontsov <avorontsov@ru.mvista.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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/gpio.h>
+#include <asm/qe.h>
+
+struct qe_gpio_chip {
+       struct of_mm_gpio_chip mm_gc;
+       spinlock_t lock;
+
+       /* shadowed data register to clear/set bits safely */
+       u32 cpdata;
+};
+
+static inline struct qe_gpio_chip *
+to_qe_gpio_chip(struct of_mm_gpio_chip *mm_gc)
+{
+       return container_of(mm_gc, struct qe_gpio_chip, mm_gc);
+}
+
+static void qe_gpio_save_regs(struct of_mm_gpio_chip *mm_gc)
+{
+       struct qe_gpio_chip *qe_gc = to_qe_gpio_chip(mm_gc);
+       struct qe_pio_regs __iomem *regs = mm_gc->regs;
+
+       qe_gc->cpdata = in_be32(&regs->cpdata);
+}
+
+static int qe_gpio_get(struct gpio_chip *gc, unsigned int gpio)
+{
+       struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+       struct qe_pio_regs __iomem *regs = mm_gc->regs;
+       u32 pin_mask = 1 << (QE_PIO_PINS - 1 - gpio);
+
+       return in_be32(&regs->cpdata) & pin_mask;
+}
+
+static void qe_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+       struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+       struct qe_gpio_chip *qe_gc = to_qe_gpio_chip(mm_gc);
+       struct qe_pio_regs __iomem *regs = mm_gc->regs;
+       unsigned long flags;
+       u32 pin_mask = 1 << (QE_PIO_PINS - 1 - gpio);
+
+       spin_lock_irqsave(&qe_gc->lock, flags);
+
+       if (val)
+               qe_gc->cpdata |= pin_mask;
+       else
+               qe_gc->cpdata &= ~pin_mask;
+
+       out_be32(&regs->cpdata, qe_gc->cpdata);
+
+       spin_unlock_irqrestore(&qe_gc->lock, flags);
+}
+
+static int qe_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio)
+{
+       struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+       struct qe_gpio_chip *qe_gc = to_qe_gpio_chip(mm_gc);
+       unsigned long flags;
+
+       spin_lock_irqsave(&qe_gc->lock, flags);
+
+       __par_io_config_pin(mm_gc->regs, gpio, QE_PIO_DIR_IN, 0, 0, 0);
+
+       spin_unlock_irqrestore(&qe_gc->lock, flags);
+
+       return 0;
+}
+
+static int qe_gpio_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 qe_gpio_chip *qe_gc = to_qe_gpio_chip(mm_gc);
+       unsigned long flags;
+
+       spin_lock_irqsave(&qe_gc->lock, flags);
+
+       __par_io_config_pin(mm_gc->regs, gpio, QE_PIO_DIR_OUT, 0, 0, 0);
+
+       spin_unlock_irqrestore(&qe_gc->lock, flags);
+
+       qe_gpio_set(gc, gpio, val);
+
+       return 0;
+}
+
+void __init qe_add_gpiochips(void)
+{
+       struct device_node *np;
+
+       for_each_compatible_node(np, NULL, "fsl,mpc8323-qe-pario-bank") {
+               int ret;
+               struct qe_gpio_chip *qe_gc;
+               struct of_mm_gpio_chip *mm_gc;
+               struct of_gpio_chip *of_gc;
+               struct gpio_chip *gc;
+
+               qe_gc = kzalloc(sizeof(*qe_gc), GFP_KERNEL);
+               if (!qe_gc) {
+                       ret = -ENOMEM;
+                       goto err;
+               }
+
+               spin_lock_init(&qe_gc->lock);
+
+               mm_gc = &qe_gc->mm_gc;
+               of_gc = &mm_gc->of_gc;
+               gc = &of_gc->gc;
+
+               mm_gc->save_regs = qe_gpio_save_regs;
+               of_gc->gpio_cells = 2;
+               gc->ngpio = QE_PIO_PINS;
+               gc->direction_input = qe_gpio_dir_in;
+               gc->direction_output = qe_gpio_dir_out;
+               gc->get = qe_gpio_get;
+               gc->set = qe_gpio_set;
+
+               ret = of_mm_gpiochip_add(np, mm_gc);
+               if (ret)
+                       goto err;
+               continue;
+err:
+               pr_err("%s: registration failed with status %d\n",
+                      np->full_name, ret);
+               kfree(qe_gc);
+               /* try others anyway */
+       }
+}
index c4523acad48b1cc0dc3417454ee4dbd6a0a2bb40..01e3c70b93abad06b4527441a60d89586b0a58fe 100644 (file)
@@ -100,8 +100,11 @@ struct qe_pio_regs {
 #endif
 };
 
+extern void __init qe_add_gpiochips(void);
 extern int par_io_init(struct device_node *np);
 extern int par_io_of_config(struct device_node *np);
+#define QE_PIO_DIR_IN  2
+#define QE_PIO_DIR_OUT 1
 extern void __par_io_config_pin(struct qe_pio_regs __iomem *par_io, u8 pin,
                                int dir, int open_drain, int assignment,
                                int has_irq);