diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2019-07-08 11:01:13 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2019-07-08 11:01:13 -0700 |
commit | 2a1ccd31420a7b1acd6ca37b2bec2d723aa093e4 (patch) | |
tree | 43946f0c4afc7dce86fc055df48d661f8ed3999c | |
parent | e0e86b111bca6bbf746c03ec5cf3e6a61fa3f8e9 (diff) | |
parent | 3a1d24ca9573fbc74a3d32c972c333b161e0e9dc (diff) |
Merge branch 'irq-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull irq updates from Thomas Gleixner:
"The irq departement provides the usual mixed bag:
Core:
- Further improvements to the irq timings code which aims to predict
the next interrupt for power state selection to achieve better
latency/power balance
- Add interrupt statistics to the core NMI handlers
- The usual small fixes and cleanups
Drivers:
- Support for Renesas RZ/A1, Annapurna Labs FIC, Meson-G12A SoC and
Amazon Gravition AMR/GIC interrupt controllers.
- Rework of the Renesas INTC controller driver
- ACPI support for Socionext SoCs
- Enhancements to the CSKY interrupt controller
- The usual small fixes and cleanups"
* 'irq-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (39 commits)
irq/irqdomain: Fix comment typo
genirq: Update irq stats from NMI handlers
irqchip/gic-pm: Remove PM_CLK dependency
irqchip/al-fic: Introduce Amazon's Annapurna Labs Fabric Interrupt Controller Driver
dt-bindings: interrupt-controller: Add Amazon's Annapurna Labs FIC
softirq: Use __this_cpu_write() in takeover_tasklets()
irqchip/mbigen: Stop printing kernel addresses
irqchip/gic: Add dependency for ARM_GIC_MAX_NR
genirq/affinity: Remove unused argument from [__]irq_build_affinity_masks()
genirq/timings: Add selftest for next event computation
genirq/timings: Add selftest for irqs circular buffer
genirq/timings: Add selftest for circular array
genirq/timings: Encapsulate storing function
genirq/timings: Encapsulate timings push
genirq/timings: Optimize the period detection speed
genirq/timings: Fix timings buffer inspection
genirq/timings: Fix next event index function
irqchip/qcom: Use struct_size() in devm_kzalloc()
irqchip/irq-csky-mpintc: Remove unnecessary loop in interrupt handler
dt-bindings: interrupt-controller: Update csky mpintc
...
32 files changed, 1528 insertions, 192 deletions
diff --git a/Documentation/devicetree/bindings/interrupt-controller/amazon,al-fic.txt b/Documentation/devicetree/bindings/interrupt-controller/amazon,al-fic.txt new file mode 100644 index 000000000000..4e82fd575cec --- /dev/null +++ b/Documentation/devicetree/bindings/interrupt-controller/amazon,al-fic.txt @@ -0,0 +1,29 @@ +Amazon's Annapurna Labs Fabric Interrupt Controller + +Required properties: + +- compatible: should be "amazon,al-fic" +- reg: physical base address and size of the registers +- interrupt-controller: identifies the node as an interrupt controller +- #interrupt-cells: must be 2. + First cell defines the index of the interrupt within the controller. + Second cell is used to specify the trigger type and must be one of the + following: + - bits[3:0] trigger type and level flags + 1 = low-to-high edge triggered + 4 = active high level-sensitive +- interrupt-parent: specifies the parent interrupt controller. +- interrupts: describes which input line in the interrupt parent, this + fic's output is connected to. This field property depends on the parent's + binding + +Example: + +amazon_fic: interrupt-controller@0xfd8a8500 { + compatible = "amazon,al-fic"; + interrupt-controller; + #interrupt-cells = <2>; + reg = <0x0 0xfd8a8500 0x0 0x1000>; + interrupt-parent = <&gic>; + interrupts = <GIC_SPI 0x0 IRQ_TYPE_LEVEL_HIGH>; +}; diff --git a/Documentation/devicetree/bindings/interrupt-controller/amlogic,meson-gpio-intc.txt b/Documentation/devicetree/bindings/interrupt-controller/amlogic,meson-gpio-intc.txt index 1502a51548bb..7d531d5fff29 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/amlogic,meson-gpio-intc.txt +++ b/Documentation/devicetree/bindings/interrupt-controller/amlogic,meson-gpio-intc.txt @@ -15,6 +15,7 @@ Required properties: "amlogic,meson-gxbb-gpio-intc" for GXBB SoCs (S905) or "amlogic,meson-gxl-gpio-intc" for GXL SoCs (S905X, S912) "amlogic,meson-axg-gpio-intc" for AXG SoCs (A113D, A113X) + "amlogic,meson-g12a-gpio-intc" for G12A SoCs (S905D2, S905X2, S905Y2) - reg : Specifies base physical address and size of the registers. - interrupt-controller : Identifies the node as an interrupt controller. - #interrupt-cells : Specifies the number of cells needed to encode an diff --git a/Documentation/devicetree/bindings/interrupt-controller/csky,mpintc.txt b/Documentation/devicetree/bindings/interrupt-controller/csky,mpintc.txt index ab921f1698fb..e13405355166 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/csky,mpintc.txt +++ b/Documentation/devicetree/bindings/interrupt-controller/csky,mpintc.txt @@ -6,11 +6,16 @@ C-SKY Multi-processors Interrupt Controller is designed for ck807/ck810/ck860 SMP soc, and it also could be used in non-SMP system. Interrupt number definition: - 0-15 : software irq, and we use 15 as our IPI_IRQ. 16-31 : private irq, and we use 16 as the co-processor timer. 31-1024: common irq for soc ip. +Interrupt triger mode: (Defined in dt-bindings/interrupt-controller/irq.h) + IRQ_TYPE_LEVEL_HIGH (default) + IRQ_TYPE_LEVEL_LOW + IRQ_TYPE_EDGE_RISING + IRQ_TYPE_EDGE_FALLING + ============================= intc node bindings definition ============================= @@ -26,15 +31,22 @@ intc node bindings definition - #interrupt-cells Usage: required Value type: <u32> - Definition: must be <1> + Definition: <2> - interrupt-controller: Usage: required -Examples: +Examples: ("interrupts = <irq_num IRQ_TYPE_XXX>") --------- +#include <dt-bindings/interrupt-controller/irq.h> intc: interrupt-controller { compatible = "csky,mpintc"; - #interrupt-cells = <1>; + #interrupt-cells = <2>; interrupt-controller; }; + + device: device-example { + ... + interrupts = <34 IRQ_TYPE_EDGE_RISING>; + interrupt-parent = <&intc>; + }; diff --git a/Documentation/devicetree/bindings/interrupt-controller/renesas,rza1-irqc.txt b/Documentation/devicetree/bindings/interrupt-controller/renesas,rza1-irqc.txt new file mode 100644 index 000000000000..727b7e4cd6e0 --- /dev/null +++ b/Documentation/devicetree/bindings/interrupt-controller/renesas,rza1-irqc.txt @@ -0,0 +1,43 @@ +DT bindings for the Renesas RZ/A1 Interrupt Controller + +The RZ/A1 Interrupt Controller is a front-end for the GIC found on Renesas +RZ/A1 and RZ/A2 SoCs: + - IRQ sense select for 8 external interrupts, 1:1-mapped to 8 GIC SPI + interrupts, + - NMI edge select. + +Required properties: + - compatible: Must be "renesas,<soctype>-irqc", and "renesas,rza1-irqc" as + fallback. + Examples with soctypes are: + - "renesas,r7s72100-irqc" (RZ/A1H) + - "renesas,r7s9210-irqc" (RZ/A2M) + - #interrupt-cells: Must be 2 (an interrupt index and flags, as defined + in interrupts.txt in this directory) + - #address-cells: Must be zero + - interrupt-controller: Marks the device as an interrupt controller + - reg: Base address and length of the memory resource used by the interrupt + controller + - interrupt-map: Specifies the mapping from external interrupts to GIC + interrupts + - interrupt-map-mask: Must be <7 0> + +Example: + + irqc: interrupt-controller@fcfef800 { + compatible = "renesas,r7s72100-irqc", "renesas,rza1-irqc"; + #interrupt-cells = <2>; + #address-cells = <0>; + interrupt-controller; + reg = <0xfcfef800 0x6>; + interrupt-map = + <0 0 &gic GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>, + <1 0 &gic GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH>, + <2 0 &gic GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>, + <3 0 &gic GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>, + <4 0 &gic GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>, + <5 0 &gic GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>, + <6 0 &gic GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>, + <7 0 &gic GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>; + interrupt-map-mask = <7 0>; + }; diff --git a/MAINTAINERS b/MAINTAINERS index badf7e89b072..bcb37428522a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1306,6 +1306,12 @@ S: Maintained F: Documentation/devicetree/bindings/interrupt-controller/arm,vic.txt F: drivers/irqchip/irq-vic.c +AMAZON ANNAPURNA LABS FIC DRIVER +M: Talel Shenhar <talel@amazon.com> +S: Maintained +F: Documentation/devicetree/bindings/interrupt-controller/amazon,al-fic.txt +F: drivers/irqchip/irq-al-fic.c + ARM SMMU DRIVERS M: Will Deacon <will@kernel.org> R: Robin Murphy <robin.murphy@arm.com> diff --git a/drivers/acpi/irq.c b/drivers/acpi/irq.c index 89690a471360..e209081d644b 100644 --- a/drivers/acpi/irq.c +++ b/drivers/acpi/irq.c @@ -292,3 +292,29 @@ void __init acpi_set_irq_model(enum acpi_irq_model_id model, acpi_irq_model = model; acpi_gsi_domain_id = fwnode; } + +/** + * acpi_irq_create_hierarchy - Create a hierarchical IRQ domain with the default + * GSI domain as its parent. + * @flags: Irq domain flags associated with the domain + * @size: Size of the domain. + * @fwnode: Optional fwnode of the interrupt controller + * @ops: Pointer to the interrupt domain callbacks + * @host_data: Controller private data pointer + */ +struct irq_domain *acpi_irq_create_hierarchy(unsigned int flags, + unsigned int size, + struct fwnode_handle *fwnode, + const struct irq_domain_ops *ops, + void *host_data) +{ + struct irq_domain *d = irq_find_matching_fwnode(acpi_gsi_domain_id, + DOMAIN_BUS_ANY); + + if (!d) + return NULL; + + return irq_domain_create_hierarchy(d, flags, size, fwnode, ops, + host_data); +} +EXPORT_SYMBOL_GPL(acpi_irq_create_hierarchy); diff --git a/drivers/gpio/gpio-mb86s7x.c b/drivers/gpio/gpio-mb86s7x.c index 9bfff171f9fe..8f466993cd24 100644 --- a/drivers/gpio/gpio-mb86s7x.c +++ b/drivers/gpio/gpio-mb86s7x.c @@ -6,6 +6,7 @@ * Copyright (C) 2015 Linaro Ltd. */ +#include <linux/acpi.h> #include <linux/io.h> #include <linux/init.h> #include <linux/clk.h> @@ -19,6 +20,8 @@ #include <linux/spinlock.h> #include <linux/slab.h> +#include "gpiolib.h" + /* * Only first 8bits of a register correspond to each pin, * so there are 4 registers for 32 pins. @@ -135,6 +138,20 @@ static void mb86s70_gpio_set(struct gpio_chip *gc, unsigned gpio, int value) spin_unlock_irqrestore(&gchip->lock, flags); } +static int mb86s70_gpio_to_irq(struct gpio_chip *gc, unsigned int offset) +{ + int irq, index; + + for (index = 0;; index++) { + irq = platform_get_irq(to_platform_device(gc->parent), index); + if (irq <= 0) + break; + if (irq_get_irq_data(irq)->hwirq == offset) + return irq; + } + return -EINVAL; +} + static int mb86s70_gpio_probe(struct platform_device *pdev) { struct mb86s70_gpio_chip *gchip; @@ -150,13 +167,15 @@ static int mb86s70_gpio_probe(struct platform_device *pdev) if (IS_ERR(gchip->base)) return PTR_ERR(gchip->base); - gchip->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(gchip->clk)) - return PTR_ERR(gchip->clk); + if (!has_acpi_companion(&pdev->dev)) { + gchip->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(gchip->clk)) + return PTR_ERR(gchip->clk); - ret = clk_prepare_enable(gchip->clk); - if (ret) - return ret; + ret = clk_prepare_enable(gchip->clk); + if (ret) + return ret; + } spin_lock_init(&gchip->lock); @@ -172,19 +191,28 @@ static int mb86s70_gpio_probe(struct platform_device *pdev) gchip->gc.parent = &pdev->dev; gchip->gc.base = -1; + if (has_acpi_companion(&pdev->dev)) + gchip->gc.to_irq = mb86s70_gpio_to_irq; + ret = gpiochip_add_data(&gchip->gc, gchip); if (ret) { dev_err(&pdev->dev, "couldn't register gpio driver\n"); clk_disable_unprepare(gchip->clk); + return ret; } - return ret; + if (has_acpi_companion(&pdev->dev)) + acpi_gpiochip_request_interrupts(&gchip->gc); + + return 0; } static int mb86s70_gpio_remove(struct platform_device *pdev) { struct mb86s70_gpio_chip *gchip = platform_get_drvdata(pdev); + if (has_acpi_companion(&pdev->dev)) + acpi_gpiochip_free_interrupts(&gchip->gc); gpiochip_remove(&gchip->gc); clk_disable_unprepare(gchip->clk); @@ -197,10 +225,19 @@ static const struct of_device_id mb86s70_gpio_dt_ids[] = { }; MODULE_DEVICE_TABLE(of, mb86s70_gpio_dt_ids); +#ifdef CONFIG_ACPI +static const struct acpi_device_id mb86s70_gpio_acpi_ids[] = { + { "SCX0007" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(acpi, mb86s70_gpio_acpi_ids); +#endif + static struct platform_driver mb86s70_gpio_driver = { .driver = { .name = "mb86s70-gpio", .of_match_table = mb86s70_gpio_dt_ids, + .acpi_match_table = ACPI_PTR(mb86s70_gpio_acpi_ids), }, .probe = mb86s70_gpio_probe, .remove = mb86s70_gpio_remove, diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 659c5e0fb835..80e10f4e213a 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -15,10 +15,10 @@ config ARM_GIC_PM bool depends on PM select ARM_GIC - select PM_CLK config ARM_GIC_MAX_NR int + depends on ARM_GIC default 2 if ARCH_REALVIEW default 1 @@ -87,6 +87,14 @@ config ALPINE_MSI select PCI_MSI select GENERIC_IRQ_CHIP +config AL_FIC + bool "Amazon's Annapurna Labs Fabric Interrupt Controller" + depends on OF || COMPILE_TEST + select GENERIC_IRQ_CHIP + select IRQ_DOMAIN + help + Support Amazon's Annapurna Labs Fabric Interrupt Controller. + config ATMEL_AIC_IRQ bool select GENERIC_IRQ_CHIP @@ -217,13 +225,26 @@ config RDA_INTC select IRQ_DOMAIN config RENESAS_INTC_IRQPIN - bool + bool "Renesas INTC External IRQ Pin Support" if COMPILE_TEST select IRQ_DOMAIN + help + Enable support for the Renesas Interrupt Controller for external + interrupt pins, as found on SH/R-Mobile and R-Car Gen1 SoCs. config RENESAS_IRQC - bool + bool "Renesas R-Mobile APE6 and R-Car IRQC support" if COMPILE_TEST select GENERIC_IRQ_CHIP select IRQ_DOMAIN + help + Enable support for the Renesas Interrupt Controller for external + devices, as found on R-Mobile APE6, R-Car Gen2, and R-Car Gen3 SoCs. + +config RENESAS_RZA1_IRQC + bool "Renesas RZ/A1 IRQC support" if COMPILE_TEST + select IRQ_DOMAIN_HIERARCHY + help + Enable support for the Renesas RZ/A1 Interrupt Controller, to use up + to 8 external interrupts with configurable sense select. config ST_IRQCHIP bool @@ -299,8 +320,11 @@ config RENESAS_H8300H_INTC select IRQ_DOMAIN config RENESAS_H8S_INTC - bool + bool "Renesas H8S Interrupt Controller Support" if COMPILE_TEST select IRQ_DOMAIN + help + Enable support for the Renesas H8/300 Interrupt Controller, as found + on Renesas H8S SoCs. config IMX_GPCV2 bool diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 606a003a0000..8d0fcec6ab23 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_IRQCHIP) += irqchip.o +obj-$(CONFIG_AL_FIC) += irq-al-fic.o obj-$(CONFIG_ALPINE_MSI) += irq-alpine-msi.o obj-$(CONFIG_ATH79) += irq-ath79-cpu.o obj-$(CONFIG_ATH79) += irq-ath79-misc.o @@ -49,6 +50,7 @@ obj-$(CONFIG_JCORE_AIC) += irq-jcore-aic.o obj-$(CONFIG_RDA_INTC) += irq-rda-intc.o obj-$(CONFIG_RENESAS_INTC_IRQPIN) += irq-renesas-intc-irqpin.o obj-$(CONFIG_RENESAS_IRQC) += irq-renesas-irqc.o +obj-$(CONFIG_RENESAS_RZA1_IRQC) += irq-renesas-rza1.o obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o obj-$(CONFIG_ARCH_NSPIRE) += irq-zevio.o obj-$(CONFIG_ARCH_VT8500) += irq-vt8500.o diff --git a/drivers/irqchip/irq-al-fic.c b/drivers/irqchip/irq-al-fic.c new file mode 100644 index 000000000000..1a57cee3efab --- /dev/null +++ b/drivers/irqchip/irq-al-fic.c @@ -0,0 +1,278 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + */ + +#include <linux/bitfield.h> +#include <linux/irq.h> +#include <linux/irqchip.h> +#include <linux/irqchip/chained_irq.h> +#include <linux/irqdomain.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> + +/* FIC Registers */ +#define AL_FIC_CAUSE 0x00 +#define AL_FIC_MASK 0x10 +#define AL_FIC_CONTROL 0x28 + +#define CONTROL_TRIGGER_RISING BIT(3) +#define CONTROL_MASK_MSI_X BIT(5) + +#define NR_FIC_IRQS 32 + +MODULE_AUTHOR("Talel Shenhar"); +MODULE_DESCRIPTION("Amazon's Annapurna Labs Interrupt Controller Driver"); +MODULE_LICENSE("GPL v2"); + +enum al_fic_state { + AL_FIC_UNCONFIGURED = 0, + AL_FIC_CONFIGURED_LEVEL, + AL_FIC_CONFIGURED_RISING_EDGE, +}; + +struct al_fic { + void __iomem *base; + struct irq_domain *domain; + const char *name; + unsigned int parent_irq; + enum al_fic_state state; +}; + +static void al_fic_set_trigger(struct al_fic *fic, + struct irq_chip_generic *gc, + enum al_fic_state new_state) +{ + irq_flow_handler_t handler; + u32 control = readl_relaxed(fic->base + AL_FIC_CONTROL); + + if (new_state == AL_FIC_CONFIGURED_LEVEL) { + handler = handle_level_irq; + control &= ~CONTROL_TRIGGER_RISING; + } else { + handler = handle_edge_irq; + control |= CONTROL_TRIGGER_RISING; + } + gc->chip_types->handler = handler; + fic->state = new_state; + writel_relaxed(control, fic->base + AL_FIC_CONTROL); +} + +static int al_fic_irq_set_type(struct irq_data *data, unsigned int flow_type) +{ + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data); + struct al_fic *fic = gc->private; + enum al_fic_state new_state; + int ret = 0; + + irq_gc_lock(gc); + + if (((flow_type & IRQ_TYPE_SENSE_MASK) != IRQ_TYPE_LEVEL_HIGH) && + ((flow_type & IRQ_TYPE_SENSE_MASK) != IRQ_TYPE_EDGE_RISING)) { + pr_debug("fic doesn't support flow type %d\n", flow_type); + ret = -EINVAL; + goto err; + } + + new_state = (flow_type & IRQ_TYPE_LEVEL_HIGH) ? + AL_FIC_CONFIGURED_LEVEL : AL_FIC_CONFIGURED_RISING_EDGE; + + /* + * A given FIC instance can be either all level or all edge triggered. + * This is generally fixed depending on what pieces of HW it's wired up + * to. + * + * We configure it based on the sensitivity of the first source + * being setup, and reject any subsequent attempt at configuring it in a + * different way. + */ + if (fic->state == AL_FIC_UNCONFIGURED) { + al_fic_set_trigger(fic, gc, new_state); + } else if (fic->state != new_state) { + pr_debug("fic %s state already configured to %d\n", + fic->name, fic->state); + ret = -EINVAL; + goto err; + } + +err: + irq_gc_unlock(gc); + + return ret; +} + +static void al_fic_irq_handler(struct irq_desc *desc) +{ + struct al_fic *fic = irq_desc_get_handler_data(desc); + struct irq_domain *domain = fic->domain; + struct irq_chip *irqchip = irq_desc_get_chip(desc); + struct irq_chip_generic *gc = irq_get_domain_generic_chip(domain, 0); + unsigned long pending; + unsigned int irq; + u32 hwirq; + + chained_irq_enter(irqchip, desc); + + pending = readl_relaxed(fic->base + AL_FIC_CAUSE); + pending &= ~gc->mask_cache; + + for_each_set_bit(hwirq, &pending, NR_FIC_IRQS) { + irq = irq_find_mapping(domain, hwirq); + generic_handle_irq(irq); + } + + chained_irq_exit(irqchip, desc); +} + +static int al_fic_register(struct device_node *node, + struct al_fic *fic) +{ + struct irq_chip_generic *gc; + int ret; + + fic->domain = irq_domain_add_linear(node, + NR_FIC_IRQS, + &irq_generic_chip_ops, + fic); + if (!fic->domain) { + pr_err("fail to add irq domain\n"); + return -ENOMEM; + } + + ret = irq_alloc_domain_generic_chips(fic->domain, + NR_FIC_IRQS, + 1, fic->name, + handle_level_irq, + 0, 0, IRQ_GC_INIT_MASK_CACHE); + if (ret) { + pr_err("fail to allocate generic chip (%d)\n", ret); + goto err_domain_remove; + } + + gc = irq_get_domain_generic_chip(fic->domain, 0); + gc->reg_base = fic->base; + gc->chip_types->regs.mask = AL_FIC_MASK; + gc->chip_types->regs.ack = AL_FIC_CAUSE; + gc->chip_types->chip.irq_mask = irq_gc_mask_set_bit; + gc->chip_types->chip.irq_unmask = irq_gc_mask_clr_bit; + gc->chip_types->chip.irq_ack = irq_gc_ack_clr_bit; + gc->chip_types->chip.irq_set_type = al_fic_irq_set_type; + gc->chip_types->chip.flags = IRQCHIP_SKIP_SET_WAKE; + gc->private = fic; + + irq_set_chained_handler_and_data(fic->parent_irq, + al_fic_irq_handler, + fic); + return 0; + +err_domain_remove: + irq_domain_remove(fic->domain); + + return ret; +} + +/* + * al_fic_wire_init() - initialize and configure fic in wire mode + * @of_node: optional pointer to interrupt controller's device tree node. + * @base: mmio to fic register + * @name: name of the fic + * @parent_irq: interrupt of parent + * + * This API will configure the fic hardware to to work in wire mode. + * In wire mode, fic hardware is generating a wire ("wired") interrupt. + * Interrupt can be generated based on positive edge or level - configuration is + * to be determined based on connected hardware to this fic. + */ +static struct al_fic *al_fic_wire_init(struct device_node *node, + void __iomem *base, + const char *name, + unsigned int parent_irq) +{ + struct al_fic *fic; + int ret; + u32 control = CONTROL_MASK_MSI_X; + + fic = kzalloc(sizeof(*fic), GFP_KERNEL); + if (!fic) + return ERR_PTR(-ENOMEM); + + fic->base = base; + fic->parent_irq = parent_irq; + fic->name = name; + + /* mask out all interrupts */ + writel_relaxed(0xFFFFFFFF, fic->base + AL_FIC_MASK); + + /* clear any pending interrupt */ + writel_relaxed(0, fic->base + AL_FIC_CAUSE); + + writel_relaxed(control, fic->base + AL_FIC_CONTROL); + + ret = al_fic_register(node, fic); + if (ret) { + pr_err("fail to register irqchip\n"); + goto err_free; + } + + pr_debug("%s initialized successfully in Legacy mode (parent-irq=%u)\n", + fic->name, parent_irq); + + return fic; + +err_free: + kfree(fic); + return ERR_PTR(ret); +} + +static int __init al_fic_init_dt(struct device_node *node, + struct device_node *parent) +{ + int ret; + void __iomem *base; + unsigned int parent_irq; + struct al_fic *fic; + + if (!parent) { + pr_err("%s: unsupported - device require a parent\n", + node->name); + return -EINVAL; + } + + base = of_iomap(node, 0); + if (!base) { + pr_err("%s: fail to map memory\n", node->name); + return -ENOMEM; + } + + parent_irq = irq_of_parse_and_map(node, 0); + if (!parent_irq) { + pr_err("%s: fail to map irq\n", node->name); + ret = -EINVAL; + goto err_unmap; + } + + fic = al_fic_wire_init(node, + base, + node->name, + parent_irq); + if (IS_ERR(fic)) { + pr_err("%s: fail to initialize irqchip (%lu)\n", + node->name, + PTR_ERR(fic)); + ret = PTR_ERR(fic); + goto err_irq_dispose; + } + + return 0; + +err_irq_dispose: + irq_dispose_mapping(parent_irq); +err_unmap: + iounmap(base); + + return ret; +} + +IRQCHIP_DECLARE(al_fic, "amazon,al-fic", al_fic_init_dt); diff --git a/drivers/irqchip/irq-csky-mpintc.c b/drivers/irqchip/irq-csky-mpintc.c index a4c1aacba1ff..a1534edef7fa 100644 --- a/drivers/irqchip/irq-csky-mpintc.c +++ b/drivers/irqchip/irq-csky-mpintc.c @@ -32,8 +32,8 @@ static void __iomem *INTCL_base; #define INTCG_CIDSTR 0x1000 #define INTCL_PICTLR 0x0 +#define INTCL_CFGR 0x14 #define INTCL_SIGR 0x60 -#define INTCL_HPPIR 0x68 #define INTCL_RDYIR 0x6c #define INTCL_SENR 0xa0 #define INTCL_CENR 0xa4 @@ -41,21 +41,49 @@ static void __iomem *INTCL_base; static DEFINE_PER_CPU(void __iomem *, intcl_reg); +static unsigned long *__trigger; + +#define IRQ_OFFSET(irq) ((irq < COMM_IRQ_BASE) ? irq : (irq - COMM_IRQ_BASE)) + +#define TRIG_BYTE_OFFSET(i) ((((i) * 2) / 32) * 4) +#define TRIG_BIT_OFFSET(i) (((i) * 2) % 32) + +#define TRIG_VAL(trigger, irq) (trigger << TRIG_BIT_OFFSET(IRQ_OFFSET(irq))) +#define TRIG_VAL_MSK(irq) (~(3 << TRIG_BIT_OFFSET(IRQ_OFFSET(irq)))) + +#define TRIG_BASE(irq) \ + (TRIG_BYTE_OFFSET(IRQ_OFFSET(irq)) + ((irq < COMM_IRQ_BASE) ? \ + (this_cpu_read(intcl_reg) + INTCL_CFGR) : (INTCG_base + INTCG_CICFGR))) + +static DEFINE_SPINLOCK(setup_lock); +static void setup_trigger(unsigned long irq, unsigned long trigger) +{ + unsigned int tmp; + + spin_lock(&setup_lock); + + /* setup trigger */ + tmp = readl_relaxed(TRIG_BASE(irq)) & TRIG_VAL_MSK(irq); + + writel_relaxed(tmp | TRIG_VAL(trigger, irq), TRIG_BASE(irq)); + + spin_unlock(&setup_lock); +} + static void csky_mpintc_handler(struct pt_regs *regs) { void __iomem *reg_base = this_cpu_read(intcl_reg); - do { - handle_domain_irq(root_domain, - readl_relaxed(reg_base + INTCL_RDYIR), - regs); - } while (readl_relaxed(reg_base + INTCL_HPPIR) & BIT(31)); + handle_domain_irq(root_domain, + readl_relaxed(reg_base + INTCL_RDYIR), regs); } static void csky_mpintc_enable(struct irq_data *d) { void __iomem *reg_base = this_cpu_read(intcl_reg); + setup_trigger(d->hwirq, __trigger[d->hwirq]); + writel_relaxed(d->hwirq, reg_base + INTCL_SENR); } @@ -73,6 +101,28 @@ static void csky_mpintc_eoi(struct irq_data *d) writel_relaxed(d->hwirq, reg_base + INTCL_CACR); } +static int csky_mpintc_set_type(struct irq_data *d, unsigned int type) +{ + switch (type & IRQ_TYPE_SENSE_MASK) { + case IRQ_TYPE_LEVEL_HIGH: + __trigger[d->hwirq] = 0; + break; + case IRQ_TYPE_LEVEL_LOW: + __trigger[d->hwirq] = 1; + break; + case IRQ_TYPE_EDGE_RISING: + __trigger[d->hwirq] = 2; + break; + case IRQ_TYPE_EDGE_FALLING: + __trigger[d->hwirq] = 3; + break; + default: + return -EINVAL; + } + + return 0; +} + #ifdef CONFIG_SMP static int csky_irq_set_affinity(struct irq_data *d, const struct cpumask *mask_val, @@ -116,6 +166,7 @@ static struct irq_chip csky_irq_chip = { .irq_eoi = csky_mpintc_eoi, .irq_enable = csky_mpintc_enable, .irq_disable = csky_mpintc_disable, + .irq_set_type = csky_mpintc_set_type, #ifdef CONFIG_SMP .irq_set_affinity = csky_irq_set_affinity, #endif @@ -136,9 +187,26 @@ static int csky_irqdomain_map(struct irq_domain *d, unsigned int irq, return 0; } +static int csky_irq_domain_xlate_cells(struct irq_domain *d, + struct device_node *ctrlr, const u32 *intspec, + unsigned int intsize, unsigned long *out_hwirq, + unsigned int *out_type) +{ + if (WARN_ON(intsize < 1)) + return -EINVAL; + + *out_hwirq = intspec[0]; + if (intsize > 1) + *out_type = intspec[1] & IRQ_TYPE_SENSE_MASK; + else + *out_type = IRQ_TYPE_LEVEL_HIGH; + + return 0; +} + static const struct irq_domain_ops csky_irqdomain_ops = { .map = csky_irqdomain_map, - .xlate = irq_domain_xlate_onecell, |