* [PATCH 0/3] SPEAr: Add plgpio support @ 2011-05-30 6:39 Viresh Kumar 2011-05-30 6:39 ` [PATCH 1/3] gpio/spear-plgpio: Add plgpio driver for SPEAr platform Viresh Kumar ` (2 more replies) 0 siblings, 3 replies; 6+ 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] 6+ messages in thread
* [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-06-03 17:50 ` Grant Likely 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, 1 reply; 6+ 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] 6+ messages in thread
* [PATCH 1/3] gpio/spear-plgpio: Add plgpio driver for SPEAr platform 2011-05-30 6:39 ` [PATCH 1/3] gpio/spear-plgpio: Add plgpio driver for SPEAr platform Viresh Kumar @ 2011-06-03 17:50 ` Grant Likely 2011-06-07 8:35 ` viresh kumar 0 siblings, 1 reply; 6+ 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] 6+ messages in thread
* [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 0 siblings, 0 replies; 6+ 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] 6+ 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 ` [PATCH 1/3] gpio/spear-plgpio: Add plgpio driver for SPEAr platform 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; 6+ 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] 6+ 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 ` [PATCH 1/3] gpio/spear-plgpio: Add plgpio driver for SPEAr platform 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; 6+ 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] 6+ messages in thread
end of thread, other threads:[~2011-06-07 8:35 UTC | newest] Thread overview: 6+ 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-06-03 17:50 ` Grant Likely 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 a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).