* [PATCH 1/3] gpio/spear-plgpio: Add plgpio driver for SPEAr platform
2011-05-30 6:39 [PATCH 0/3] SPEAr: Add plgpio support Viresh Kumar
@ 2011-05-30 6:39 ` Viresh Kumar
2011-05-30 6:39 ` [PATCH 2/3] SPEAr: Add machine support for plgpio Viresh Kumar
2011-05-30 6:39 ` [PATCH 3/3] SPEAr3xx: Update defconfig " Viresh Kumar
2 siblings, 0 replies; 9+ messages in thread
From: Viresh Kumar @ 2011-05-30 6:39 UTC (permalink / raw)
To: grant.likely
Cc: linux-arm-kernel, armando.visconti, shiraz.hashim, vipin.kumar,
rajeev-dlh.kumar, deepak.sikri, vipulkumar.samar, amit.virdi,
viresh.kumar, pratyush.anand, bhupesh.sharma, viresh.linux,
linux-kernel
Plgpio pads on few spear machines can be configured as gpios. This patch adds
support for configuring these PLGPIOs.
This was earlier posted & reviewed as part of arch/arm/plat-spear/ code.
Reviewed-by: Stanley Miao <stanley.miao@windriver.com>
Signed-off-by: Viresh Kumar <viresh.kumar@st.com>
---
MAINTAINERS | 7 +
drivers/gpio/Kconfig | 7 +
drivers/gpio/Makefile | 1 +
drivers/gpio/spear-plgpio.c | 490 ++++++++++++++++++++++++++++++++++++++++++
include/linux/spear-plgpio.h | 69 ++++++
5 files changed, 574 insertions(+), 0 deletions(-)
create mode 100644 drivers/gpio/spear-plgpio.c
create mode 100644 include/linux/spear-plgpio.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 29801f7..48b0a4f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5978,6 +5978,13 @@ F: arch/arm/mach-spear3xx/spear3*0_evb.c
F: arch/arm/mach-spear6xx/spear600.c
F: arch/arm/mach-spear6xx/spear600_evb.c
+SPEAR PLGPIO SUPPORT
+M: Viresh Kumar <viresh.kumar@st.com>
+W: http://www.st.com/spear
+S: Maintained
+F: drivers/gpio/spear-plgpio.c
+F: include/linux/spear-plgpio.h
+
SPECIALIX IO8+ MULTIPORT SERIAL CARD DRIVER
S: Orphan
F: Documentation/serial/specialix.txt
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 4a7f631..227d2e7 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -164,6 +164,13 @@ config GPIO_VX855
additional drivers must be enabled in order to use the
functionality of the device.
+config SPEAR_PLGPIO
+ bool "ST Micro SPEAr PLGPIO"
+ depends on PLAT_SPEAR
+ help
+ This enables support for the PLGPIOs found on the ST Microelectronics
+ SPEAr platform
+
comment "I2C GPIO expanders:"
config GPIO_MAX7300
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index b605f8e..2d325b0 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -48,3 +48,4 @@ obj-$(CONFIG_GPIO_VX855) += vx855_gpio.o
obj-$(CONFIG_GPIO_ML_IOH) += ml_ioh_gpio.o
obj-$(CONFIG_AB8500_GPIO) += ab8500-gpio.o
obj-$(CONFIG_GPIO_TPS65910) += tps65910-gpio.o
+obj-$(CONFIG_SPEAR_PLGPIO) += spear-plgpio.o
diff --git a/drivers/gpio/spear-plgpio.c b/drivers/gpio/spear-plgpio.c
new file mode 100644
index 0000000..885c0da
--- /dev/null
+++ b/drivers/gpio/spear-plgpio.c
@@ -0,0 +1,490 @@
+/*
+ * drivers/gpio/spear-plgpio.c
+ *
+ * SPEAr platform PLGPIO driver source file
+ *
+ * Copyright (C) 2010-2011 ST Microelectronics
+ * Viresh Kumar<viresh.kumar@st.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/bitops.h>
+#include <linux/compiler.h>
+#include <linux/errno.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spear-plgpio.h>
+#include <linux/spinlock.h>
+
+#define MAX_GPIO_PER_REG 32
+#define PIN_OFFSET(pin) (pin % MAX_GPIO_PER_REG)
+#define REG_OFFSET(base, reg, pin) (base + reg + (pin / MAX_GPIO_PER_REG)\
+ * sizeof(int *))
+
+/*
+ * struct plgpio: plgpio driver specific structure
+ *
+ * lock: lock for guarding gpio registers
+ * base: base address of plgpio block
+ * irq_base: irq number of plgpio0
+ * chip: gpio framework specific chip information structure
+ * p2o: function ptr for pin to offset conversion. This is required only for
+ * machines where mapping b/w pin and offset is not 1-to-1.
+ * o2p: function ptr for offset to pin conversion. This is required only for
+ * machines where mapping b/w pin and offset is not 1-to-1.
+ * p2o_regs: mask of registers for which p2o and o2p are applicable
+ * regs: register offsets
+ * irq_trigger_type: irq type supported
+ */
+struct plgpio {
+ spinlock_t lock;
+ void __iomem *base;
+ unsigned irq_base;
+ struct gpio_chip chip;
+ int (*p2o)(int pin); /* pin_to_offset */
+ int (*o2p)(int offset); /* offset_to_pin */
+ unsigned p2o_regs;
+ struct plgpio_regs regs;
+ unsigned irq_trigger_type;
+};
+
+/* register manipulation inline functions */
+static inline u32 is_plgpio_set(void __iomem *base, u32 pin, u32 reg)
+{
+ u32 offset = PIN_OFFSET(pin);
+ void __iomem *reg_off = REG_OFFSET(base, reg, pin);
+ u32 val = readl(reg_off);
+
+ return val & (1 << offset);
+}
+
+static inline void plgpio_reg_set(void __iomem *base, u32 pin, u32 reg)
+{
+ u32 offset = PIN_OFFSET(pin);
+ void __iomem *reg_off = REG_OFFSET(base, reg, pin);
+ u32 val = readl(reg_off);
+
+ writel(val | (1 << offset), reg_off);
+}
+
+static inline void plgpio_reg_reset(void __iomem *base, u32 pin, u32 reg)
+{
+ u32 offset = PIN_OFFSET(pin);
+ void __iomem *reg_off = REG_OFFSET(base, reg, pin);
+ u32 val = readl(reg_off);
+
+ writel(val & ~(1 << offset), reg_off);
+}
+
+/* gpio framework specific routines */
+static int plgpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+ struct plgpio *plgpio = container_of(chip, struct plgpio, chip);
+ unsigned long flags;
+
+ if (offset >= chip->ngpio)
+ return -EINVAL;
+
+ /* get correct offset for "offset" pin */
+ if (plgpio->p2o && (plgpio->p2o_regs & PTO_DIR_REG)) {
+ offset = plgpio->p2o(offset);
+ if (offset == -1)
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&plgpio->lock, flags);
+ plgpio_reg_set(plgpio->base, offset, plgpio->regs.dir);
+ spin_unlock_irqrestore(&plgpio->lock, flags);
+
+ return 0;
+}
+
+static int plgpio_direction_output(struct gpio_chip *chip, unsigned offset,
+ int value)
+{
+ struct plgpio *plgpio = container_of(chip, struct plgpio, chip);
+ unsigned long flags;
+ unsigned dir_offset = offset, wdata_offset = offset, tmp;
+
+ if (offset >= chip->ngpio)
+ return -EINVAL;
+
+ /* get correct offset for "offset" pin */
+ if (plgpio->p2o && (plgpio->p2o_regs & (PTO_DIR_REG | PTO_WDATA_REG))) {
+ tmp = plgpio->p2o(offset);
+ if (tmp == -1)
+ return -EINVAL;
+
+ if (plgpio->p2o_regs & PTO_DIR_REG)
+ dir_offset = tmp;
+ if (plgpio->p2o_regs & PTO_WDATA_REG)
+ wdata_offset = tmp;
+ }
+
+ spin_lock_irqsave(&plgpio->lock, flags);
+ plgpio_reg_reset(plgpio->base, dir_offset, plgpio->regs.dir);
+ if (value)
+ plgpio_reg_set(plgpio->base, wdata_offset,
+ plgpio->regs.wdata);
+ else
+ plgpio_reg_reset(plgpio->base, wdata_offset,
+ plgpio->regs.wdata);
+ spin_unlock_irqrestore(&plgpio->lock, flags);
+
+ return 0;
+}
+
+static int plgpio_get_value(struct gpio_chip *chip, unsigned offset)
+{
+ struct plgpio *plgpio = container_of(chip, struct plgpio, chip);
+
+ if (offset >= chip->ngpio)
+ return -EINVAL;
+
+ /* get correct offset for "offset" pin */
+ if (plgpio->p2o && (plgpio->p2o_regs & PTO_RDATA_REG)) {
+ offset = plgpio->p2o(offset);
+ if (offset == -1)
+ return -EINVAL;
+ }
+
+ return is_plgpio_set(plgpio->base, offset, plgpio->regs.rdata);
+}
+
+static void plgpio_set_value(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct plgpio *plgpio = container_of(chip, struct plgpio, chip);
+
+ if (offset >= chip->ngpio)
+ return;
+
+ /* get correct offset for "offset" pin */
+ if (plgpio->p2o && (plgpio->p2o_regs & PTO_WDATA_REG)) {
+ offset = plgpio->p2o(offset);
+ if (offset == -1)
+ return;
+ }
+
+ if (value)
+ plgpio_reg_set(plgpio->base, offset, plgpio->regs.wdata);
+ else
+ plgpio_reg_reset(plgpio->base, offset, plgpio->regs.wdata);
+}
+
+static int plgpio_request(struct gpio_chip *chip, unsigned offset)
+{
+ struct plgpio *plgpio = container_of(chip, struct plgpio, chip);
+ unsigned long flags;
+ int ret = 0;
+
+ if (offset >= chip->ngpio)
+ return -EINVAL;
+
+ /*
+ * put gpio in IN mode before enabling it. This make enabling gpio safe
+ */
+ ret = plgpio_direction_input(chip, offset);
+ if (ret)
+ return ret;
+
+ /* get correct offset for "offset" pin */
+ if (plgpio->p2o && (plgpio->p2o_regs & PTO_ENB_REG)) {
+ offset = plgpio->p2o(offset);
+ if (offset == -1)
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&plgpio->lock, flags);
+ plgpio_reg_set(plgpio->base, offset, plgpio->regs.enb);
+ spin_unlock_irqrestore(&plgpio->lock, flags);
+
+ return 0;
+}
+
+static void plgpio_free(struct gpio_chip *chip, unsigned offset)
+{
+ struct plgpio *plgpio = container_of(chip, struct plgpio, chip);
+ unsigned long flags;
+
+ if (offset >= chip->ngpio)
+ return;
+
+ /* get correct offset for "offset" pin */
+ if (plgpio->p2o && (plgpio->p2o_regs & PTO_ENB_REG)) {
+ offset = plgpio->p2o(offset);
+ if (offset == -1)
+ return;
+ }
+
+ spin_lock_irqsave(&plgpio->lock, flags);
+ plgpio_reg_reset(plgpio->base, offset, plgpio->regs.enb);
+ spin_unlock_irqrestore(&plgpio->lock, flags);
+}
+
+static int plgpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ struct plgpio *plgpio = container_of(chip, struct plgpio, chip);
+
+ if (plgpio->irq_base == (unsigned) -1)
+ return -EINVAL;
+
+ return plgpio->irq_base + offset;
+}
+
+/* PLGPIO IRQ */
+static void plgpio_irq_mask(struct irq_data *d)
+{
+ struct plgpio *plgpio = irq_data_get_irq_chip_data(d);
+ int offset = d->irq - plgpio->irq_base;
+ unsigned long flags;
+
+ /* get correct offset for "offset" pin */
+ if (plgpio->p2o && (plgpio->p2o_regs & PTO_IE_REG)) {
+ offset = plgpio->p2o(offset);
+ if (offset == -1)
+ return;
+ }
+
+ spin_lock_irqsave(&plgpio->lock, flags);
+ plgpio_reg_set(plgpio->base, offset, plgpio->regs.ie);
+ spin_unlock_irqrestore(&plgpio->lock, flags);
+}
+
+static void plgpio_irq_unmask(struct irq_data *d)
+{
+ struct plgpio *plgpio = irq_data_get_irq_chip_data(d);
+ int offset = d->irq - plgpio->irq_base;
+ unsigned long flags;
+
+ /* get correct offset for "offset" pin */
+ if (plgpio->p2o && (plgpio->p2o_regs & PTO_IE_REG)) {
+ offset = plgpio->p2o(offset);
+ if (offset == -1)
+ return;
+ }
+
+ spin_lock_irqsave(&plgpio->lock, flags);
+ plgpio_reg_reset(plgpio->base, offset, plgpio->regs.ie);
+ spin_unlock_irqrestore(&plgpio->lock, flags);
+}
+
+static int plgpio_irq_type(struct irq_data *d, unsigned trigger)
+{
+ struct plgpio *plgpio = irq_data_get_irq_chip_data(d);
+ int offset = d->irq - plgpio->irq_base;
+
+ if (!(plgpio->irq_trigger_type & trigger))
+ return -EINVAL;
+ if (offset >= plgpio->chip.ngpio)
+ return -EINVAL;
+
+ /*
+ * Currently we only need to configure register in case of edge
+ * interrupt
+ */
+ if (plgpio->irq_trigger_type == (IRQ_TYPE_EDGE_RISING |
+ IRQ_TYPE_EDGE_FALLING)) {
+ void __iomem *reg_off = REG_OFFSET(plgpio->base,
+ plgpio->regs.eit, offset);
+ u32 val = readl(reg_off);
+
+ offset = PIN_OFFSET(offset);
+ if (trigger == IRQ_TYPE_EDGE_RISING)
+ writel(val | (1 << offset), reg_off);
+ else
+ writel(val & ~(1 << offset), reg_off);
+ }
+
+ return 0;
+}
+
+static struct irq_chip plgpio_irqchip = {
+ .name = "PLGPIO",
+ .irq_mask = plgpio_irq_mask,
+ .irq_unmask = plgpio_irq_unmask,
+ .irq_set_type = plgpio_irq_type,
+};
+
+static void plgpio_irq_handler(unsigned irq, struct irq_desc *desc)
+{
+ struct plgpio *plgpio = irq_get_handler_data(irq);
+ int regs_count = DIV_ROUND_UP(plgpio->chip.ngpio, MAX_GPIO_PER_REG),
+ count, pin, offset, i = 0;
+ unsigned long pending;
+
+ desc->irq_data.chip->irq_ack(&desc->irq_data);
+ /* check all plgpio MIS registers for a possible interrupt */
+ for (; i < regs_count; i++) {
+ pending = readl(plgpio->base + plgpio->regs.mis +
+ i * sizeof(int *));
+ if (!pending)
+ continue;
+
+ /* clear interrupts */
+ writel(~pending, plgpio->base + plgpio->regs.mis +
+ i * sizeof(int *));
+ /*
+ * clear extra bits in last register having gpios < MAX/REG
+ * ex: Suppose there are max 102 plgpios. then last register
+ * must have only (102 - MAX_GPIO_PER_REG * 3) = 6 relevant bits
+ * so, we must not take other 28 bits into consideration for
+ * checking interrupt. so clear those bits.
+ */
+ count = plgpio->chip.ngpio - i * MAX_GPIO_PER_REG;
+ if (count < MAX_GPIO_PER_REG)
+ pending &= (1 << count) - 1;
+
+ for_each_set_bit(offset, &pending, MAX_GPIO_PER_REG) {
+ /* get correct pin for "offset" */
+ if (plgpio->o2p && (plgpio->p2o_regs & PTO_MIS_REG)) {
+ pin = plgpio->o2p(offset);
+ if (pin == -1)
+ continue;
+ } else
+ pin = offset;
+
+ generic_handle_irq(plgpio_to_irq(&plgpio->chip,
+ i * MAX_GPIO_PER_REG + pin));
+ }
+ }
+ desc->irq_data.chip->irq_unmask(&desc->irq_data);
+}
+
+static int __devinit plgpio_probe(struct platform_device *pdev)
+{
+ struct spear_plgpio_pdata *pdata;
+ struct plgpio *plgpio;
+ int ret, irq, i;
+ struct resource *res;
+
+ pdata = pdev->dev.platform_data;
+ if (!pdata) {
+ ret = -ENODEV;
+ dev_dbg(&pdev->dev, "invalid platform data\n");
+ goto fail;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ ret = -EBUSY;
+ dev_dbg(&pdev->dev, "invalid IORESOURCE_MEM\n");
+ goto fail;
+ }
+
+ if (!request_mem_region(res->start, resource_size(res), "plgpio")) {
+ ret = -EBUSY;
+ dev_dbg(&pdev->dev, "request mem region fail\n");
+ goto fail;
+ }
+
+ plgpio = kzalloc(sizeof(*plgpio), GFP_KERNEL);
+ if (!plgpio) {
+ ret = -ENOMEM;
+ dev_dbg(&pdev->dev, "memory allocation fail\n");
+ goto release_region;
+ }
+
+ plgpio->base = ioremap(res->start, resource_size(res));
+ if (!plgpio->base) {
+ ret = -ENOMEM;
+ dev_dbg(&pdev->dev, "ioremap fail\n");
+ goto kfree;
+ }
+
+ spin_lock_init(&plgpio->lock);
+
+ plgpio->chip.request = plgpio_request;
+ plgpio->chip.free = plgpio_free;
+ plgpio->chip.direction_input = plgpio_direction_input;
+ plgpio->chip.direction_output = plgpio_direction_output;
+ plgpio->chip.get = plgpio_get_value;
+ plgpio->chip.set = plgpio_set_value;
+ plgpio->chip.to_irq = plgpio_to_irq;
+ plgpio->chip.base = pdata->gpio_base;
+ plgpio->chip.ngpio = pdata->gpio_count;
+ plgpio->chip.label = dev_name(&pdev->dev);
+ plgpio->chip.dev = &pdev->dev;
+ plgpio->chip.owner = THIS_MODULE;
+ plgpio->irq_base = pdata->irq_base;
+ plgpio->p2o = pdata->p2o;
+ plgpio->o2p = pdata->o2p;
+ plgpio->p2o_regs = pdata->p2o_regs;
+ plgpio->regs.enb = pdata->regs.enb;
+ plgpio->regs.wdata = pdata->regs.wdata;
+ plgpio->regs.dir = pdata->regs.dir;
+ plgpio->regs.ie = pdata->regs.ie;
+ plgpio->regs.rdata = pdata->regs.rdata;
+ plgpio->regs.mis = pdata->regs.mis;
+ plgpio->irq_trigger_type = pdata->irq_trigger_type;
+
+ ret = gpiochip_add(&plgpio->chip);
+ if (ret) {
+ dev_dbg(&pdev->dev, "unable to add gpio chip\n");
+ goto iounmap;
+ }
+
+ /* irq_chip support */
+ if (pdata->irq_base == (unsigned) -1) {
+ dev_info(&pdev->dev, "Successfully registered without irqs\n");
+ return 0;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ ret = -ENODEV;
+ dev_dbg(&pdev->dev, "invalid irq number\n");
+ goto remove_gpiochip;
+ }
+
+ irq_set_chained_handler(irq, plgpio_irq_handler);
+ for (i = 0; i < pdata->gpio_count; i++) {
+ irq_set_chip_and_handler(i+plgpio->irq_base, &plgpio_irqchip,
+ handle_simple_irq);
+ set_irq_flags(i+plgpio->irq_base, IRQF_VALID);
+ irq_set_chip_data(i+plgpio->irq_base, plgpio);
+ }
+ irq_set_handler_data(irq, plgpio);
+ dev_info(&pdev->dev, "Successfully registered with irqs\n");
+
+ return 0;
+
+remove_gpiochip:
+ if (gpiochip_remove(&plgpio->chip))
+ dev_dbg(&pdev->dev, "unable to remove gpiochip\n");
+iounmap:
+ iounmap(plgpio->base);
+kfree:
+ kfree(plgpio);
+release_region:
+ release_mem_region(res->start, resource_size(res));
+fail:
+ dev_err(&pdev->dev, "probe fail: %d\n", ret);
+ return ret;
+}
+
+static struct platform_driver plgpio_driver = {
+ .probe = plgpio_probe,
+ .driver = {
+ .name = "spear-plgpio",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init plgpio_init(void)
+{
+ return platform_driver_register(&plgpio_driver);
+}
+subsys_initcall(plgpio_init);
+
+MODULE_AUTHOR("Viresh Kumar <viresh.kumar@st.com>");
+MODULE_DESCRIPTION("SPEAr PLGPIO driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/spear-plgpio.h b/include/linux/spear-plgpio.h
new file mode 100644
index 0000000..a04986a
--- /dev/null
+++ b/include/linux/spear-plgpio.h
@@ -0,0 +1,69 @@
+/*
+ * include/linux/spear-plgpio.h
+ *
+ * SPEAr platform PLGPIO driver header file
+ *
+ * Copyright (C) 2010-2011 ST Microelectronics
+ * Viresh Kumar<viresh.kumar@st.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __SPEAR_PLGPIO_H
+#define __SPEAR_PLGPIO_H
+
+#include <linux/types.h>
+
+/* plgpio driver declarations */
+/*
+ * plgpio pins in all machines are not one to one mapped, bitwise with
+ * registers bits. These set of macros define register masks for which below
+ * functions (pin_to_offset and offset_to_pin) are required to be called.
+ */
+#define PTO_ENB_REG 0x001
+#define PTO_WDATA_REG 0x002
+#define PTO_DIR_REG 0x004
+#define PTO_IE_REG 0x008
+#define PTO_RDATA_REG 0x010
+#define PTO_MIS_REG 0x020
+
+/* plgpio registers */
+struct plgpio_regs {
+ u32 enb; /* enable register */
+ u32 wdata; /* write data register */
+ u32 dir; /* direction set register */
+ u32 rdata; /* read data register */
+ u32 ie; /* interrupt enable register */
+ u32 mis; /* mask interrupt status register */
+ u32 eit; /* edge interrupt type */
+};
+
+/**
+ * struct spear_plgpio_pdata: plgpio driver platform data
+ *
+ * gpio_base: gpio start number of plgpios
+ * irq_base: irq number of plgpio0
+ * gpio_count: total count of plgpios
+ * p2o: function ptr for pin to offset conversion. This is required only for
+ * machines where mapping b/w pin and offset is not 1-to-1.
+ * o2p: function ptr for offset to pin conversion. This is required only for
+ * machines where mapping b/w pin and offset is not 1-to-1.
+ * p2o_regs: mask of registers for which p2o and o2p are applicable
+ * regs: register offsets
+ * irq_trigger_type: irq type supported
+ */
+struct spear_plgpio_pdata {
+ u32 gpio_base;
+ u32 irq_base;
+ u32 gpio_count;
+ int (*p2o)(int pin); /* pin_to_offset */
+ int (*o2p)(int offset); /* offset_to_pin */
+ u32 p2o_regs;
+ struct plgpio_regs regs;
+ unsigned irq_trigger_type;
+};
+
+#endif /* __SPEAR_PLGPIO_H */
+
--
1.7.2.2
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 1/3] gpio/spear-plgpio: Add plgpio driver for SPEAr platform
@ 2011-05-30 6:39 ` Viresh Kumar
0 siblings, 0 replies; 9+ messages in thread
From: Viresh Kumar @ 2011-05-30 6:39 UTC (permalink / raw)
To: linux-arm-kernel
Plgpio pads on few spear machines can be configured as gpios. This patch adds
support for configuring these PLGPIOs.
This was earlier posted & reviewed as part of arch/arm/plat-spear/ code.
Reviewed-by: Stanley Miao <stanley.miao@windriver.com>
Signed-off-by: Viresh Kumar <viresh.kumar@st.com>
---
MAINTAINERS | 7 +
drivers/gpio/Kconfig | 7 +
drivers/gpio/Makefile | 1 +
drivers/gpio/spear-plgpio.c | 490 ++++++++++++++++++++++++++++++++++++++++++
include/linux/spear-plgpio.h | 69 ++++++
5 files changed, 574 insertions(+), 0 deletions(-)
create mode 100644 drivers/gpio/spear-plgpio.c
create mode 100644 include/linux/spear-plgpio.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 29801f7..48b0a4f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5978,6 +5978,13 @@ F: arch/arm/mach-spear3xx/spear3*0_evb.c
F: arch/arm/mach-spear6xx/spear600.c
F: arch/arm/mach-spear6xx/spear600_evb.c
+SPEAR PLGPIO SUPPORT
+M: Viresh Kumar <viresh.kumar@st.com>
+W: http://www.st.com/spear
+S: Maintained
+F: drivers/gpio/spear-plgpio.c
+F: include/linux/spear-plgpio.h
+
SPECIALIX IO8+ MULTIPORT SERIAL CARD DRIVER
S: Orphan
F: Documentation/serial/specialix.txt
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 4a7f631..227d2e7 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -164,6 +164,13 @@ config GPIO_VX855
additional drivers must be enabled in order to use the
functionality of the device.
+config SPEAR_PLGPIO
+ bool "ST Micro SPEAr PLGPIO"
+ depends on PLAT_SPEAR
+ help
+ This enables support for the PLGPIOs found on the ST Microelectronics
+ SPEAr platform
+
comment "I2C GPIO expanders:"
config GPIO_MAX7300
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index b605f8e..2d325b0 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -48,3 +48,4 @@ obj-$(CONFIG_GPIO_VX855) += vx855_gpio.o
obj-$(CONFIG_GPIO_ML_IOH) += ml_ioh_gpio.o
obj-$(CONFIG_AB8500_GPIO) += ab8500-gpio.o
obj-$(CONFIG_GPIO_TPS65910) += tps65910-gpio.o
+obj-$(CONFIG_SPEAR_PLGPIO) += spear-plgpio.o
diff --git a/drivers/gpio/spear-plgpio.c b/drivers/gpio/spear-plgpio.c
new file mode 100644
index 0000000..885c0da
--- /dev/null
+++ b/drivers/gpio/spear-plgpio.c
@@ -0,0 +1,490 @@
+/*
+ * drivers/gpio/spear-plgpio.c
+ *
+ * SPEAr platform PLGPIO driver source file
+ *
+ * Copyright (C) 2010-2011 ST Microelectronics
+ * Viresh Kumar<viresh.kumar@st.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/bitops.h>
+#include <linux/compiler.h>
+#include <linux/errno.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spear-plgpio.h>
+#include <linux/spinlock.h>
+
+#define MAX_GPIO_PER_REG 32
+#define PIN_OFFSET(pin) (pin % MAX_GPIO_PER_REG)
+#define REG_OFFSET(base, reg, pin) (base + reg + (pin / MAX_GPIO_PER_REG)\
+ * sizeof(int *))
+
+/*
+ * struct plgpio: plgpio driver specific structure
+ *
+ * lock: lock for guarding gpio registers
+ * base: base address of plgpio block
+ * irq_base: irq number of plgpio0
+ * chip: gpio framework specific chip information structure
+ * p2o: function ptr for pin to offset conversion. This is required only for
+ * machines where mapping b/w pin and offset is not 1-to-1.
+ * o2p: function ptr for offset to pin conversion. This is required only for
+ * machines where mapping b/w pin and offset is not 1-to-1.
+ * p2o_regs: mask of registers for which p2o and o2p are applicable
+ * regs: register offsets
+ * irq_trigger_type: irq type supported
+ */
+struct plgpio {
+ spinlock_t lock;
+ void __iomem *base;
+ unsigned irq_base;
+ struct gpio_chip chip;
+ int (*p2o)(int pin); /* pin_to_offset */
+ int (*o2p)(int offset); /* offset_to_pin */
+ unsigned p2o_regs;
+ struct plgpio_regs regs;
+ unsigned irq_trigger_type;
+};
+
+/* register manipulation inline functions */
+static inline u32 is_plgpio_set(void __iomem *base, u32 pin, u32 reg)
+{
+ u32 offset = PIN_OFFSET(pin);
+ void __iomem *reg_off = REG_OFFSET(base, reg, pin);
+ u32 val = readl(reg_off);
+
+ return val & (1 << offset);
+}
+
+static inline void plgpio_reg_set(void __iomem *base, u32 pin, u32 reg)
+{
+ u32 offset = PIN_OFFSET(pin);
+ void __iomem *reg_off = REG_OFFSET(base, reg, pin);
+ u32 val = readl(reg_off);
+
+ writel(val | (1 << offset), reg_off);
+}
+
+static inline void plgpio_reg_reset(void __iomem *base, u32 pin, u32 reg)
+{
+ u32 offset = PIN_OFFSET(pin);
+ void __iomem *reg_off = REG_OFFSET(base, reg, pin);
+ u32 val = readl(reg_off);
+
+ writel(val & ~(1 << offset), reg_off);
+}
+
+/* gpio framework specific routines */
+static int plgpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+ struct plgpio *plgpio = container_of(chip, struct plgpio, chip);
+ unsigned long flags;
+
+ if (offset >= chip->ngpio)
+ return -EINVAL;
+
+ /* get correct offset for "offset" pin */
+ if (plgpio->p2o && (plgpio->p2o_regs & PTO_DIR_REG)) {
+ offset = plgpio->p2o(offset);
+ if (offset == -1)
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&plgpio->lock, flags);
+ plgpio_reg_set(plgpio->base, offset, plgpio->regs.dir);
+ spin_unlock_irqrestore(&plgpio->lock, flags);
+
+ return 0;
+}
+
+static int plgpio_direction_output(struct gpio_chip *chip, unsigned offset,
+ int value)
+{
+ struct plgpio *plgpio = container_of(chip, struct plgpio, chip);
+ unsigned long flags;
+ unsigned dir_offset = offset, wdata_offset = offset, tmp;
+
+ if (offset >= chip->ngpio)
+ return -EINVAL;
+
+ /* get correct offset for "offset" pin */
+ if (plgpio->p2o && (plgpio->p2o_regs & (PTO_DIR_REG | PTO_WDATA_REG))) {
+ tmp = plgpio->p2o(offset);
+ if (tmp == -1)
+ return -EINVAL;
+
+ if (plgpio->p2o_regs & PTO_DIR_REG)
+ dir_offset = tmp;
+ if (plgpio->p2o_regs & PTO_WDATA_REG)
+ wdata_offset = tmp;
+ }
+
+ spin_lock_irqsave(&plgpio->lock, flags);
+ plgpio_reg_reset(plgpio->base, dir_offset, plgpio->regs.dir);
+ if (value)
+ plgpio_reg_set(plgpio->base, wdata_offset,
+ plgpio->regs.wdata);
+ else
+ plgpio_reg_reset(plgpio->base, wdata_offset,
+ plgpio->regs.wdata);
+ spin_unlock_irqrestore(&plgpio->lock, flags);
+
+ return 0;
+}
+
+static int plgpio_get_value(struct gpio_chip *chip, unsigned offset)
+{
+ struct plgpio *plgpio = container_of(chip, struct plgpio, chip);
+
+ if (offset >= chip->ngpio)
+ return -EINVAL;
+
+ /* get correct offset for "offset" pin */
+ if (plgpio->p2o && (plgpio->p2o_regs & PTO_RDATA_REG)) {
+ offset = plgpio->p2o(offset);
+ if (offset == -1)
+ return -EINVAL;
+ }
+
+ return is_plgpio_set(plgpio->base, offset, plgpio->regs.rdata);
+}
+
+static void plgpio_set_value(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct plgpio *plgpio = container_of(chip, struct plgpio, chip);
+
+ if (offset >= chip->ngpio)
+ return;
+
+ /* get correct offset for "offset" pin */
+ if (plgpio->p2o && (plgpio->p2o_regs & PTO_WDATA_REG)) {
+ offset = plgpio->p2o(offset);
+ if (offset == -1)
+ return;
+ }
+
+ if (value)
+ plgpio_reg_set(plgpio->base, offset, plgpio->regs.wdata);
+ else
+ plgpio_reg_reset(plgpio->base, offset, plgpio->regs.wdata);
+}
+
+static int plgpio_request(struct gpio_chip *chip, unsigned offset)
+{
+ struct plgpio *plgpio = container_of(chip, struct plgpio, chip);
+ unsigned long flags;
+ int ret = 0;
+
+ if (offset >= chip->ngpio)
+ return -EINVAL;
+
+ /*
+ * put gpio in IN mode before enabling it. This make enabling gpio safe
+ */
+ ret = plgpio_direction_input(chip, offset);
+ if (ret)
+ return ret;
+
+ /* get correct offset for "offset" pin */
+ if (plgpio->p2o && (plgpio->p2o_regs & PTO_ENB_REG)) {
+ offset = plgpio->p2o(offset);
+ if (offset == -1)
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&plgpio->lock, flags);
+ plgpio_reg_set(plgpio->base, offset, plgpio->regs.enb);
+ spin_unlock_irqrestore(&plgpio->lock, flags);
+
+ return 0;
+}
+
+static void plgpio_free(struct gpio_chip *chip, unsigned offset)
+{
+ struct plgpio *plgpio = container_of(chip, struct plgpio, chip);
+ unsigned long flags;
+
+ if (offset >= chip->ngpio)
+ return;
+
+ /* get correct offset for "offset" pin */
+ if (plgpio->p2o && (plgpio->p2o_regs & PTO_ENB_REG)) {
+ offset = plgpio->p2o(offset);
+ if (offset == -1)
+ return;
+ }
+
+ spin_lock_irqsave(&plgpio->lock, flags);
+ plgpio_reg_reset(plgpio->base, offset, plgpio->regs.enb);
+ spin_unlock_irqrestore(&plgpio->lock, flags);
+}
+
+static int plgpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ struct plgpio *plgpio = container_of(chip, struct plgpio, chip);
+
+ if (plgpio->irq_base == (unsigned) -1)
+ return -EINVAL;
+
+ return plgpio->irq_base + offset;
+}
+
+/* PLGPIO IRQ */
+static void plgpio_irq_mask(struct irq_data *d)
+{
+ struct plgpio *plgpio = irq_data_get_irq_chip_data(d);
+ int offset = d->irq - plgpio->irq_base;
+ unsigned long flags;
+
+ /* get correct offset for "offset" pin */
+ if (plgpio->p2o && (plgpio->p2o_regs & PTO_IE_REG)) {
+ offset = plgpio->p2o(offset);
+ if (offset == -1)
+ return;
+ }
+
+ spin_lock_irqsave(&plgpio->lock, flags);
+ plgpio_reg_set(plgpio->base, offset, plgpio->regs.ie);
+ spin_unlock_irqrestore(&plgpio->lock, flags);
+}
+
+static void plgpio_irq_unmask(struct irq_data *d)
+{
+ struct plgpio *plgpio = irq_data_get_irq_chip_data(d);
+ int offset = d->irq - plgpio->irq_base;
+ unsigned long flags;
+
+ /* get correct offset for "offset" pin */
+ if (plgpio->p2o && (plgpio->p2o_regs & PTO_IE_REG)) {
+ offset = plgpio->p2o(offset);
+ if (offset == -1)
+ return;
+ }
+
+ spin_lock_irqsave(&plgpio->lock, flags);
+ plgpio_reg_reset(plgpio->base, offset, plgpio->regs.ie);
+ spin_unlock_irqrestore(&plgpio->lock, flags);
+}
+
+static int plgpio_irq_type(struct irq_data *d, unsigned trigger)
+{
+ struct plgpio *plgpio = irq_data_get_irq_chip_data(d);
+ int offset = d->irq - plgpio->irq_base;
+
+ if (!(plgpio->irq_trigger_type & trigger))
+ return -EINVAL;
+ if (offset >= plgpio->chip.ngpio)
+ return -EINVAL;
+
+ /*
+ * Currently we only need to configure register in case of edge
+ * interrupt
+ */
+ if (plgpio->irq_trigger_type == (IRQ_TYPE_EDGE_RISING |
+ IRQ_TYPE_EDGE_FALLING)) {
+ void __iomem *reg_off = REG_OFFSET(plgpio->base,
+ plgpio->regs.eit, offset);
+ u32 val = readl(reg_off);
+
+ offset = PIN_OFFSET(offset);
+ if (trigger == IRQ_TYPE_EDGE_RISING)
+ writel(val | (1 << offset), reg_off);
+ else
+ writel(val & ~(1 << offset), reg_off);
+ }
+
+ return 0;
+}
+
+static struct irq_chip plgpio_irqchip = {
+ .name = "PLGPIO",
+ .irq_mask = plgpio_irq_mask,
+ .irq_unmask = plgpio_irq_unmask,
+ .irq_set_type = plgpio_irq_type,
+};
+
+static void plgpio_irq_handler(unsigned irq, struct irq_desc *desc)
+{
+ struct plgpio *plgpio = irq_get_handler_data(irq);
+ int regs_count = DIV_ROUND_UP(plgpio->chip.ngpio, MAX_GPIO_PER_REG),
+ count, pin, offset, i = 0;
+ unsigned long pending;
+
+ desc->irq_data.chip->irq_ack(&desc->irq_data);
+ /* check all plgpio MIS registers for a possible interrupt */
+ for (; i < regs_count; i++) {
+ pending = readl(plgpio->base + plgpio->regs.mis +
+ i * sizeof(int *));
+ if (!pending)
+ continue;
+
+ /* clear interrupts */
+ writel(~pending, plgpio->base + plgpio->regs.mis +
+ i * sizeof(int *));
+ /*
+ * clear extra bits in last register having gpios < MAX/REG
+ * ex: Suppose there are max 102 plgpios. then last register
+ * must have only (102 - MAX_GPIO_PER_REG * 3) = 6 relevant bits
+ * so, we must not take other 28 bits into consideration for
+ * checking interrupt. so clear those bits.
+ */
+ count = plgpio->chip.ngpio - i * MAX_GPIO_PER_REG;
+ if (count < MAX_GPIO_PER_REG)
+ pending &= (1 << count) - 1;
+
+ for_each_set_bit(offset, &pending, MAX_GPIO_PER_REG) {
+ /* get correct pin for "offset" */
+ if (plgpio->o2p && (plgpio->p2o_regs & PTO_MIS_REG)) {
+ pin = plgpio->o2p(offset);
+ if (pin == -1)
+ continue;
+ } else
+ pin = offset;
+
+ generic_handle_irq(plgpio_to_irq(&plgpio->chip,
+ i * MAX_GPIO_PER_REG + pin));
+ }
+ }
+ desc->irq_data.chip->irq_unmask(&desc->irq_data);
+}
+
+static int __devinit plgpio_probe(struct platform_device *pdev)
+{
+ struct spear_plgpio_pdata *pdata;
+ struct plgpio *plgpio;
+ int ret, irq, i;
+ struct resource *res;
+
+ pdata = pdev->dev.platform_data;
+ if (!pdata) {
+ ret = -ENODEV;
+ dev_dbg(&pdev->dev, "invalid platform data\n");
+ goto fail;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ ret = -EBUSY;
+ dev_dbg(&pdev->dev, "invalid IORESOURCE_MEM\n");
+ goto fail;
+ }
+
+ if (!request_mem_region(res->start, resource_size(res), "plgpio")) {
+ ret = -EBUSY;
+ dev_dbg(&pdev->dev, "request mem region fail\n");
+ goto fail;
+ }
+
+ plgpio = kzalloc(sizeof(*plgpio), GFP_KERNEL);
+ if (!plgpio) {
+ ret = -ENOMEM;
+ dev_dbg(&pdev->dev, "memory allocation fail\n");
+ goto release_region;
+ }
+
+ plgpio->base = ioremap(res->start, resource_size(res));
+ if (!plgpio->base) {
+ ret = -ENOMEM;
+ dev_dbg(&pdev->dev, "ioremap fail\n");
+ goto kfree;
+ }
+
+ spin_lock_init(&plgpio->lock);
+
+ plgpio->chip.request = plgpio_request;
+ plgpio->chip.free = plgpio_free;
+ plgpio->chip.direction_input = plgpio_direction_input;
+ plgpio->chip.direction_output = plgpio_direction_output;
+ plgpio->chip.get = plgpio_get_value;
+ plgpio->chip.set = plgpio_set_value;
+ plgpio->chip.to_irq = plgpio_to_irq;
+ plgpio->chip.base = pdata->gpio_base;
+ plgpio->chip.ngpio = pdata->gpio_count;
+ plgpio->chip.label = dev_name(&pdev->dev);
+ plgpio->chip.dev = &pdev->dev;
+ plgpio->chip.owner = THIS_MODULE;
+ plgpio->irq_base = pdata->irq_base;
+ plgpio->p2o = pdata->p2o;
+ plgpio->o2p = pdata->o2p;
+ plgpio->p2o_regs = pdata->p2o_regs;
+ plgpio->regs.enb = pdata->regs.enb;
+ plgpio->regs.wdata = pdata->regs.wdata;
+ plgpio->regs.dir = pdata->regs.dir;
+ plgpio->regs.ie = pdata->regs.ie;
+ plgpio->regs.rdata = pdata->regs.rdata;
+ plgpio->regs.mis = pdata->regs.mis;
+ plgpio->irq_trigger_type = pdata->irq_trigger_type;
+
+ ret = gpiochip_add(&plgpio->chip);
+ if (ret) {
+ dev_dbg(&pdev->dev, "unable to add gpio chip\n");
+ goto iounmap;
+ }
+
+ /* irq_chip support */
+ if (pdata->irq_base == (unsigned) -1) {
+ dev_info(&pdev->dev, "Successfully registered without irqs\n");
+ return 0;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ ret = -ENODEV;
+ dev_dbg(&pdev->dev, "invalid irq number\n");
+ goto remove_gpiochip;
+ }
+
+ irq_set_chained_handler(irq, plgpio_irq_handler);
+ for (i = 0; i < pdata->gpio_count; i++) {
+ irq_set_chip_and_handler(i+plgpio->irq_base, &plgpio_irqchip,
+ handle_simple_irq);
+ set_irq_flags(i+plgpio->irq_base, IRQF_VALID);
+ irq_set_chip_data(i+plgpio->irq_base, plgpio);
+ }
+ irq_set_handler_data(irq, plgpio);
+ dev_info(&pdev->dev, "Successfully registered with irqs\n");
+
+ return 0;
+
+remove_gpiochip:
+ if (gpiochip_remove(&plgpio->chip))
+ dev_dbg(&pdev->dev, "unable to remove gpiochip\n");
+iounmap:
+ iounmap(plgpio->base);
+kfree:
+ kfree(plgpio);
+release_region:
+ release_mem_region(res->start, resource_size(res));
+fail:
+ dev_err(&pdev->dev, "probe fail: %d\n", ret);
+ return ret;
+}
+
+static struct platform_driver plgpio_driver = {
+ .probe = plgpio_probe,
+ .driver = {
+ .name = "spear-plgpio",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init plgpio_init(void)
+{
+ return platform_driver_register(&plgpio_driver);
+}
+subsys_initcall(plgpio_init);
+
+MODULE_AUTHOR("Viresh Kumar <viresh.kumar@st.com>");
+MODULE_DESCRIPTION("SPEAr PLGPIO driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/spear-plgpio.h b/include/linux/spear-plgpio.h
new file mode 100644
index 0000000..a04986a
--- /dev/null
+++ b/include/linux/spear-plgpio.h
@@ -0,0 +1,69 @@
+/*
+ * include/linux/spear-plgpio.h
+ *
+ * SPEAr platform PLGPIO driver header file
+ *
+ * Copyright (C) 2010-2011 ST Microelectronics
+ * Viresh Kumar<viresh.kumar@st.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __SPEAR_PLGPIO_H
+#define __SPEAR_PLGPIO_H
+
+#include <linux/types.h>
+
+/* plgpio driver declarations */
+/*
+ * plgpio pins in all machines are not one to one mapped, bitwise with
+ * registers bits. These set of macros define register masks for which below
+ * functions (pin_to_offset and offset_to_pin) are required to be called.
+ */
+#define PTO_ENB_REG 0x001
+#define PTO_WDATA_REG 0x002
+#define PTO_DIR_REG 0x004
+#define PTO_IE_REG 0x008
+#define PTO_RDATA_REG 0x010
+#define PTO_MIS_REG 0x020
+
+/* plgpio registers */
+struct plgpio_regs {
+ u32 enb; /* enable register */
+ u32 wdata; /* write data register */
+ u32 dir; /* direction set register */
+ u32 rdata; /* read data register */
+ u32 ie; /* interrupt enable register */
+ u32 mis; /* mask interrupt status register */
+ u32 eit; /* edge interrupt type */
+};
+
+/**
+ * struct spear_plgpio_pdata: plgpio driver platform data
+ *
+ * gpio_base: gpio start number of plgpios
+ * irq_base: irq number of plgpio0
+ * gpio_count: total count of plgpios
+ * p2o: function ptr for pin to offset conversion. This is required only for
+ * machines where mapping b/w pin and offset is not 1-to-1.
+ * o2p: function ptr for offset to pin conversion. This is required only for
+ * machines where mapping b/w pin and offset is not 1-to-1.
+ * p2o_regs: mask of registers for which p2o and o2p are applicable
+ * regs: register offsets
+ * irq_trigger_type: irq type supported
+ */
+struct spear_plgpio_pdata {
+ u32 gpio_base;
+ u32 irq_base;
+ u32 gpio_count;
+ int (*p2o)(int pin); /* pin_to_offset */
+ int (*o2p)(int offset); /* offset_to_pin */
+ u32 p2o_regs;
+ struct plgpio_regs regs;
+ unsigned irq_trigger_type;
+};
+
+#endif /* __SPEAR_PLGPIO_H */
+
--
1.7.2.2
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 0/3] SPEAr: Add plgpio support
@ 2011-05-30 6:39 Viresh Kumar
2011-05-30 6:39 ` Viresh Kumar
` (2 more replies)
0 siblings, 3 replies; 9+ messages in thread
From: Viresh Kumar @ 2011-05-30 6:39 UTC (permalink / raw)
To: linux-arm-kernel
This patchset adds spear-plgpio driver and its support on
SPEAr Machines.
It was earlier reviewed as part of following patchset:
"[PATCH V6 00/17] Adding devices support for all spear machines"
Changes since earlier patchset:
- Moved driver to drivers/gpio/ directory.
- used new irq routines & definitions in driver.
- add support for edge irqs.
Viresh Kumar (3):
gpio/spear-plgpio: Add plgpio driver for SPEAr platform
SPEAr: Add machine support for plgpio
SPEAr3xx: Update defconfig for plgpio
MAINTAINERS | 7 +
arch/arm/configs/spear3xx_defconfig | 1 +
arch/arm/mach-spear3xx/include/mach/generic.h | 2 +
arch/arm/mach-spear3xx/include/mach/gpio.h | 143 +++++++
arch/arm/mach-spear3xx/spear310.c | 80 ++++-
arch/arm/mach-spear3xx/spear310_evb.c | 1 +
arch/arm/mach-spear3xx/spear320.c | 40 ++-
arch/arm/mach-spear3xx/spear320_evb.c | 1 +
arch/arm/mach-spear6xx/include/mach/gpio.h | 27 ++
drivers/gpio/Kconfig | 7 +
drivers/gpio/Makefile | 1 +
drivers/gpio/spear-plgpio.c | 490 +++++++++++++++++++++++++
include/linux/spear-plgpio.h | 69 ++++
13 files changed, 867 insertions(+), 2 deletions(-)
create mode 100644 drivers/gpio/spear-plgpio.c
create mode 100644 include/linux/spear-plgpio.h
--
1.7.2.2
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 2/3] SPEAr: Add machine support for plgpio
2011-05-30 6:39 [PATCH 0/3] SPEAr: Add plgpio support Viresh Kumar
2011-05-30 6:39 ` Viresh Kumar
@ 2011-05-30 6:39 ` Viresh Kumar
2011-05-30 6:39 ` [PATCH 3/3] SPEAr3xx: Update defconfig " Viresh Kumar
2 siblings, 0 replies; 9+ messages in thread
From: Viresh Kumar @ 2011-05-30 6:39 UTC (permalink / raw)
To: linux-arm-kernel
This patch adds machine support for plgpio on SPEAr310 & SPEAr320.
Also it defines macro's for gpio lines for both SPEAr3xx & 6xx.
Reviewed-by: Stanley Miao <stanley.miao@windriver.com>
Signed-off-by: Viresh Kumar <viresh.kumar@st.com>
---
arch/arm/mach-spear3xx/include/mach/generic.h | 2 +
arch/arm/mach-spear3xx/include/mach/gpio.h | 143 +++++++++++++++++++++++++
arch/arm/mach-spear3xx/spear310.c | 80 ++++++++++++++-
arch/arm/mach-spear3xx/spear310_evb.c | 1 +
arch/arm/mach-spear3xx/spear320.c | 40 +++++++-
arch/arm/mach-spear3xx/spear320_evb.c | 1 +
arch/arm/mach-spear6xx/include/mach/gpio.h | 27 +++++
7 files changed, 292 insertions(+), 2 deletions(-)
diff --git a/arch/arm/mach-spear3xx/include/mach/generic.h b/arch/arm/mach-spear3xx/include/mach/generic.h
index b8f31c3..8cc3a73 100644
--- a/arch/arm/mach-spear3xx/include/mach/generic.h
+++ b/arch/arm/mach-spear3xx/include/mach/generic.h
@@ -140,6 +140,7 @@ void __init spear300_init(struct pmx_mode *pmx_mode, struct pmx_dev **pmx_devs,
/* spear310 declarations */
#ifdef CONFIG_MACH_SPEAR310
/* Add spear310 machine device structure declarations here */
+extern struct platform_device spear310_plgpio_device;
/* pad mux devices */
extern struct pmx_dev spear310_pmx_emi_cs_0_1_4_5;
@@ -160,6 +161,7 @@ void __init spear310_init(struct pmx_mode *pmx_mode, struct pmx_dev **pmx_devs,
/* spear320 declarations */
#ifdef CONFIG_MACH_SPEAR320
/* Add spear320 machine device structure declarations here */
+extern struct platform_device spear320_plgpio_device;
/* pad mux modes */
extern struct pmx_mode spear320_auto_net_smii_mode;
diff --git a/arch/arm/mach-spear3xx/include/mach/gpio.h b/arch/arm/mach-spear3xx/include/mach/gpio.h
index 451b208..70d0e61 100644
--- a/arch/arm/mach-spear3xx/include/mach/gpio.h
+++ b/arch/arm/mach-spear3xx/include/mach/gpio.h
@@ -16,4 +16,147 @@
#include <plat/gpio.h>
+#ifdef CONFIG_MACH_SPEAR310
+#define SPEAR310_PLGPIO_ENB_OFF 0x0010
+#define SPEAR310_PLGPIO_WDATA_OFF 0x0020
+#define SPEAR310_PLGPIO_DIR_OFF 0x0030
+#define SPEAR310_PLGPIO_RDATA_OFF 0x0040
+#define SPEAR310_PLGPIO_IE_OFF 0x0050
+#define SPEAR310_PLGPIO_MIS_OFF 0x0060
+#endif
+
+#if defined(CONFIG_MACH_SPEAR320)
+#define SPEAR320_PLGPIO_ENB_OFF 0x0024
+#define SPEAR320_PLGPIO_WDATA_OFF 0x0034
+#define SPEAR320_PLGPIO_DIR_OFF 0x0044
+#define SPEAR320_PLGPIO_IE_OFF 0x0064
+#define SPEAR320_PLGPIO_RDATA_OFF 0x0054
+#define SPEAR320_PLGPIO_MIS_OFF 0x0074
+#endif
+
+#define BASIC_GPIO_0 0
+#define BASIC_GPIO_1 1
+#define BASIC_GPIO_2 2
+#define BASIC_GPIO_3 3
+#define BASIC_GPIO_4 4
+#define BASIC_GPIO_5 5
+#define BASIC_GPIO_6 6
+#define BASIC_GPIO_7 7
+
+#ifdef CONFIG_MACH_SPEAR300
+#define RAS_GPIO_0 8
+#define RAS_GPIO_1 9
+#define RAS_GPIO_2 10
+#define RAS_GPIO_3 11
+#define RAS_GPIO_4 12
+#define RAS_GPIO_5 13
+#define RAS_GPIO_6 14
+#define RAS_GPIO_7 15
+#endif /* CONFIG_MACH_SPEAR300 */
+
+#if defined(CONFIG_MACH_SPEAR310) || defined(CONFIG_MACH_SPEAR320)
+#define PLGPIO_0 8
+#define PLGPIO_1 9
+#define PLGPIO_2 10
+#define PLGPIO_3 11
+#define PLGPIO_4 12
+#define PLGPIO_5 13
+#define PLGPIO_6 14
+#define PLGPIO_7 15
+#define PLGPIO_8 16
+#define PLGPIO_9 17
+#define PLGPIO_10 18
+#define PLGPIO_11 19
+#define PLGPIO_12 20
+#define PLGPIO_13 21
+#define PLGPIO_14 22
+#define PLGPIO_15 23
+#define PLGPIO_16 24
+#define PLGPIO_17 25
+#define PLGPIO_18 26
+#define PLGPIO_19 27
+#define PLGPIO_20 28
+#define PLGPIO_21 29
+#define PLGPIO_22 30
+#define PLGPIO_23 31
+#define PLGPIO_24 32
+#define PLGPIO_25 33
+#define PLGPIO_26 34
+#define PLGPIO_27 35
+#define PLGPIO_28 36
+#define PLGPIO_29 37
+#define PLGPIO_30 38
+#define PLGPIO_31 39
+#define PLGPIO_32 40
+#define PLGPIO_33 41
+#define PLGPIO_34 42
+#define PLGPIO_35 43
+#define PLGPIO_36 44
+#define PLGPIO_37 45
+#define PLGPIO_38 46
+#define PLGPIO_39 47
+#define PLGPIO_40 48
+#define PLGPIO_41 49
+#define PLGPIO_42 50
+#define PLGPIO_43 51
+#define PLGPIO_44 52
+#define PLGPIO_45 53
+#define PLGPIO_46 54
+#define PLGPIO_47 55
+#define PLGPIO_48 56
+#define PLGPIO_49 57
+#define PLGPIO_50 58
+#define PLGPIO_51 59
+#define PLGPIO_52 60
+#define PLGPIO_53 61
+#define PLGPIO_54 62
+#define PLGPIO_55 63
+#define PLGPIO_56 64
+#define PLGPIO_57 65
+#define PLGPIO_58 66
+#define PLGPIO_59 67
+#define PLGPIO_60 68
+#define PLGPIO_61 69
+#define PLGPIO_62 70
+#define PLGPIO_63 71
+#define PLGPIO_64 72
+#define PLGPIO_65 73
+#define PLGPIO_66 74
+#define PLGPIO_67 75
+#define PLGPIO_68 76
+#define PLGPIO_69 77
+#define PLGPIO_70 78
+#define PLGPIO_71 79
+#define PLGPIO_72 80
+#define PLGPIO_73 81
+#define PLGPIO_74 82
+#define PLGPIO_75 83
+#define PLGPIO_76 84
+#define PLGPIO_77 85
+#define PLGPIO_78 86
+#define PLGPIO_79 87
+#define PLGPIO_80 88
+#define PLGPIO_81 89
+#define PLGPIO_82 90
+#define PLGPIO_83 91
+#define PLGPIO_84 92
+#define PLGPIO_85 93
+#define PLGPIO_86 94
+#define PLGPIO_87 95
+#define PLGPIO_88 96
+#define PLGPIO_89 97
+#define PLGPIO_90 98
+#define PLGPIO_91 99
+#define PLGPIO_92 100
+#define PLGPIO_93 101
+#define PLGPIO_94 102
+#define PLGPIO_95 103
+#define PLGPIO_96 104
+#define PLGPIO_97 105
+#define PLGPIO_98 106
+#define PLGPIO_99 107
+#define PLGPIO_100 108
+#define PLGPIO_101 109
+#endif /* CONFIG_MACH_SPEAR310 || CONFIG_MACH_SPEAR320 */
+
#endif /* __MACH_GPIO_H */
diff --git a/arch/arm/mach-spear3xx/spear310.c b/arch/arm/mach-spear3xx/spear310.c
index 9004cf9..a49be3b 100644
--- a/arch/arm/mach-spear3xx/spear310.c
+++ b/arch/arm/mach-spear3xx/spear310.c
@@ -11,10 +11,12 @@
* warranty of any kind, whether express or implied.
*/
+#include <linux/irq.h>
#include <linux/ptrace.h>
-#include <asm/irq.h>
+#include <linux/spear-plgpio.h>
#include <plat/shirq.h>
#include <mach/generic.h>
+#include <mach/gpio.h>
#include <mach/hardware.h>
/* pad multiplexing support */
@@ -256,6 +258,82 @@ static struct spear_shirq shirq_intrcomm_ras = {
};
/* Add spear310 specific devices here */
+/* plgpio device registeration */
+/*
+ * pin to offset and offset to pin converter functions
+ *
+ * In spear310 there is inconsistency among bit positions in plgpio regiseters,
+ * for different plgpio pins. For example: for pin 27, bit offset is 23, pin
+ * 28-33 are not supported, pin 95 has offset bit 95, bit 100 has offset bit 1
+ */
+static int spear300_p2o(int pin)
+{
+ int offset = pin;
+
+ if (pin <= 27)
+ offset += 4;
+ else if (pin <= 33)
+ offset = -1;
+ else if (pin <= 97)
+ offset -= 2;
+ else if (pin <= 101)
+ offset = 101 - pin;
+ else
+ offset = -1;
+
+ return offset;
+}
+
+int spear300_o2p(int offset)
+{
+ if (offset <= 3)
+ return 101 - offset;
+ else if (offset <= 31)
+ return offset - 4;
+ else
+ return offset + 2;
+}
+
+static struct spear_plgpio_pdata plgpio_pdata = {
+ .gpio_base = 8,
+ .irq_base = SPEAR3XX_PLGPIO_INT_BASE,
+ .gpio_count = SPEAR3XX_PLGPIO_COUNT,
+ .p2o = spear300_p2o,
+ .o2p = spear300_o2p,
+ /* list of registers with inconsistency */
+ .p2o_regs = PTO_RDATA_REG | PTO_WDATA_REG | PTO_DIR_REG |
+ PTO_IE_REG | PTO_RDATA_REG | PTO_MIS_REG,
+ .regs = {
+ .enb = SPEAR310_PLGPIO_ENB_OFF,
+ .wdata = SPEAR310_PLGPIO_WDATA_OFF,
+ .dir = SPEAR310_PLGPIO_DIR_OFF,
+ .ie = SPEAR310_PLGPIO_IE_OFF,
+ .rdata = SPEAR310_PLGPIO_RDATA_OFF,
+ .mis = SPEAR310_PLGPIO_MIS_OFF,
+ },
+ .irq_trigger_type = IRQ_TYPE_LEVEL_HIGH,
+};
+
+static struct resource plgpio_resources[] = {
+ {
+ .start = SPEAR310_SOC_CONFIG_BASE,
+ .end = SPEAR310_SOC_CONFIG_BASE + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = SPEAR310_VIRQ_PLGPIO,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device spear310_plgpio_device = {
+ .name = "spear-plgpio",
+ .id = -1,
+ .dev = {
+ .platform_data = &plgpio_pdata,
+ },
+ .num_resources = ARRAY_SIZE(plgpio_resources),
+ .resource = plgpio_resources,
+};
/* spear310 routines */
void __init spear310_init(struct pmx_mode *pmx_mode, struct pmx_dev **pmx_devs,
diff --git a/arch/arm/mach-spear3xx/spear310_evb.c b/arch/arm/mach-spear3xx/spear310_evb.c
index c8684ce..f6832c4 100644
--- a/arch/arm/mach-spear3xx/spear310_evb.c
+++ b/arch/arm/mach-spear3xx/spear310_evb.c
@@ -52,6 +52,7 @@ static struct platform_device *plat_devs[] __initdata = {
/* spear3xx specific devices */
/* spear310 specific devices */
+ &spear310_plgpio_device,
};
static void __init spear310_evb_init(void)
diff --git a/arch/arm/mach-spear3xx/spear320.c b/arch/arm/mach-spear3xx/spear320.c
index ee29bef..575493a 100644
--- a/arch/arm/mach-spear3xx/spear320.c
+++ b/arch/arm/mach-spear3xx/spear320.c
@@ -11,10 +11,12 @@
* warranty of any kind, whether express or implied.
*/
+#include <linux/irq.h>
#include <linux/ptrace.h>
-#include <asm/irq.h>
+#include <linux/spear-plgpio.h>
#include <plat/shirq.h>
#include <mach/generic.h>
+#include <mach/gpio.h>
#include <mach/hardware.h>
/* pad multiplexing support */
@@ -509,6 +511,42 @@ static struct spear_shirq shirq_intrcomm_ras = {
};
/* Add spear320 specific devices here */
+/* plgpio device registeration */
+static struct spear_plgpio_pdata plgpio_pdata = {
+ .gpio_base = 8,
+ .irq_base = SPEAR3XX_PLGPIO_INT_BASE,
+ .gpio_count = SPEAR3XX_PLGPIO_COUNT,
+ .regs = {
+ .enb = SPEAR320_PLGPIO_ENB_OFF,
+ .wdata = SPEAR320_PLGPIO_WDATA_OFF,
+ .dir = SPEAR320_PLGPIO_DIR_OFF,
+ .ie = SPEAR320_PLGPIO_IE_OFF,
+ .rdata = SPEAR320_PLGPIO_RDATA_OFF,
+ .mis = SPEAR320_PLGPIO_MIS_OFF,
+ },
+ .irq_trigger_type = IRQ_TYPE_LEVEL_HIGH,
+};
+
+static struct resource plgpio_resources[] = {
+ {
+ .start = SPEAR320_SOC_CONFIG_BASE,
+ .end = SPEAR320_SOC_CONFIG_BASE + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = SPEAR320_VIRQ_PLGPIO,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device spear320_plgpio_device = {
+ .name = "spear-plgpio",
+ .id = -1,
+ .dev = {
+ .platform_data = &plgpio_pdata,
+ },
+ .num_resources = ARRAY_SIZE(plgpio_resources),
+ .resource = plgpio_resources,
+};
/* spear320 routines */
void __init spear320_init(struct pmx_mode *pmx_mode, struct pmx_dev **pmx_devs,
diff --git a/arch/arm/mach-spear3xx/spear320_evb.c b/arch/arm/mach-spear3xx/spear320_evb.c
index a12b353..4d747db 100644
--- a/arch/arm/mach-spear3xx/spear320_evb.c
+++ b/arch/arm/mach-spear3xx/spear320_evb.c
@@ -49,6 +49,7 @@ static struct platform_device *plat_devs[] __initdata = {
/* spear3xx specific devices */
/* spear320 specific devices */
+ &spear320_plgpio_device,
};
static void __init spear320_evb_init(void)
diff --git a/arch/arm/mach-spear6xx/include/mach/gpio.h b/arch/arm/mach-spear6xx/include/mach/gpio.h
index 3a789db..465b2e7 100644
--- a/arch/arm/mach-spear6xx/include/mach/gpio.h
+++ b/arch/arm/mach-spear6xx/include/mach/gpio.h
@@ -16,4 +16,31 @@
#include <plat/gpio.h>
+#define CPU_GPIO_0 0
+#define CPU_GPIO_1 1
+#define CPU_GPIO_2 2
+#define CPU_GPIO_3 3
+#define CPU_GPIO_4 4
+#define CPU_GPIO_5 5
+#define CPU_GPIO_6 6
+#define CPU_GPIO_7 7
+
+#define BASIC_GPIO_0 8
+#define BASIC_GPIO_1 9
+#define BASIC_GPIO_2 10
+#define BASIC_GPIO_3 11
+#define BASIC_GPIO_4 12
+#define BASIC_GPIO_5 13
+#define BASIC_GPIO_6 14
+#define BASIC_GPIO_7 15
+
+#define APPL_GPIO_0 16
+#define APPL_GPIO_1 17
+#define APPL_GPIO_2 18
+#define APPL_GPIO_3 19
+#define APPL_GPIO_4 20
+#define APPL_GPIO_5 21
+#define APPL_GPIO_6 22
+#define APPL_GPIO_7 23
+
#endif /* __MACH_GPIO_H */
--
1.7.2.2
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 3/3] SPEAr3xx: Update defconfig for plgpio
2011-05-30 6:39 [PATCH 0/3] SPEAr: Add plgpio support Viresh Kumar
2011-05-30 6:39 ` Viresh Kumar
2011-05-30 6:39 ` [PATCH 2/3] SPEAr: Add machine support for plgpio Viresh Kumar
@ 2011-05-30 6:39 ` Viresh Kumar
2 siblings, 0 replies; 9+ messages in thread
From: Viresh Kumar @ 2011-05-30 6:39 UTC (permalink / raw)
To: linux-arm-kernel
Signed-off-by: Viresh Kumar <viresh.kumar@st.com>
---
arch/arm/configs/spear3xx_defconfig | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/arch/arm/configs/spear3xx_defconfig b/arch/arm/configs/spear3xx_defconfig
index fea7e1f..07e9613 100644
--- a/arch/arm/configs/spear3xx_defconfig
+++ b/arch/arm/configs/spear3xx_defconfig
@@ -26,6 +26,7 @@ CONFIG_RAW_DRIVER=y
CONFIG_MAX_RAW_DEVS=8192
CONFIG_GPIO_SYSFS=y
CONFIG_GPIO_PL061=y
+CONFIG_SPEAR_PLGPIO=y
# CONFIG_HWMON is not set
# CONFIG_HID_SUPPORT is not set
# CONFIG_USB_SUPPORT is not set
--
1.7.2.2
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH 1/3] gpio/spear-plgpio: Add plgpio driver for SPEAr platform
2011-05-30 6:39 ` Viresh Kumar
@ 2011-06-03 17:50 ` Grant Likely
-1 siblings, 0 replies; 9+ messages in thread
From: Grant Likely @ 2011-06-03 17:50 UTC (permalink / raw)
To: Viresh Kumar
Cc: linux-arm-kernel, armando.visconti, shiraz.hashim, vipin.kumar,
rajeev-dlh.kumar, deepak.sikri, vipulkumar.samar, amit.virdi,
pratyush.anand, bhupesh.sharma, viresh.linux, linux-kernel
On Mon, May 30, 2011 at 12:09:02PM +0530, Viresh Kumar wrote:
> Plgpio pads on few spear machines can be configured as gpios. This patch adds
> support for configuring these PLGPIOs.
>
> This was earlier posted & reviewed as part of arch/arm/plat-spear/ code.
>
> Reviewed-by: Stanley Miao <stanley.miao@windriver.com>
> Signed-off-by: Viresh Kumar <viresh.kumar@st.com>
Hi Viresh,
This ends up being yet-another-mmio-gpio implementation. Please look
at bgpio_init() in drivers/basic_mmio_gpio.c. This driver should be
refactored to use that.
g.
> ---
> MAINTAINERS | 7 +
> drivers/gpio/Kconfig | 7 +
> drivers/gpio/Makefile | 1 +
> drivers/gpio/spear-plgpio.c | 490 ++++++++++++++++++++++++++++++++++++++++++
> include/linux/spear-plgpio.h | 69 ++++++
> 5 files changed, 574 insertions(+), 0 deletions(-)
> create mode 100644 drivers/gpio/spear-plgpio.c
> create mode 100644 include/linux/spear-plgpio.h
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 29801f7..48b0a4f 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -5978,6 +5978,13 @@ F: arch/arm/mach-spear3xx/spear3*0_evb.c
> F: arch/arm/mach-spear6xx/spear600.c
> F: arch/arm/mach-spear6xx/spear600_evb.c
>
> +SPEAR PLGPIO SUPPORT
> +M: Viresh Kumar <viresh.kumar@st.com>
> +W: http://www.st.com/spear
> +S: Maintained
> +F: drivers/gpio/spear-plgpio.c
> +F: include/linux/spear-plgpio.h
> +
> SPECIALIX IO8+ MULTIPORT SERIAL CARD DRIVER
> S: Orphan
> F: Documentation/serial/specialix.txt
> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
> index 4a7f631..227d2e7 100644
> --- a/drivers/gpio/Kconfig
> +++ b/drivers/gpio/Kconfig
> @@ -164,6 +164,13 @@ config GPIO_VX855
> additional drivers must be enabled in order to use the
> functionality of the device.
>
> +config SPEAR_PLGPIO
> + bool "ST Micro SPEAr PLGPIO"
> + depends on PLAT_SPEAR
> + help
> + This enables support for the PLGPIOs found on the ST Microelectronics
> + SPEAr platform
> +
> comment "I2C GPIO expanders:"
>
> config GPIO_MAX7300
> diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
> index b605f8e..2d325b0 100644
> --- a/drivers/gpio/Makefile
> +++ b/drivers/gpio/Makefile
> @@ -48,3 +48,4 @@ obj-$(CONFIG_GPIO_VX855) += vx855_gpio.o
> obj-$(CONFIG_GPIO_ML_IOH) += ml_ioh_gpio.o
> obj-$(CONFIG_AB8500_GPIO) += ab8500-gpio.o
> obj-$(CONFIG_GPIO_TPS65910) += tps65910-gpio.o
> +obj-$(CONFIG_SPEAR_PLGPIO) += spear-plgpio.o
> diff --git a/drivers/gpio/spear-plgpio.c b/drivers/gpio/spear-plgpio.c
> new file mode 100644
> index 0000000..885c0da
> --- /dev/null
> +++ b/drivers/gpio/spear-plgpio.c
> @@ -0,0 +1,490 @@
> +/*
> + * drivers/gpio/spear-plgpio.c
> + *
> + * SPEAr platform PLGPIO driver source file
> + *
> + * Copyright (C) 2010-2011 ST Microelectronics
> + * Viresh Kumar<viresh.kumar@st.com>
> + *
> + * This file is licensed under the terms of the GNU General Public
> + * License version 2. This program is licensed "as is" without any
> + * warranty of any kind, whether express or implied.
> + */
> +
> +#include <linux/bitops.h>
> +#include <linux/compiler.h>
> +#include <linux/errno.h>
> +#include <linux/gpio.h>
> +#include <linux/io.h>
> +#include <linux/ioport.h>
> +#include <linux/irq.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <linux/spear-plgpio.h>
> +#include <linux/spinlock.h>
> +
> +#define MAX_GPIO_PER_REG 32
> +#define PIN_OFFSET(pin) (pin % MAX_GPIO_PER_REG)
> +#define REG_OFFSET(base, reg, pin) (base + reg + (pin / MAX_GPIO_PER_REG)\
> + * sizeof(int *))
> +
> +/*
> + * struct plgpio: plgpio driver specific structure
> + *
> + * lock: lock for guarding gpio registers
> + * base: base address of plgpio block
> + * irq_base: irq number of plgpio0
> + * chip: gpio framework specific chip information structure
> + * p2o: function ptr for pin to offset conversion. This is required only for
> + * machines where mapping b/w pin and offset is not 1-to-1.
> + * o2p: function ptr for offset to pin conversion. This is required only for
> + * machines where mapping b/w pin and offset is not 1-to-1.
> + * p2o_regs: mask of registers for which p2o and o2p are applicable
> + * regs: register offsets
> + * irq_trigger_type: irq type supported
> + */
> +struct plgpio {
> + spinlock_t lock;
> + void __iomem *base;
> + unsigned irq_base;
> + struct gpio_chip chip;
> + int (*p2o)(int pin); /* pin_to_offset */
> + int (*o2p)(int offset); /* offset_to_pin */
> + unsigned p2o_regs;
> + struct plgpio_regs regs;
> + unsigned irq_trigger_type;
> +};
> +
> +/* register manipulation inline functions */
> +static inline u32 is_plgpio_set(void __iomem *base, u32 pin, u32 reg)
> +{
> + u32 offset = PIN_OFFSET(pin);
> + void __iomem *reg_off = REG_OFFSET(base, reg, pin);
> + u32 val = readl(reg_off);
> +
> + return val & (1 << offset);
> +}
> +
> +static inline void plgpio_reg_set(void __iomem *base, u32 pin, u32 reg)
> +{
> + u32 offset = PIN_OFFSET(pin);
> + void __iomem *reg_off = REG_OFFSET(base, reg, pin);
> + u32 val = readl(reg_off);
> +
> + writel(val | (1 << offset), reg_off);
> +}
> +
> +static inline void plgpio_reg_reset(void __iomem *base, u32 pin, u32 reg)
> +{
> + u32 offset = PIN_OFFSET(pin);
> + void __iomem *reg_off = REG_OFFSET(base, reg, pin);
> + u32 val = readl(reg_off);
> +
> + writel(val & ~(1 << offset), reg_off);
> +}
> +
> +/* gpio framework specific routines */
> +static int plgpio_direction_input(struct gpio_chip *chip, unsigned offset)
> +{
> + struct plgpio *plgpio = container_of(chip, struct plgpio, chip);
> + unsigned long flags;
> +
> + if (offset >= chip->ngpio)
> + return -EINVAL;
> +
> + /* get correct offset for "offset" pin */
> + if (plgpio->p2o && (plgpio->p2o_regs & PTO_DIR_REG)) {
> + offset = plgpio->p2o(offset);
> + if (offset == -1)
> + return -EINVAL;
> + }
> +
> + spin_lock_irqsave(&plgpio->lock, flags);
> + plgpio_reg_set(plgpio->base, offset, plgpio->regs.dir);
> + spin_unlock_irqrestore(&plgpio->lock, flags);
> +
> + return 0;
> +}
> +
> +static int plgpio_direction_output(struct gpio_chip *chip, unsigned offset,
> + int value)
> +{
> + struct plgpio *plgpio = container_of(chip, struct plgpio, chip);
> + unsigned long flags;
> + unsigned dir_offset = offset, wdata_offset = offset, tmp;
> +
> + if (offset >= chip->ngpio)
> + return -EINVAL;
> +
> + /* get correct offset for "offset" pin */
> + if (plgpio->p2o && (plgpio->p2o_regs & (PTO_DIR_REG | PTO_WDATA_REG))) {
> + tmp = plgpio->p2o(offset);
> + if (tmp == -1)
> + return -EINVAL;
> +
> + if (plgpio->p2o_regs & PTO_DIR_REG)
> + dir_offset = tmp;
> + if (plgpio->p2o_regs & PTO_WDATA_REG)
> + wdata_offset = tmp;
> + }
> +
> + spin_lock_irqsave(&plgpio->lock, flags);
> + plgpio_reg_reset(plgpio->base, dir_offset, plgpio->regs.dir);
> + if (value)
> + plgpio_reg_set(plgpio->base, wdata_offset,
> + plgpio->regs.wdata);
> + else
> + plgpio_reg_reset(plgpio->base, wdata_offset,
> + plgpio->regs.wdata);
> + spin_unlock_irqrestore(&plgpio->lock, flags);
> +
> + return 0;
> +}
> +
> +static int plgpio_get_value(struct gpio_chip *chip, unsigned offset)
> +{
> + struct plgpio *plgpio = container_of(chip, struct plgpio, chip);
> +
> + if (offset >= chip->ngpio)
> + return -EINVAL;
> +
> + /* get correct offset for "offset" pin */
> + if (plgpio->p2o && (plgpio->p2o_regs & PTO_RDATA_REG)) {
> + offset = plgpio->p2o(offset);
> + if (offset == -1)
> + return -EINVAL;
> + }
> +
> + return is_plgpio_set(plgpio->base, offset, plgpio->regs.rdata);
> +}
> +
> +static void plgpio_set_value(struct gpio_chip *chip, unsigned offset, int value)
> +{
> + struct plgpio *plgpio = container_of(chip, struct plgpio, chip);
> +
> + if (offset >= chip->ngpio)
> + return;
> +
> + /* get correct offset for "offset" pin */
> + if (plgpio->p2o && (plgpio->p2o_regs & PTO_WDATA_REG)) {
> + offset = plgpio->p2o(offset);
> + if (offset == -1)
> + return;
> + }
> +
> + if (value)
> + plgpio_reg_set(plgpio->base, offset, plgpio->regs.wdata);
> + else
> + plgpio_reg_reset(plgpio->base, offset, plgpio->regs.wdata);
> +}
> +
> +static int plgpio_request(struct gpio_chip *chip, unsigned offset)
> +{
> + struct plgpio *plgpio = container_of(chip, struct plgpio, chip);
> + unsigned long flags;
> + int ret = 0;
> +
> + if (offset >= chip->ngpio)
> + return -EINVAL;
> +
> + /*
> + * put gpio in IN mode before enabling it. This make enabling gpio safe
> + */
> + ret = plgpio_direction_input(chip, offset);
> + if (ret)
> + return ret;
> +
> + /* get correct offset for "offset" pin */
> + if (plgpio->p2o && (plgpio->p2o_regs & PTO_ENB_REG)) {
> + offset = plgpio->p2o(offset);
> + if (offset == -1)
> + return -EINVAL;
> + }
> +
> + spin_lock_irqsave(&plgpio->lock, flags);
> + plgpio_reg_set(plgpio->base, offset, plgpio->regs.enb);
> + spin_unlock_irqrestore(&plgpio->lock, flags);
> +
> + return 0;
> +}
> +
> +static void plgpio_free(struct gpio_chip *chip, unsigned offset)
> +{
> + struct plgpio *plgpio = container_of(chip, struct plgpio, chip);
> + unsigned long flags;
> +
> + if (offset >= chip->ngpio)
> + return;
> +
> + /* get correct offset for "offset" pin */
> + if (plgpio->p2o && (plgpio->p2o_regs & PTO_ENB_REG)) {
> + offset = plgpio->p2o(offset);
> + if (offset == -1)
> + return;
> + }
> +
> + spin_lock_irqsave(&plgpio->lock, flags);
> + plgpio_reg_reset(plgpio->base, offset, plgpio->regs.enb);
> + spin_unlock_irqrestore(&plgpio->lock, flags);
> +}
> +
> +static int plgpio_to_irq(struct gpio_chip *chip, unsigned offset)
> +{
> + struct plgpio *plgpio = container_of(chip, struct plgpio, chip);
> +
> + if (plgpio->irq_base == (unsigned) -1)
> + return -EINVAL;
> +
> + return plgpio->irq_base + offset;
> +}
> +
> +/* PLGPIO IRQ */
> +static void plgpio_irq_mask(struct irq_data *d)
> +{
> + struct plgpio *plgpio = irq_data_get_irq_chip_data(d);
> + int offset = d->irq - plgpio->irq_base;
> + unsigned long flags;
> +
> + /* get correct offset for "offset" pin */
> + if (plgpio->p2o && (plgpio->p2o_regs & PTO_IE_REG)) {
> + offset = plgpio->p2o(offset);
> + if (offset == -1)
> + return;
> + }
> +
> + spin_lock_irqsave(&plgpio->lock, flags);
> + plgpio_reg_set(plgpio->base, offset, plgpio->regs.ie);
> + spin_unlock_irqrestore(&plgpio->lock, flags);
> +}
> +
> +static void plgpio_irq_unmask(struct irq_data *d)
> +{
> + struct plgpio *plgpio = irq_data_get_irq_chip_data(d);
> + int offset = d->irq - plgpio->irq_base;
> + unsigned long flags;
> +
> + /* get correct offset for "offset" pin */
> + if (plgpio->p2o && (plgpio->p2o_regs & PTO_IE_REG)) {
> + offset = plgpio->p2o(offset);
> + if (offset == -1)
> + return;
> + }
> +
> + spin_lock_irqsave(&plgpio->lock, flags);
> + plgpio_reg_reset(plgpio->base, offset, plgpio->regs.ie);
> + spin_unlock_irqrestore(&plgpio->lock, flags);
> +}
> +
> +static int plgpio_irq_type(struct irq_data *d, unsigned trigger)
> +{
> + struct plgpio *plgpio = irq_data_get_irq_chip_data(d);
> + int offset = d->irq - plgpio->irq_base;
> +
> + if (!(plgpio->irq_trigger_type & trigger))
> + return -EINVAL;
> + if (offset >= plgpio->chip.ngpio)
> + return -EINVAL;
> +
> + /*
> + * Currently we only need to configure register in case of edge
> + * interrupt
> + */
> + if (plgpio->irq_trigger_type == (IRQ_TYPE_EDGE_RISING |
> + IRQ_TYPE_EDGE_FALLING)) {
> + void __iomem *reg_off = REG_OFFSET(plgpio->base,
> + plgpio->regs.eit, offset);
> + u32 val = readl(reg_off);
> +
> + offset = PIN_OFFSET(offset);
> + if (trigger == IRQ_TYPE_EDGE_RISING)
> + writel(val | (1 << offset), reg_off);
> + else
> + writel(val & ~(1 << offset), reg_off);
> + }
> +
> + return 0;
> +}
> +
> +static struct irq_chip plgpio_irqchip = {
> + .name = "PLGPIO",
> + .irq_mask = plgpio_irq_mask,
> + .irq_unmask = plgpio_irq_unmask,
> + .irq_set_type = plgpio_irq_type,
> +};
> +
> +static void plgpio_irq_handler(unsigned irq, struct irq_desc *desc)
> +{
> + struct plgpio *plgpio = irq_get_handler_data(irq);
> + int regs_count = DIV_ROUND_UP(plgpio->chip.ngpio, MAX_GPIO_PER_REG),
> + count, pin, offset, i = 0;
> + unsigned long pending;
> +
> + desc->irq_data.chip->irq_ack(&desc->irq_data);
> + /* check all plgpio MIS registers for a possible interrupt */
> + for (; i < regs_count; i++) {
> + pending = readl(plgpio->base + plgpio->regs.mis +
> + i * sizeof(int *));
> + if (!pending)
> + continue;
> +
> + /* clear interrupts */
> + writel(~pending, plgpio->base + plgpio->regs.mis +
> + i * sizeof(int *));
> + /*
> + * clear extra bits in last register having gpios < MAX/REG
> + * ex: Suppose there are max 102 plgpios. then last register
> + * must have only (102 - MAX_GPIO_PER_REG * 3) = 6 relevant bits
> + * so, we must not take other 28 bits into consideration for
> + * checking interrupt. so clear those bits.
> + */
> + count = plgpio->chip.ngpio - i * MAX_GPIO_PER_REG;
> + if (count < MAX_GPIO_PER_REG)
> + pending &= (1 << count) - 1;
> +
> + for_each_set_bit(offset, &pending, MAX_GPIO_PER_REG) {
> + /* get correct pin for "offset" */
> + if (plgpio->o2p && (plgpio->p2o_regs & PTO_MIS_REG)) {
> + pin = plgpio->o2p(offset);
> + if (pin == -1)
> + continue;
> + } else
> + pin = offset;
> +
> + generic_handle_irq(plgpio_to_irq(&plgpio->chip,
> + i * MAX_GPIO_PER_REG + pin));
> + }
> + }
> + desc->irq_data.chip->irq_unmask(&desc->irq_data);
> +}
> +
> +static int __devinit plgpio_probe(struct platform_device *pdev)
> +{
> + struct spear_plgpio_pdata *pdata;
> + struct plgpio *plgpio;
> + int ret, irq, i;
> + struct resource *res;
> +
> + pdata = pdev->dev.platform_data;
> + if (!pdata) {
> + ret = -ENODEV;
> + dev_dbg(&pdev->dev, "invalid platform data\n");
> + goto fail;
> + }
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!res) {
> + ret = -EBUSY;
> + dev_dbg(&pdev->dev, "invalid IORESOURCE_MEM\n");
> + goto fail;
> + }
> +
> + if (!request_mem_region(res->start, resource_size(res), "plgpio")) {
> + ret = -EBUSY;
> + dev_dbg(&pdev->dev, "request mem region fail\n");
> + goto fail;
> + }
> +
> + plgpio = kzalloc(sizeof(*plgpio), GFP_KERNEL);
> + if (!plgpio) {
> + ret = -ENOMEM;
> + dev_dbg(&pdev->dev, "memory allocation fail\n");
> + goto release_region;
> + }
> +
> + plgpio->base = ioremap(res->start, resource_size(res));
> + if (!plgpio->base) {
> + ret = -ENOMEM;
> + dev_dbg(&pdev->dev, "ioremap fail\n");
> + goto kfree;
> + }
> +
> + spin_lock_init(&plgpio->lock);
> +
> + plgpio->chip.request = plgpio_request;
> + plgpio->chip.free = plgpio_free;
> + plgpio->chip.direction_input = plgpio_direction_input;
> + plgpio->chip.direction_output = plgpio_direction_output;
> + plgpio->chip.get = plgpio_get_value;
> + plgpio->chip.set = plgpio_set_value;
> + plgpio->chip.to_irq = plgpio_to_irq;
> + plgpio->chip.base = pdata->gpio_base;
> + plgpio->chip.ngpio = pdata->gpio_count;
> + plgpio->chip.label = dev_name(&pdev->dev);
> + plgpio->chip.dev = &pdev->dev;
> + plgpio->chip.owner = THIS_MODULE;
> + plgpio->irq_base = pdata->irq_base;
> + plgpio->p2o = pdata->p2o;
> + plgpio->o2p = pdata->o2p;
> + plgpio->p2o_regs = pdata->p2o_regs;
> + plgpio->regs.enb = pdata->regs.enb;
> + plgpio->regs.wdata = pdata->regs.wdata;
> + plgpio->regs.dir = pdata->regs.dir;
> + plgpio->regs.ie = pdata->regs.ie;
> + plgpio->regs.rdata = pdata->regs.rdata;
> + plgpio->regs.mis = pdata->regs.mis;
> + plgpio->irq_trigger_type = pdata->irq_trigger_type;
> +
> + ret = gpiochip_add(&plgpio->chip);
> + if (ret) {
> + dev_dbg(&pdev->dev, "unable to add gpio chip\n");
> + goto iounmap;
> + }
> +
> + /* irq_chip support */
> + if (pdata->irq_base == (unsigned) -1) {
> + dev_info(&pdev->dev, "Successfully registered without irqs\n");
> + return 0;
> + }
> +
> + irq = platform_get_irq(pdev, 0);
> + if (irq < 0) {
> + ret = -ENODEV;
> + dev_dbg(&pdev->dev, "invalid irq number\n");
> + goto remove_gpiochip;
> + }
> +
> + irq_set_chained_handler(irq, plgpio_irq_handler);
> + for (i = 0; i < pdata->gpio_count; i++) {
> + irq_set_chip_and_handler(i+plgpio->irq_base, &plgpio_irqchip,
> + handle_simple_irq);
> + set_irq_flags(i+plgpio->irq_base, IRQF_VALID);
> + irq_set_chip_data(i+plgpio->irq_base, plgpio);
> + }
> + irq_set_handler_data(irq, plgpio);
> + dev_info(&pdev->dev, "Successfully registered with irqs\n");
> +
> + return 0;
> +
> +remove_gpiochip:
> + if (gpiochip_remove(&plgpio->chip))
> + dev_dbg(&pdev->dev, "unable to remove gpiochip\n");
> +iounmap:
> + iounmap(plgpio->base);
> +kfree:
> + kfree(plgpio);
> +release_region:
> + release_mem_region(res->start, resource_size(res));
> +fail:
> + dev_err(&pdev->dev, "probe fail: %d\n", ret);
> + return ret;
> +}
> +
> +static struct platform_driver plgpio_driver = {
> + .probe = plgpio_probe,
> + .driver = {
> + .name = "spear-plgpio",
> + .owner = THIS_MODULE,
> + },
> +};
> +
> +static int __init plgpio_init(void)
> +{
> + return platform_driver_register(&plgpio_driver);
> +}
> +subsys_initcall(plgpio_init);
> +
> +MODULE_AUTHOR("Viresh Kumar <viresh.kumar@st.com>");
> +MODULE_DESCRIPTION("SPEAr PLGPIO driver");
> +MODULE_LICENSE("GPL");
> diff --git a/include/linux/spear-plgpio.h b/include/linux/spear-plgpio.h
> new file mode 100644
> index 0000000..a04986a
> --- /dev/null
> +++ b/include/linux/spear-plgpio.h
> @@ -0,0 +1,69 @@
> +/*
> + * include/linux/spear-plgpio.h
> + *
> + * SPEAr platform PLGPIO driver header file
> + *
> + * Copyright (C) 2010-2011 ST Microelectronics
> + * Viresh Kumar<viresh.kumar@st.com>
> + *
> + * This file is licensed under the terms of the GNU General Public
> + * License version 2. This program is licensed "as is" without any
> + * warranty of any kind, whether express or implied.
> + */
> +
> +#ifndef __SPEAR_PLGPIO_H
> +#define __SPEAR_PLGPIO_H
> +
> +#include <linux/types.h>
> +
> +/* plgpio driver declarations */
> +/*
> + * plgpio pins in all machines are not one to one mapped, bitwise with
> + * registers bits. These set of macros define register masks for which below
> + * functions (pin_to_offset and offset_to_pin) are required to be called.
> + */
> +#define PTO_ENB_REG 0x001
> +#define PTO_WDATA_REG 0x002
> +#define PTO_DIR_REG 0x004
> +#define PTO_IE_REG 0x008
> +#define PTO_RDATA_REG 0x010
> +#define PTO_MIS_REG 0x020
> +
> +/* plgpio registers */
> +struct plgpio_regs {
> + u32 enb; /* enable register */
> + u32 wdata; /* write data register */
> + u32 dir; /* direction set register */
> + u32 rdata; /* read data register */
> + u32 ie; /* interrupt enable register */
> + u32 mis; /* mask interrupt status register */
> + u32 eit; /* edge interrupt type */
> +};
> +
> +/**
> + * struct spear_plgpio_pdata: plgpio driver platform data
> + *
> + * gpio_base: gpio start number of plgpios
> + * irq_base: irq number of plgpio0
> + * gpio_count: total count of plgpios
> + * p2o: function ptr for pin to offset conversion. This is required only for
> + * machines where mapping b/w pin and offset is not 1-to-1.
> + * o2p: function ptr for offset to pin conversion. This is required only for
> + * machines where mapping b/w pin and offset is not 1-to-1.
> + * p2o_regs: mask of registers for which p2o and o2p are applicable
> + * regs: register offsets
> + * irq_trigger_type: irq type supported
> + */
> +struct spear_plgpio_pdata {
> + u32 gpio_base;
> + u32 irq_base;
> + u32 gpio_count;
> + int (*p2o)(int pin); /* pin_to_offset */
> + int (*o2p)(int offset); /* offset_to_pin */
> + u32 p2o_regs;
> + struct plgpio_regs regs;
> + unsigned irq_trigger_type;
> +};
> +
> +#endif /* __SPEAR_PLGPIO_H */
> +
> --
> 1.7.2.2
>
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 1/3] gpio/spear-plgpio: Add plgpio driver for SPEAr platform
@ 2011-06-03 17:50 ` Grant Likely
0 siblings, 0 replies; 9+ messages in thread
From: Grant Likely @ 2011-06-03 17:50 UTC (permalink / raw)
To: linux-arm-kernel
On Mon, May 30, 2011 at 12:09:02PM +0530, Viresh Kumar wrote:
> Plgpio pads on few spear machines can be configured as gpios. This patch adds
> support for configuring these PLGPIOs.
>
> This was earlier posted & reviewed as part of arch/arm/plat-spear/ code.
>
> Reviewed-by: Stanley Miao <stanley.miao@windriver.com>
> Signed-off-by: Viresh Kumar <viresh.kumar@st.com>
Hi Viresh,
This ends up being yet-another-mmio-gpio implementation. Please look
at bgpio_init() in drivers/basic_mmio_gpio.c. This driver should be
refactored to use that.
g.
> ---
> MAINTAINERS | 7 +
> drivers/gpio/Kconfig | 7 +
> drivers/gpio/Makefile | 1 +
> drivers/gpio/spear-plgpio.c | 490 ++++++++++++++++++++++++++++++++++++++++++
> include/linux/spear-plgpio.h | 69 ++++++
> 5 files changed, 574 insertions(+), 0 deletions(-)
> create mode 100644 drivers/gpio/spear-plgpio.c
> create mode 100644 include/linux/spear-plgpio.h
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 29801f7..48b0a4f 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -5978,6 +5978,13 @@ F: arch/arm/mach-spear3xx/spear3*0_evb.c
> F: arch/arm/mach-spear6xx/spear600.c
> F: arch/arm/mach-spear6xx/spear600_evb.c
>
> +SPEAR PLGPIO SUPPORT
> +M: Viresh Kumar <viresh.kumar@st.com>
> +W: http://www.st.com/spear
> +S: Maintained
> +F: drivers/gpio/spear-plgpio.c
> +F: include/linux/spear-plgpio.h
> +
> SPECIALIX IO8+ MULTIPORT SERIAL CARD DRIVER
> S: Orphan
> F: Documentation/serial/specialix.txt
> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
> index 4a7f631..227d2e7 100644
> --- a/drivers/gpio/Kconfig
> +++ b/drivers/gpio/Kconfig
> @@ -164,6 +164,13 @@ config GPIO_VX855
> additional drivers must be enabled in order to use the
> functionality of the device.
>
> +config SPEAR_PLGPIO
> + bool "ST Micro SPEAr PLGPIO"
> + depends on PLAT_SPEAR
> + help
> + This enables support for the PLGPIOs found on the ST Microelectronics
> + SPEAr platform
> +
> comment "I2C GPIO expanders:"
>
> config GPIO_MAX7300
> diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
> index b605f8e..2d325b0 100644
> --- a/drivers/gpio/Makefile
> +++ b/drivers/gpio/Makefile
> @@ -48,3 +48,4 @@ obj-$(CONFIG_GPIO_VX855) += vx855_gpio.o
> obj-$(CONFIG_GPIO_ML_IOH) += ml_ioh_gpio.o
> obj-$(CONFIG_AB8500_GPIO) += ab8500-gpio.o
> obj-$(CONFIG_GPIO_TPS65910) += tps65910-gpio.o
> +obj-$(CONFIG_SPEAR_PLGPIO) += spear-plgpio.o
> diff --git a/drivers/gpio/spear-plgpio.c b/drivers/gpio/spear-plgpio.c
> new file mode 100644
> index 0000000..885c0da
> --- /dev/null
> +++ b/drivers/gpio/spear-plgpio.c
> @@ -0,0 +1,490 @@
> +/*
> + * drivers/gpio/spear-plgpio.c
> + *
> + * SPEAr platform PLGPIO driver source file
> + *
> + * Copyright (C) 2010-2011 ST Microelectronics
> + * Viresh Kumar<viresh.kumar@st.com>
> + *
> + * This file is licensed under the terms of the GNU General Public
> + * License version 2. This program is licensed "as is" without any
> + * warranty of any kind, whether express or implied.
> + */
> +
> +#include <linux/bitops.h>
> +#include <linux/compiler.h>
> +#include <linux/errno.h>
> +#include <linux/gpio.h>
> +#include <linux/io.h>
> +#include <linux/ioport.h>
> +#include <linux/irq.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <linux/spear-plgpio.h>
> +#include <linux/spinlock.h>
> +
> +#define MAX_GPIO_PER_REG 32
> +#define PIN_OFFSET(pin) (pin % MAX_GPIO_PER_REG)
> +#define REG_OFFSET(base, reg, pin) (base + reg + (pin / MAX_GPIO_PER_REG)\
> + * sizeof(int *))
> +
> +/*
> + * struct plgpio: plgpio driver specific structure
> + *
> + * lock: lock for guarding gpio registers
> + * base: base address of plgpio block
> + * irq_base: irq number of plgpio0
> + * chip: gpio framework specific chip information structure
> + * p2o: function ptr for pin to offset conversion. This is required only for
> + * machines where mapping b/w pin and offset is not 1-to-1.
> + * o2p: function ptr for offset to pin conversion. This is required only for
> + * machines where mapping b/w pin and offset is not 1-to-1.
> + * p2o_regs: mask of registers for which p2o and o2p are applicable
> + * regs: register offsets
> + * irq_trigger_type: irq type supported
> + */
> +struct plgpio {
> + spinlock_t lock;
> + void __iomem *base;
> + unsigned irq_base;
> + struct gpio_chip chip;
> + int (*p2o)(int pin); /* pin_to_offset */
> + int (*o2p)(int offset); /* offset_to_pin */
> + unsigned p2o_regs;
> + struct plgpio_regs regs;
> + unsigned irq_trigger_type;
> +};
> +
> +/* register manipulation inline functions */
> +static inline u32 is_plgpio_set(void __iomem *base, u32 pin, u32 reg)
> +{
> + u32 offset = PIN_OFFSET(pin);
> + void __iomem *reg_off = REG_OFFSET(base, reg, pin);
> + u32 val = readl(reg_off);
> +
> + return val & (1 << offset);
> +}
> +
> +static inline void plgpio_reg_set(void __iomem *base, u32 pin, u32 reg)
> +{
> + u32 offset = PIN_OFFSET(pin);
> + void __iomem *reg_off = REG_OFFSET(base, reg, pin);
> + u32 val = readl(reg_off);
> +
> + writel(val | (1 << offset), reg_off);
> +}
> +
> +static inline void plgpio_reg_reset(void __iomem *base, u32 pin, u32 reg)
> +{
> + u32 offset = PIN_OFFSET(pin);
> + void __iomem *reg_off = REG_OFFSET(base, reg, pin);
> + u32 val = readl(reg_off);
> +
> + writel(val & ~(1 << offset), reg_off);
> +}
> +
> +/* gpio framework specific routines */
> +static int plgpio_direction_input(struct gpio_chip *chip, unsigned offset)
> +{
> + struct plgpio *plgpio = container_of(chip, struct plgpio, chip);
> + unsigned long flags;
> +
> + if (offset >= chip->ngpio)
> + return -EINVAL;
> +
> + /* get correct offset for "offset" pin */
> + if (plgpio->p2o && (plgpio->p2o_regs & PTO_DIR_REG)) {
> + offset = plgpio->p2o(offset);
> + if (offset == -1)
> + return -EINVAL;
> + }
> +
> + spin_lock_irqsave(&plgpio->lock, flags);
> + plgpio_reg_set(plgpio->base, offset, plgpio->regs.dir);
> + spin_unlock_irqrestore(&plgpio->lock, flags);
> +
> + return 0;
> +}
> +
> +static int plgpio_direction_output(struct gpio_chip *chip, unsigned offset,
> + int value)
> +{
> + struct plgpio *plgpio = container_of(chip, struct plgpio, chip);
> + unsigned long flags;
> + unsigned dir_offset = offset, wdata_offset = offset, tmp;
> +
> + if (offset >= chip->ngpio)
> + return -EINVAL;
> +
> + /* get correct offset for "offset" pin */
> + if (plgpio->p2o && (plgpio->p2o_regs & (PTO_DIR_REG | PTO_WDATA_REG))) {
> + tmp = plgpio->p2o(offset);
> + if (tmp == -1)
> + return -EINVAL;
> +
> + if (plgpio->p2o_regs & PTO_DIR_REG)
> + dir_offset = tmp;
> + if (plgpio->p2o_regs & PTO_WDATA_REG)
> + wdata_offset = tmp;
> + }
> +
> + spin_lock_irqsave(&plgpio->lock, flags);
> + plgpio_reg_reset(plgpio->base, dir_offset, plgpio->regs.dir);
> + if (value)
> + plgpio_reg_set(plgpio->base, wdata_offset,
> + plgpio->regs.wdata);
> + else
> + plgpio_reg_reset(plgpio->base, wdata_offset,
> + plgpio->regs.wdata);
> + spin_unlock_irqrestore(&plgpio->lock, flags);
> +
> + return 0;
> +}
> +
> +static int plgpio_get_value(struct gpio_chip *chip, unsigned offset)
> +{
> + struct plgpio *plgpio = container_of(chip, struct plgpio, chip);
> +
> + if (offset >= chip->ngpio)
> + return -EINVAL;
> +
> + /* get correct offset for "offset" pin */
> + if (plgpio->p2o && (plgpio->p2o_regs & PTO_RDATA_REG)) {
> + offset = plgpio->p2o(offset);
> + if (offset == -1)
> + return -EINVAL;
> + }
> +
> + return is_plgpio_set(plgpio->base, offset, plgpio->regs.rdata);
> +}
> +
> +static void plgpio_set_value(struct gpio_chip *chip, unsigned offset, int value)
> +{
> + struct plgpio *plgpio = container_of(chip, struct plgpio, chip);
> +
> + if (offset >= chip->ngpio)
> + return;
> +
> + /* get correct offset for "offset" pin */
> + if (plgpio->p2o && (plgpio->p2o_regs & PTO_WDATA_REG)) {
> + offset = plgpio->p2o(offset);
> + if (offset == -1)
> + return;
> + }
> +
> + if (value)
> + plgpio_reg_set(plgpio->base, offset, plgpio->regs.wdata);
> + else
> + plgpio_reg_reset(plgpio->base, offset, plgpio->regs.wdata);
> +}
> +
> +static int plgpio_request(struct gpio_chip *chip, unsigned offset)
> +{
> + struct plgpio *plgpio = container_of(chip, struct plgpio, chip);
> + unsigned long flags;
> + int ret = 0;
> +
> + if (offset >= chip->ngpio)
> + return -EINVAL;
> +
> + /*
> + * put gpio in IN mode before enabling it. This make enabling gpio safe
> + */
> + ret = plgpio_direction_input(chip, offset);
> + if (ret)
> + return ret;
> +
> + /* get correct offset for "offset" pin */
> + if (plgpio->p2o && (plgpio->p2o_regs & PTO_ENB_REG)) {
> + offset = plgpio->p2o(offset);
> + if (offset == -1)
> + return -EINVAL;
> + }
> +
> + spin_lock_irqsave(&plgpio->lock, flags);
> + plgpio_reg_set(plgpio->base, offset, plgpio->regs.enb);
> + spin_unlock_irqrestore(&plgpio->lock, flags);
> +
> + return 0;
> +}
> +
> +static void plgpio_free(struct gpio_chip *chip, unsigned offset)
> +{
> + struct plgpio *plgpio = container_of(chip, struct plgpio, chip);
> + unsigned long flags;
> +
> + if (offset >= chip->ngpio)
> + return;
> +
> + /* get correct offset for "offset" pin */
> + if (plgpio->p2o && (plgpio->p2o_regs & PTO_ENB_REG)) {
> + offset = plgpio->p2o(offset);
> + if (offset == -1)
> + return;
> + }
> +
> + spin_lock_irqsave(&plgpio->lock, flags);
> + plgpio_reg_reset(plgpio->base, offset, plgpio->regs.enb);
> + spin_unlock_irqrestore(&plgpio->lock, flags);
> +}
> +
> +static int plgpio_to_irq(struct gpio_chip *chip, unsigned offset)
> +{
> + struct plgpio *plgpio = container_of(chip, struct plgpio, chip);
> +
> + if (plgpio->irq_base == (unsigned) -1)
> + return -EINVAL;
> +
> + return plgpio->irq_base + offset;
> +}
> +
> +/* PLGPIO IRQ */
> +static void plgpio_irq_mask(struct irq_data *d)
> +{
> + struct plgpio *plgpio = irq_data_get_irq_chip_data(d);
> + int offset = d->irq - plgpio->irq_base;
> + unsigned long flags;
> +
> + /* get correct offset for "offset" pin */
> + if (plgpio->p2o && (plgpio->p2o_regs & PTO_IE_REG)) {
> + offset = plgpio->p2o(offset);
> + if (offset == -1)
> + return;
> + }
> +
> + spin_lock_irqsave(&plgpio->lock, flags);
> + plgpio_reg_set(plgpio->base, offset, plgpio->regs.ie);
> + spin_unlock_irqrestore(&plgpio->lock, flags);
> +}
> +
> +static void plgpio_irq_unmask(struct irq_data *d)
> +{
> + struct plgpio *plgpio = irq_data_get_irq_chip_data(d);
> + int offset = d->irq - plgpio->irq_base;
> + unsigned long flags;
> +
> + /* get correct offset for "offset" pin */
> + if (plgpio->p2o && (plgpio->p2o_regs & PTO_IE_REG)) {
> + offset = plgpio->p2o(offset);
> + if (offset == -1)
> + return;
> + }
> +
> + spin_lock_irqsave(&plgpio->lock, flags);
> + plgpio_reg_reset(plgpio->base, offset, plgpio->regs.ie);
> + spin_unlock_irqrestore(&plgpio->lock, flags);
> +}
> +
> +static int plgpio_irq_type(struct irq_data *d, unsigned trigger)
> +{
> + struct plgpio *plgpio = irq_data_get_irq_chip_data(d);
> + int offset = d->irq - plgpio->irq_base;
> +
> + if (!(plgpio->irq_trigger_type & trigger))
> + return -EINVAL;
> + if (offset >= plgpio->chip.ngpio)
> + return -EINVAL;
> +
> + /*
> + * Currently we only need to configure register in case of edge
> + * interrupt
> + */
> + if (plgpio->irq_trigger_type == (IRQ_TYPE_EDGE_RISING |
> + IRQ_TYPE_EDGE_FALLING)) {
> + void __iomem *reg_off = REG_OFFSET(plgpio->base,
> + plgpio->regs.eit, offset);
> + u32 val = readl(reg_off);
> +
> + offset = PIN_OFFSET(offset);
> + if (trigger == IRQ_TYPE_EDGE_RISING)
> + writel(val | (1 << offset), reg_off);
> + else
> + writel(val & ~(1 << offset), reg_off);
> + }
> +
> + return 0;
> +}
> +
> +static struct irq_chip plgpio_irqchip = {
> + .name = "PLGPIO",
> + .irq_mask = plgpio_irq_mask,
> + .irq_unmask = plgpio_irq_unmask,
> + .irq_set_type = plgpio_irq_type,
> +};
> +
> +static void plgpio_irq_handler(unsigned irq, struct irq_desc *desc)
> +{
> + struct plgpio *plgpio = irq_get_handler_data(irq);
> + int regs_count = DIV_ROUND_UP(plgpio->chip.ngpio, MAX_GPIO_PER_REG),
> + count, pin, offset, i = 0;
> + unsigned long pending;
> +
> + desc->irq_data.chip->irq_ack(&desc->irq_data);
> + /* check all plgpio MIS registers for a possible interrupt */
> + for (; i < regs_count; i++) {
> + pending = readl(plgpio->base + plgpio->regs.mis +
> + i * sizeof(int *));
> + if (!pending)
> + continue;
> +
> + /* clear interrupts */
> + writel(~pending, plgpio->base + plgpio->regs.mis +
> + i * sizeof(int *));
> + /*
> + * clear extra bits in last register having gpios < MAX/REG
> + * ex: Suppose there are max 102 plgpios. then last register
> + * must have only (102 - MAX_GPIO_PER_REG * 3) = 6 relevant bits
> + * so, we must not take other 28 bits into consideration for
> + * checking interrupt. so clear those bits.
> + */
> + count = plgpio->chip.ngpio - i * MAX_GPIO_PER_REG;
> + if (count < MAX_GPIO_PER_REG)
> + pending &= (1 << count) - 1;
> +
> + for_each_set_bit(offset, &pending, MAX_GPIO_PER_REG) {
> + /* get correct pin for "offset" */
> + if (plgpio->o2p && (plgpio->p2o_regs & PTO_MIS_REG)) {
> + pin = plgpio->o2p(offset);
> + if (pin == -1)
> + continue;
> + } else
> + pin = offset;
> +
> + generic_handle_irq(plgpio_to_irq(&plgpio->chip,
> + i * MAX_GPIO_PER_REG + pin));
> + }
> + }
> + desc->irq_data.chip->irq_unmask(&desc->irq_data);
> +}
> +
> +static int __devinit plgpio_probe(struct platform_device *pdev)
> +{
> + struct spear_plgpio_pdata *pdata;
> + struct plgpio *plgpio;
> + int ret, irq, i;
> + struct resource *res;
> +
> + pdata = pdev->dev.platform_data;
> + if (!pdata) {
> + ret = -ENODEV;
> + dev_dbg(&pdev->dev, "invalid platform data\n");
> + goto fail;
> + }
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!res) {
> + ret = -EBUSY;
> + dev_dbg(&pdev->dev, "invalid IORESOURCE_MEM\n");
> + goto fail;
> + }
> +
> + if (!request_mem_region(res->start, resource_size(res), "plgpio")) {
> + ret = -EBUSY;
> + dev_dbg(&pdev->dev, "request mem region fail\n");
> + goto fail;
> + }
> +
> + plgpio = kzalloc(sizeof(*plgpio), GFP_KERNEL);
> + if (!plgpio) {
> + ret = -ENOMEM;
> + dev_dbg(&pdev->dev, "memory allocation fail\n");
> + goto release_region;
> + }
> +
> + plgpio->base = ioremap(res->start, resource_size(res));
> + if (!plgpio->base) {
> + ret = -ENOMEM;
> + dev_dbg(&pdev->dev, "ioremap fail\n");
> + goto kfree;
> + }
> +
> + spin_lock_init(&plgpio->lock);
> +
> + plgpio->chip.request = plgpio_request;
> + plgpio->chip.free = plgpio_free;
> + plgpio->chip.direction_input = plgpio_direction_input;
> + plgpio->chip.direction_output = plgpio_direction_output;
> + plgpio->chip.get = plgpio_get_value;
> + plgpio->chip.set = plgpio_set_value;
> + plgpio->chip.to_irq = plgpio_to_irq;
> + plgpio->chip.base = pdata->gpio_base;
> + plgpio->chip.ngpio = pdata->gpio_count;
> + plgpio->chip.label = dev_name(&pdev->dev);
> + plgpio->chip.dev = &pdev->dev;
> + plgpio->chip.owner = THIS_MODULE;
> + plgpio->irq_base = pdata->irq_base;
> + plgpio->p2o = pdata->p2o;
> + plgpio->o2p = pdata->o2p;
> + plgpio->p2o_regs = pdata->p2o_regs;
> + plgpio->regs.enb = pdata->regs.enb;
> + plgpio->regs.wdata = pdata->regs.wdata;
> + plgpio->regs.dir = pdata->regs.dir;
> + plgpio->regs.ie = pdata->regs.ie;
> + plgpio->regs.rdata = pdata->regs.rdata;
> + plgpio->regs.mis = pdata->regs.mis;
> + plgpio->irq_trigger_type = pdata->irq_trigger_type;
> +
> + ret = gpiochip_add(&plgpio->chip);
> + if (ret) {
> + dev_dbg(&pdev->dev, "unable to add gpio chip\n");
> + goto iounmap;
> + }
> +
> + /* irq_chip support */
> + if (pdata->irq_base == (unsigned) -1) {
> + dev_info(&pdev->dev, "Successfully registered without irqs\n");
> + return 0;
> + }
> +
> + irq = platform_get_irq(pdev, 0);
> + if (irq < 0) {
> + ret = -ENODEV;
> + dev_dbg(&pdev->dev, "invalid irq number\n");
> + goto remove_gpiochip;
> + }
> +
> + irq_set_chained_handler(irq, plgpio_irq_handler);
> + for (i = 0; i < pdata->gpio_count; i++) {
> + irq_set_chip_and_handler(i+plgpio->irq_base, &plgpio_irqchip,
> + handle_simple_irq);
> + set_irq_flags(i+plgpio->irq_base, IRQF_VALID);
> + irq_set_chip_data(i+plgpio->irq_base, plgpio);
> + }
> + irq_set_handler_data(irq, plgpio);
> + dev_info(&pdev->dev, "Successfully registered with irqs\n");
> +
> + return 0;
> +
> +remove_gpiochip:
> + if (gpiochip_remove(&plgpio->chip))
> + dev_dbg(&pdev->dev, "unable to remove gpiochip\n");
> +iounmap:
> + iounmap(plgpio->base);
> +kfree:
> + kfree(plgpio);
> +release_region:
> + release_mem_region(res->start, resource_size(res));
> +fail:
> + dev_err(&pdev->dev, "probe fail: %d\n", ret);
> + return ret;
> +}
> +
> +static struct platform_driver plgpio_driver = {
> + .probe = plgpio_probe,
> + .driver = {
> + .name = "spear-plgpio",
> + .owner = THIS_MODULE,
> + },
> +};
> +
> +static int __init plgpio_init(void)
> +{
> + return platform_driver_register(&plgpio_driver);
> +}
> +subsys_initcall(plgpio_init);
> +
> +MODULE_AUTHOR("Viresh Kumar <viresh.kumar@st.com>");
> +MODULE_DESCRIPTION("SPEAr PLGPIO driver");
> +MODULE_LICENSE("GPL");
> diff --git a/include/linux/spear-plgpio.h b/include/linux/spear-plgpio.h
> new file mode 100644
> index 0000000..a04986a
> --- /dev/null
> +++ b/include/linux/spear-plgpio.h
> @@ -0,0 +1,69 @@
> +/*
> + * include/linux/spear-plgpio.h
> + *
> + * SPEAr platform PLGPIO driver header file
> + *
> + * Copyright (C) 2010-2011 ST Microelectronics
> + * Viresh Kumar<viresh.kumar@st.com>
> + *
> + * This file is licensed under the terms of the GNU General Public
> + * License version 2. This program is licensed "as is" without any
> + * warranty of any kind, whether express or implied.
> + */
> +
> +#ifndef __SPEAR_PLGPIO_H
> +#define __SPEAR_PLGPIO_H
> +
> +#include <linux/types.h>
> +
> +/* plgpio driver declarations */
> +/*
> + * plgpio pins in all machines are not one to one mapped, bitwise with
> + * registers bits. These set of macros define register masks for which below
> + * functions (pin_to_offset and offset_to_pin) are required to be called.
> + */
> +#define PTO_ENB_REG 0x001
> +#define PTO_WDATA_REG 0x002
> +#define PTO_DIR_REG 0x004
> +#define PTO_IE_REG 0x008
> +#define PTO_RDATA_REG 0x010
> +#define PTO_MIS_REG 0x020
> +
> +/* plgpio registers */
> +struct plgpio_regs {
> + u32 enb; /* enable register */
> + u32 wdata; /* write data register */
> + u32 dir; /* direction set register */
> + u32 rdata; /* read data register */
> + u32 ie; /* interrupt enable register */
> + u32 mis; /* mask interrupt status register */
> + u32 eit; /* edge interrupt type */
> +};
> +
> +/**
> + * struct spear_plgpio_pdata: plgpio driver platform data
> + *
> + * gpio_base: gpio start number of plgpios
> + * irq_base: irq number of plgpio0
> + * gpio_count: total count of plgpios
> + * p2o: function ptr for pin to offset conversion. This is required only for
> + * machines where mapping b/w pin and offset is not 1-to-1.
> + * o2p: function ptr for offset to pin conversion. This is required only for
> + * machines where mapping b/w pin and offset is not 1-to-1.
> + * p2o_regs: mask of registers for which p2o and o2p are applicable
> + * regs: register offsets
> + * irq_trigger_type: irq type supported
> + */
> +struct spear_plgpio_pdata {
> + u32 gpio_base;
> + u32 irq_base;
> + u32 gpio_count;
> + int (*p2o)(int pin); /* pin_to_offset */
> + int (*o2p)(int offset); /* offset_to_pin */
> + u32 p2o_regs;
> + struct plgpio_regs regs;
> + unsigned irq_trigger_type;
> +};
> +
> +#endif /* __SPEAR_PLGPIO_H */
> +
> --
> 1.7.2.2
>
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 1/3] gpio/spear-plgpio: Add plgpio driver for SPEAr platform
2011-06-03 17:50 ` Grant Likely
@ 2011-06-07 8:35 ` viresh kumar
-1 siblings, 0 replies; 9+ messages in thread
From: viresh kumar @ 2011-06-07 8:35 UTC (permalink / raw)
To: Grant Likely
Cc: linux-arm-kernel, Armando VISCONTI, Shiraz HASHIM, Vipin KUMAR,
Rajeev KUMAR, Deepak SIKRI, Vipul Kumar SAMAR, Amit VIRDI,
Pratyush ANAND, Bhupesh SHARMA, viresh.linux, linux-kernel
On 06/03/2011 11:20 PM, Grant Likely wrote:
> On Mon, May 30, 2011 at 12:09:02PM +0530, Viresh Kumar wrote:
>> > Plgpio pads on few spear machines can be configured as gpios. This patch adds
>> > support for configuring these PLGPIOs.
>> >
>> > This was earlier posted & reviewed as part of arch/arm/plat-spear/ code.
>> >
>> > Reviewed-by: Stanley Miao <stanley.miao@windriver.com>
>> > Signed-off-by: Viresh Kumar <viresh.kumar@st.com>
>
> This ends up being yet-another-mmio-gpio implementation. Please look
> at bgpio_init() in drivers/basic_mmio_gpio.c. This driver should be
> refactored to use that.
Ok. Will check that.
--
viresh
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 1/3] gpio/spear-plgpio: Add plgpio driver for SPEAr platform
@ 2011-06-07 8:35 ` viresh kumar
0 siblings, 0 replies; 9+ messages in thread
From: viresh kumar @ 2011-06-07 8:35 UTC (permalink / raw)
To: linux-arm-kernel
On 06/03/2011 11:20 PM, Grant Likely wrote:
> On Mon, May 30, 2011 at 12:09:02PM +0530, Viresh Kumar wrote:
>> > Plgpio pads on few spear machines can be configured as gpios. This patch adds
>> > support for configuring these PLGPIOs.
>> >
>> > This was earlier posted & reviewed as part of arch/arm/plat-spear/ code.
>> >
>> > Reviewed-by: Stanley Miao <stanley.miao@windriver.com>
>> > Signed-off-by: Viresh Kumar <viresh.kumar@st.com>
>
> This ends up being yet-another-mmio-gpio implementation. Please look
> at bgpio_init() in drivers/basic_mmio_gpio.c. This driver should be
> refactored to use that.
Ok. Will check that.
--
viresh
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2011-06-07 8:36 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-05-30 6:39 [PATCH 0/3] SPEAr: Add plgpio support Viresh Kumar
2011-05-30 6:39 ` [PATCH 1/3] gpio/spear-plgpio: Add plgpio driver for SPEAr platform Viresh Kumar
2011-05-30 6:39 ` Viresh Kumar
2011-06-03 17:50 ` Grant Likely
2011-06-03 17:50 ` Grant Likely
2011-06-07 8:35 ` viresh kumar
2011-06-07 8:35 ` viresh kumar
2011-05-30 6:39 ` [PATCH 2/3] SPEAr: Add machine support for plgpio Viresh Kumar
2011-05-30 6:39 ` [PATCH 3/3] SPEAr3xx: Update defconfig " Viresh Kumar
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.