From 7e10ee68f8ccc62e0934ff02f39ce541f3879844 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Sat, 27 Oct 2012 15:21:35 +0530 Subject: Revert "pinctrl: remove pinctrl_remove_gpio_range" This reverts earlier commit which removed pinctrl_remove_gpio_range(), because at that time there weren't any more users of that routine. It was removed as the removal of ranges was done in unregister of pinctrl. But as we are now registering stuff from gpiolib, we may remove and insert a gpio module multiple times. So, we need this routine again. Signed-off-by: Viresh Kumar Signed-off-by: Linus Walleij --- include/linux/pinctrl/pinctrl.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/linux') diff --git a/include/linux/pinctrl/pinctrl.h b/include/linux/pinctrl/pinctrl.h index 7d087f03e91e..eda04674633d 100644 --- a/include/linux/pinctrl/pinctrl.h +++ b/include/linux/pinctrl/pinctrl.h @@ -134,6 +134,8 @@ extern void pinctrl_add_gpio_range(struct pinctrl_dev *pctldev, extern void pinctrl_add_gpio_ranges(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *ranges, unsigned nranges); +extern void pinctrl_remove_gpio_range(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range); extern const char *pinctrl_dev_get_name(struct pinctrl_dev *pctldev); extern void *pinctrl_dev_get_drvdata(struct pinctrl_dev *pctldev); #else -- cgit v1.2.3 From f23f1516b6757c326cc638bed8c402c77e2a596e Mon Sep 17 00:00:00 2001 From: Shiraz Hashim Date: Sat, 27 Oct 2012 15:21:36 +0530 Subject: gpiolib: provide provision to register pin ranges pinctrl subsystem needs gpio chip base to prepare set of gpio pin ranges, which a given pinctrl driver can handle. This is important to handle pinctrl gpio request calls in order to program a given pin properly for gpio operation. As gpio base is allocated dynamically during gpiochip registration, presently there exists no clean way to pass this information to the pinctrl subsystem. After few discussions from [1], it was concluded that may be gpio controller reporting the pin range it supports, is a better way than pinctrl subsystem directly registering it. [1] http://comments.gmane.org/gmane.linux.ports.arm.kernel/184816 Cc: Grant Likely Signed-off-by: Viresh Kumar Signed-off-by: Shiraz Hashim [Edited documentation a bit] Signed-off-by: Linus Walleij --- Documentation/devicetree/bindings/gpio/gpio.txt | 36 ++++++++++++++++ Documentation/gpio.txt | 42 +++++++++++++++++++ Documentation/pinctrl.txt | 3 ++ drivers/gpio/gpiolib-of.c | 56 +++++++++++++++++++++++++ drivers/gpio/gpiolib.c | 43 +++++++++++++++++++ drivers/pinctrl/core.c | 13 ++++++ drivers/pinctrl/devicetree.c | 13 ++++++ include/asm-generic/gpio.h | 25 +++++++++++ include/linux/gpio.h | 3 ++ include/linux/pinctrl/pinctrl.h | 17 ++++++++ 10 files changed, 251 insertions(+) (limited to 'include/linux') diff --git a/Documentation/devicetree/bindings/gpio/gpio.txt b/Documentation/devicetree/bindings/gpio/gpio.txt index 4e16ba4feab0..a33628759d36 100644 --- a/Documentation/devicetree/bindings/gpio/gpio.txt +++ b/Documentation/devicetree/bindings/gpio/gpio.txt @@ -75,4 +75,40 @@ Example of two SOC GPIO banks defined as gpio-controller nodes: gpio-controller; }; +2.1) gpio-controller and pinctrl subsystem +------------------------------------------ +gpio-controller on a SOC might be tightly coupled with the pinctrl +subsystem, in the sense that the pins can be used by other functions +together with optional gpio feature. + +While the pin allocation is totally managed by the pin ctrl subsystem, +gpio (under gpiolib) is still maintained by gpio drivers. It may happen +that different pin ranges in a SoC is managed by different gpio drivers. + +This makes it logical to let gpio drivers announce their pin ranges to +the pin ctrl subsystem and call 'pinctrl_request_gpio' in order to +request the corresponding pin before any gpio usage. + +For this, the gpio controller can use a pinctrl phandle and pins to +announce the pinrange to the pin ctrl subsystem. For example, + + qe_pio_e: gpio-controller@1460 { + #gpio-cells = <2>; + compatible = "fsl,qe-pario-bank-e", "fsl,qe-pario-bank"; + reg = <0x1460 0x18>; + gpio-controller; + gpio-ranges = <&pinctrl1 20 10>, <&pinctrl2 50 20>; + + } + +where, + &pinctrl1 and &pinctrl2 is the phandle to the pinctrl DT node. + + Next values specify the base pin and number of pins for the range + handled by 'qe_pio_e' gpio. In the given example from base pin 20 to + pin 29 under pinctrl1 and pin 50 to pin 69 under pinctrl2 is handled + by this gpio controller. + +The pinctrl node must have "#gpio-range-cells" property to show number of +arguments to pass with phandle from gpio controllers node. diff --git a/Documentation/gpio.txt b/Documentation/gpio.txt index e08a883de36e..77a1d11af723 100644 --- a/Documentation/gpio.txt +++ b/Documentation/gpio.txt @@ -439,6 +439,48 @@ slower clock delays the rising edge of SCK, and the I2C master adjusts its signaling rate accordingly. +GPIO controllers and the pinctrl subsystem +------------------------------------------ + +A GPIO controller on a SOC might be tightly coupled with the pinctrl +subsystem, in the sense that the pins can be used by other functions +together with an optional gpio feature. We have already covered the +case where e.g. a GPIO controller need to reserve a pin or set the +direction of a pin by calling any of: + +pinctrl_request_gpio() +pinctrl_free_gpio() +pinctrl_gpio_direction_input() +pinctrl_gpio_direction_output() + +But how does the pin control subsystem cross-correlate the GPIO +numbers (which are a global business) to a certain pin on a certain +pin controller? + +This is done by registering "ranges" of pins, which are essentially +cross-reference tables. These are described in +Documentation/pinctrl.txt + +While the pin allocation is totally managed by the pinctrl subsystem, +gpio (under gpiolib) is still maintained by gpio drivers. It may happen +that different pin ranges in a SoC is managed by different gpio drivers. + +This makes it logical to let gpio drivers announce their pin ranges to +the pin ctrl subsystem before it will call 'pinctrl_request_gpio' in order +to request the corresponding pin to be prepared by the pinctrl subsystem +before any gpio usage. + +For this, the gpio controller can register its pin range with pinctrl +subsystem. There are two ways of doing it currently: with or without DT. + +For with DT support refer to Documentation/devicetree/bindings/gpio/gpio.txt. + +For non-DT support, user can call gpiochip_add_pin_range() with appropriate +parameters to register a range of gpio pins with a pinctrl driver. For this +exact name string of pinctrl device has to be passed as one of the +argument to this routine. + + What do these conventions omit? =============================== One of the biggest things these conventions omit is pin multiplexing, since diff --git a/Documentation/pinctrl.txt b/Documentation/pinctrl.txt index a1cd2f9428d7..da40efbef6ec 100644 --- a/Documentation/pinctrl.txt +++ b/Documentation/pinctrl.txt @@ -364,6 +364,9 @@ will get an pin number into its handled number range. Further it is also passed the range ID value, so that the pin controller knows which range it should deal with. +Calling pinctrl_add_gpio_range from pinctrl driver is DEPRECATED. Please see +section 2.1 of Documentation/devicetree/bindings/gpio/gpio.txt on how to bind +pinctrl and gpio drivers. PINMUX interfaces ================= diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index f1a45997aea8..a5b90c8e9844 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -19,6 +19,7 @@ #include #include #include +#include #include /* Private data structure for of_gpiochip_find_and_xlate */ @@ -216,6 +217,58 @@ err0: } EXPORT_SYMBOL(of_mm_gpiochip_add); +#ifdef CONFIG_PINCTRL +void of_gpiochip_add_pin_range(struct gpio_chip *chip) +{ + struct device_node *np = chip->of_node; + struct gpio_pin_range *pin_range; + struct of_phandle_args pinspec; + int index = 0, ret; + + if (!np) + return; + + do { + ret = of_parse_phandle_with_args(np, "gpio-ranges", + "#gpio-range-cells", index, &pinspec); + if (ret) + break; + + pin_range = devm_kzalloc(chip->dev, sizeof(*pin_range), + GFP_KERNEL); + if (!pin_range) { + pr_err("%s: GPIO chip: failed to allocate pin ranges\n", + chip->label); + break; + } + + pin_range->range.name = chip->label; + pin_range->range.base = chip->base; + pin_range->range.pin_base = pinspec.args[0]; + pin_range->range.npins = pinspec.args[1]; + pin_range->pctldev = of_pinctrl_add_gpio_range(pinspec.np, + &pin_range->range); + + list_add_tail(&pin_range->node, &chip->pin_ranges); + + } while (index++); +} + +void of_gpiochip_remove_pin_range(struct gpio_chip *chip) +{ + struct gpio_pin_range *pin_range, *tmp; + + list_for_each_entry_safe(pin_range, tmp, &chip->pin_ranges, node) { + list_del(&pin_range->node); + pinctrl_remove_gpio_range(pin_range->pctldev, + &pin_range->range); + } +} +#else +void of_gpiochip_add_pin_range(struct gpio_chip *chip) {} +void of_gpiochip_remove_pin_range(struct gpio_chip *chip) {} +#endif + void of_gpiochip_add(struct gpio_chip *chip) { if ((!chip->of_node) && (chip->dev)) @@ -229,11 +282,14 @@ void of_gpiochip_add(struct gpio_chip *chip) chip->of_xlate = of_gpio_simple_xlate; } + of_gpiochip_add_pin_range(chip); of_node_get(chip->of_node); } void of_gpiochip_remove(struct gpio_chip *chip) { + of_gpiochip_remove_pin_range(chip); + if (chip->of_node) of_node_put(chip->of_node); } diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 1c8d9e3380e1..f0b07bbfcc9a 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1083,6 +1083,10 @@ int gpiochip_add(struct gpio_chip *chip) } } +#ifdef CONFIG_PINCTRL + INIT_LIST_HEAD(&chip->pin_ranges); +#endif + of_gpiochip_add(chip); unlock: @@ -1180,6 +1184,45 @@ struct gpio_chip *gpiochip_find(void *data, } EXPORT_SYMBOL_GPL(gpiochip_find); +#ifdef CONFIG_PINCTRL +void gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, + unsigned int pin_base, unsigned int npins) +{ + struct gpio_pin_range *pin_range; + + pin_range = devm_kzalloc(chip->dev, sizeof(*pin_range), GFP_KERNEL); + if (!pin_range) { + pr_err("%s: GPIO chip: failed to allocate pin ranges\n", + chip->label); + return; + } + + pin_range->range.name = chip->label; + pin_range->range.base = chip->base; + pin_range->range.pin_base = pin_base; + pin_range->range.npins = npins; + pin_range->pctldev = find_pinctrl_and_add_gpio_range(pinctl_name, + &pin_range->range); + + list_add_tail(&pin_range->node, &chip->pin_ranges); +} + +void gpiochip_remove_pin_ranges(struct gpio_chip *chip) +{ + struct gpio_pin_range *pin_range, *tmp; + + list_for_each_entry_safe(pin_range, tmp, &chip->pin_ranges, node) { + list_del(&pin_range->node); + pinctrl_remove_gpio_range(pin_range->pctldev, + &pin_range->range); + } +} +#else +void gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, + unsigned int pin_base, unsigned int npins) {} +void gpiochip_remove_pin_ranges(struct gpio_chip *chip) {} +#endif + /* These "optional" allocation calls help prevent drivers from stomping * on each other, and help provide better diagnostics in debugfs. * They're called even less than the "set direction" calls. diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index b1086dcde15d..71db586b2afd 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -345,6 +345,19 @@ void pinctrl_add_gpio_ranges(struct pinctrl_dev *pctldev, } EXPORT_SYMBOL_GPL(pinctrl_add_gpio_ranges); +struct pinctrl_dev *find_pinctrl_and_add_gpio_range(const char *devname, + struct pinctrl_gpio_range *range) +{ + struct pinctrl_dev *pctldev = get_pinctrl_dev_from_devname(devname); + + if (!pctldev) + return NULL; + + pinctrl_add_gpio_range(pctldev, range); + return pctldev; +} +EXPORT_SYMBOL_GPL(find_pinctrl_and_add_gpio_range); + /** * pinctrl_remove_gpio_range() - remove a range of GPIOs fro a pin controller * @pctldev: pin controller device to remove the range from diff --git a/drivers/pinctrl/devicetree.c b/drivers/pinctrl/devicetree.c index fcb1de45473c..6728ec71cb65 100644 --- a/drivers/pinctrl/devicetree.c +++ b/drivers/pinctrl/devicetree.c @@ -106,6 +106,19 @@ static struct pinctrl_dev *find_pinctrl_by_of_node(struct device_node *np) return NULL; } +struct pinctrl_dev *of_pinctrl_add_gpio_range(struct device_node *np, + struct pinctrl_gpio_range *range) +{ + struct pinctrl_dev *pctldev; + + pctldev = find_pinctrl_by_of_node(np); + if (!pctldev) + return NULL; + + pinctrl_add_gpio_range(pctldev, range); + return pctldev; +} + static int dt_to_map_one_config(struct pinctrl *p, const char *statename, struct device_node *np_config) { diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index a9432fc6b8ba..92e5c432421c 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -5,6 +5,7 @@ #include #include #include +#include #ifdef CONFIG_GPIOLIB @@ -47,6 +48,21 @@ struct seq_file; struct module; struct device_node; +#ifdef CONFIG_PINCTRL +/** + * struct gpio_pin_range - pin range controlled by a gpio chip + * @head: list for maintaining set of pin ranges, used internally + * @pctldev: pinctrl device which handles corresponding pins + * @range: actual range of pins controlled by a gpio controller + */ + +struct gpio_pin_range { + struct list_head node; + struct pinctrl_dev *pctldev; + struct pinctrl_gpio_range range; +}; +#endif + /** * struct gpio_chip - abstract a GPIO controller * @label: for diagnostics @@ -134,6 +150,15 @@ struct gpio_chip { int (*of_xlate)(struct gpio_chip *gc, const struct of_phandle_args *gpiospec, u32 *flags); #endif +#ifdef CONFIG_PINCTRL + /* + * If CONFIG_PINCTRL is enabled, then gpio controllers can optionally + * describe the actual pin range which they serve in an SoC. This + * information would be used by pinctrl subsystem to configure + * corresponding pins for gpio usage. + */ + struct list_head pin_ranges; +#endif }; extern const char *gpiochip_is_requested(struct gpio_chip *chip, diff --git a/include/linux/gpio.h b/include/linux/gpio.h index 2e31e8b3a190..a28445992b7f 100644 --- a/include/linux/gpio.h +++ b/include/linux/gpio.h @@ -231,6 +231,9 @@ static inline int irq_to_gpio(unsigned irq) return -EINVAL; } +void gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, + unsigned int pin_base, unsigned int npins); +void gpiochip_remove_pin_ranges(struct gpio_chip *chip); #endif #endif /* __LINUX_GPIO_H */ diff --git a/include/linux/pinctrl/pinctrl.h b/include/linux/pinctrl/pinctrl.h index eda04674633d..434e5a94e131 100644 --- a/include/linux/pinctrl/pinctrl.h +++ b/include/linux/pinctrl/pinctrl.h @@ -136,6 +136,23 @@ extern void pinctrl_add_gpio_ranges(struct pinctrl_dev *pctldev, unsigned nranges); extern void pinctrl_remove_gpio_range(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range); + +extern struct pinctrl_dev *find_pinctrl_and_add_gpio_range(const char *devname, + struct pinctrl_gpio_range *range); + +#ifdef CONFIG_OF +extern struct pinctrl_dev *of_pinctrl_add_gpio_range(struct device_node *np, + struct pinctrl_gpio_range *range); +#else +static inline +struct pinctrl_dev *of_pinctrl_add_gpio_range(struct device_node *np, + struct pinctrl_gpio_range *range) +{ + return NULL; +} + +#endif /* CONFIG_OF */ + extern const char *pinctrl_dev_get_name(struct pinctrl_dev *pctldev); extern void *pinctrl_dev_get_drvdata(struct pinctrl_dev *pctldev); #else -- cgit v1.2.3 From a6c45b99a658521291cfb66ecf035cc58b38f206 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 17 Oct 2012 18:31:20 +0200 Subject: pinctrl/coh901: use irqdomain, allocate irqdescs This switches the COH 901 pinctrl driver to allocate its GPIO IRQs dynamically, and start to use a linear irqdomain to map from the hardware IRQs. This way we can cut away the complex allocation of IRQ numbers from the file. Signed-off-by: Linus Walleij --- arch/arm/mach-u300/core.c | 1 - arch/arm/mach-u300/include/mach/irqs.h | 10 ----- drivers/pinctrl/pinctrl-coh901.c | 60 +++++++++++++++++++++------- include/linux/platform_data/pinctrl-coh901.h | 2 - 4 files changed, 46 insertions(+), 27 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/mach-u300/core.c b/arch/arm/mach-u300/core.c index 603c08ec8786..ce2de0d6f2eb 100644 --- a/arch/arm/mach-u300/core.c +++ b/arch/arm/mach-u300/core.c @@ -1445,7 +1445,6 @@ static struct platform_device pinctrl_device = { static struct u300_gpio_platform u300_gpio_plat = { .ports = 7, .gpio_base = 0, - .gpio_irq_base = IRQ_U300_GPIO_BASE, .pinctrl_device = &pinctrl_device, }; diff --git a/arch/arm/mach-u300/include/mach/irqs.h b/arch/arm/mach-u300/include/mach/irqs.h index e85ec3868d04..21d5e76a6cd3 100644 --- a/arch/arm/mach-u300/include/mach/irqs.h +++ b/arch/arm/mach-u300/include/mach/irqs.h @@ -77,14 +77,4 @@ #define IRQ_U300_GPIO_PORT6 87 #define U300_VIC_IRQS_END 88 -/* Maximum 8*7 GPIO lines */ -#ifdef CONFIG_PINCTRL_COH901 -#define IRQ_U300_GPIO_BASE (U300_VIC_IRQS_END) -#define IRQ_U300_GPIO_END (IRQ_U300_GPIO_BASE + 56) -#else -#define IRQ_U300_GPIO_END (U300_VIC_IRQS_END) -#endif - -#define NR_IRQS_U300 (IRQ_U300_GPIO_END - IRQ_U300_INTCON0_START) - #endif diff --git a/drivers/pinctrl/pinctrl-coh901.c b/drivers/pinctrl/pinctrl-coh901.c index b446c9641212..152efae3df8f 100644 --- a/drivers/pinctrl/pinctrl-coh901.c +++ b/drivers/pinctrl/pinctrl-coh901.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -67,7 +68,6 @@ struct u300_gpio { struct resource *memres; void __iomem *base; struct device *dev; - int irq_base; u32 stride; /* Register offsets */ u32 pcr; @@ -83,6 +83,7 @@ struct u300_gpio_port { struct list_head node; struct u300_gpio *gpio; char name[8]; + struct irq_domain *domain; int irq; int number; u8 toggle_edge_mode; @@ -314,10 +315,30 @@ static int u300_gpio_direction_output(struct gpio_chip *chip, unsigned offset, static int u300_gpio_to_irq(struct gpio_chip *chip, unsigned offset) { struct u300_gpio *gpio = to_u300_gpio(chip); - int retirq = gpio->irq_base + offset; + int portno = offset >> 3; + struct u300_gpio_port *port = NULL; + struct list_head *p; + int retirq; - dev_dbg(gpio->dev, "request IRQ for GPIO %d, return %d\n", offset, - retirq); + list_for_each(p, &gpio->port_list) { + port = list_entry(p, struct u300_gpio_port, node); + if (port->number == portno) + break; + } + if (port == NULL) { + dev_err(gpio->dev, "could not locate port for GPIO %d IRQ\n", + offset); + return -EINVAL; + } + + /* + * The local hwirqs on the port are the lower three bits, there + * are exactly 8 IRQs per port since they are 8-bit + */ + retirq = irq_find_mapping(port->domain, (offset & 0x7)); + + dev_dbg(gpio->dev, "request IRQ for GPIO %d, return %d from port %d\n", + offset, retirq, port->number); return retirq; } @@ -467,7 +488,7 @@ static int u300_gpio_irq_type(struct irq_data *d, unsigned trigger) { struct u300_gpio_port *port = irq_data_get_irq_chip_data(d); struct u300_gpio *gpio = port->gpio; - int offset = d->irq - gpio->irq_base; + int offset = (port->number << 3) + d->hwirq; u32 val; if ((trigger & IRQF_TRIGGER_RISING) && @@ -503,10 +524,12 @@ static void u300_gpio_irq_enable(struct irq_data *d) { struct u300_gpio_port *port = irq_data_get_irq_chip_data(d); struct u300_gpio *gpio = port->gpio; - int offset = d->irq - gpio->irq_base; + int offset = (port->number << 3) + d->hwirq; u32 val; unsigned long flags; + dev_dbg(gpio->dev, "enable IRQ for hwirq %lu on port %s, offset %d\n", + d->hwirq, port->name, offset); local_irq_save(flags); val = readl(U300_PIN_REG(offset, ien)); writel(val | U300_PIN_BIT(offset), U300_PIN_REG(offset, ien)); @@ -517,7 +540,7 @@ static void u300_gpio_irq_disable(struct irq_data *d) { struct u300_gpio_port *port = irq_data_get_irq_chip_data(d); struct u300_gpio *gpio = port->gpio; - int offset = d->irq - gpio->irq_base; + int offset = (port->number << 3) + d->hwirq; u32 val; unsigned long flags; @@ -555,8 +578,7 @@ static void u300_gpio_irq_handler(unsigned irq, struct irq_desc *desc) int irqoffset; for_each_set_bit(irqoffset, &val, U300_GPIO_PINS_PER_PORT) { - int pin_irq = gpio->irq_base + (port->number << 3) - + irqoffset; + int pin_irq = irq_find_mapping(port->domain, irqoffset); int offset = pinoffset + irqoffset; dev_dbg(gpio->dev, "GPIO IRQ %d on pin %d\n", @@ -631,6 +653,8 @@ static inline void u300_gpio_free_ports(struct u300_gpio *gpio) list_for_each_safe(p, n, &gpio->port_list) { port = list_entry(p, struct u300_gpio_port, node); list_del(&port->node); + if (port->domain) + irq_domain_remove(port->domain); kfree(port); } } @@ -653,7 +677,6 @@ static int __init u300_gpio_probe(struct platform_device *pdev) gpio->chip = u300_gpio_chip; gpio->chip.ngpio = plat->ports * U300_GPIO_PINS_PER_PORT; - gpio->irq_base = plat->gpio_irq_base; gpio->chip.dev = &pdev->dev; gpio->chip.base = plat->gpio_base; gpio->dev = &pdev->dev; @@ -732,18 +755,26 @@ static int __init u300_gpio_probe(struct platform_device *pdev) port->irq = platform_get_irq_byname(pdev, port->name); - dev_dbg(gpio->dev, "register IRQ %d for %s\n", port->irq, + dev_dbg(gpio->dev, "register IRQ %d for port %s\n", port->irq, port->name); + port->domain = irq_domain_add_linear(pdev->dev.of_node, + U300_GPIO_PINS_PER_PORT, + &irq_domain_simple_ops, + port); + if (!port->domain) + goto err_no_domain; + irq_set_chained_handler(port->irq, u300_gpio_irq_handler); irq_set_handler_data(port->irq, port); /* For each GPIO pin set the unique IRQ handler */ for (i = 0; i < U300_GPIO_PINS_PER_PORT; i++) { - int irqno = gpio->irq_base + (portno << 3) + i; + int irqno = irq_create_mapping(port->domain, i); - dev_dbg(gpio->dev, "handler for IRQ %d on %s\n", - irqno, port->name); + dev_dbg(gpio->dev, "GPIO%d on port %s gets IRQ %d\n", + gpio->chip.base + (port->number << 3) + i, + port->name, irqno); irq_set_chip_and_handler(irqno, &u300_gpio_irqchip, handle_simple_irq); set_irq_flags(irqno, IRQF_VALID); @@ -776,6 +807,7 @@ static int __init u300_gpio_probe(struct platform_device *pdev) err_no_pinctrl: err = gpiochip_remove(&gpio->chip); err_no_chip: +err_no_domain: err_no_port: u300_gpio_free_ports(gpio); iounmap(gpio->base); diff --git a/include/linux/platform_data/pinctrl-coh901.h b/include/linux/platform_data/pinctrl-coh901.h index 30dea251b835..27a23b318cef 100644 --- a/include/linux/platform_data/pinctrl-coh901.h +++ b/include/linux/platform_data/pinctrl-coh901.h @@ -13,13 +13,11 @@ * struct u300_gpio_platform - U300 GPIO platform data * @ports: number of GPIO block ports * @gpio_base: first GPIO number for this block (use a free range) - * @gpio_irq_base: first GPIO IRQ number for this block (use a free range) * @pinctrl_device: pin control device to spawn as child */ struct u300_gpio_platform { u8 ports; int gpio_base; - int gpio_irq_base; struct platform_device *pinctrl_device; }; -- cgit v1.2.3 From 165adc9c1734a3f3bdbc6dc7c7a29bbefb424006 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 6 Nov 2012 14:49:39 +0100 Subject: gpiolib: fix up function prototypes etc Commit 69e1601bca88809dc118abd1becb02c15a02ec71 "gpiolib: provide provision to register pin ranges" Got most of it's function prototypes wrong, so fix this up by: - Moving the void declarations into static inlines in (previously the actual prototypes were declared here...) - Declare the gpiochip_add_pin_range() and gpiochip_remove_pin_ranges() functions in together with the pin range struct declaration itself. - Actually only implement these very functions in gpiolib.c if CONFIG_PINCTRL is set. - Additionally export the symbols since modules will need to be able to do this. Reviewed-by: Stephen Warren Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 10 +++++----- include/asm-generic/gpio.h | 6 ++++++ include/linux/gpio.h | 24 ++++++++++++++++++------ 3 files changed, 29 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index f0b07bbfcc9a..1e1a7cabc57e 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1185,6 +1185,7 @@ struct gpio_chip *gpiochip_find(void *data, EXPORT_SYMBOL_GPL(gpiochip_find); #ifdef CONFIG_PINCTRL + void gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, unsigned int pin_base, unsigned int npins) { @@ -1206,6 +1207,7 @@ void gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, list_add_tail(&pin_range->node, &chip->pin_ranges); } +EXPORT_SYMBOL_GPL(gpiochip_add_pin_range); void gpiochip_remove_pin_ranges(struct gpio_chip *chip) { @@ -1217,11 +1219,9 @@ void gpiochip_remove_pin_ranges(struct gpio_chip *chip) &pin_range->range); } } -#else -void gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, - unsigned int pin_base, unsigned int npins) {} -void gpiochip_remove_pin_ranges(struct gpio_chip *chip) {} -#endif +EXPORT_SYMBOL_GPL(gpiochip_remove_pin_ranges); + +#endif /* CONFIG_PINCTRL */ /* These "optional" allocation calls help prevent drivers from stomping * on each other, and help provide better diagnostics in debugfs. diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index 92e5c432421c..2e60de4265ac 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -49,6 +49,7 @@ struct module; struct device_node; #ifdef CONFIG_PINCTRL + /** * struct gpio_pin_range - pin range controlled by a gpio chip * @head: list for maintaining set of pin ranges, used internally @@ -61,6 +62,11 @@ struct gpio_pin_range { struct pinctrl_dev *pctldev; struct pinctrl_gpio_range range; }; + +void gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, + unsigned int pin_base, unsigned int npins); +void gpiochip_remove_pin_ranges(struct gpio_chip *chip); + #endif /** diff --git a/include/linux/gpio.h b/include/linux/gpio.h index a28445992b7f..21d28b930dc7 100644 --- a/include/linux/gpio.h +++ b/include/linux/gpio.h @@ -72,9 +72,9 @@ static inline int irq_to_gpio(unsigned int irq) return -EINVAL; } -#endif +#endif /* ! CONFIG_ARCH_HAVE_CUSTOM_GPIO_H */ -#else +#else /* ! CONFIG_GENERIC_GPIO */ #include #include @@ -231,9 +231,21 @@ static inline int irq_to_gpio(unsigned irq) return -EINVAL; } -void gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, - unsigned int pin_base, unsigned int npins); -void gpiochip_remove_pin_ranges(struct gpio_chip *chip); -#endif +#ifdef CONFIG_PINCTRL + +static inline void +gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, + unsigned int pin_base, unsigned int npins) +{ +} + +static inline void +gpiochip_remove_pin_ranges(struct gpio_chip *chip) +{ +} + +#endif /* CONFIG_PINCTRL */ + +#endif /* ! CONFIG_GENERIC_GPIO */ #endif /* __LINUX_GPIO_H */ -- cgit v1.2.3 From 1e63d7b9363f0c57d00991f9f2e0af374dfc591a Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 6 Nov 2012 16:03:35 +0100 Subject: gpiolib: separation of pin concerns The fact that of_gpiochip_add_pin_range() and gpiochip_add_pin_range() share too much code is fragile and will invariably mean that bugs need to be fixed in two places instead of one. So separate the concerns of gpiolib.c and gpiolib-of.c and have the latter call the former as back-end. This is necessary also when going forward with other device descriptions such as ACPI. This is done by: - Adding a return code to gpiochip_add_pin_range() so we can reliably check whether this succeeds. - Get rid of the custom of_pinctrl_add_gpio_range() from pinctrl. Instead create of_pinctrl_get() to just retrive the pin controller per se from an OF node. This composite function was just begging to be deleted, it was way to purpose-specific. - Use pinctrl_dev_get_name() to get the name of the retrieved pin controller and use that to call back into the generic gpiochip_add_pin_range(). Now the pin range is only allocated and tied to a pin controller from the core implementation in gpiolib.c. Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-of.c | 23 +++++++++-------------- drivers/gpio/gpiolib.c | 8 +++++--- drivers/pinctrl/devicetree.c | 4 +--- include/asm-generic/gpio.h | 4 ++-- include/linux/gpio.h | 2 +- include/linux/pinctrl/pinctrl.h | 7 ++----- 6 files changed, 20 insertions(+), 28 deletions(-) (limited to 'include/linux') diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 67403e47e4dc..a40cd84c5c10 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -221,8 +221,8 @@ EXPORT_SYMBOL(of_mm_gpiochip_add); static void of_gpiochip_add_pin_range(struct gpio_chip *chip) { struct device_node *np = chip->of_node; - struct gpio_pin_range *pin_range; struct of_phandle_args pinspec; + struct pinctrl_dev *pctldev; int index = 0, ret; if (!np) @@ -234,22 +234,17 @@ static void of_gpiochip_add_pin_range(struct gpio_chip *chip) if (ret) break; - pin_range = devm_kzalloc(chip->dev, sizeof(*pin_range), - GFP_KERNEL); - if (!pin_range) { - pr_err("%s: GPIO chip: failed to allocate pin ranges\n", - chip->label); + pctldev = of_pinctrl_get(pinspec.np); + if (!pctldev) break; - } - pin_range->range.name = chip->label; - pin_range->range.base = chip->base; - pin_range->range.pin_base = pinspec.args[0]; - pin_range->range.npins = pinspec.args[1]; - pin_range->pctldev = of_pinctrl_add_gpio_range(pinspec.np, - &pin_range->range); + ret = gpiochip_add_pin_range(chip, + pinctrl_dev_get_name(pctldev), + pinspec.args[0], + pinspec.args[1]); - list_add_tail(&pin_range->node, &chip->pin_ranges); + if (ret) + break; } while (index++); } diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index bcf9b9914fb7..c5f650095faa 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1187,8 +1187,8 @@ EXPORT_SYMBOL_GPL(gpiochip_find); #ifdef CONFIG_PINCTRL -void gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, - unsigned int pin_base, unsigned int npins) +int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, + unsigned int pin_base, unsigned int npins) { struct gpio_pin_range *pin_range; @@ -1196,7 +1196,7 @@ void gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, if (!pin_range) { pr_err("%s: GPIO chip: failed to allocate pin ranges\n", chip->label); - return; + return -ENOMEM; } pin_range->range.name = chip->label; @@ -1207,6 +1207,8 @@ void gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, &pin_range->range); list_add_tail(&pin_range->node, &chip->pin_ranges); + + return 0; } EXPORT_SYMBOL_GPL(gpiochip_add_pin_range); diff --git a/drivers/pinctrl/devicetree.c b/drivers/pinctrl/devicetree.c index 6728ec71cb65..fe2d1af7cfa0 100644 --- a/drivers/pinctrl/devicetree.c +++ b/drivers/pinctrl/devicetree.c @@ -106,8 +106,7 @@ static struct pinctrl_dev *find_pinctrl_by_of_node(struct device_node *np) return NULL; } -struct pinctrl_dev *of_pinctrl_add_gpio_range(struct device_node *np, - struct pinctrl_gpio_range *range) +struct pinctrl_dev *of_pinctrl_get(struct device_node *np) { struct pinctrl_dev *pctldev; @@ -115,7 +114,6 @@ struct pinctrl_dev *of_pinctrl_add_gpio_range(struct device_node *np, if (!pctldev) return NULL; - pinctrl_add_gpio_range(pctldev, range); return pctldev; } diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index 2e60de4265ac..50d995e7e44a 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -63,8 +63,8 @@ struct gpio_pin_range { struct pinctrl_gpio_range range; }; -void gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, - unsigned int pin_base, unsigned int npins); +int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, + unsigned int pin_base, unsigned int npins); void gpiochip_remove_pin_ranges(struct gpio_chip *chip); #endif diff --git a/include/linux/gpio.h b/include/linux/gpio.h index 21d28b930dc7..81bbfe5b5de6 100644 --- a/include/linux/gpio.h +++ b/include/linux/gpio.h @@ -233,7 +233,7 @@ static inline int irq_to_gpio(unsigned irq) #ifdef CONFIG_PINCTRL -static inline void +static inline int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, unsigned int pin_base, unsigned int npins) { diff --git a/include/linux/pinctrl/pinctrl.h b/include/linux/pinctrl/pinctrl.h index 434e5a94e131..4a58428bc793 100644 --- a/include/linux/pinctrl/pinctrl.h +++ b/include/linux/pinctrl/pinctrl.h @@ -141,16 +141,13 @@ extern struct pinctrl_dev *find_pinctrl_and_add_gpio_range(const char *devname, struct pinctrl_gpio_range *range); #ifdef CONFIG_OF -extern struct pinctrl_dev *of_pinctrl_add_gpio_range(struct device_node *np, - struct pinctrl_gpio_range *range); +extern struct pinctrl_dev *of_pinctrl_get(struct device_node *np); #else static inline -struct pinctrl_dev *of_pinctrl_add_gpio_range(struct device_node *np, - struct pinctrl_gpio_range *range) +struct pinctrl_dev *of_pinctrl_get(struct device_node *np) { return NULL; } - #endif /* CONFIG_OF */ extern const char *pinctrl_dev_get_name(struct pinctrl_dev *pctldev); -- cgit v1.2.3 From 50309a9c2e576ac4ad29e30f5854acb87bdc2ac4 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 6 Nov 2012 17:16:39 +0100 Subject: gpiolib: iron out include ladder mistakes The <*/gpio.h> includes are updated again: now we need to account for the problem introduced by commit: 595679a8038584df7b9398bf34f61db3c038bfea "gpiolib: fix up function prototypes etc" Actually we need static inlines in include/asm-generic/gpio.h as well since we may have GPIOLIB but not PINCTRL. Make sure to move all the CONFIG_PINCTRL business to the end of the file so we are sure we have declared struct gpio_chip. And we need to keep the static inlines in but here for the !CONFIG_GENERIC_GPIO case, and then we may as well throw in a few warnings like the other prototypes there, if someone would have the bad taste of compiling without GENERIC_GPIO even. Signed-off-by: Linus Walleij --- include/asm-generic/gpio.h | 56 +++++++++++++++++++++++++++++----------------- include/linux/gpio.h | 7 +++--- 2 files changed, 38 insertions(+), 25 deletions(-) (limited to 'include/linux') diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index 50d995e7e44a..2b84fc32fae2 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -48,27 +48,6 @@ struct seq_file; struct module; struct device_node; -#ifdef CONFIG_PINCTRL - -/** - * struct gpio_pin_range - pin range controlled by a gpio chip - * @head: list for maintaining set of pin ranges, used internally - * @pctldev: pinctrl device which handles corresponding pins - * @range: actual range of pins controlled by a gpio controller - */ - -struct gpio_pin_range { - struct list_head node; - struct pinctrl_dev *pctldev; - struct pinctrl_gpio_range range; -}; - -int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, - unsigned int pin_base, unsigned int npins); -void gpiochip_remove_pin_ranges(struct gpio_chip *chip); - -#endif - /** * struct gpio_chip - abstract a GPIO controller * @label: for diagnostics @@ -288,4 +267,39 @@ static inline void gpio_unexport(unsigned gpio) } #endif /* CONFIG_GPIO_SYSFS */ +#ifdef CONFIG_PINCTRL + +/** + * struct gpio_pin_range - pin range controlled by a gpio chip + * @head: list for maintaining set of pin ranges, used internally + * @pctldev: pinctrl device which handles corresponding pins + * @range: actual range of pins controlled by a gpio controller + */ + +struct gpio_pin_range { + struct list_head node; + struct pinctrl_dev *pctldev; + struct pinctrl_gpio_range range; +}; + +int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, + unsigned int pin_base, unsigned int npins); +void gpiochip_remove_pin_ranges(struct gpio_chip *chip); + +#else + +static inline int +gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, + unsigned int pin_base, unsigned int npins) +{ + return 0; +} + +static inline void +gpiochip_remove_pin_ranges(struct gpio_chip *chip) +{ +} + +#endif /* CONFIG_PINCTRL */ + #endif /* _ASM_GENERIC_GPIO_H */ diff --git a/include/linux/gpio.h b/include/linux/gpio.h index 81bbfe5b5de6..7ba2762abbc9 100644 --- a/include/linux/gpio.h +++ b/include/linux/gpio.h @@ -231,21 +231,20 @@ static inline int irq_to_gpio(unsigned irq) return -EINVAL; } -#ifdef CONFIG_PINCTRL - static inline int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, unsigned int pin_base, unsigned int npins) { + WARN_ON(1); + return -EINVAL; } static inline void gpiochip_remove_pin_ranges(struct gpio_chip *chip) { + WARN_ON(1); } -#endif /* CONFIG_PINCTRL */ - #endif /* ! CONFIG_GENERIC_GPIO */ #endif /* __LINUX_GPIO_H */ -- cgit v1.2.3 From df1590d9ae5e37e07e7cf91107e4c2c946ce8bf4 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 12 Nov 2012 22:56:03 +0530 Subject: ARM: SPEAr3xx: Shirq: Move shirq controller out of plat/ This patch moves shirq interrupt controllers driver and header file out of plat-spear directory. It is moved to drivers/irqchip/ directory. Signed-off-by: Viresh Kumar --- arch/arm/mach-spear3xx/spear3xx.c | 2 +- arch/arm/plat-spear/Makefile | 2 +- arch/arm/plat-spear/include/plat/shirq.h | 66 ------- arch/arm/plat-spear/shirq.c | 315 ------------------------------ drivers/irqchip/Makefile | 3 +- drivers/irqchip/spear-shirq.c | 316 +++++++++++++++++++++++++++++++ include/linux/irqchip/spear-shirq.h | 64 +++++++ 7 files changed, 384 insertions(+), 384 deletions(-) delete mode 100644 arch/arm/plat-spear/include/plat/shirq.h delete mode 100644 arch/arm/plat-spear/shirq.c create mode 100644 drivers/irqchip/spear-shirq.c create mode 100644 include/linux/irqchip/spear-shirq.h (limited to 'include/linux') diff --git a/arch/arm/mach-spear3xx/spear3xx.c b/arch/arm/mach-spear3xx/spear3xx.c index f1aaf5b168b2..38fe95db31a7 100644 --- a/arch/arm/mach-spear3xx/spear3xx.c +++ b/arch/arm/mach-spear3xx/spear3xx.c @@ -15,12 +15,12 @@ #include #include +#include #include #include #include #include #include -#include #include #include diff --git a/arch/arm/plat-spear/Makefile b/arch/arm/plat-spear/Makefile index 2607bd05c525..01e88532a5db 100644 --- a/arch/arm/plat-spear/Makefile +++ b/arch/arm/plat-spear/Makefile @@ -5,5 +5,5 @@ # Common support obj-y := restart.o time.o -obj-$(CONFIG_ARCH_SPEAR3XX) += pl080.o shirq.o +obj-$(CONFIG_ARCH_SPEAR3XX) += pl080.o obj-$(CONFIG_ARCH_SPEAR6XX) += pl080.o diff --git a/arch/arm/plat-spear/include/plat/shirq.h b/arch/arm/plat-spear/include/plat/shirq.h deleted file mode 100644 index c51b355f00de..000000000000 --- a/arch/arm/plat-spear/include/plat/shirq.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * arch/arm/plat-spear/include/plat/shirq.h - * - * SPEAr platform shared irq layer header file - * - * Copyright (C) 2009 ST Microelectronics - * Viresh Kumar - * - * 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 __PLAT_SHIRQ_H -#define __PLAT_SHIRQ_H - -#include -#include - -/* - * struct shirq_regs: shared irq register configuration - * - * enb_reg: enable register offset - * reset_to_enb: val 1 indicates, we need to clear bit for enabling interrupt - * status_reg: status register offset - * status_reg_mask: status register valid mask - * clear_reg: clear register offset - * reset_to_clear: val 1 indicates, we need to clear bit for clearing interrupt - */ -struct shirq_regs { - u32 enb_reg; - u32 reset_to_enb; - u32 status_reg; - u32 clear_reg; - u32 reset_to_clear; -}; - -/* - * struct spear_shirq: shared irq structure - * - * irq: hardware irq number - * irq_base: base irq in linux domain - * irq_nr: no. of shared interrupts in a particular block - * irq_bit_off: starting bit offset in the status register - * invalid_irq: irq group is currently disabled - * base: base address of shared irq register - * regs: register configuration for shared irq block - */ -struct spear_shirq { - u32 irq; - u32 irq_base; - u32 irq_nr; - u32 irq_bit_off; - int invalid_irq; - void __iomem *base; - struct shirq_regs regs; -}; - -int __init spear300_shirq_of_init(struct device_node *np, - struct device_node *parent); -int __init spear310_shirq_of_init(struct device_node *np, - struct device_node *parent); -int __init spear320_shirq_of_init(struct device_node *np, - struct device_node *parent); - -#endif /* __PLAT_SHIRQ_H */ diff --git a/arch/arm/plat-spear/shirq.c b/arch/arm/plat-spear/shirq.c deleted file mode 100644 index 955c7249a5c1..000000000000 --- a/arch/arm/plat-spear/shirq.c +++ /dev/null @@ -1,315 +0,0 @@ -/* - * arch/arm/plat-spear/shirq.c - * - * SPEAr platform shared irq layer source file - * - * Copyright (C) 2009 ST Microelectronics - * Viresh Kumar - * - * 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. - */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static DEFINE_SPINLOCK(lock); - -/* spear300 shared irq registers offsets and masks */ -#define SPEAR300_INT_ENB_MASK_REG 0x54 -#define SPEAR300_INT_STS_MASK_REG 0x58 - -static struct spear_shirq spear300_shirq_ras1 = { - .irq_nr = 9, - .irq_bit_off = 0, - .regs = { - .enb_reg = SPEAR300_INT_ENB_MASK_REG, - .status_reg = SPEAR300_INT_STS_MASK_REG, - .clear_reg = -1, - }, -}; - -static struct spear_shirq *spear300_shirq_blocks[] = { - &spear300_shirq_ras1, -}; - -/* spear310 shared irq registers offsets and masks */ -#define SPEAR310_INT_STS_MASK_REG 0x04 - -static struct spear_shirq spear310_shirq_ras1 = { - .irq_nr = 8, - .irq_bit_off = 0, - .regs = { - .enb_reg = -1, - .status_reg = SPEAR310_INT_STS_MASK_REG, - .clear_reg = -1, - }, -}; - -static struct spear_shirq spear310_shirq_ras2 = { - .irq_nr = 5, - .irq_bit_off = 8, - .regs = { - .enb_reg = -1, - .status_reg = SPEAR310_INT_STS_MASK_REG, - .clear_reg = -1, - }, -}; - -static struct spear_shirq spear310_shirq_ras3 = { - .irq_nr = 1, - .irq_bit_off = 13, - .regs = { - .enb_reg = -1, - .status_reg = SPEAR310_INT_STS_MASK_REG, - .clear_reg = -1, - }, -}; - -static struct spear_shirq spear310_shirq_intrcomm_ras = { - .irq_nr = 3, - .irq_bit_off = 14, - .regs = { - .enb_reg = -1, - .status_reg = SPEAR310_INT_STS_MASK_REG, - .clear_reg = -1, - }, -}; - -static struct spear_shirq *spear310_shirq_blocks[] = { - &spear310_shirq_ras1, - &spear310_shirq_ras2, - &spear310_shirq_ras3, - &spear310_shirq_intrcomm_ras, -}; - -/* spear320 shared irq registers offsets and masks */ -#define SPEAR320_INT_STS_MASK_REG 0x04 -#define SPEAR320_INT_CLR_MASK_REG 0x04 -#define SPEAR320_INT_ENB_MASK_REG 0x08 - -static struct spear_shirq spear320_shirq_ras1 = { - .irq_nr = 3, - .irq_bit_off = 7, - .regs = { - .enb_reg = -1, - .status_reg = SPEAR320_INT_STS_MASK_REG, - .clear_reg = SPEAR320_INT_CLR_MASK_REG, - .reset_to_clear = 1, - }, -}; - -static struct spear_shirq spear320_shirq_ras2 = { - .irq_nr = 1, - .irq_bit_off = 10, - .regs = { - .enb_reg = -1, - .status_reg = SPEAR320_INT_STS_MASK_REG, - .clear_reg = SPEAR320_INT_CLR_MASK_REG, - .reset_to_clear = 1, - }, -}; - -static struct spear_shirq spear320_shirq_ras3 = { - .irq_nr = 3, - .irq_bit_off = 0, - .invalid_irq = 1, - .regs = { - .enb_reg = SPEAR320_INT_ENB_MASK_REG, - .reset_to_enb = 1, - .status_reg = SPEAR320_INT_STS_MASK_REG, - .clear_reg = SPEAR320_INT_CLR_MASK_REG, - .reset_to_clear = 1, - }, -}; - -static struct spear_shirq spear320_shirq_intrcomm_ras = { - .irq_nr = 11, - .irq_bit_off = 11, - .regs = { - .enb_reg = -1, - .status_reg = SPEAR320_INT_STS_MASK_REG, - .clear_reg = SPEAR320_INT_CLR_MASK_REG, - .reset_to_clear = 1, - }, -}; - -static struct spear_shirq *spear320_shirq_blocks[] = { - &spear320_shirq_ras3, - &spear320_shirq_ras1, - &spear320_shirq_ras2, - &spear320_shirq_intrcomm_ras, -}; - -static void shirq_irq_mask_unmask(struct irq_data *d, bool mask) -{ - struct spear_shirq *shirq = irq_data_get_irq_chip_data(d); - u32 val, offset = d->irq - shirq->irq_base; - unsigned long flags; - - if (shirq->regs.enb_reg == -1) - return; - - spin_lock_irqsave(&lock, flags); - val = readl(shirq->base + shirq->regs.enb_reg); - - if (mask ^ shirq->regs.reset_to_enb) - val &= ~(0x1 << shirq->irq_bit_off << offset); - else - val |= 0x1 << shirq->irq_bit_off << offset; - - writel(val, shirq->base + shirq->regs.enb_reg); - spin_unlock_irqrestore(&lock, flags); - -} - -static void shirq_irq_mask(struct irq_data *d) -{ - shirq_irq_mask_unmask(d, 1); -} - -static void shirq_irq_unmask(struct irq_data *d) -{ - shirq_irq_mask_unmask(d, 0); -} - -static struct irq_chip shirq_chip = { - .name = "spear-shirq", - .irq_ack = shirq_irq_mask, - .irq_mask = shirq_irq_mask, - .irq_unmask = shirq_irq_unmask, -}; - -static void shirq_handler(unsigned irq, struct irq_desc *desc) -{ - u32 i, j, val, mask, tmp; - struct irq_chip *chip; - struct spear_shirq *shirq = irq_get_handler_data(irq); - - chip = irq_get_chip(irq); - chip->irq_ack(&desc->irq_data); - - mask = ((0x1 << shirq->irq_nr) - 1) << shirq->irq_bit_off; - while ((val = readl(shirq->base + shirq->regs.status_reg) & - mask)) { - - val >>= shirq->irq_bit_off; - for (i = 0, j = 1; i < shirq->irq_nr; i++, j <<= 1) { - - if (!(j & val)) - continue; - - generic_handle_irq(shirq->irq_base + i); - - /* clear interrupt */ - if (shirq->regs.clear_reg == -1) - continue; - - tmp = readl(shirq->base + shirq->regs.clear_reg); - if (shirq->regs.reset_to_clear) - tmp &= ~(j << shirq->irq_bit_off); - else - tmp |= (j << shirq->irq_bit_off); - writel(tmp, shirq->base + shirq->regs.clear_reg); - } - } - chip->irq_unmask(&desc->irq_data); -} - -static void __init spear_shirq_register(struct spear_shirq *shirq) -{ - int i; - - if (shirq->invalid_irq) - return; - - irq_set_chained_handler(shirq->irq, shirq_handler); - for (i = 0; i < shirq->irq_nr; i++) { - irq_set_chip_and_handler(shirq->irq_base + i, - &shirq_chip, handle_simple_irq); - set_irq_flags(shirq->irq_base + i, IRQF_VALID); - irq_set_chip_data(shirq->irq_base + i, shirq); - } - - irq_set_handler_data(shirq->irq, shirq); -} - -static int __init shirq_init(struct spear_shirq **shirq_blocks, int block_nr, - struct device_node *np) -{ - int i, irq_base, hwirq = 0, irq_nr = 0; - static struct irq_domain *shirq_domain; - void __iomem *base; - - base = of_iomap(np, 0); - if (!base) { - pr_err("%s: failed to map shirq registers\n", __func__); - return -ENXIO; - } - - for (i = 0; i < block_nr; i++) - irq_nr += shirq_blocks[i]->irq_nr; - - irq_base = irq_alloc_descs(-1, 0, irq_nr, 0); - if (IS_ERR_VALUE(irq_base)) { - pr_err("%s: irq desc alloc failed\n", __func__); - goto err_unmap; - } - - shirq_domain = irq_domain_add_legacy(np, irq_nr, irq_base, 0, - &irq_domain_simple_ops, NULL); - if (WARN_ON(!shirq_domain)) { - pr_warn("%s: irq domain init failed\n", __func__); - goto err_free_desc; - } - - for (i = 0; i < block_nr; i++) { - shirq_blocks[i]->base = base; - shirq_blocks[i]->irq_base = irq_find_mapping(shirq_domain, - hwirq); - shirq_blocks[i]->irq = irq_of_parse_and_map(np, i); - - spear_shirq_register(shirq_blocks[i]); - hwirq += shirq_blocks[i]->irq_nr; - } - - return 0; - -err_free_desc: - irq_free_descs(irq_base, irq_nr); -err_unmap: - iounmap(base); - return -ENXIO; -} - -int __init spear300_shirq_of_init(struct device_node *np, - struct device_node *parent) -{ - return shirq_init(spear300_shirq_blocks, - ARRAY_SIZE(spear300_shirq_blocks), np); -} - -int __init spear310_shirq_of_init(struct device_node *np, - struct device_node *parent) -{ - return shirq_init(spear310_shirq_blocks, - ARRAY_SIZE(spear310_shirq_blocks), np); -} - -int __init spear320_shirq_of_init(struct device_node *np, - struct device_node *parent) -{ - return shirq_init(spear320_shirq_blocks, - ARRAY_SIZE(spear320_shirq_blocks), np); -} diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 054321db4350..bf7b43696cdb 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -1 +1,2 @@ -obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2835.o +obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2835.o +obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o diff --git a/drivers/irqchip/spear-shirq.c b/drivers/irqchip/spear-shirq.c new file mode 100644 index 000000000000..80e1d2fd9d4c --- /dev/null +++ b/drivers/irqchip/spear-shirq.c @@ -0,0 +1,316 @@ +/* + * SPEAr platform shared irq layer source file + * + * Copyright (C) 2009-2012 ST Microelectronics + * Viresh Kumar + * + * Copyright (C) 2012 ST Microelectronics + * Shiraz Hashim + * + * 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. + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static DEFINE_SPINLOCK(lock); + +/* spear300 shared irq registers offsets and masks */ +#define SPEAR300_INT_ENB_MASK_REG 0x54 +#define SPEAR300_INT_STS_MASK_REG 0x58 + +static struct spear_shirq spear300_shirq_ras1 = { + .irq_nr = 9, + .irq_bit_off = 0, + .regs = { + .enb_reg = SPEAR300_INT_ENB_MASK_REG, + .status_reg = SPEAR300_INT_STS_MASK_REG, + .clear_reg = -1, + }, +}; + +static struct spear_shirq *spear300_shirq_blocks[] = { + &spear300_shirq_ras1, +}; + +/* spear310 shared irq registers offsets and masks */ +#define SPEAR310_INT_STS_MASK_REG 0x04 + +static struct spear_shirq spear310_shirq_ras1 = { + .irq_nr = 8, + .irq_bit_off = 0, + .regs = { + .enb_reg = -1, + .status_reg = SPEAR310_INT_STS_MASK_REG, + .clear_reg = -1, + }, +}; + +static struct spear_shirq spear310_shirq_ras2 = { + .irq_nr = 5, + .irq_bit_off = 8, + .regs = { + .enb_reg = -1, + .status_reg = SPEAR310_INT_STS_MASK_REG, + .clear_reg = -1, + }, +}; + +static struct spear_shirq spear310_shirq_ras3 = { + .irq_nr = 1, + .irq_bit_off = 13, + .regs = { + .enb_reg = -1, + .status_reg = SPEAR310_INT_STS_MASK_REG, + .clear_reg = -1, + }, +}; + +static struct spear_shirq spear310_shirq_intrcomm_ras = { + .irq_nr = 3, + .irq_bit_off = 14, + .regs = { + .enb_reg = -1, + .status_reg = SPEAR310_INT_STS_MASK_REG, + .clear_reg = -1, + }, +}; + +static struct spear_shirq *spear310_shirq_blocks[] = { + &spear310_shirq_ras1, + &spear310_shirq_ras2, + &spear310_shirq_ras3, + &spear310_shirq_intrcomm_ras, +}; + +/* spear320 shared irq registers offsets and masks */ +#define SPEAR320_INT_STS_MASK_REG 0x04 +#define SPEAR320_INT_CLR_MASK_REG 0x04 +#define SPEAR320_INT_ENB_MASK_REG 0x08 + +static struct spear_shirq spear320_shirq_ras1 = { + .irq_nr = 3, + .irq_bit_off = 7, + .regs = { + .enb_reg = -1, + .status_reg = SPEAR320_INT_STS_MASK_REG, + .clear_reg = SPEAR320_INT_CLR_MASK_REG, + .reset_to_clear = 1, + }, +}; + +static struct spear_shirq spear320_shirq_ras2 = { + .irq_nr = 1, + .irq_bit_off = 10, + .regs = { + .enb_reg = -1, + .status_reg = SPEAR320_INT_STS_MASK_REG, + .clear_reg = SPEAR320_INT_CLR_MASK_REG, + .reset_to_clear = 1, + }, +}; + +static struct spear_shirq spear320_shirq_ras3 = { + .irq_nr = 3, + .irq_bit_off = 0, + .invalid_irq = 1, + .regs = { + .enb_reg = SPEAR320_INT_ENB_MASK_REG, + .reset_to_enb = 1, + .status_reg = SPEAR320_INT_STS_MASK_REG, + .clear_reg = SPEAR320_INT_CLR_MASK_REG, + .reset_to_clear = 1, + }, +}; + +static struct spear_shirq spear320_shirq_intrcomm_ras = { + .irq_nr = 11, + .irq_bit_off = 11, + .regs = { + .enb_reg = -1, + .status_reg = SPEAR320_INT_STS_MASK_REG, + .clear_reg = SPEAR320_INT_CLR_MASK_REG, + .reset_to_clear = 1, + }, +}; + +static struct spear_shirq *spear320_shirq_blocks[] = { + &spear320_shirq_ras3, + &spear320_shirq_ras1, + &spear320_shirq_ras2, + &spear320_shirq_intrcomm_ras, +}; + +static void shirq_irq_mask_unmask(struct irq_data *d, bool mask) +{ + struct spear_shirq *shirq = irq_data_get_irq_chip_data(d); + u32 val, offset = d->irq - shirq->irq_base; + unsigned long flags; + + if (shirq->regs.enb_reg == -1) + return; + + spin_lock_irqsave(&lock, flags); + val = readl(shirq->base + shirq->regs.enb_reg); + + if (mask ^ shirq->regs.reset_to_enb) + val &= ~(0x1 << shirq->irq_bit_off << offset); + else + val |= 0x1 << shirq->irq_bit_off << offset; + + writel(val, shirq->base + shirq->regs.enb_reg); + spin_unlock_irqrestore(&lock, flags); + +} + +static void shirq_irq_mask(struct irq_data *d) +{ + shirq_irq_mask_unmask(d, 1); +} + +static void shirq_irq_unmask(struct irq_data *d) +{ + shirq_irq_mask_unmask(d, 0); +} + +static struct irq_chip shirq_chip = { + .name = "spear-shirq", + .irq_ack = shirq_irq_mask, + .irq_mask = shirq_irq_mask, + .irq_unmask = shirq_irq_unmask, +}; + +static void shirq_handler(unsigned irq, struct irq_desc *desc) +{ + u32 i, j, val, mask, tmp; + struct irq_chip *chip; + struct spear_shirq *shirq = irq_get_handler_data(irq); + + chip = irq_get_chip(irq); + chip->irq_ack(&desc->irq_data); + + mask = ((0x1 << shirq->irq_nr) - 1) << shirq->irq_bit_off; + while ((val = readl(shirq->base + shirq->regs.status_reg) & + mask)) { + + val >>= shirq->irq_bit_off; + for (i = 0, j = 1; i < shirq->irq_nr; i++, j <<= 1) { + + if (!(j & val)) + continue; + + generic_handle_irq(shirq->irq_base + i); + + /* clear interrupt */ + if (shirq->regs.clear_reg == -1) + continue; + + tmp = readl(shirq->base + shirq->regs.clear_reg); + if (shirq->regs.reset_to_clear) + tmp &= ~(j << shirq->irq_bit_off); + else + tmp |= (j << shirq->irq_bit_off); + writel(tmp, shirq->base + shirq->regs.clear_reg); + } + } + chip->irq_unmask(&desc->irq_data); +} + +static void __init spear_shirq_register(struct spear_shirq *shirq) +{ + int i; + + if (shirq->invalid_irq) + return; + + irq_set_chained_handler(shirq->irq, shirq_handler); + for (i = 0; i < shirq->irq_nr; i++) { + irq_set_chip_and_handler(shirq->irq_base + i, + &shirq_chip, handle_simple_irq); + set_irq_flags(shirq->irq_base + i, IRQF_VALID); + irq_set_chip_data(shirq->irq_base + i, shirq); + } + + irq_set_handler_data(shirq->irq, shirq); +} + +static int __init shirq_init(struct spear_shirq **shirq_blocks, int block_nr, + struct device_node *np) +{ + int i, irq_base, hwirq = 0, irq_nr = 0; + static struct irq_domain *shirq_domain; + void __iomem *base; + + base = of_iomap(np, 0); + if (!base) { + pr_err("%s: failed to map shirq registers\n", __func__); + return -ENXIO; + } + + for (i = 0; i < block_nr; i++) + irq_nr += shirq_blocks[i]->irq_nr; + + irq_base = irq_alloc_descs(-1, 0, irq_nr, 0); + if (IS_ERR_VALUE(irq_base)) { + pr_err("%s: irq desc alloc failed\n", __func__); + goto err_unmap; + } + + shirq_domain = irq_domain_add_legacy(np, irq_nr, irq_base, 0, + &irq_domain_simple_ops, NULL); + if (WARN_ON(!shirq_domain)) { + pr_warn("%s: irq domain init failed\n", __func__); + goto err_free_desc; + } + + for (i = 0; i < block_nr; i++) { + shirq_blocks[i]->base = base; + shirq_blocks[i]->irq_base = irq_find_mapping(shirq_domain, + hwirq); + shirq_blocks[i]->irq = irq_of_parse_and_map(np, i); + + spear_shirq_register(shirq_blocks[i]); + hwirq += shirq_blocks[i]->irq_nr; + } + + return 0; + +err_free_desc: + irq_free_descs(irq_base, irq_nr); +err_unmap: + iounmap(base); + return -ENXIO; +} + +int __init spear300_shirq_of_init(struct device_node *np, + struct device_node *parent) +{ + return shirq_init(spear300_shirq_blocks, + ARRAY_SIZE(spear300_shirq_blocks), np); +} + +int __init spear310_shirq_of_init(struct device_node *np, + struct device_node *parent) +{ + return shirq_init(spear310_shirq_blocks, + ARRAY_SIZE(spear310_shirq_blocks), np); +} + +int __init spear320_shirq_of_init(struct device_node *np, + struct device_node *parent) +{ + return shirq_init(spear320_shirq_blocks, + ARRAY_SIZE(spear320_shirq_blocks), np); +} diff --git a/include/linux/irqchip/spear-shirq.h b/include/linux/irqchip/spear-shirq.h new file mode 100644 index 000000000000..c8be16d213a3 --- /dev/null +++ b/include/linux/irqchip/spear-shirq.h @@ -0,0 +1,64 @@ +/* + * SPEAr platform shared irq layer header file + * + * Copyright (C) 2009-2012 ST Microelectronics + * Viresh Kumar + * + * 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_SHIRQ_H +#define __SPEAR_SHIRQ_H + +#include +#include + +/* + * struct shirq_regs: shared irq register configuration + * + * enb_reg: enable register offset + * reset_to_enb: val 1 indicates, we need to clear bit for enabling interrupt + * status_reg: status register offset + * status_reg_mask: status register valid mask + * clear_reg: clear register offset + * reset_to_clear: val 1 indicates, we need to clear bit for clearing interrupt + */ +struct shirq_regs { + u32 enb_reg; + u32 reset_to_enb; + u32 status_reg; + u32 clear_reg; + u32 reset_to_clear; +}; + +/* + * struct spear_shirq: shared irq structure + * + * irq: hardware irq number + * irq_base: base irq in linux domain + * irq_nr: no. of shared interrupts in a particular block + * irq_bit_off: starting bit offset in the status register + * invalid_irq: irq group is currently disabled + * base: base address of shared irq register + * regs: register configuration for shared irq block + */ +struct spear_shirq { + u32 irq; + u32 irq_base; + u32 irq_nr; + u32 irq_bit_off; + int invalid_irq; + void __iomem *base; + struct shirq_regs regs; +}; + +int __init spear300_shirq_of_init(struct device_node *np, + struct device_node *parent); +int __init spear310_shirq_of_init(struct device_node *np, + struct device_node *parent); +int __init spear320_shirq_of_init(struct device_node *np, + struct device_node *parent); + +#endif /* __SPEAR_SHIRQ_H */ -- cgit v1.2.3