浏览代码

cpm2: Implement GPIO LIB API on CPM2 Freescale SoC.

This patch implement GPIO LIB support for the CPM2 GPIOs. The code can
also be used for CPM1 GPIO port E, as both cores are compatible at the
register level.

Based on earlier work by Laurent Pinchart.

Signed-off-by: Jochen Friedrich <jochen@scram.de>
Cc: Laurent Pinchart <laurentp@cse-semaphore.com>
Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
Laurent Pinchart 17 年之前
父节点
当前提交
e193325e3e
共有 4 个文件被更改,包括 139 次插入0 次删除
  1. 2 0
      arch/powerpc/platforms/Kconfig
  2. 11 0
      arch/powerpc/sysdev/cpm2.c
  3. 123 0
      arch/powerpc/sysdev/cpm_common.c
  4. 3 0
      include/asm-powerpc/cpm.h

+ 2 - 0
arch/powerpc/platforms/Kconfig

@@ -254,6 +254,8 @@ config CPM2
 	select CPM
 	select CPM
 	select PPC_LIB_RHEAP
 	select PPC_LIB_RHEAP
 	select PPC_PCI_CHOICE
 	select PPC_PCI_CHOICE
+	select ARCH_REQUIRE_GPIOLIB
+	select GENERIC_GPIO
 	help
 	help
 	  The CPM2 (Communications Processor Module) is a coprocessor on
 	  The CPM2 (Communications Processor Module) is a coprocessor on
 	  embedded CPUs made by Freescale.  Selecting this option means that
 	  embedded CPUs made by Freescale.  Selecting this option means that

+ 11 - 0
arch/powerpc/sysdev/cpm2.c

@@ -377,3 +377,14 @@ void cpm2_set_pin(int port, int pin, int flags)
 	else
 	else
 		clrbits32(&iop[port].odr, pin);
 		clrbits32(&iop[port].odr, pin);
 }
 }
+
+static int cpm_init_par_io(void)
+{
+	struct device_node *np;
+
+	for_each_compatible_node(np, NULL, "fsl,cpm2-pario-bank")
+		cpm2_gpiochip_add32(np);
+	return 0;
+}
+arch_initcall(cpm_init_par_io);
+

+ 123 - 0
arch/powerpc/sysdev/cpm_common.c

@@ -19,6 +19,8 @@
 
 
 #include <linux/init.h>
 #include <linux/init.h>
 #include <linux/of_device.h>
 #include <linux/of_device.h>
+#include <linux/spinlock.h>
+#include <linux/of.h>
 
 
 #include <asm/udbg.h>
 #include <asm/udbg.h>
 #include <asm/io.h>
 #include <asm/io.h>
@@ -28,6 +30,10 @@
 
 
 #include <mm/mmu_decl.h>
 #include <mm/mmu_decl.h>
 
 
+#if defined(CONFIG_CPM2) || defined(CONFIG_8xx_GPIO)
+#include <linux/of_gpio.h>
+#endif
+
 #ifdef CONFIG_PPC_EARLY_DEBUG_CPM
 #ifdef CONFIG_PPC_EARLY_DEBUG_CPM
 static u32 __iomem *cpm_udbg_txdesc =
 static u32 __iomem *cpm_udbg_txdesc =
 	(u32 __iomem __force *)CONFIG_PPC_EARLY_DEBUG_CPM_ADDR;
 	(u32 __iomem __force *)CONFIG_PPC_EARLY_DEBUG_CPM_ADDR;
@@ -207,3 +213,120 @@ dma_addr_t cpm_muram_dma(void __iomem *addr)
 	return muram_pbase + ((u8 __iomem *)addr - muram_vbase);
 	return muram_pbase + ((u8 __iomem *)addr - muram_vbase);
 }
 }
 EXPORT_SYMBOL(cpm_muram_dma);
 EXPORT_SYMBOL(cpm_muram_dma);
+
+#if defined(CONFIG_CPM2) || defined(CONFIG_8xx_GPIO)
+
+struct cpm2_ioports {
+	u32 dir, par, sor, odr, dat;
+	u32 res[3];
+};
+
+struct cpm2_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 cpm2_gpio32_chip *
+to_cpm2_gpio32_chip(struct of_mm_gpio_chip *mm_gc)
+{
+	return container_of(mm_gc, struct cpm2_gpio32_chip, mm_gc);
+}
+
+static void cpm2_gpio32_save_regs(struct of_mm_gpio_chip *mm_gc)
+{
+	struct cpm2_gpio32_chip *cpm2_gc = to_cpm2_gpio32_chip(mm_gc);
+	struct cpm2_ioports __iomem *iop = mm_gc->regs;
+
+	cpm2_gc->cpdata = in_be32(&iop->dat);
+}
+
+static int cpm2_gpio32_get(struct gpio_chip *gc, unsigned int gpio)
+{
+	struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+	struct cpm2_ioports __iomem *iop = mm_gc->regs;
+	u32 pin_mask;
+
+	pin_mask = 1 << (31 - gpio);
+
+	return !!(in_be32(&iop->dat) & pin_mask);
+}
+
+static void cpm2_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 cpm2_gpio32_chip *cpm2_gc = to_cpm2_gpio32_chip(mm_gc);
+	struct cpm2_ioports __iomem *iop = mm_gc->regs;
+	unsigned long flags;
+	u32 pin_mask = 1 << (31 - gpio);
+
+	spin_lock_irqsave(&cpm2_gc->lock, flags);
+
+	if (value)
+		cpm2_gc->cpdata |= pin_mask;
+	else
+		cpm2_gc->cpdata &= ~pin_mask;
+
+	out_be32(&iop->dat, cpm2_gc->cpdata);
+
+	spin_unlock_irqrestore(&cpm2_gc->lock, flags);
+}
+
+static int cpm2_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 cpm2_ioports __iomem *iop = mm_gc->regs;
+	u32 pin_mask;
+
+	pin_mask = 1 << (31 - gpio);
+
+	setbits32(&iop->dir, pin_mask);
+
+	cpm2_gpio32_set(gc, gpio, val);
+
+	return 0;
+}
+
+static int cpm2_gpio32_dir_in(struct gpio_chip *gc, unsigned int gpio)
+{
+	struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+	struct cpm2_ioports __iomem *iop = mm_gc->regs;
+	u32 pin_mask;
+
+	pin_mask = 1 << (31 - gpio);
+
+	clrbits32(&iop->dir, pin_mask);
+
+	return 0;
+}
+
+int cpm2_gpiochip_add32(struct device_node *np)
+{
+	struct cpm2_gpio32_chip *cpm2_gc;
+	struct of_mm_gpio_chip *mm_gc;
+	struct of_gpio_chip *of_gc;
+	struct gpio_chip *gc;
+
+	cpm2_gc = kzalloc(sizeof(*cpm2_gc), GFP_KERNEL);
+	if (!cpm2_gc)
+		return -ENOMEM;
+
+	spin_lock_init(&cpm2_gc->lock);
+
+	mm_gc = &cpm2_gc->mm_gc;
+	of_gc = &mm_gc->of_gc;
+	gc = &of_gc->gc;
+
+	mm_gc->save_regs = cpm2_gpio32_save_regs;
+	of_gc->gpio_cells = 2;
+	gc->ngpio = 32;
+	gc->direction_input = cpm2_gpio32_dir_in;
+	gc->direction_output = cpm2_gpio32_dir_out;
+	gc->get = cpm2_gpio32_get;
+	gc->set = cpm2_gpio32_set;
+
+	return of_mm_gpiochip_add(np, mm_gc);
+}
+#endif /* CONFIG_CPM2 || CONFIG_8xx_GPIO */

+ 3 - 0
include/asm-powerpc/cpm.h

@@ -3,6 +3,7 @@
 
 
 #include <linux/compiler.h>
 #include <linux/compiler.h>
 #include <linux/types.h>
 #include <linux/types.h>
+#include <linux/of.h>
 
 
 /* Opcodes common to CPM1 and CPM2
 /* Opcodes common to CPM1 and CPM2
 */
 */
@@ -100,4 +101,6 @@ unsigned long cpm_muram_offset(void __iomem *addr);
 dma_addr_t cpm_muram_dma(void __iomem *addr);
 dma_addr_t cpm_muram_dma(void __iomem *addr);
 int cpm_command(u32 command, u8 opcode);
 int cpm_command(u32 command, u8 opcode);
 
 
+int cpm2_gpiochip_add32(struct device_node *np);
+
 #endif
 #endif