diff options
author | Olof Johansson | 2012-10-04 20:17:25 -0700 |
---|---|---|
committer | Olof Johansson | 2012-10-04 20:17:25 -0700 |
commit | 54d69df5849ec2e660aa12ac75562618c10fb499 (patch) | |
tree | adbfb8bcc7cc73b83bf2b784fa331911ba03573a /drivers/gpio | |
parent | ad932bb6b549722a561fb31ac2fa50dcbcb3e36b (diff) | |
parent | 46f2007c1efadfa4071c17e75f140c47f09293de (diff) |
Merge branch 'late/kirkwood' into late/soc
Merge in the late Kirkwood branch with the OMAP late branch for upstream
submission.
Final contents described in shared tag.
Fixup remove/change conflicts in arch/arm/mach-omap2/devices.c and
drivers/spi/spi-omap2-mcspi.c.
Signed-off-by: Olof Johansson <olof@lixom.net>
Diffstat (limited to 'drivers/gpio')
-rw-r--r-- | drivers/gpio/Kconfig | 6 | ||||
-rw-r--r-- | drivers/gpio/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpio/gpio-mvebu.c | 679 | ||||
-rw-r--r-- | drivers/gpio/gpio-pxa.c | 77 | ||||
-rw-r--r-- | drivers/gpio/gpio-samsung.c | 103 | ||||
-rw-r--r-- | drivers/gpio/gpio-tegra.c | 3 | ||||
-rw-r--r-- | drivers/gpio/gpio-twl4030.c | 77 |
7 files changed, 824 insertions, 122 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index ba7926f5c099..66a59510887f 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -150,6 +150,12 @@ config GPIO_MSM_V2 Qualcomm MSM chips. Most of the pins on the MSM can be selected for GPIO, and are controlled by this driver. +config GPIO_MVEBU + def_bool y + depends on ARCH_MVEBU + select GPIO_GENERIC + select GENERIC_IRQ_CHIP + config GPIO_MXC def_bool y depends on ARCH_MXC diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 153caceeb053..1a33e6716e10 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -41,6 +41,7 @@ obj-$(CONFIG_GPIO_MPC8XXX) += gpio-mpc8xxx.o obj-$(CONFIG_GPIO_MSIC) += gpio-msic.o obj-$(CONFIG_GPIO_MSM_V1) += gpio-msm-v1.o obj-$(CONFIG_GPIO_MSM_V2) += gpio-msm-v2.o +obj-$(CONFIG_GPIO_MVEBU) += gpio-mvebu.o obj-$(CONFIG_GPIO_MXC) += gpio-mxc.o obj-$(CONFIG_GPIO_MXS) += gpio-mxs.o obj-$(CONFIG_ARCH_OMAP) += gpio-omap.o diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c new file mode 100644 index 000000000000..902af437eaf2 --- /dev/null +++ b/drivers/gpio/gpio-mvebu.c @@ -0,0 +1,679 @@ +/* + * GPIO driver for Marvell SoCs + * + * Copyright (C) 2012 Marvell + * + * Thomas Petazzoni <thomas.petazzoni@free-electrons.com> + * Andrew Lunn <andrew@lunn.ch> + * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.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. + * + * This driver is a fairly straightforward GPIO driver for the + * complete family of Marvell EBU SoC platforms (Orion, Dove, + * Kirkwood, Discovery, Armada 370/XP). The only complexity of this + * driver is the different register layout that exists between the + * non-SMP platforms (Orion, Dove, Kirkwood, Armada 370) and the SMP + * platforms (MV78200 from the Discovery family and the Armada + * XP). Therefore, this driver handles three variants of the GPIO + * block: + * - the basic variant, called "orion-gpio", with the simplest + * register set. Used on Orion, Dove, Kirkwoord, Armada 370 and + * non-SMP Discovery systems + * - the mv78200 variant for MV78200 Discovery systems. This variant + * turns the edge mask and level mask registers into CPU0 edge + * mask/level mask registers, and adds CPU1 edge mask/level mask + * registers. + * - the armadaxp variant for Armada XP systems. This variant keeps + * the normal cause/edge mask/level mask registers when the global + * interrupts are used, but adds per-CPU cause/edge mask/level mask + * registers n a separate memory area for the per-CPU GPIO + * interrupts. + */ + +#include <linux/module.h> +#include <linux/gpio.h> +#include <linux/irq.h> +#include <linux/slab.h> +#include <linux/irqdomain.h> +#include <linux/io.h> +#include <linux/of_irq.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/pinctrl/consumer.h> + +/* + * GPIO unit register offsets. + */ +#define GPIO_OUT_OFF 0x0000 +#define GPIO_IO_CONF_OFF 0x0004 +#define GPIO_BLINK_EN_OFF 0x0008 +#define GPIO_IN_POL_OFF 0x000c +#define GPIO_DATA_IN_OFF 0x0010 +#define GPIO_EDGE_CAUSE_OFF 0x0014 +#define GPIO_EDGE_MASK_OFF 0x0018 +#define GPIO_LEVEL_MASK_OFF 0x001c + +/* The MV78200 has per-CPU registers for edge mask and level mask */ +#define GPIO_EDGE_MASK_MV78200_OFF(cpu) ((cpu) ? 0x30 : 0x18) +#define GPIO_LEVEL_MASK_MV78200_OFF(cpu) ((cpu) ? 0x34 : 0x1C) + +/* The Armada XP has per-CPU registers for interrupt cause, interrupt + * mask and interrupt level mask. Those are relative to the + * percpu_membase. */ +#define GPIO_EDGE_CAUSE_ARMADAXP_OFF(cpu) ((cpu) * 0x4) +#define GPIO_EDGE_MASK_ARMADAXP_OFF(cpu) (0x10 + (cpu) * 0x4) +#define GPIO_LEVEL_MASK_ARMADAXP_OFF(cpu) (0x20 + (cpu) * 0x4) + +#define MVEBU_GPIO_SOC_VARIANT_ORION 0x1 +#define MVEBU_GPIO_SOC_VARIANT_MV78200 0x2 +#define MVEBU_GPIO_SOC_VARIANT_ARMADAXP 0x3 + +#define MVEBU_MAX_GPIO_PER_BANK 32 + +struct mvebu_gpio_chip { + struct gpio_chip chip; + spinlock_t lock; + void __iomem *membase; + void __iomem *percpu_membase; + unsigned int irqbase; + struct irq_domain *domain; + int soc_variant; +}; + +/* + * Functions returning addresses of individual registers for a given + * GPIO controller. + */ +static inline void __iomem *mvebu_gpioreg_out(struct mvebu_gpio_chip *mvchip) +{ + return mvchip->membase + GPIO_OUT_OFF; +} + +static inline void __iomem *mvebu_gpioreg_io_conf(struct mvebu_gpio_chip *mvchip) +{ + return mvchip->membase + GPIO_IO_CONF_OFF; +} + +static inline void __iomem *mvebu_gpioreg_in_pol(struct mvebu_gpio_chip *mvchip) +{ + return mvchip->membase + GPIO_IN_POL_OFF; +} + +static inline void __iomem *mvebu_gpioreg_data_in(struct mvebu_gpio_chip *mvchip) +{ + return mvchip->membase + GPIO_DATA_IN_OFF; +} + +static inline void __iomem *mvebu_gpioreg_edge_cause(struct mvebu_gpio_chip *mvchip) +{ + int cpu; + + switch(mvchip->soc_variant) { + case MVEBU_GPIO_SOC_VARIANT_ORION: + case MVEBU_GPIO_SOC_VARIANT_MV78200: + return mvchip->membase + GPIO_EDGE_CAUSE_OFF; + case MVEBU_GPIO_SOC_VARIANT_ARMADAXP: + cpu = smp_processor_id(); + return mvchip->percpu_membase + GPIO_EDGE_CAUSE_ARMADAXP_OFF(cpu); + default: + BUG(); + } +} + +static inline void __iomem *mvebu_gpioreg_edge_mask(struct mvebu_gpio_chip *mvchip) +{ + int cpu; + + switch(mvchip->soc_variant) { + case MVEBU_GPIO_SOC_VARIANT_ORION: + return mvchip->membase + GPIO_EDGE_MASK_OFF; + case MVEBU_GPIO_SOC_VARIANT_MV78200: + cpu = smp_processor_id(); + return mvchip->membase + GPIO_EDGE_MASK_MV78200_OFF(cpu); + case MVEBU_GPIO_SOC_VARIANT_ARMADAXP: + cpu = smp_processor_id(); + return mvchip->percpu_membase + GPIO_EDGE_MASK_ARMADAXP_OFF(cpu); + default: + BUG(); + } +} + +static void __iomem *mvebu_gpioreg_level_mask(struct mvebu_gpio_chip *mvchip) +{ + int cpu; + + switch(mvchip->soc_variant) { + case MVEBU_GPIO_SOC_VARIANT_ORION: + return mvchip->membase + GPIO_LEVEL_MASK_OFF; + case MVEBU_GPIO_SOC_VARIANT_MV78200: + cpu = smp_processor_id(); + return mvchip->membase + GPIO_LEVEL_MASK_MV78200_OFF(cpu); + case MVEBU_GPIO_SOC_VARIANT_ARMADAXP: + cpu = smp_processor_id(); + return mvchip->percpu_membase + GPIO_LEVEL_MASK_ARMADAXP_OFF(cpu); + default: + BUG(); + } +} + +/* + * Functions implementing the gpio_chip methods + */ + +int mvebu_gpio_request(struct gpio_chip *chip, unsigned pin) +{ + return pinctrl_request_gpio(chip->base + pin); +} + +void mvebu_gpio_free(struct gpio_chip *chip, unsigned pin) +{ + pinctrl_free_gpio(chip->base + pin); +} + +static void mvebu_gpio_set(struct gpio_chip *chip, unsigned pin, int value) +{ + struct mvebu_gpio_chip *mvchip = + container_of(chip, struct mvebu_gpio_chip, chip); + unsigned long flags; + u32 u; + + spin_lock_irqsave(&mvchip->lock, flags); + u = readl_relaxed(mvebu_gpioreg_out(mvchip)); + if (value) + u |= 1 << pin; + else + u &= ~(1 << pin); + writel_relaxed(u, mvebu_gpioreg_out(mvchip)); + spin_unlock_irqrestore(&mvchip->lock, flags); +} + +static int mvebu_gpio_get(struct gpio_chip *chip, unsigned pin) +{ + struct mvebu_gpio_chip *mvchip = + container_of(chip, struct mvebu_gpio_chip, chip); + u32 u; + + if (readl_relaxed(mvebu_gpioreg_io_conf(mvchip)) & (1 << pin)) { + u = readl_relaxed(mvebu_gpioreg_data_in(mvchip)) ^ + readl_relaxed(mvebu_gpioreg_in_pol(mvchip)); + } else { + u = readl_relaxed(mvebu_gpioreg_out(mvchip)); + } + + return (u >> pin) & 1; +} + +static int mvebu_gpio_direction_input(struct gpio_chip *chip, unsigned pin) +{ + struct mvebu_gpio_chip *mvchip = + container_of(chip, struct mvebu_gpio_chip, chip); + unsigned long flags; + int ret; + u32 u; + + /* Check with the pinctrl driver whether this pin is usable as + * an input GPIO */ + ret = pinctrl_gpio_direction_input(chip->base + pin); + if (ret) + return ret; + + spin_lock_irqsave(&mvchip->lock, flags); + u = readl_relaxed(mvebu_gpioreg_io_conf(mvchip)); + u |= 1 << pin; + writel_relaxed(u, mvebu_gpioreg_io_conf(mvchip)); + spin_unlock_irqrestore(&mvchip->lock, flags); + + return 0; +} + +static int mvebu_gpio_direction_output(struct gpio_chip *chip, unsigned pin, + int value) +{ + struct mvebu_gpio_chip *mvchip = + container_of(chip, struct mvebu_gpio_chip, chip); + unsigned long flags; + int ret; + u32 u; + + /* Check with the pinctrl driver whether this pin is usable as + * an output GPIO */ + ret = pinctrl_gpio_direction_output(chip->base + pin); + if (ret) + return ret; + + spin_lock_irqsave(&mvchip->lock, flags); + u = readl_relaxed(mvebu_gpioreg_io_conf(mvchip)); + u &= ~(1 << pin); + writel_relaxed(u, mvebu_gpioreg_io_conf(mvchip)); + spin_unlock_irqrestore(&mvchip->lock, flags); + + return 0; +} + +static int mvebu_gpio_to_irq(struct gpio_chip *chip, unsigned pin) +{ + struct mvebu_gpio_chip *mvchip = + container_of(chip, struct mvebu_gpio_chip, chip); + return irq_create_mapping(mvchip->domain, pin); +} + +/* + * Functions implementing the irq_chip methods + */ +static void mvebu_gpio_irq_ack(struct irq_data *d) +{ + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + struct mvebu_gpio_chip *mvchip = gc->private; + u32 mask = ~(1 << (d->irq - gc->irq_base)); + + irq_gc_lock(gc); + writel_relaxed(mask, mvebu_gpioreg_edge_cause(mvchip)); + irq_gc_unlock(gc); +} + +static void mvebu_gpio_edge_irq_mask(struct irq_data *d) +{ + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + struct mvebu_gpio_chip *mvchip = gc->private; + u32 mask = 1 << (d->irq - gc->irq_base); + + irq_gc_lock(gc); + gc->mask_cache &= ~mask; + writel_relaxed(gc->mask_cache, mvebu_gpioreg_edge_mask(mvchip)); + irq_gc_unlock(gc); +} + +static void mvebu_gpio_edge_irq_unmask(struct irq_data *d) +{ + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + struct mvebu_gpio_chip *mvchip = gc->private; + u32 mask = 1 << (d->irq - gc->irq_base); + + irq_gc_lock(gc); + gc->mask_cache |= mask; + writel_relaxed(gc->mask_cache, mvebu_gpioreg_edge_mask(mvchip)); + irq_gc_unlock(gc); +} + +static void mvebu_gpio_level_irq_mask(struct irq_data *d) +{ + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + struct mvebu_gpio_chip *mvchip = gc->private; + u32 mask = 1 << (d->irq - gc->irq_base); + + irq_gc_lock(gc); + gc->mask_cache &= ~mask; + writel_relaxed(gc->mask_cache, mvebu_gpioreg_level_mask(mvchip)); + irq_gc_unlock(gc); +} + +static void mvebu_gpio_level_irq_unmask(struct irq_data *d) +{ + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + struct mvebu_gpio_chip *mvchip = gc->private; + u32 mask = 1 << (d->irq - gc->irq_base); + + irq_gc_lock(gc); + gc->mask_cache |= mask; + writel_relaxed(gc->mask_cache, mvebu_gpioreg_level_mask(mvchip)); + irq_gc_unlock(gc); +} + +/***************************************************************************** + * MVEBU GPIO IRQ + * + * GPIO_IN_POL register controls whether GPIO_DATA_IN will hold the same + * value of the line or the opposite value. + * + * Level IRQ handlers: DATA_IN is used directly as cause register. + * Interrupt are masked by LEVEL_MASK registers. + * Edge IRQ handlers: Change in DATA_IN are latched in EDGE_CAUSE. + * Interrupt are masked by EDGE_MASK registers. + * Both-edge handlers: Similar to regular Edge handlers, but also swaps + * the polarity to catch the next line transaction. + * This is a race condition that might not perfectly + * work on some use cases. + * + * Every eight GPIO lines are grouped (OR'ed) before going up to main + * cause register. + * + * EDGE cause mask + * data-in /--------| |-----| |----\ + * -----| |----- ---- to main cause reg + * X \----------------| |----/ + * polarity LEVEL mask + * + ****************************************************************************/ + +static int mvebu_gpio_irq_set_type(struct irq_data *d, unsigned int type) +{ + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + struct irq_chip_type *ct = irq_data_get_chip_type(d); + struct mvebu_gpio_chip *mvchip = gc->private; + int pin; + u32 u; + + pin = d->hwirq; + + u = readl_relaxed(mvebu_gpioreg_io_conf(mvchip)) & (1 << pin); + if (!u) { + return -EINVAL; + } + + type &= IRQ_TYPE_SENSE_MASK; + if (type == IRQ_TYPE_NONE) + return -EINVAL; + + /* Check if we need to change chip and handler */ + if (!(ct->type & type)) + if (irq_setup_alt_chip(d, type)) + return -EINVAL; + + /* + * Configure interrupt polarity. + */ + switch(type) { + case IRQ_TYPE_EDGE_RISING: + case IRQ_TYPE_LEVEL_HIGH: + u = readl_relaxed(mvebu_gpioreg_in_pol(mvchip)); + u &= ~(1 << pin); + writel_relaxed(u, mvebu_gpioreg_in_pol(mvchip)); + case IRQ_TYPE_EDGE_FALLING: + case IRQ_TYPE_LEVEL_LOW: + u = readl_relaxed(mvebu_gpioreg_in_pol(mvchip)); + u |= 1 << pin; + writel_relaxed(u, mvebu_gpioreg_in_pol(mvchip)); + case IRQ_TYPE_EDGE_BOTH: { + u32 v; + + v = readl_relaxed(mvebu_gpioreg_in_pol(mvchip)) ^ + readl_relaxed(mvebu_gpioreg_data_in(mvchip)); + + /* + * set initial polarity based on current input level + */ + u = readl_relaxed(mvebu_gpioreg_in_pol(mvchip)); + if (v & (1 << pin)) + u |= 1 << pin; /* falling */ + else + u &= ~(1 << pin); /* rising */ + writel_relaxed(u, mvebu_gpioreg_in_pol(mvchip)); + } + } + return 0; +} + +static void mvebu_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) +{ + struct mvebu_gpio_chip *mvchip = irq_get_handler_data(irq); + u32 cause, type; + int i; + + if (mvchip == NULL) + return; + + cause = readl_relaxed(mvebu_gpioreg_data_in(mvchip)) & + readl_relaxed(mvebu_gpioreg_level_mask(mvchip)); + cause |= readl_relaxed(mvebu_gpioreg_edge_cause(mvchip)) & + readl_relaxed(mvebu_gpioreg_edge_mask(mvchip)); + + for (i = 0; i < mvchip->chip.ngpio; i++) { + int irq; + + irq = mvchip->irqbase + i; + + if (!(cause & (1 << i))) + continue; + + type = irqd_get_trigger_type(irq_get_irq_data(irq)); + if ((type & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH) { + /* Swap polarity (race with GPIO line) */ + u32 polarity; + + polarity = readl_relaxed(mvebu_gpioreg_in_pol(mvchip)); + polarity ^= 1 << i; + writel_relaxed(polarity, mvebu_gpioreg_in_pol(mvchip)); + } + generic_handle_irq(irq); + } +} + +static struct platform_device_id mvebu_gpio_ids[] = { + { + .name = "orion-gpio", + }, { + .name = "mv78200-gpio", + }, { + .name = "armadaxp-gpio", + }, { + /* sentinel */ + }, +}; +MODULE_DEVICE_TABLE(platform, mvebu_gpio_ids); + +static struct of_device_id mvebu_gpio_of_match[] __devinitdata = { + { + .compatible = "marvell,orion-gpio", + .data = (void*) MVEBU_GPIO_SOC_VARIANT_ORION, + }, + { + .compatible = "marvell,mv78200-gpio", + .data = (void*) MVEBU_GPIO_SOC_VARIANT_MV78200, + }, + { + .compatible = "marvell,armadaxp-gpio", + .data = (void*) MVEBU_GPIO_SOC_VARIANT_ARMADAXP, + }, + { + /* sentinel */ + }, +}; +MODULE_DEVICE_TABLE(of, mvebu_gpio_of_match); + +static int __devinit mvebu_gpio_probe(struct platform_device *pdev) +{ + struct mvebu_gpio_chip *mvchip; + const struct of_device_id *match; + struct device_node *np = pdev->dev.of_node; + struct resource *res; + struct irq_chip_generic *gc; + struct irq_chip_type *ct; + unsigned int ngpios; + int soc_variant; + int i, cpu, id; + + match = of_match_device(mvebu_gpio_of_match, &pdev->dev); + if (match) + soc_variant = (int) match->data; + else + soc_variant = MVEBU_GPIO_SOC_VARIANT_ORION; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (! res) { + dev_err(&pdev->dev, "Cannot get memory resource\n"); + return -ENODEV; + } + + mvchip = devm_kzalloc(&pdev->dev, sizeof(struct mvebu_gpio_chip), GFP_KERNEL); + if (! mvchip){ + dev_err(&pdev->dev, "Cannot allocate memory\n"); + return -ENOMEM; + } + + if (of_property_read_u32(pdev->dev.of_node, "ngpios", &ngpios)) { + dev_err(&pdev->dev, "Missing ngpios OF property\n"); + return -ENODEV; + } + + id = of_alias_get_id(pdev->dev.of_node, "gpio"); + if (id < 0) { + dev_err(&pdev->dev, "Couldn't get OF id\n"); + return id; + } + + mvchip->soc_variant = soc_variant; + mvchip->chip.label = dev_name(&pdev->dev); + mvchip->chip.dev = &pdev->dev; + mvchip->chip.request = mvebu_gpio_request; + mvchip->chip.direction_input = mvebu_gpio_direction_input; + mvchip->chip.get = mvebu_gpio_get; + mvchip->chip.direction_output = mvebu_gpio_direction_output; + mvchip->chip.set = mvebu_gpio_set; + mvchip->chip.to_irq = mvebu_gpio_to_irq; + mvchip->chip.base = id * MVEBU_MAX_GPIO_PER_BANK; + mvchip->chip.ngpio = ngpios; + mvchip->chip.can_sleep = 0; +#ifdef CONFIG_OF + mvchip->chip.of_node = np; +#endif + + spin_lock_init(&mvchip->lock); + mvchip->membase = devm_request_and_ioremap(&pdev->dev, res); + if (! mvchip->membase) { + dev_err(&pdev->dev, "Cannot ioremap\n"); + kfree(mvchip->chip.label); + return -ENOMEM; + } + + /* The Armada XP has a second range of registers for the + * per-CPU registers */ + if (soc_variant == MVEBU_GPIO_SOC_VARIANT_ARMADAXP) { + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (! res) { + dev_err(&pdev->dev, "Cannot get memory resource\n"); + kfree(mvchip->chip.label); + return -ENODEV; + } + + mvchip->percpu_membase = devm_request_and_ioremap(&pdev->dev, res); + if (! mvchip->percpu_membase) { + dev_err(&pdev->dev, "Cannot ioremap\n"); + kfree(mvchip->chip.label); + return -ENOMEM; + } + } + + /* + * Mask and clear GPIO interrupts. + */ + switch(soc_variant) { + case MVEBU_GPIO_SOC_VARIANT_ORION: + writel_relaxed(0, mvchip->membase + GPIO_EDGE_CAUSE_OFF); + writel_relaxed(0, mvchip->membase + GPIO_EDGE_MASK_OFF); + writel_relaxed(0, mvchip->membase + GPIO_LEVEL_MASK_OFF); + break; + case MVEBU_GPIO_SOC_VARIANT_MV78200: + writel_relaxed(0, mvchip->membase + GPIO_EDGE_CAUSE_OFF); + for (cpu = 0; cpu < 2; cpu++) { + writel_relaxed(0, mvchip->membase + + GPIO_EDGE_MASK_MV78200_OFF(cpu)); + writel_relaxed(0, mvchip->membase + + GPIO_LEVEL_MASK_MV78200_OFF(cpu)); + } + break; + case MVEBU_GPIO_SOC_VARIANT_ARMADAXP: + writel_relaxed(0, mvchip->membase + GPIO_EDGE_CAUSE_OFF); + writel_relaxed(0, mvchip->membase + GPIO_EDGE_MASK_OFF); + writel_relaxed(0, mvchip->membase + GPIO_LEVEL_MASK_OFF); + for (cpu = 0; cpu < 4; cpu++) { + writel_relaxed(0, mvchip->percpu_membase + + GPIO_EDGE_CAUSE_ARMADAXP_OFF(cpu)); + writel_relaxed(0, mvchip->percpu_membase + + GPIO_EDGE_MASK_ARMADAXP_OFF(cpu)); + writel_relaxed(0, mvchip->percpu_membase + + GPIO_LEVEL_MASK_ARMADAXP_OFF(cpu)); + } + break; + default: + BUG(); + } + + gpiochip_add(&mvchip->chip); + + /* Some gpio controllers do not provide irq support */ + if (!of_irq_count(np)) + return 0; + + /* Setup the interrupt handlers. Each chip can have up to 4 + * interrupt handlers, with each handler dealing with 8 GPIO + * pins. */ + for (i = 0; i < 4; i++) { + int irq; + irq = platform_get_irq(pdev, i); + if (irq < 0) + continue; + irq_set_handler_data(irq, mvchip); + irq_set_chained_handler(irq, mvebu_gpio_irq_handler); + } + + mvchip->irqbase = irq_alloc_descs(-1, 0, ngpios, -1); + if (mvchip->irqbase < 0) { + dev_err(&pdev->dev, "no irqs\n"); + kfree(mvchip->chip.label); + return -ENOMEM; + } + + gc = irq_alloc_generic_chip("mvebu_gpio_irq", 2, mvchip->irqbase, + mvchip->membase, handle_level_irq); + if (! gc) { + dev_err(&pdev->dev, "Cannot allocate generic irq_chip\n"); + kfree(mvchip->chip.label); + return -ENOMEM; + } + + gc->private = mvchip; + ct = &gc->chip_types[0]; + ct->type = IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW; + ct->chip.irq_mask = mvebu_gpio_level_irq_mask; + ct->chip.irq_unmask = mvebu_gpio_level_irq_unmask; + ct->chip.irq_set_type = mvebu_gpio_irq_set_type; + ct->chip.name = mvchip->chip.label; + + ct = &gc->chip_types[1]; + ct->type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING; + ct->chip.irq_ack = mvebu_gpio_irq_ack; + ct->chip.irq_mask = mvebu_gpio_edge_irq_mask; + ct->chip.irq_unmask = mvebu_gpio_edge_irq_unmask; + ct->chip.irq_set_type = mvebu_gpio_irq_set_type; + ct->handler = handle_edge_irq; + ct->chip.name = mvchip->chip.label; + + irq_setup_generic_chip(gc, IRQ_MSK(ngpios), IRQ_GC_INIT_MASK_CACHE, + IRQ_NOREQUEST, IRQ_LEVEL | IRQ_NOPROBE); + + /* Setup irq domain on top of the generic chip. */ + mvchip->domain = irq_domain_add_legacy(np, mvchip->chip.ngpio, + mvchip->irqbase, 0, + &irq_domain_simple_ops, + mvchip); + if (!mvchip->domain) { + dev_err(&pdev->dev, "couldn't allocate irq domain %s (DT).\n", + mvchip->chip.label); + irq_remove_generic_chip(gc, IRQ_MSK(ngpios), IRQ_NOREQUEST, + IRQ_LEVEL | IRQ_NOPROBE); + kfree(gc); + kfree(mvchip->chip.label); + return -ENODEV; + } + + return 0; +} + +static struct platform_driver mvebu_gpio_driver = { + .driver = { + .name = "mvebu-gpio", + .owner = THIS_MODULE, + .of_match_table = mvebu_gpio_of_match, + }, + .probe = mvebu_gpio_probe, + .id_table = mvebu_gpio_ids, +}; + +static int __init mvebu_gpio_init(void) +{ + return platform_driver_register(&mvebu_gpio_driver); +} +postcore_initcall(mvebu_gpio_init); diff --git a/drivers/gpio/gpio-pxa.c b/drivers/gpio/gpio-pxa.c index 9cac88a65f78..9528779ca463 100644 --- a/drivers/gpio/gpio-pxa.c +++ b/drivers/gpio/gpio-pxa.c @@ -26,6 +26,8 @@ #include <linux/syscore_ops.h> #include <linux/slab.h> +#include <asm/mach/irq.h> + #include <mach/irqs.h> /* @@ -59,6 +61,7 @@ #define BANK_OFF(n) (((n) < 3) ? (n) << 2 : 0x100 + (((n) - 3) << 2)) int pxa_last_gpio; +static int irq_base; #ifdef CONFIG_OF static struct irq_domain *domain; @@ -167,63 +170,14 @@ static inline int __gpio_is_occupied(unsigned gpio) return ret; } -#ifdef CONFIG_ARCH_PXA -static inline int __pxa_gpio_to_irq(int gpio) -{ - if (gpio_is_pxa_type(gpio_type)) - return PXA_GPIO_TO_IRQ(gpio); - return -1; -} - -static inline int __pxa_irq_to_gpio(int irq) -{ - if (gpio_is_pxa_type(gpio_type)) - return irq - PXA_GPIO_TO_IRQ(0); - return -1; -} -#else -static inline int __pxa_gpio_to_irq(int gpio) { return -1; } -static inline int __pxa_irq_to_gpio(int irq) { return -1; } -#endif - -#ifdef CONFIG_ARCH_MMP -static inline int __mmp_gpio_to_irq(int gpio) -{ - if (gpio_is_mmp_type(gpio_type)) - return MMP_GPIO_TO_IRQ(gpio); - return -1; -} - -static inline int __mmp_irq_to_gpio(int irq) -{ - if (gpio_is_mmp_type(gpio_type)) - return irq - MMP_GPIO_TO_IRQ(0); - return -1; -} -#else -static inline int __mmp_gpio_to_irq(int gpio) { return -1; } -static inline int __mmp_irq_to_gpio(int irq) { return -1; } -#endif - static int pxa_gpio_to_irq(struct gpio_chip *chip, unsigned offset) { - int gpio, ret; - - gpio = chip->base + offset; - ret = __pxa_gpio_to_irq(gpio); - if (ret >= 0) - return ret; - return __mmp_gpio_to_irq(gpio); + return chip->base + offset + irq_base; } int pxa_irq_to_gpio(int irq) { - int ret; - - ret = __pxa_irq_to_gpio(irq); - if (ret >= 0) - return ret; - return __mmp_irq_to_gpio(irq); + return irq - irq_base; } static int pxa_gpio_direction_input(struct gpio_chip *chip, unsigned offset) @@ -403,6 +357,9 @@ static void pxa_gpio_demux_handler(unsigned int irq, struct irq_desc *desc) struct pxa_gpio_chip *c; int loop, gpio, gpio_base, n; unsigned long gedr; + struct irq_chip *chip = irq_desc_get_chip(desc); + + chained_irq_enter(chip, desc); do { loop = 0; @@ -422,6 +379,8 @@ static void pxa_gpio_demux_handler(unsigned int irq, struct irq_desc *desc) } } } while (loop); + + chained_irq_exit(chip, desc); } static void pxa_ack_muxed_gpio(struct irq_data *d) @@ -535,7 +494,7 @@ const struct irq_domain_ops pxa_irq_domain_ops = { static int __devinit pxa_gpio_probe_dt(struct platform_device *pdev) { - int ret, nr_banks, nr_gpios, irq_base; + int ret, nr_banks, nr_gpios; struct device_node *prev, *next, *np = pdev->dev.of_node; const struct of_device_id *of_id = of_match_device(pxa_gpio_dt_ids, &pdev->dev); @@ -590,10 +549,20 @@ static int __devinit pxa_gpio_probe(struct platform_device *pdev) int irq0 = 0, irq1 = 0, irq_mux, gpio_offset = 0; ret = pxa_gpio_probe_dt(pdev); - if (ret < 0) + if (ret < 0) { pxa_last_gpio = pxa_gpio_nums(); - else +#ifdef CONFIG_ARCH_PXA + if (gpio_is_pxa_type(gpio_type)) + irq_base = PXA_GPIO_TO_IRQ(0); +#endif +#ifdef CONFIG_ARCH_MMP + if (gpio_is_mmp_type(gpio_type)) + irq_base = MMP_GPIO_TO_IRQ(0); +#endif + } else { use_of = 1; + } + if (!pxa_last_gpio) return -EINVAL; diff --git a/drivers/gpio/gpio-samsung.c b/drivers/gpio/gpio-samsung.c index ba126cc04073..8af4b06e80f7 100644 --- a/drivers/gpio/gpio-samsung.c +++ b/drivers/gpio/gpio-samsung.c @@ -938,6 +938,67 @@ static void __init samsung_gpiolib_add(struct samsung_gpio_chip *chip) s3c_gpiolib_track(chip); } +#if defined(CONFIG_PLAT_S3C24XX) && defined(CONFIG_OF) +static int s3c24xx_gpio_xlate(struct gpio_chip *gc, + const struct of_phandle_args *gpiospec, u32 *flags) +{ + unsigned int pin; + + if (WARN_ON(gc->of_gpio_n_cells < 3)) + return -EINVAL; + + if (WARN_ON(gpiospec->args_count < gc->of_gpio_n_cells)) + return -EINVAL; + + if (gpiospec->args[0] > gc->ngpio) + return -EINVAL; + + pin = gc->base + gpiospec->args[0]; + + if (s3c_gpio_cfgpin(pin, S3C_GPIO_SFN(gpiospec->args[1]))) + pr_warn("gpio_xlate: failed to set pin function\n"); + if (s3c_gpio_setpull(pin, gpiospec->args[2] & 0xffff)) + pr_warn("gpio_xlate: failed to set pin pull up/down\n"); + + if (flags) + *flags = gpiospec->args[2] >> 16; + + return gpiospec->args[0]; +} + +static const struct of_device_id s3c24xx_gpio_dt_match[] __initdata = { + { .compatible = "samsung,s3c24xx-gpio", }, + {} +}; + +static __init void s3c24xx_gpiolib_attach_ofnode(struct samsung_gpio_chip *chip, + u64 base, u64 offset) +{ + struct gpio_chip *gc = &chip->chip; + u64 address; + + if (!of_have_populated_dt()) + return; + + address = chip->base ? base + ((u32)chip->base & 0xfff) : base + offset; + gc->of_node = of_find_matching_node_by_address(NULL, + s3c24xx_gpio_dt_match, address); + if (!gc->of_node) { + pr_info("gpio: device tree node not found for gpio controller" + " with base address %08llx\n", address); + return; + } + gc->of_gpio_n_cells = 3; + gc->of_xlate = s3c24xx_gpio_xlate; +} +#else +static __init void s3c24xx_gpiolib_attach_ofnode(struct samsung_gpio_chip *chip, + u64 base, u64 offset) +{ + return; +} +#endif /* defined(CONFIG_PLAT_S3C24XX) && defined(CONFIG_OF) */ + static void __init s3c24xx_gpiolib_add_chips(struct samsung_gpio_chip *chip, int nr_chips, void __iomem *base) { @@ -962,6 +1023,8 @@ static void __init s3c24xx_gpiolib_add_chips(struct samsung_gpio_chip *chip, gc->direction_output = samsung_gpiolib_2bit_output; samsung_gpiolib_add(chip); + + s3c24xx_gpiolib_attach_ofnode(chip, S3C24XX_PA_GPIO, i * 0x10); } } @@ -3131,46 +3194,6 @@ samsung_gpio_pull_t s3c_gpio_getpull(unsigned int pin) } EXPORT_SYMBOL(s3c_gpio_getpull); -/* gpiolib wrappers until these are totally eliminated */ - -void s3c2410_gpio_pullup(unsigned int pin, unsigned int to) -{ - int ret; - - WARN_ON(to); /* should be none of these left */ - - if (!to) { - /* if pull is enabled, try first with up, and if that - * fails, try using down */ - - ret = s3c_gpio_setpull(pin, S3C_GPIO_PULL_UP); - if (ret) - s3c_gpio_setpull(pin, S3C_GPIO_PULL_DOWN); - } else { - s3c_gpio_setpull(pin, S3C_GPIO_PULL_NONE); - } -} -EXPORT_SYMBOL(s3c2410_gpio_pullup); - -void s3c2410_gpio_setpin(unsigned int pin, unsigned int to) -{ - /* do this via gpiolib until all users removed */ - - gpio_request(pin, "temporary"); - gpio_set_value(pin, to); - gpio_free(pin); -} -EXPORT_SYMBOL(s3c2410_gpio_setpin); - -unsigned int s3c2410_gpio_getpin(unsigned int pin) -{ - struct samsung_gpio_chip *chip = samsung_gpiolib_getchip(pin); - unsigned long offs = pin - chip->chip.base; - - return __raw_readl(chip->base + 0x04) & (1 << offs); -} -EXPORT_SYMBOL(s3c2410_gpio_getpin); - #ifdef CONFIG_S5P_GPIO_DRVSTR s5p_gpio_drvstr_t s5p_gpio_get_drvstr(unsigned int pin) { diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c index dc5184d57892..d982593d7563 100644 --- a/drivers/gpio/gpio-tegra.c +++ b/drivers/gpio/gpio-tegra.c @@ -30,9 +30,6 @@ #include <asm/mach/irq.h> -#include <mach/iomap.h> -#include <mach/suspend.h> - #define GPIO_BANK(x) ((x) >> 5) #define GPIO_PORT(x) (((x) >> 3) & 0x3) #define GPIO_BIT(x) ((x) & 0x7) diff --git a/drivers/gpio/gpio-twl4030.c b/drivers/gpio/gpio-twl4030.c index f030880bc9bb..c5f8ca233e1f 100644 --- a/drivers/gpio/gpio-twl4030.c +++ b/drivers/gpio/gpio-twl4030.c @@ -396,6 +396,29 @@ static int __devinit gpio_twl4030_debounce(u32 debounce, u8 mmc_cd) static int gpio_twl4030_remove(struct platform_device *pdev); +static struct twl4030_gpio_platform_data *of_gpio_twl4030(struct device *dev) +{ + struct twl4030_gpio_platform_data *omap_twl_info; + + omap_twl_info = devm_kzalloc(dev, sizeof(*omap_twl_info), GFP_KERNEL); + if (!omap_twl_info) + return NULL; + + omap_twl_info->use_leds = of_property_read_bool(dev->of_node, + "ti,use-leds"); + + of_property_read_u32(dev->of_node, "ti,debounce", + &omap_twl_info->debounce); + of_property_read_u32(dev->of_node, "ti,mmc-cd", + (u32 *)&omap_twl_info->mmc_cd); + of_property_read_u32(dev->of_node, "ti,pullups", + &omap_twl_info->pullups); + of_property_read_u32(dev->of_node, "ti,pulldowns", + &omap_twl_info->pulldowns); + + return omap_twl_info; +} + static int __devinit gpio_twl4030_probe(struct platform_device *pdev) { struct twl4030_gpio_platform_data *pdata = pdev->dev.platform_data; @@ -428,33 +451,37 @@ no_irqs: twl_gpiochip.ngpio = TWL4030_GPIO_MAX; twl_gpiochip.dev = &pdev->dev; - if (pdata) { - /* - * NOTE: boards may waste power if they don't set pullups - * and pulldowns correctly ... default for non-ULPI pins is - * pulldown, and some other pins may have external pullups - * or pulldowns. Careful! - */ - ret = gpio_twl4030_pulls(pdata->pullups, pdata->pulldowns); - if (ret) - dev_dbg(&pdev->dev, "pullups %.05x %.05x --> %d\n", - pdata->pullups, pdata->pulldowns, - ret); - - ret = gpio_twl4030_debounce(pdata->debounce, pdata->mmc_cd); - if (ret) - dev_dbg(&pdev->dev, "debounce %.03x %.01x --> %d\n", - pdata->debounce, pdata->mmc_cd, - ret); - - /* - * NOTE: we assume VIBRA_CTL.VIBRA_EN, in MODULE_AUDIO_VOICE, - * is (still) clear if use_leds is set. - */ - if (pdata->use_leds) - twl_gpiochip.ngpio += 2; + if (node) + pdata = of_gpio_twl4030(&pdev->dev); + + if (pdata == NULL) { + dev_err(&pdev->dev, "Platform data is missing\n"); + return -ENXIO; } + /* + * NOTE: boards may waste power if they don't set pullups + * and pulldowns correctly ... default for non-ULPI pins is + * pulldown, and some other pins may have external pullups + * or pulldowns. Careful! + */ + ret = gpio_twl4030_pulls(pdata->pullups, pdata->pulldowns); + if (ret) + dev_dbg(&pdev->dev, "pullups %.05x %.05x --> %d\n", + pdata->pullups, pdata->pulldowns, ret); + + ret = gpio_twl4030_debounce(pdata->debounce, pdata->mmc_cd); + if (ret) + dev_dbg(&pdev->dev, "debounce %.03x %.01x --> %d\n", + pdata->debounce, pdata->mmc_cd, ret); + + /* + * NOTE: we assume VIBRA_CTL.VIBRA_EN, in MODULE_AUDIO_VOICE, + * is (still) clear if use_leds is set. + */ + if (pdata->use_leds) + twl_gpiochip.ngpio += 2; + ret = gpiochip_add(&twl_gpiochip); if (ret < 0) { dev_err(&pdev->dev, "could not register gpiochip, %d\n", ret); |