|
@@ -0,0 +1,197 @@
|
|
|
|
+/*
|
|
|
|
+ * linux/arch/arm/mach-pxa/gpio.c
|
|
|
|
+ *
|
|
|
|
+ * Generic PXA GPIO handling
|
|
|
|
+ *
|
|
|
|
+ * Author: Nicolas Pitre
|
|
|
|
+ * Created: Jun 15, 2001
|
|
|
|
+ * Copyright: MontaVista Software Inc.
|
|
|
|
+ *
|
|
|
|
+ * 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/init.h>
|
|
|
|
+#include <linux/module.h>
|
|
|
|
+
|
|
|
|
+#include <asm/gpio.h>
|
|
|
|
+#include <asm/hardware.h>
|
|
|
|
+#include <asm/io.h>
|
|
|
|
+#include <asm/arch/pxa-regs.h>
|
|
|
|
+
|
|
|
|
+#include "generic.h"
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+struct pxa_gpio_chip {
|
|
|
|
+ struct gpio_chip chip;
|
|
|
|
+ void __iomem *regbase;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+int pxa_last_gpio;
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Configure pins for GPIO or other functions
|
|
|
|
+ */
|
|
|
|
+int pxa_gpio_mode(int gpio_mode)
|
|
|
|
+{
|
|
|
|
+ unsigned long flags;
|
|
|
|
+ int gpio = gpio_mode & GPIO_MD_MASK_NR;
|
|
|
|
+ int fn = (gpio_mode & GPIO_MD_MASK_FN) >> 8;
|
|
|
|
+ int gafr;
|
|
|
|
+
|
|
|
|
+ if (gpio > pxa_last_gpio)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ local_irq_save(flags);
|
|
|
|
+ if (gpio_mode & GPIO_DFLT_LOW)
|
|
|
|
+ GPCR(gpio) = GPIO_bit(gpio);
|
|
|
|
+ else if (gpio_mode & GPIO_DFLT_HIGH)
|
|
|
|
+ GPSR(gpio) = GPIO_bit(gpio);
|
|
|
|
+ if (gpio_mode & GPIO_MD_MASK_DIR)
|
|
|
|
+ GPDR(gpio) |= GPIO_bit(gpio);
|
|
|
|
+ else
|
|
|
|
+ GPDR(gpio) &= ~GPIO_bit(gpio);
|
|
|
|
+ gafr = GAFR(gpio) & ~(0x3 << (((gpio) & 0xf)*2));
|
|
|
|
+ GAFR(gpio) = gafr | (fn << (((gpio) & 0xf)*2));
|
|
|
|
+ local_irq_restore(flags);
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+EXPORT_SYMBOL(pxa_gpio_mode);
|
|
|
|
+
|
|
|
|
+static int pxa_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
|
|
|
|
+{
|
|
|
|
+ unsigned long flags;
|
|
|
|
+ u32 mask = 1 << offset;
|
|
|
|
+ u32 value;
|
|
|
|
+ struct pxa_gpio_chip *pxa;
|
|
|
|
+ void __iomem *gpdr;
|
|
|
|
+
|
|
|
|
+ pxa = container_of(chip, struct pxa_gpio_chip, chip);
|
|
|
|
+ gpdr = pxa->regbase + GPDR_OFFSET;
|
|
|
|
+ local_irq_save(flags);
|
|
|
|
+ value = __raw_readl(gpdr);
|
|
|
|
+ value &= ~mask;
|
|
|
|
+ __raw_writel(value, gpdr);
|
|
|
|
+ local_irq_restore(flags);
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int pxa_gpio_direction_output(struct gpio_chip *chip,
|
|
|
|
+ unsigned offset, int value)
|
|
|
|
+{
|
|
|
|
+ unsigned long flags;
|
|
|
|
+ u32 mask = 1 << offset;
|
|
|
|
+ u32 tmp;
|
|
|
|
+ struct pxa_gpio_chip *pxa;
|
|
|
|
+ void __iomem *gpdr;
|
|
|
|
+
|
|
|
|
+ pxa = container_of(chip, struct pxa_gpio_chip, chip);
|
|
|
|
+ __raw_writel(mask,
|
|
|
|
+ pxa->regbase + (value ? GPSR_OFFSET : GPCR_OFFSET));
|
|
|
|
+ gpdr = pxa->regbase + GPDR_OFFSET;
|
|
|
|
+ local_irq_save(flags);
|
|
|
|
+ tmp = __raw_readl(gpdr);
|
|
|
|
+ tmp |= mask;
|
|
|
|
+ __raw_writel(tmp, gpdr);
|
|
|
|
+ local_irq_restore(flags);
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Return GPIO level
|
|
|
|
+ */
|
|
|
|
+static int pxa_gpio_get(struct gpio_chip *chip, unsigned offset)
|
|
|
|
+{
|
|
|
|
+ u32 mask = 1 << offset;
|
|
|
|
+ struct pxa_gpio_chip *pxa;
|
|
|
|
+
|
|
|
|
+ pxa = container_of(chip, struct pxa_gpio_chip, chip);
|
|
|
|
+ return __raw_readl(pxa->regbase + GPLR_OFFSET) & mask;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Set output GPIO level
|
|
|
|
+ */
|
|
|
|
+static void pxa_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
|
|
|
|
+{
|
|
|
|
+ u32 mask = 1 << offset;
|
|
|
|
+ struct pxa_gpio_chip *pxa;
|
|
|
|
+
|
|
|
|
+ pxa = container_of(chip, struct pxa_gpio_chip, chip);
|
|
|
|
+
|
|
|
|
+ if (value)
|
|
|
|
+ __raw_writel(mask, pxa->regbase + GPSR_OFFSET);
|
|
|
|
+ else
|
|
|
|
+ __raw_writel(mask, pxa->regbase + GPCR_OFFSET);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static struct pxa_gpio_chip pxa_gpio_chip[] = {
|
|
|
|
+ [0] = {
|
|
|
|
+ .regbase = GPIO0_BASE,
|
|
|
|
+ .chip = {
|
|
|
|
+ .label = "gpio-0",
|
|
|
|
+ .direction_input = pxa_gpio_direction_input,
|
|
|
|
+ .direction_output = pxa_gpio_direction_output,
|
|
|
|
+ .get = pxa_gpio_get,
|
|
|
|
+ .set = pxa_gpio_set,
|
|
|
|
+ .base = 0,
|
|
|
|
+ .ngpio = 32,
|
|
|
|
+ },
|
|
|
|
+ },
|
|
|
|
+ [1] = {
|
|
|
|
+ .regbase = GPIO1_BASE,
|
|
|
|
+ .chip = {
|
|
|
|
+ .label = "gpio-1",
|
|
|
|
+ .direction_input = pxa_gpio_direction_input,
|
|
|
|
+ .direction_output = pxa_gpio_direction_output,
|
|
|
|
+ .get = pxa_gpio_get,
|
|
|
|
+ .set = pxa_gpio_set,
|
|
|
|
+ .base = 32,
|
|
|
|
+ .ngpio = 32,
|
|
|
|
+ },
|
|
|
|
+ },
|
|
|
|
+ [2] = {
|
|
|
|
+ .regbase = GPIO2_BASE,
|
|
|
|
+ .chip = {
|
|
|
|
+ .label = "gpio-2",
|
|
|
|
+ .direction_input = pxa_gpio_direction_input,
|
|
|
|
+ .direction_output = pxa_gpio_direction_output,
|
|
|
|
+ .get = pxa_gpio_get,
|
|
|
|
+ .set = pxa_gpio_set,
|
|
|
|
+ .base = 64,
|
|
|
|
+ .ngpio = 32, /* 21 for PXA25x */
|
|
|
|
+ },
|
|
|
|
+ },
|
|
|
|
+#if defined(CONFIG_PXA27x) || defined(CONFIG_PXA3xx)
|
|
|
|
+ [3] = {
|
|
|
|
+ .regbase = GPIO3_BASE,
|
|
|
|
+ .chip = {
|
|
|
|
+ .label = "gpio-3",
|
|
|
|
+ .direction_input = pxa_gpio_direction_input,
|
|
|
|
+ .direction_output = pxa_gpio_direction_output,
|
|
|
|
+ .get = pxa_gpio_get,
|
|
|
|
+ .set = pxa_gpio_set,
|
|
|
|
+ .base = 96,
|
|
|
|
+ .ngpio = 32,
|
|
|
|
+ },
|
|
|
|
+ },
|
|
|
|
+#endif
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+void __init pxa_init_gpio(int gpio_nr)
|
|
|
|
+{
|
|
|
|
+ int i;
|
|
|
|
+
|
|
|
|
+ /* add a GPIO chip for each register bank.
|
|
|
|
+ * the last PXA25x register only contains 21 GPIOs
|
|
|
|
+ */
|
|
|
|
+ for (i = 0; i < gpio_nr; i += 32) {
|
|
|
|
+ if (i+32 > gpio_nr)
|
|
|
|
+ pxa_gpio_chip[i/32].chip.ngpio = gpio_nr - i;
|
|
|
|
+ gpiochip_add(&pxa_gpio_chip[i/32].chip);
|
|
|
|
+ }
|
|
|
|
+}
|