123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * irqchip for the IXP4xx interrupt controller
- * Copyright (C) 2019 Linus Walleij <[email protected]>
- *
- * Based on arch/arm/mach-ixp4xx/common.c
- * Copyright 2002 (C) Intel Corporation
- * Copyright 2003-2004 (C) MontaVista, Software, Inc.
- * Copyright (C) Deepak Saxena <[email protected]>
- */
- #include <linux/bitops.h>
- #include <linux/gpio/driver.h>
- #include <linux/irq.h>
- #include <linux/io.h>
- #include <linux/irqchip.h>
- #include <linux/irqdomain.h>
- #include <linux/of.h>
- #include <linux/of_address.h>
- #include <linux/of_irq.h>
- #include <linux/platform_device.h>
- #include <linux/cpu.h>
- #include <asm/exception.h>
- #include <asm/mach/irq.h>
- #define IXP4XX_ICPR 0x00 /* Interrupt Status */
- #define IXP4XX_ICMR 0x04 /* Interrupt Enable */
- #define IXP4XX_ICLR 0x08 /* Interrupt IRQ/FIQ Select */
- #define IXP4XX_ICIP 0x0C /* IRQ Status */
- #define IXP4XX_ICFP 0x10 /* FIQ Status */
- #define IXP4XX_ICHR 0x14 /* Interrupt Priority */
- #define IXP4XX_ICIH 0x18 /* IRQ Highest Pri Int */
- #define IXP4XX_ICFH 0x1C /* FIQ Highest Pri Int */
- /* IXP43x and IXP46x-only */
- #define IXP4XX_ICPR2 0x20 /* Interrupt Status 2 */
- #define IXP4XX_ICMR2 0x24 /* Interrupt Enable 2 */
- #define IXP4XX_ICLR2 0x28 /* Interrupt IRQ/FIQ Select 2 */
- #define IXP4XX_ICIP2 0x2C /* IRQ Status */
- #define IXP4XX_ICFP2 0x30 /* FIQ Status */
- #define IXP4XX_ICEEN 0x34 /* Error High Pri Enable */
- /**
- * struct ixp4xx_irq - state container for the Faraday IRQ controller
- * @irqbase: IRQ controller memory base in virtual memory
- * @is_356: if this is an IXP43x, IXP45x or IX46x SoC (with 64 IRQs)
- * @irqchip: irqchip for this instance
- * @domain: IRQ domain for this instance
- */
- struct ixp4xx_irq {
- void __iomem *irqbase;
- bool is_356;
- struct irq_chip irqchip;
- struct irq_domain *domain;
- };
- /* Local static state container */
- static struct ixp4xx_irq ixirq;
- /* GPIO Clocks */
- #define IXP4XX_GPIO_CLK_0 14
- #define IXP4XX_GPIO_CLK_1 15
- static int ixp4xx_set_irq_type(struct irq_data *d, unsigned int type)
- {
- /* All are level active high (asserted) here */
- if (type != IRQ_TYPE_LEVEL_HIGH)
- return -EINVAL;
- return 0;
- }
- static void ixp4xx_irq_mask(struct irq_data *d)
- {
- struct ixp4xx_irq *ixi = irq_data_get_irq_chip_data(d);
- u32 val;
- if (ixi->is_356 && d->hwirq >= 32) {
- val = __raw_readl(ixi->irqbase + IXP4XX_ICMR2);
- val &= ~BIT(d->hwirq - 32);
- __raw_writel(val, ixi->irqbase + IXP4XX_ICMR2);
- } else {
- val = __raw_readl(ixi->irqbase + IXP4XX_ICMR);
- val &= ~BIT(d->hwirq);
- __raw_writel(val, ixi->irqbase + IXP4XX_ICMR);
- }
- }
- /*
- * Level triggered interrupts on GPIO lines can only be cleared when the
- * interrupt condition disappears.
- */
- static void ixp4xx_irq_unmask(struct irq_data *d)
- {
- struct ixp4xx_irq *ixi = irq_data_get_irq_chip_data(d);
- u32 val;
- if (ixi->is_356 && d->hwirq >= 32) {
- val = __raw_readl(ixi->irqbase + IXP4XX_ICMR2);
- val |= BIT(d->hwirq - 32);
- __raw_writel(val, ixi->irqbase + IXP4XX_ICMR2);
- } else {
- val = __raw_readl(ixi->irqbase + IXP4XX_ICMR);
- val |= BIT(d->hwirq);
- __raw_writel(val, ixi->irqbase + IXP4XX_ICMR);
- }
- }
- static asmlinkage void __exception_irq_entry
- ixp4xx_handle_irq(struct pt_regs *regs)
- {
- struct ixp4xx_irq *ixi = &ixirq;
- unsigned long status;
- int i;
- status = __raw_readl(ixi->irqbase + IXP4XX_ICIP);
- for_each_set_bit(i, &status, 32)
- generic_handle_domain_irq(ixi->domain, i);
- /*
- * IXP465/IXP435 has an upper IRQ status register
- */
- if (ixi->is_356) {
- status = __raw_readl(ixi->irqbase + IXP4XX_ICIP2);
- for_each_set_bit(i, &status, 32)
- generic_handle_domain_irq(ixi->domain, i + 32);
- }
- }
- static int ixp4xx_irq_domain_translate(struct irq_domain *domain,
- struct irq_fwspec *fwspec,
- unsigned long *hwirq,
- unsigned int *type)
- {
- /* We support standard DT translation */
- if (is_of_node(fwspec->fwnode) && fwspec->param_count == 2) {
- *hwirq = fwspec->param[0];
- *type = fwspec->param[1];
- return 0;
- }
- if (is_fwnode_irqchip(fwspec->fwnode)) {
- if (fwspec->param_count != 2)
- return -EINVAL;
- *hwirq = fwspec->param[0];
- *type = fwspec->param[1];
- WARN_ON(*type == IRQ_TYPE_NONE);
- return 0;
- }
- return -EINVAL;
- }
- static int ixp4xx_irq_domain_alloc(struct irq_domain *d,
- unsigned int irq, unsigned int nr_irqs,
- void *data)
- {
- struct ixp4xx_irq *ixi = d->host_data;
- irq_hw_number_t hwirq;
- unsigned int type = IRQ_TYPE_NONE;
- struct irq_fwspec *fwspec = data;
- int ret;
- int i;
- ret = ixp4xx_irq_domain_translate(d, fwspec, &hwirq, &type);
- if (ret)
- return ret;
- for (i = 0; i < nr_irqs; i++) {
- /*
- * TODO: after converting IXP4xx to only device tree, set
- * handle_bad_irq as default handler and assume all consumers
- * call .set_type() as this is provided in the second cell in
- * the device tree phandle.
- */
- irq_domain_set_info(d,
- irq + i,
- hwirq + i,
- &ixi->irqchip,
- ixi,
- handle_level_irq,
- NULL, NULL);
- irq_set_probe(irq + i);
- }
- return 0;
- }
- /*
- * This needs to be a hierarchical irqdomain to work well with the
- * GPIO irqchip (which is lower in the hierarchy)
- */
- static const struct irq_domain_ops ixp4xx_irqdomain_ops = {
- .translate = ixp4xx_irq_domain_translate,
- .alloc = ixp4xx_irq_domain_alloc,
- .free = irq_domain_free_irqs_common,
- };
- /**
- * ixp4x_irq_setup() - Common setup code for the IXP4xx interrupt controller
- * @ixi: State container
- * @irqbase: Virtual memory base for the interrupt controller
- * @fwnode: Corresponding fwnode abstraction for this controller
- * @is_356: if this is an IXP43x, IXP45x or IXP46x SoC variant
- */
- static int __init ixp4xx_irq_setup(struct ixp4xx_irq *ixi,
- void __iomem *irqbase,
- struct fwnode_handle *fwnode,
- bool is_356)
- {
- int nr_irqs;
- ixi->irqbase = irqbase;
- ixi->is_356 = is_356;
- /* Route all sources to IRQ instead of FIQ */
- __raw_writel(0x0, ixi->irqbase + IXP4XX_ICLR);
- /* Disable all interrupts */
- __raw_writel(0x0, ixi->irqbase + IXP4XX_ICMR);
- if (is_356) {
- /* Route upper 32 sources to IRQ instead of FIQ */
- __raw_writel(0x0, ixi->irqbase + IXP4XX_ICLR2);
- /* Disable upper 32 interrupts */
- __raw_writel(0x0, ixi->irqbase + IXP4XX_ICMR2);
- nr_irqs = 64;
- } else {
- nr_irqs = 32;
- }
- ixi->irqchip.name = "IXP4xx";
- ixi->irqchip.irq_mask = ixp4xx_irq_mask;
- ixi->irqchip.irq_unmask = ixp4xx_irq_unmask;
- ixi->irqchip.irq_set_type = ixp4xx_set_irq_type;
- ixi->domain = irq_domain_create_linear(fwnode, nr_irqs,
- &ixp4xx_irqdomain_ops,
- ixi);
- if (!ixi->domain) {
- pr_crit("IXP4XX: can not add primary irqdomain\n");
- return -ENODEV;
- }
- set_handle_irq(ixp4xx_handle_irq);
- return 0;
- }
- static int __init ixp4xx_of_init_irq(struct device_node *np,
- struct device_node *parent)
- {
- struct ixp4xx_irq *ixi = &ixirq;
- void __iomem *base;
- struct fwnode_handle *fwnode;
- bool is_356;
- int ret;
- base = of_iomap(np, 0);
- if (!base) {
- pr_crit("IXP4XX: could not ioremap interrupt controller\n");
- return -ENODEV;
- }
- fwnode = of_node_to_fwnode(np);
- /* These chip variants have 64 interrupts */
- is_356 = of_device_is_compatible(np, "intel,ixp43x-interrupt") ||
- of_device_is_compatible(np, "intel,ixp45x-interrupt") ||
- of_device_is_compatible(np, "intel,ixp46x-interrupt");
- ret = ixp4xx_irq_setup(ixi, base, fwnode, is_356);
- if (ret)
- pr_crit("IXP4XX: failed to set up irqchip\n");
- return ret;
- }
- IRQCHIP_DECLARE(ixp42x, "intel,ixp42x-interrupt",
- ixp4xx_of_init_irq);
- IRQCHIP_DECLARE(ixp43x, "intel,ixp43x-interrupt",
- ixp4xx_of_init_irq);
- IRQCHIP_DECLARE(ixp45x, "intel,ixp45x-interrupt",
- ixp4xx_of_init_irq);
- IRQCHIP_DECLARE(ixp46x, "intel,ixp46x-interrupt",
- ixp4xx_of_init_irq);
|