From: John Jacques <john.jacq...@intel.com> Some of the Axxia SoCs have a different interrupt structure than the standard PL061 driver. This commit adds a driver based on the PL061 driver but with the Axxia interrupt structure.
Signed-off-by: John Jacques <john.jacq...@intel.com> --- drivers/gpio/gpio-axxia.c | 159 +++++++++++++++++++++++++++++----------------- 1 file changed, 102 insertions(+), 57 deletions(-) diff --git a/drivers/gpio/gpio-axxia.c b/drivers/gpio/gpio-axxia.c index 791a9257..14616cb 100644 --- a/drivers/gpio/gpio-axxia.c +++ b/drivers/gpio/gpio-axxia.c @@ -1,22 +1,25 @@ /* * Copyright (C) 2017, Intel * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version - * 2 as published by the Free Software Foundation. + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. * * Based on the driver for the ARM PrimeCell(tm) General Purpose * Input/Output (PL061), but modified for Axxia SoCs. Interrupt * handling is different. * + * Driver for the ARM PrimeCell(tm) General Purpose Input/Output (PL061) + * * Data sheet: ARM DDI 0190B, September 2000 */ - #include <linux/spinlock.h> #include <linux/errno.h> #include <linux/module.h> +#include <linux/init.h> #include <linux/io.h> #include <linux/ioport.h> +#include <linux/interrupt.h> #include <linux/irq.h> #include <linux/irqchip/chained_irq.h> #include <linux/bitops.h> @@ -55,45 +58,25 @@ struct pl061_gpio { void __iomem *base; struct gpio_chip gc; - bool uses_pinctrl; #ifdef CONFIG_PM struct pl061_context_save_regs csave_regs; #endif }; -static int pl061_gpio_request(struct gpio_chip *gc, unsigned int offset) +static int pl061_get_direction(struct gpio_chip *gc, unsigned int offset) { - /* - * Map back to global GPIO space and request muxing, the direction - * parameter does not matter for this controller. - */ - struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc); - int gpio = gc->base + offset; + struct pl061_gpio *chip = gpiochip_get_data(gc); - if (chip->uses_pinctrl) - return pinctrl_request_gpio(gpio); - return 0; -} - -static void pl061_gpio_free(struct gpio_chip *gc, unsigned int offset) -{ - struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc); - int gpio = gc->base + offset; - - if (chip->uses_pinctrl) - pinctrl_free_gpio(gpio); + return !(readb(chip->base + GPIODIR) & BIT(offset)); } static int pl061_direction_input(struct gpio_chip *gc, unsigned int offset) { - struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc); + struct pl061_gpio *chip = gpiochip_get_data(gc); unsigned long flags; unsigned char gpiodir; - if (offset >= gc->ngpio) - return -EINVAL; - spin_lock_irqsave(&chip->lock, flags); gpiodir = readb(chip->base + GPIODIR); gpiodir &= ~(BIT(offset)); @@ -106,13 +89,10 @@ static int pl061_direction_input(struct gpio_chip *gc, unsigned int offset) static int pl061_direction_output(struct gpio_chip *gc, unsigned int offset, int value) { - struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc); + struct pl061_gpio *chip = gpiochip_get_data(gc); unsigned long flags; unsigned char gpiodir; - if (offset >= gc->ngpio) - return -EINVAL; - spin_lock_irqsave(&chip->lock, flags); writeb(!!value << offset, chip->base + (BIT(offset + 2))); gpiodir = readb(chip->base + GPIODIR); @@ -131,14 +111,14 @@ static int pl061_direction_output(struct gpio_chip *gc, unsigned int offset, static int pl061_get_value(struct gpio_chip *gc, unsigned int offset) { - struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc); + struct pl061_gpio *chip = gpiochip_get_data(gc); return !!readb(chip->base + (BIT(offset + 2))); } static void pl061_set_value(struct gpio_chip *gc, unsigned int offset, int value) { - struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc); + struct pl061_gpio *chip = gpiochip_get_data(gc); writeb(!!value << offset, chip->base + (BIT(offset + 2))); } @@ -146,7 +126,7 @@ static void pl061_set_value(struct gpio_chip *gc, unsigned int offset, int value static int pl061_irq_type(struct irq_data *d, unsigned int trigger) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); - struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc); + struct pl061_gpio *chip = gpiochip_get_data(gc); int offset = irqd_to_hwirq(d); unsigned long flags; u8 gpiois, gpioibe, gpioiev; @@ -155,6 +135,15 @@ static int pl061_irq_type(struct irq_data *d, unsigned int trigger) if (offset < 0 || offset >= PL061_GPIO_NR) return -EINVAL; + if ((trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) && + (trigger & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING))) { + dev_err(gc->parent, + "trying to configure line %d for both level and edge detection, choose one!\n", + offset); + return -EINVAL; + } + + spin_lock_irqsave(&chip->lock, flags); gpioiev = readb(chip->base + GPIOIEV); @@ -162,23 +151,53 @@ static int pl061_irq_type(struct irq_data *d, unsigned int trigger) gpioibe = readb(chip->base + GPIOIBE); if (trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) { + bool polarity = trigger & IRQ_TYPE_LEVEL_HIGH; + + /* Disable edge detection */ + gpioibe &= ~bit; + /* Enable level detection */ gpiois |= bit; - if (trigger & IRQ_TYPE_LEVEL_HIGH) + /* Select polarity */ + if (polarity) gpioiev |= bit; else gpioiev &= ~bit; - } else + irq_set_handler_locked(d, handle_level_irq); + dev_dbg(gc->parent, "line %d: IRQ on %s level\n", + offset, + polarity ? "HIGH" : "LOW"); + } else if ((trigger & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) { + /* Disable level detection */ gpiois &= ~bit; - - if ((trigger & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) - /* Setting this makes GPIOEV be ignored */ + /* Select both edges, setting this makes GPIOEV be ignored */ gpioibe |= bit; - else { + irq_set_handler_locked(d, handle_edge_irq); + dev_dbg(gc->parent, "line %d: IRQ on both edges\n", offset); + } else if ((trigger & IRQ_TYPE_EDGE_RISING) || + (trigger & IRQ_TYPE_EDGE_FALLING)) { + bool rising = trigger & IRQ_TYPE_EDGE_RISING; + + /* Disable level detection */ + gpiois &= ~bit; + /* Clear detection on both edges */ gpioibe &= ~bit; - if (trigger & IRQ_TYPE_EDGE_RISING) + /* Select edge */ + if (rising) gpioiev |= bit; - else if (trigger & IRQ_TYPE_EDGE_FALLING) + else gpioiev &= ~bit; + irq_set_handler_locked(d, handle_edge_irq); + dev_dbg(gc->parent, "line %d: IRQ on %s edge\n", + offset, + rising ? "RISING" : "FALLING"); + } else { + /* No trigger: disable everything */ + gpiois &= ~bit; + gpioibe &= ~bit; + gpioiev &= ~bit; + irq_set_handler_locked(d, handle_bad_irq); + dev_warn(gc->parent, "no trigger selected for line %d\n", + offset); } writeb(gpiois, chip->base + GPIOIS); @@ -195,13 +214,12 @@ static void pl061_irq_handler(struct irq_desc *desc) unsigned long pending; int offset; struct gpio_chip *gc = irq_desc_get_handler_data(desc); - struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc); + struct pl061_gpio *chip = gpiochip_get_data(gc); struct irq_chip *irqchip = irq_desc_get_chip(desc); chained_irq_enter(irqchip, desc); pending = readb(chip->base + GPIOMIS); - writeb(pending, chip->base + GPIOIC); if (pending) { for_each_set_bit(offset, &pending, PL061_GPIO_NR) generic_handle_irq(irq_find_mapping(gc->irqdomain, @@ -214,7 +232,7 @@ static void pl061_irq_handler(struct irq_desc *desc) static void pl061_irq_mask(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); - struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc); + struct pl061_gpio *chip = gpiochip_get_data(gc); u8 mask = BIT(irqd_to_hwirq(d) % PL061_GPIO_NR); u8 gpioie; @@ -227,7 +245,7 @@ static void pl061_irq_mask(struct irq_data *d) static void pl061_irq_unmask(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); - struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc); + struct pl061_gpio *chip = gpiochip_get_data(gc); u8 mask = BIT(irqd_to_hwirq(d) % PL061_GPIO_NR); u8 gpioie; @@ -237,11 +255,39 @@ static void pl061_irq_unmask(struct irq_data *d) spin_unlock(&chip->lock); } +/** + * pl061_irq_ack() - ACK an edge IRQ + * @d: IRQ data for this IRQ + * + * This gets called from the edge IRQ handler to ACK the edge IRQ + * in the GPIOIC (interrupt-clear) register. For level IRQs this is + * not needed: these go away when the level signal goes away. + */ +static void pl061_irq_ack(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct pl061_gpio *chip = gpiochip_get_data(gc); + u8 mask = BIT(irqd_to_hwirq(d) % PL061_GPIO_NR); + + spin_lock(&chip->lock); + writeb(mask, chip->base + GPIOIC); + spin_unlock(&chip->lock); +} + +static int pl061_irq_set_wake(struct irq_data *d, unsigned int state) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + + return irq_set_irq_wake(gc->irq_parent, state); +} + static struct irq_chip pl061_irqchip = { .name = "pl061", + .irq_ack = pl061_irq_ack, .irq_mask = pl061_irq_mask, .irq_unmask = pl061_irq_unmask, .irq_set_type = pl061_irq_type, + .irq_set_wake = pl061_irq_set_wake, }; static int pl061_probe(struct amba_device *adev, const struct amba_id *id) @@ -272,20 +318,22 @@ static int pl061_probe(struct amba_device *adev, const struct amba_id *id) return PTR_ERR(chip->base); spin_lock_init(&chip->lock); - if (of_property_read_bool(dev->of_node, "gpio-ranges")) - chip->uses_pinctrl = true; + if (of_property_read_bool(dev->of_node, "gpio-ranges")) { + chip->gc.request = gpiochip_generic_request; + chip->gc.free = gpiochip_generic_free; + } - chip->gc.request = pl061_gpio_request; - chip->gc.free = pl061_gpio_free; + chip->gc.get_direction = pl061_get_direction; chip->gc.direction_input = pl061_direction_input; chip->gc.direction_output = pl061_direction_output; chip->gc.get = pl061_get_value; chip->gc.set = pl061_set_value; chip->gc.ngpio = PL061_GPIO_NR; chip->gc.label = dev_name(dev); + chip->gc.parent = dev; chip->gc.owner = THIS_MODULE; - ret = gpiochip_add(&chip->gc); + ret = gpiochip_add_data(&chip->gc, chip); if (ret) return ret; @@ -300,7 +348,7 @@ static int pl061_probe(struct amba_device *adev, const struct amba_id *id) } ret = gpiochip_irqchip_add(&chip->gc, &pl061_irqchip, - irq_base, handle_simple_irq, + irq_base, handle_bad_irq, IRQ_TYPE_NONE); if (ret) { dev_info(&adev->dev, "could not add irqchip\n"); @@ -398,8 +446,6 @@ static struct amba_id pl061_ids[] = { { 0, 0 }, }; -MODULE_DEVICE_TABLE(amba, pl061_ids); - static struct amba_driver pl061_gpio_driver = { .drv = { .name = "pl061_gpio", @@ -415,8 +461,7 @@ static int __init pl061_gpio_init(void) { return amba_driver_register(&pl061_gpio_driver); } -module_init(pl061_gpio_init); +device_initcall(pl061_gpio_init); MODULE_AUTHOR("John Jacques <john.jacq...@intel.com>"); MODULE_DESCRIPTION("PL061 GPIO driver for Axxia"); -MODULE_LICENSE("GPL"); -- 2.7.4 -- _______________________________________________ linux-yocto mailing list linux-yocto@yoctoproject.org https://lists.yoctoproject.org/listinfo/linux-yocto