Updates for the interrupt subsystem:
Core code: - Provide generic_handle_irq_safe() which can be invoked from any context (hard interrupt or threaded). This allows to remove ugly workarounds in drivers all over the place. - Use generic_handle_irq_safe() in the affected drivers. - The usual cleanups and improvements. Interrupt chip drivers: - Support for new interrupt chips or not yet supported variants: STM32MP14, Meson GPIO, Apple M1 PMU, Apple M1 AICv2, Qualcomm MPM - Convert the Xilinx driver to generic interrupt domains - Cleanup the irq_chip::name handling - The usual cleanups and improvements all over the place -----BEGIN PGP SIGNATURE----- iQJHBAABCgAxFiEEQp8+kY+LLUocC4bMphj1TA10mKEFAmI4VmETHHRnbHhAbGlu dXRyb25peC5kZQAKCRCmGPVMDXSYoYkQEAC8Lo4tGLVKqcDewYLrLLtUk2GrbXW0 j/L0Su6xcdyCV6h2i8oB7SHBDP6nJ9dHOqR+Rk9/XZrDV5e32cnsY4TuipZ271B7 H+6VNykO60cbhRox+hHqe4bHO3qOdPUJvZ5DBt3+TC5T4P3/Il9DUIxWrDliqAzI KewgNwGFXLmuvoTGpGoEKRbAHEXwYy6WMwDSmDhMl0CQOE6i5JM5Fkt25hQppq+T 4LZ3Peb4LoBtQJnT+VTgb3ZTlSJSVb82qR0mkldLmIGtdOD9sG8DhCYNzoB09hKz Wiz+pQWrVcfX4H3lHwhInlaWU2Li/3LQ9hBqR1Kdm9NvT1e2eiVkPJGZc6sDsrg9 tAUutG6OXJpw0x9zvO98rfUiUg1Z9Ofnb5eR9NZi8/IMLqlxQ/HTpP1RBaDPmZ+V srU16y7tX7wyqv/Le0yZlRY9y4TNHQPF+ffcu6dw4PFl4niXTNBzuVvkyCGNZl9C 5q6yu+0l/0scxoirzVEmWJOMQTY3TufObpTEY4j5e9Ji6xfM1JvL8rZkVV2DKZAw NgJrkM+lFFdMMOfDCjqm5/b38LteU3etChwSnrgsSe/W26vIZP5eljKKAcxGDTzX tdE5sq+EpjjHjFPOwjJNE6i37XucRtoGWGVL3xjbcsbLb2AVo4V7EbxVT3GBCsxL oEGTL2hDt2UksQ== =4eWv -----END PGP SIGNATURE----- Merge tag 'irq-core-2022-03-21' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip Pull interrupt updates from Thomas Gleixner: "Core code: - Provide generic_handle_irq_safe() which can be invoked from any context (hard interrupt or threaded). This allows to remove ugly workarounds in drivers all over the place. - Use generic_handle_irq_safe() in the affected drivers. - The usual cleanups and improvements. Interrupt chip drivers: - Support for new interrupt chips or not yet supported variants: STM32MP14, Meson GPIO, Apple M1 PMU, Apple M1 AICv2, Qualcomm MPM - Convert the Xilinx driver to generic interrupt domains - Cleanup the irq_chip::name handling - The usual cleanups and improvements all over the place" * tag 'irq-core-2022-03-21' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (52 commits) irqchip: Add Qualcomm MPM controller driver dt-bindings: interrupt-controller: Add Qualcomm MPM support irqchip/apple-aic: Add support for AICv2 irqchip/apple-aic: Support multiple dies irqchip/apple-aic: Dynamically compute register offsets irqchip/apple-aic: Switch to irq_domain_create_tree and sparse hwirqs irqchip/apple-aic: Add Fast IPI support dt-bindings: interrupt-controller: apple,aic2: New binding for AICv2 PCI: apple: Change MSI handling to handle 4-cell AIC fwspec form irqchip/apple-aic: Fix cpumask allocation for FIQs irqchip/meson-gpio: Add support for meson s4 SoCs irqchip/meson-gpio: add select trigger type callback irqchip/meson-gpio: support more than 8 channels gpio irq dt-bindings: interrupt-controller: New binding for Meson-S4 SoCs irqchip/xilinx: Switch to GENERIC_IRQ_MULTI_HANDLER staging: greybus: gpio: Use generic_handle_irq_safe(). net: usb: lan78xx: Use generic_handle_irq_safe(). mfd: ezx-pcap: Use generic_handle_irq_safe(). misc: hi6421-spmi-pmic: Use generic_handle_irq_safe(). irqchip/sifive-plic: Disable S-mode IRQs if running in M-mode ...
This commit is contained in:
commit
93287e28bc
@ -18,6 +18,7 @@ Required properties:
|
|||||||
"amlogic,meson-g12a-gpio-intc" for G12A SoCs (S905D2, S905X2, S905Y2)
|
"amlogic,meson-g12a-gpio-intc" for G12A SoCs (S905D2, S905X2, S905Y2)
|
||||||
"amlogic,meson-sm1-gpio-intc" for SM1 SoCs (S905D3, S905X3, S905Y3)
|
"amlogic,meson-sm1-gpio-intc" for SM1 SoCs (S905D3, S905X3, S905Y3)
|
||||||
"amlogic,meson-a1-gpio-intc" for A1 SoCs (A113L)
|
"amlogic,meson-a1-gpio-intc" for A1 SoCs (A113L)
|
||||||
|
"amlogic,meson-s4-gpio-intc" for S4 SoCs (S802X2, S905Y4, S805X2G, S905W2)
|
||||||
- reg : Specifies base physical address and size of the registers.
|
- reg : Specifies base physical address and size of the registers.
|
||||||
- interrupt-controller : Identifies the node as an interrupt controller.
|
- interrupt-controller : Identifies the node as an interrupt controller.
|
||||||
- #interrupt-cells : Specifies the number of cells needed to encode an
|
- #interrupt-cells : Specifies the number of cells needed to encode an
|
||||||
|
@ -0,0 +1,98 @@
|
|||||||
|
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||||
|
%YAML 1.2
|
||||||
|
---
|
||||||
|
$id: http://devicetree.org/schemas/interrupt-controller/apple,aic2.yaml#
|
||||||
|
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||||
|
|
||||||
|
title: Apple Interrupt Controller 2
|
||||||
|
|
||||||
|
maintainers:
|
||||||
|
- Hector Martin <marcan@marcan.st>
|
||||||
|
|
||||||
|
description: |
|
||||||
|
The Apple Interrupt Controller 2 is a simple interrupt controller present on
|
||||||
|
Apple ARM SoC platforms starting with t600x (M1 Pro and Max).
|
||||||
|
|
||||||
|
It provides the following features:
|
||||||
|
|
||||||
|
- Level-triggered hardware IRQs wired to SoC blocks
|
||||||
|
- Single mask bit per IRQ
|
||||||
|
- Automatic masking on event delivery (auto-ack)
|
||||||
|
- Software triggering (ORed with hw line)
|
||||||
|
- Automatic prioritization (single event/ack register per CPU, lower IRQs =
|
||||||
|
higher priority)
|
||||||
|
- Automatic masking on ack
|
||||||
|
- Support for multiple dies
|
||||||
|
|
||||||
|
This device also represents the FIQ interrupt sources on platforms using AIC,
|
||||||
|
which do not go through a discrete interrupt controller. It also handles
|
||||||
|
FIQ-based Fast IPIs.
|
||||||
|
|
||||||
|
properties:
|
||||||
|
compatible:
|
||||||
|
items:
|
||||||
|
- const: apple,t6000-aic
|
||||||
|
- const: apple,aic2
|
||||||
|
|
||||||
|
interrupt-controller: true
|
||||||
|
|
||||||
|
'#interrupt-cells':
|
||||||
|
const: 4
|
||||||
|
description: |
|
||||||
|
The 1st cell contains the interrupt type:
|
||||||
|
- 0: Hardware IRQ
|
||||||
|
- 1: FIQ
|
||||||
|
|
||||||
|
The 2nd cell contains the die ID.
|
||||||
|
|
||||||
|
The next cell contains the interrupt number.
|
||||||
|
- HW IRQs: interrupt number
|
||||||
|
- FIQs:
|
||||||
|
- 0: physical HV timer
|
||||||
|
- 1: virtual HV timer
|
||||||
|
- 2: physical guest timer
|
||||||
|
- 3: virtual guest timer
|
||||||
|
|
||||||
|
The last cell contains the interrupt flags. This is normally
|
||||||
|
IRQ_TYPE_LEVEL_HIGH (4).
|
||||||
|
|
||||||
|
reg:
|
||||||
|
items:
|
||||||
|
- description: Address and size of the main AIC2 registers.
|
||||||
|
- description: Address and size of the AIC2 Event register.
|
||||||
|
|
||||||
|
reg-names:
|
||||||
|
items:
|
||||||
|
- const: core
|
||||||
|
- const: event
|
||||||
|
|
||||||
|
power-domains:
|
||||||
|
maxItems: 1
|
||||||
|
|
||||||
|
required:
|
||||||
|
- compatible
|
||||||
|
- '#interrupt-cells'
|
||||||
|
- interrupt-controller
|
||||||
|
- reg
|
||||||
|
- reg-names
|
||||||
|
|
||||||
|
additionalProperties: false
|
||||||
|
|
||||||
|
allOf:
|
||||||
|
- $ref: /schemas/interrupt-controller.yaml#
|
||||||
|
|
||||||
|
examples:
|
||||||
|
- |
|
||||||
|
soc {
|
||||||
|
#address-cells = <2>;
|
||||||
|
#size-cells = <2>;
|
||||||
|
|
||||||
|
aic: interrupt-controller@28e100000 {
|
||||||
|
compatible = "apple,t6000-aic", "apple,aic2";
|
||||||
|
#interrupt-cells = <4>;
|
||||||
|
interrupt-controller;
|
||||||
|
reg = <0x2 0x8e100000 0x0 0xc000>,
|
||||||
|
<0x2 0x8e10c000 0x0 0x4>;
|
||||||
|
reg-names = "core", "event";
|
||||||
|
};
|
||||||
|
};
|
@ -0,0 +1,96 @@
|
|||||||
|
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||||
|
%YAML 1.2
|
||||||
|
---
|
||||||
|
$id: http://devicetree.org/schemas/interrupt-controller/qcom,mpm.yaml#
|
||||||
|
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||||
|
|
||||||
|
title: Qualcom MPM Interrupt Controller
|
||||||
|
|
||||||
|
maintainers:
|
||||||
|
- Shawn Guo <shawn.guo@linaro.org>
|
||||||
|
|
||||||
|
description:
|
||||||
|
Qualcomm Technologies Inc. SoCs based on the RPM architecture have a
|
||||||
|
MSM Power Manager (MPM) that is in always-on domain. In addition to managing
|
||||||
|
resources during sleep, the hardware also has an interrupt controller that
|
||||||
|
monitors the interrupts when the system is asleep, wakes up the APSS when
|
||||||
|
one of these interrupts occur and replays it to GIC interrupt controller
|
||||||
|
after GIC becomes operational.
|
||||||
|
|
||||||
|
allOf:
|
||||||
|
- $ref: /schemas/interrupt-controller.yaml#
|
||||||
|
|
||||||
|
properties:
|
||||||
|
compatible:
|
||||||
|
items:
|
||||||
|
- const: qcom,mpm
|
||||||
|
|
||||||
|
reg:
|
||||||
|
maxItems: 1
|
||||||
|
description:
|
||||||
|
Specifies the base address and size of vMPM registers in RPM MSG RAM.
|
||||||
|
|
||||||
|
interrupts:
|
||||||
|
maxItems: 1
|
||||||
|
description:
|
||||||
|
Specify the IRQ used by RPM to wakeup APSS.
|
||||||
|
|
||||||
|
mboxes:
|
||||||
|
maxItems: 1
|
||||||
|
description:
|
||||||
|
Specify the mailbox used to notify RPM for writing vMPM registers.
|
||||||
|
|
||||||
|
interrupt-controller: true
|
||||||
|
|
||||||
|
'#interrupt-cells':
|
||||||
|
const: 2
|
||||||
|
description:
|
||||||
|
The first cell is the MPM pin number for the interrupt, and the second
|
||||||
|
is the trigger type.
|
||||||
|
|
||||||
|
qcom,mpm-pin-count:
|
||||||
|
description:
|
||||||
|
Specify the total MPM pin count that a SoC supports.
|
||||||
|
$ref: /schemas/types.yaml#/definitions/uint32
|
||||||
|
|
||||||
|
qcom,mpm-pin-map:
|
||||||
|
description:
|
||||||
|
A set of MPM pin numbers and the corresponding GIC SPIs.
|
||||||
|
$ref: /schemas/types.yaml#/definitions/uint32-matrix
|
||||||
|
items:
|
||||||
|
items:
|
||||||
|
- description: MPM pin number
|
||||||
|
- description: GIC SPI number for the MPM pin
|
||||||
|
|
||||||
|
required:
|
||||||
|
- compatible
|
||||||
|
- reg
|
||||||
|
- interrupts
|
||||||
|
- mboxes
|
||||||
|
- interrupt-controller
|
||||||
|
- '#interrupt-cells'
|
||||||
|
- qcom,mpm-pin-count
|
||||||
|
- qcom,mpm-pin-map
|
||||||
|
|
||||||
|
additionalProperties: false
|
||||||
|
|
||||||
|
examples:
|
||||||
|
- |
|
||||||
|
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||||
|
mpm: interrupt-controller@45f01b8 {
|
||||||
|
compatible = "qcom,mpm";
|
||||||
|
interrupts = <GIC_SPI 197 IRQ_TYPE_EDGE_RISING>;
|
||||||
|
reg = <0x45f01b8 0x1000>;
|
||||||
|
mboxes = <&apcs_glb 1>;
|
||||||
|
interrupt-controller;
|
||||||
|
#interrupt-cells = <2>;
|
||||||
|
interrupt-parent = <&intc>;
|
||||||
|
qcom,mpm-pin-count = <96>;
|
||||||
|
qcom,mpm-pin-map = <2 275>,
|
||||||
|
<5 296>,
|
||||||
|
<12 422>,
|
||||||
|
<24 79>,
|
||||||
|
<86 183>,
|
||||||
|
<90 260>,
|
||||||
|
<91 260>;
|
||||||
|
};
|
@ -20,6 +20,7 @@ properties:
|
|||||||
- items:
|
- items:
|
||||||
- enum:
|
- enum:
|
||||||
- st,stm32mp1-exti
|
- st,stm32mp1-exti
|
||||||
|
- st,stm32mp13-exti
|
||||||
- const: syscon
|
- const: syscon
|
||||||
|
|
||||||
"#interrupt-cells":
|
"#interrupt-cells":
|
||||||
|
@ -1769,7 +1769,7 @@ T: git https://github.com/AsahiLinux/linux.git
|
|||||||
F: Documentation/devicetree/bindings/arm/apple.yaml
|
F: Documentation/devicetree/bindings/arm/apple.yaml
|
||||||
F: Documentation/devicetree/bindings/arm/apple/*
|
F: Documentation/devicetree/bindings/arm/apple/*
|
||||||
F: Documentation/devicetree/bindings/i2c/apple,i2c.yaml
|
F: Documentation/devicetree/bindings/i2c/apple,i2c.yaml
|
||||||
F: Documentation/devicetree/bindings/interrupt-controller/apple,aic.yaml
|
F: Documentation/devicetree/bindings/interrupt-controller/apple,*
|
||||||
F: Documentation/devicetree/bindings/mailbox/apple,mailbox.yaml
|
F: Documentation/devicetree/bindings/mailbox/apple,mailbox.yaml
|
||||||
F: Documentation/devicetree/bindings/pci/apple,pcie.yaml
|
F: Documentation/devicetree/bindings/pci/apple,pcie.yaml
|
||||||
F: Documentation/devicetree/bindings/pinctrl/apple,pinctrl.yaml
|
F: Documentation/devicetree/bindings/pinctrl/apple,pinctrl.yaml
|
||||||
|
@ -45,6 +45,8 @@ config MICROBLAZE
|
|||||||
select SET_FS
|
select SET_FS
|
||||||
select ZONE_DMA
|
select ZONE_DMA
|
||||||
select TRACE_IRQFLAGS_SUPPORT
|
select TRACE_IRQFLAGS_SUPPORT
|
||||||
|
select GENERIC_IRQ_MULTI_HANDLER
|
||||||
|
select HANDLE_DOMAIN_IRQ
|
||||||
|
|
||||||
# Endianness selection
|
# Endianness selection
|
||||||
choice
|
choice
|
||||||
|
@ -11,7 +11,4 @@
|
|||||||
struct pt_regs;
|
struct pt_regs;
|
||||||
extern void do_IRQ(struct pt_regs *regs);
|
extern void do_IRQ(struct pt_regs *regs);
|
||||||
|
|
||||||
/* should be defined in each interrupt controller driver */
|
|
||||||
extern unsigned int xintc_get_irq(void);
|
|
||||||
|
|
||||||
#endif /* _ASM_MICROBLAZE_IRQ_H */
|
#endif /* _ASM_MICROBLAZE_IRQ_H */
|
||||||
|
@ -20,27 +20,13 @@
|
|||||||
#include <linux/irqchip.h>
|
#include <linux/irqchip.h>
|
||||||
#include <linux/of_irq.h>
|
#include <linux/of_irq.h>
|
||||||
|
|
||||||
static u32 concurrent_irq;
|
|
||||||
|
|
||||||
void __irq_entry do_IRQ(struct pt_regs *regs)
|
void __irq_entry do_IRQ(struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
unsigned int irq;
|
|
||||||
struct pt_regs *old_regs = set_irq_regs(regs);
|
struct pt_regs *old_regs = set_irq_regs(regs);
|
||||||
trace_hardirqs_off();
|
trace_hardirqs_off();
|
||||||
|
|
||||||
irq_enter();
|
irq_enter();
|
||||||
irq = xintc_get_irq();
|
handle_arch_irq(regs);
|
||||||
next_irq:
|
|
||||||
BUG_ON(!irq);
|
|
||||||
generic_handle_irq(irq);
|
|
||||||
|
|
||||||
irq = xintc_get_irq();
|
|
||||||
if (irq != -1U) {
|
|
||||||
pr_debug("next irq: %d\n", irq);
|
|
||||||
++concurrent_irq;
|
|
||||||
goto next_irq;
|
|
||||||
}
|
|
||||||
|
|
||||||
irq_exit();
|
irq_exit();
|
||||||
set_irq_regs(old_regs);
|
set_irq_regs(old_regs);
|
||||||
trace_hardirqs_on();
|
trace_hardirqs_on();
|
||||||
|
@ -239,7 +239,6 @@ mediatek_gpio_bank_probe(struct device *dev, int bank)
|
|||||||
|
|
||||||
rg->chip.offset = bank * MTK_BANK_WIDTH;
|
rg->chip.offset = bank * MTK_BANK_WIDTH;
|
||||||
rg->irq_chip.name = dev_name(dev);
|
rg->irq_chip.name = dev_name(dev);
|
||||||
rg->irq_chip.parent_device = dev;
|
|
||||||
rg->irq_chip.irq_unmask = mediatek_gpio_irq_unmask;
|
rg->irq_chip.irq_unmask = mediatek_gpio_irq_unmask;
|
||||||
rg->irq_chip.irq_mask = mediatek_gpio_irq_mask;
|
rg->irq_chip.irq_mask = mediatek_gpio_irq_mask;
|
||||||
rg->irq_chip.irq_mask_ack = mediatek_gpio_irq_mask;
|
rg->irq_chip.irq_mask_ack = mediatek_gpio_irq_mask;
|
||||||
|
@ -986,7 +986,8 @@ static void omap_gpio_mod_init(struct gpio_bank *bank)
|
|||||||
writel_relaxed(0, base + bank->regs->ctrl);
|
writel_relaxed(0, base + bank->regs->ctrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int omap_gpio_chip_init(struct gpio_bank *bank, struct irq_chip *irqc)
|
static int omap_gpio_chip_init(struct gpio_bank *bank, struct irq_chip *irqc,
|
||||||
|
struct device *pm_dev)
|
||||||
{
|
{
|
||||||
struct gpio_irq_chip *irq;
|
struct gpio_irq_chip *irq;
|
||||||
static int gpio;
|
static int gpio;
|
||||||
@ -1052,6 +1053,7 @@ static int omap_gpio_chip_init(struct gpio_bank *bank, struct irq_chip *irqc)
|
|||||||
if (ret)
|
if (ret)
|
||||||
return dev_err_probe(bank->chip.parent, ret, "Could not register gpio chip\n");
|
return dev_err_probe(bank->chip.parent, ret, "Could not register gpio chip\n");
|
||||||
|
|
||||||
|
irq_domain_set_pm_device(bank->chip.irq.domain, pm_dev);
|
||||||
ret = devm_request_irq(bank->chip.parent, bank->irq,
|
ret = devm_request_irq(bank->chip.parent, bank->irq,
|
||||||
omap_gpio_irq_handler,
|
omap_gpio_irq_handler,
|
||||||
0, dev_name(bank->chip.parent), bank);
|
0, dev_name(bank->chip.parent), bank);
|
||||||
@ -1402,7 +1404,6 @@ static int omap_gpio_probe(struct platform_device *pdev)
|
|||||||
irqc->irq_bus_sync_unlock = gpio_irq_bus_sync_unlock,
|
irqc->irq_bus_sync_unlock = gpio_irq_bus_sync_unlock,
|
||||||
irqc->name = dev_name(&pdev->dev);
|
irqc->name = dev_name(&pdev->dev);
|
||||||
irqc->flags = IRQCHIP_MASK_ON_SUSPEND;
|
irqc->flags = IRQCHIP_MASK_ON_SUSPEND;
|
||||||
irqc->parent_device = dev;
|
|
||||||
|
|
||||||
bank->irq = platform_get_irq(pdev, 0);
|
bank->irq = platform_get_irq(pdev, 0);
|
||||||
if (bank->irq <= 0) {
|
if (bank->irq <= 0) {
|
||||||
@ -1466,7 +1467,7 @@ static int omap_gpio_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
omap_gpio_mod_init(bank);
|
omap_gpio_mod_init(bank);
|
||||||
|
|
||||||
ret = omap_gpio_chip_init(bank, irqc);
|
ret = omap_gpio_chip_init(bank, irqc, dev);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
pm_runtime_put_sync(dev);
|
pm_runtime_put_sync(dev);
|
||||||
pm_runtime_disable(dev);
|
pm_runtime_disable(dev);
|
||||||
|
@ -530,7 +530,6 @@ static int gpio_rcar_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
irq_chip = &p->irq_chip;
|
irq_chip = &p->irq_chip;
|
||||||
irq_chip->name = "gpio-rcar";
|
irq_chip->name = "gpio-rcar";
|
||||||
irq_chip->parent_device = dev;
|
|
||||||
irq_chip->irq_mask = gpio_rcar_irq_disable;
|
irq_chip->irq_mask = gpio_rcar_irq_disable;
|
||||||
irq_chip->irq_unmask = gpio_rcar_irq_enable;
|
irq_chip->irq_unmask = gpio_rcar_irq_enable;
|
||||||
irq_chip->irq_set_type = gpio_rcar_irq_set_type;
|
irq_chip->irq_set_type = gpio_rcar_irq_set_type;
|
||||||
@ -552,6 +551,7 @@ static int gpio_rcar_probe(struct platform_device *pdev)
|
|||||||
goto err0;
|
goto err0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
irq_domain_set_pm_device(gpio_chip->irq.domain, dev);
|
||||||
ret = devm_request_irq(dev, p->irq_parent, gpio_rcar_irq_handler,
|
ret = devm_request_irq(dev, p->irq_parent, gpio_rcar_irq_handler,
|
||||||
IRQF_SHARED, name, p);
|
IRQF_SHARED, name, p);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
@ -281,7 +281,6 @@ static int tqmx86_gpio_probe(struct platform_device *pdev)
|
|||||||
u8 irq_status;
|
u8 irq_status;
|
||||||
|
|
||||||
irq_chip->name = chip->label;
|
irq_chip->name = chip->label;
|
||||||
irq_chip->parent_device = &pdev->dev;
|
|
||||||
irq_chip->irq_mask = tqmx86_gpio_irq_mask;
|
irq_chip->irq_mask = tqmx86_gpio_irq_mask;
|
||||||
irq_chip->irq_unmask = tqmx86_gpio_irq_unmask;
|
irq_chip->irq_unmask = tqmx86_gpio_irq_unmask;
|
||||||
irq_chip->irq_set_type = tqmx86_gpio_irq_set_type;
|
irq_chip->irq_set_type = tqmx86_gpio_irq_set_type;
|
||||||
@ -316,6 +315,8 @@ static int tqmx86_gpio_probe(struct platform_device *pdev)
|
|||||||
goto out_pm_dis;
|
goto out_pm_dis;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
irq_domain_set_pm_device(girq->domain, dev);
|
||||||
|
|
||||||
dev_info(dev, "GPIO functionality initialized with %d pins\n",
|
dev_info(dev, "GPIO functionality initialized with %d pins\n",
|
||||||
chip->ngpio);
|
chip->ngpio);
|
||||||
|
|
||||||
|
@ -430,6 +430,14 @@ config QCOM_PDC
|
|||||||
Power Domain Controller driver to manage and configure wakeup
|
Power Domain Controller driver to manage and configure wakeup
|
||||||
IRQs for Qualcomm Technologies Inc (QTI) mobile chips.
|
IRQs for Qualcomm Technologies Inc (QTI) mobile chips.
|
||||||
|
|
||||||
|
config QCOM_MPM
|
||||||
|
tristate "QCOM MPM"
|
||||||
|
depends on ARCH_QCOM
|
||||||
|
select IRQ_DOMAIN_HIERARCHY
|
||||||
|
help
|
||||||
|
MSM Power Manager driver to manage and configure wakeup
|
||||||
|
IRQs for Qualcomm Technologies Inc (QTI) mobile chips.
|
||||||
|
|
||||||
config CSKY_MPINTC
|
config CSKY_MPINTC
|
||||||
bool
|
bool
|
||||||
depends on CSKY
|
depends on CSKY
|
||||||
|
@ -94,6 +94,7 @@ obj-$(CONFIG_MESON_IRQ_GPIO) += irq-meson-gpio.o
|
|||||||
obj-$(CONFIG_GOLDFISH_PIC) += irq-goldfish-pic.o
|
obj-$(CONFIG_GOLDFISH_PIC) += irq-goldfish-pic.o
|
||||||
obj-$(CONFIG_NDS32) += irq-ativic32.o
|
obj-$(CONFIG_NDS32) += irq-ativic32.o
|
||||||
obj-$(CONFIG_QCOM_PDC) += qcom-pdc.o
|
obj-$(CONFIG_QCOM_PDC) += qcom-pdc.o
|
||||||
|
obj-$(CONFIG_QCOM_MPM) += irq-qcom-mpm.o
|
||||||
obj-$(CONFIG_CSKY_MPINTC) += irq-csky-mpintc.o
|
obj-$(CONFIG_CSKY_MPINTC) += irq-csky-mpintc.o
|
||||||
obj-$(CONFIG_CSKY_APB_INTC) += irq-csky-apb-intc.o
|
obj-$(CONFIG_CSKY_APB_INTC) += irq-csky-apb-intc.o
|
||||||
obj-$(CONFIG_RISCV_INTC) += irq-riscv-intc.o
|
obj-$(CONFIG_RISCV_INTC) += irq-riscv-intc.o
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
* - Default "this CPU" register view and explicit per-CPU views
|
* - Default "this CPU" register view and explicit per-CPU views
|
||||||
*
|
*
|
||||||
* In addition, this driver also handles FIQs, as these are routed to the same
|
* In addition, this driver also handles FIQs, as these are routed to the same
|
||||||
* IRQ vector. These are used for Fast IPIs (TODO), the ARMv8 timer IRQs, and
|
* IRQ vector. These are used for Fast IPIs, the ARMv8 timer IRQs, and
|
||||||
* performance counters (TODO).
|
* performance counters (TODO).
|
||||||
*
|
*
|
||||||
* Implementation notes:
|
* Implementation notes:
|
||||||
@ -52,10 +52,12 @@
|
|||||||
#include <linux/irqchip.h>
|
#include <linux/irqchip.h>
|
||||||
#include <linux/irqchip/arm-vgic-info.h>
|
#include <linux/irqchip/arm-vgic-info.h>
|
||||||
#include <linux/irqdomain.h>
|
#include <linux/irqdomain.h>
|
||||||
|
#include <linux/jump_label.h>
|
||||||
#include <linux/limits.h>
|
#include <linux/limits.h>
|
||||||
#include <linux/of_address.h>
|
#include <linux/of_address.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <asm/apple_m1_pmu.h>
|
#include <asm/apple_m1_pmu.h>
|
||||||
|
#include <asm/cputype.h>
|
||||||
#include <asm/exception.h>
|
#include <asm/exception.h>
|
||||||
#include <asm/sysreg.h>
|
#include <asm/sysreg.h>
|
||||||
#include <asm/virt.h>
|
#include <asm/virt.h>
|
||||||
@ -63,20 +65,22 @@
|
|||||||
#include <dt-bindings/interrupt-controller/apple-aic.h>
|
#include <dt-bindings/interrupt-controller/apple-aic.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* AIC registers (MMIO)
|
* AIC v1 registers (MMIO)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define AIC_INFO 0x0004
|
#define AIC_INFO 0x0004
|
||||||
#define AIC_INFO_NR_HW GENMASK(15, 0)
|
#define AIC_INFO_NR_IRQ GENMASK(15, 0)
|
||||||
|
|
||||||
#define AIC_CONFIG 0x0010
|
#define AIC_CONFIG 0x0010
|
||||||
|
|
||||||
#define AIC_WHOAMI 0x2000
|
#define AIC_WHOAMI 0x2000
|
||||||
#define AIC_EVENT 0x2004
|
#define AIC_EVENT 0x2004
|
||||||
#define AIC_EVENT_TYPE GENMASK(31, 16)
|
#define AIC_EVENT_DIE GENMASK(31, 24)
|
||||||
|
#define AIC_EVENT_TYPE GENMASK(23, 16)
|
||||||
#define AIC_EVENT_NUM GENMASK(15, 0)
|
#define AIC_EVENT_NUM GENMASK(15, 0)
|
||||||
|
|
||||||
#define AIC_EVENT_TYPE_HW 1
|
#define AIC_EVENT_TYPE_FIQ 0 /* Software use */
|
||||||
|
#define AIC_EVENT_TYPE_IRQ 1
|
||||||
#define AIC_EVENT_TYPE_IPI 4
|
#define AIC_EVENT_TYPE_IPI 4
|
||||||
#define AIC_EVENT_IPI_OTHER 1
|
#define AIC_EVENT_IPI_OTHER 1
|
||||||
#define AIC_EVENT_IPI_SELF 2
|
#define AIC_EVENT_IPI_SELF 2
|
||||||
@ -92,22 +96,71 @@
|
|||||||
#define AIC_IPI_SELF BIT(31)
|
#define AIC_IPI_SELF BIT(31)
|
||||||
|
|
||||||
#define AIC_TARGET_CPU 0x3000
|
#define AIC_TARGET_CPU 0x3000
|
||||||
#define AIC_SW_SET 0x4000
|
|
||||||
#define AIC_SW_CLR 0x4080
|
|
||||||
#define AIC_MASK_SET 0x4100
|
|
||||||
#define AIC_MASK_CLR 0x4180
|
|
||||||
|
|
||||||
#define AIC_CPU_IPI_SET(cpu) (0x5008 + ((cpu) << 7))
|
#define AIC_CPU_IPI_SET(cpu) (0x5008 + ((cpu) << 7))
|
||||||
#define AIC_CPU_IPI_CLR(cpu) (0x500c + ((cpu) << 7))
|
#define AIC_CPU_IPI_CLR(cpu) (0x500c + ((cpu) << 7))
|
||||||
#define AIC_CPU_IPI_MASK_SET(cpu) (0x5024 + ((cpu) << 7))
|
#define AIC_CPU_IPI_MASK_SET(cpu) (0x5024 + ((cpu) << 7))
|
||||||
#define AIC_CPU_IPI_MASK_CLR(cpu) (0x5028 + ((cpu) << 7))
|
#define AIC_CPU_IPI_MASK_CLR(cpu) (0x5028 + ((cpu) << 7))
|
||||||
|
|
||||||
|
#define AIC_MAX_IRQ 0x400
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AIC v2 registers (MMIO)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define AIC2_VERSION 0x0000
|
||||||
|
#define AIC2_VERSION_VER GENMASK(7, 0)
|
||||||
|
|
||||||
|
#define AIC2_INFO1 0x0004
|
||||||
|
#define AIC2_INFO1_NR_IRQ GENMASK(15, 0)
|
||||||
|
#define AIC2_INFO1_LAST_DIE GENMASK(27, 24)
|
||||||
|
|
||||||
|
#define AIC2_INFO2 0x0008
|
||||||
|
|
||||||
|
#define AIC2_INFO3 0x000c
|
||||||
|
#define AIC2_INFO3_MAX_IRQ GENMASK(15, 0)
|
||||||
|
#define AIC2_INFO3_MAX_DIE GENMASK(27, 24)
|
||||||
|
|
||||||
|
#define AIC2_RESET 0x0010
|
||||||
|
#define AIC2_RESET_RESET BIT(0)
|
||||||
|
|
||||||
|
#define AIC2_CONFIG 0x0014
|
||||||
|
#define AIC2_CONFIG_ENABLE BIT(0)
|
||||||
|
#define AIC2_CONFIG_PREFER_PCPU BIT(28)
|
||||||
|
|
||||||
|
#define AIC2_TIMEOUT 0x0028
|
||||||
|
#define AIC2_CLUSTER_PRIO 0x0030
|
||||||
|
#define AIC2_DELAY_GROUPS 0x0100
|
||||||
|
|
||||||
|
#define AIC2_IRQ_CFG 0x2000
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AIC2 registers are laid out like this, starting at AIC2_IRQ_CFG:
|
||||||
|
*
|
||||||
|
* Repeat for each die:
|
||||||
|
* IRQ_CFG: u32 * MAX_IRQS
|
||||||
|
* SW_SET: u32 * (MAX_IRQS / 32)
|
||||||
|
* SW_CLR: u32 * (MAX_IRQS / 32)
|
||||||
|
* MASK_SET: u32 * (MAX_IRQS / 32)
|
||||||
|
* MASK_CLR: u32 * (MAX_IRQS / 32)
|
||||||
|
* HW_STATE: u32 * (MAX_IRQS / 32)
|
||||||
|
*
|
||||||
|
* This is followed by a set of event registers, each 16K page aligned.
|
||||||
|
* The first one is the AP event register we will use. Unfortunately,
|
||||||
|
* the actual implemented die count is not specified anywhere in the
|
||||||
|
* capability registers, so we have to explicitly specify the event
|
||||||
|
* register as a second reg entry in the device tree to remain
|
||||||
|
* forward-compatible.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define AIC2_IRQ_CFG_TARGET GENMASK(3, 0)
|
||||||
|
#define AIC2_IRQ_CFG_DELAY_IDX GENMASK(7, 5)
|
||||||
|
|
||||||
#define MASK_REG(x) (4 * ((x) >> 5))
|
#define MASK_REG(x) (4 * ((x) >> 5))
|
||||||
#define MASK_BIT(x) BIT((x) & GENMASK(4, 0))
|
#define MASK_BIT(x) BIT((x) & GENMASK(4, 0))
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* IMP-DEF sysregs that control FIQ sources
|
* IMP-DEF sysregs that control FIQ sources
|
||||||
* Note: sysreg-based IPIs are not supported yet.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* IPI request registers */
|
/* IPI request registers */
|
||||||
@ -146,6 +199,17 @@
|
|||||||
#define SYS_IMP_APL_UPMSR_EL1 sys_reg(3, 7, 15, 6, 4)
|
#define SYS_IMP_APL_UPMSR_EL1 sys_reg(3, 7, 15, 6, 4)
|
||||||
#define UPMSR_IACT BIT(0)
|
#define UPMSR_IACT BIT(0)
|
||||||
|
|
||||||
|
/* MPIDR fields */
|
||||||
|
#define MPIDR_CPU(x) MPIDR_AFFINITY_LEVEL(x, 0)
|
||||||
|
#define MPIDR_CLUSTER(x) MPIDR_AFFINITY_LEVEL(x, 1)
|
||||||
|
|
||||||
|
#define AIC_IRQ_HWIRQ(die, irq) (FIELD_PREP(AIC_EVENT_DIE, die) | \
|
||||||
|
FIELD_PREP(AIC_EVENT_TYPE, AIC_EVENT_TYPE_IRQ) | \
|
||||||
|
FIELD_PREP(AIC_EVENT_NUM, irq))
|
||||||
|
#define AIC_FIQ_HWIRQ(x) (FIELD_PREP(AIC_EVENT_TYPE, AIC_EVENT_TYPE_FIQ) | \
|
||||||
|
FIELD_PREP(AIC_EVENT_NUM, x))
|
||||||
|
#define AIC_HWIRQ_IRQ(x) FIELD_GET(AIC_EVENT_NUM, x)
|
||||||
|
#define AIC_HWIRQ_DIE(x) FIELD_GET(AIC_EVENT_DIE, x)
|
||||||
#define AIC_NR_FIQ 6
|
#define AIC_NR_FIQ 6
|
||||||
#define AIC_NR_SWIPI 32
|
#define AIC_NR_SWIPI 32
|
||||||
|
|
||||||
@ -164,14 +228,81 @@
|
|||||||
#define AIC_TMR_EL02_PHYS AIC_TMR_GUEST_PHYS
|
#define AIC_TMR_EL02_PHYS AIC_TMR_GUEST_PHYS
|
||||||
#define AIC_TMR_EL02_VIRT AIC_TMR_GUEST_VIRT
|
#define AIC_TMR_EL02_VIRT AIC_TMR_GUEST_VIRT
|
||||||
|
|
||||||
|
DEFINE_STATIC_KEY_TRUE(use_fast_ipi);
|
||||||
|
|
||||||
|
struct aic_info {
|
||||||
|
int version;
|
||||||
|
|
||||||
|
/* Register offsets */
|
||||||
|
u32 event;
|
||||||
|
u32 target_cpu;
|
||||||
|
u32 irq_cfg;
|
||||||
|
u32 sw_set;
|
||||||
|
u32 sw_clr;
|
||||||
|
u32 mask_set;
|
||||||
|
u32 mask_clr;
|
||||||
|
|
||||||
|
u32 die_stride;
|
||||||
|
|
||||||
|
/* Features */
|
||||||
|
bool fast_ipi;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct aic_info aic1_info = {
|
||||||
|
.version = 1,
|
||||||
|
|
||||||
|
.event = AIC_EVENT,
|
||||||
|
.target_cpu = AIC_TARGET_CPU,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct aic_info aic1_fipi_info = {
|
||||||
|
.version = 1,
|
||||||
|
|
||||||
|
.event = AIC_EVENT,
|
||||||
|
.target_cpu = AIC_TARGET_CPU,
|
||||||
|
|
||||||
|
.fast_ipi = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct aic_info aic2_info = {
|
||||||
|
.version = 2,
|
||||||
|
|
||||||
|
.irq_cfg = AIC2_IRQ_CFG,
|
||||||
|
|
||||||
|
.fast_ipi = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct of_device_id aic_info_match[] = {
|
||||||
|
{
|
||||||
|
.compatible = "apple,t8103-aic",
|
||||||
|
.data = &aic1_fipi_info,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.compatible = "apple,aic",
|
||||||
|
.data = &aic1_info,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.compatible = "apple,aic2",
|
||||||
|
.data = &aic2_info,
|
||||||
|
},
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
struct aic_irq_chip {
|
struct aic_irq_chip {
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
|
void __iomem *event;
|
||||||
struct irq_domain *hw_domain;
|
struct irq_domain *hw_domain;
|
||||||
struct irq_domain *ipi_domain;
|
struct irq_domain *ipi_domain;
|
||||||
struct {
|
struct {
|
||||||
cpumask_t aff;
|
cpumask_t aff;
|
||||||
} *fiq_aff[AIC_NR_FIQ];
|
} *fiq_aff[AIC_NR_FIQ];
|
||||||
int nr_hw;
|
|
||||||
|
int nr_irq;
|
||||||
|
int max_irq;
|
||||||
|
int nr_die;
|
||||||
|
int max_die;
|
||||||
|
|
||||||
|
struct aic_info info;
|
||||||
};
|
};
|
||||||
|
|
||||||
static DEFINE_PER_CPU(uint32_t, aic_fiq_unmasked);
|
static DEFINE_PER_CPU(uint32_t, aic_fiq_unmasked);
|
||||||
@ -199,18 +330,24 @@ static void aic_ic_write(struct aic_irq_chip *ic, u32 reg, u32 val)
|
|||||||
|
|
||||||
static void aic_irq_mask(struct irq_data *d)
|
static void aic_irq_mask(struct irq_data *d)
|
||||||
{
|
{
|
||||||
|
irq_hw_number_t hwirq = irqd_to_hwirq(d);
|
||||||
struct aic_irq_chip *ic = irq_data_get_irq_chip_data(d);
|
struct aic_irq_chip *ic = irq_data_get_irq_chip_data(d);
|
||||||
|
|
||||||
aic_ic_write(ic, AIC_MASK_SET + MASK_REG(irqd_to_hwirq(d)),
|
u32 off = AIC_HWIRQ_DIE(hwirq) * ic->info.die_stride;
|
||||||
MASK_BIT(irqd_to_hwirq(d)));
|
u32 irq = AIC_HWIRQ_IRQ(hwirq);
|
||||||
|
|
||||||
|
aic_ic_write(ic, ic->info.mask_set + off + MASK_REG(irq), MASK_BIT(irq));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void aic_irq_unmask(struct irq_data *d)
|
static void aic_irq_unmask(struct irq_data *d)
|
||||||
{
|
{
|
||||||
|
irq_hw_number_t hwirq = irqd_to_hwirq(d);
|
||||||
struct aic_irq_chip *ic = irq_data_get_irq_chip_data(d);
|
struct aic_irq_chip *ic = irq_data_get_irq_chip_data(d);
|
||||||
|
|
||||||
aic_ic_write(ic, AIC_MASK_CLR + MASK_REG(d->hwirq),
|
u32 off = AIC_HWIRQ_DIE(hwirq) * ic->info.die_stride;
|
||||||
MASK_BIT(irqd_to_hwirq(d)));
|
u32 irq = AIC_HWIRQ_IRQ(hwirq);
|
||||||
|
|
||||||
|
aic_ic_write(ic, ic->info.mask_clr + off + MASK_REG(irq), MASK_BIT(irq));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void aic_irq_eoi(struct irq_data *d)
|
static void aic_irq_eoi(struct irq_data *d)
|
||||||
@ -233,12 +370,12 @@ static void __exception_irq_entry aic_handle_irq(struct pt_regs *regs)
|
|||||||
* We cannot use a relaxed read here, as reads from DMA buffers
|
* We cannot use a relaxed read here, as reads from DMA buffers
|
||||||
* need to be ordered after the IRQ fires.
|
* need to be ordered after the IRQ fires.
|
||||||
*/
|
*/
|
||||||
event = readl(ic->base + AIC_EVENT);
|
event = readl(ic->event + ic->info.event);
|
||||||
type = FIELD_GET(AIC_EVENT_TYPE, event);
|
type = FIELD_GET(AIC_EVENT_TYPE, event);
|
||||||
irq = FIELD_GET(AIC_EVENT_NUM, event);
|
irq = FIELD_GET(AIC_EVENT_NUM, event);
|
||||||
|
|
||||||
if (type == AIC_EVENT_TYPE_HW)
|
if (type == AIC_EVENT_TYPE_IRQ)
|
||||||
generic_handle_domain_irq(aic_irqc->hw_domain, irq);
|
generic_handle_domain_irq(aic_irqc->hw_domain, event);
|
||||||
else if (type == AIC_EVENT_TYPE_IPI && irq == 1)
|
else if (type == AIC_EVENT_TYPE_IPI && irq == 1)
|
||||||
aic_handle_ipi(regs);
|
aic_handle_ipi(regs);
|
||||||
else if (event != 0)
|
else if (event != 0)
|
||||||
@ -265,12 +402,14 @@ static int aic_irq_set_affinity(struct irq_data *d,
|
|||||||
struct aic_irq_chip *ic = irq_data_get_irq_chip_data(d);
|
struct aic_irq_chip *ic = irq_data_get_irq_chip_data(d);
|
||||||
int cpu;
|
int cpu;
|
||||||
|
|
||||||
|
BUG_ON(!ic->info.target_cpu);
|
||||||
|
|
||||||
if (force)
|
if (force)
|
||||||
cpu = cpumask_first(mask_val);
|
cpu = cpumask_first(mask_val);
|
||||||
else
|
else
|
||||||
cpu = cpumask_any_and(mask_val, cpu_online_mask);
|
cpu = cpumask_any_and(mask_val, cpu_online_mask);
|
||||||
|
|
||||||
aic_ic_write(ic, AIC_TARGET_CPU + hwirq * 4, BIT(cpu));
|
aic_ic_write(ic, ic->info.target_cpu + AIC_HWIRQ_IRQ(hwirq) * 4, BIT(cpu));
|
||||||
irq_data_update_effective_affinity(d, cpumask_of(cpu));
|
irq_data_update_effective_affinity(d, cpumask_of(cpu));
|
||||||
|
|
||||||
return IRQ_SET_MASK_OK;
|
return IRQ_SET_MASK_OK;
|
||||||
@ -294,15 +433,21 @@ static struct irq_chip aic_chip = {
|
|||||||
.irq_set_type = aic_irq_set_type,
|
.irq_set_type = aic_irq_set_type,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static struct irq_chip aic2_chip = {
|
||||||
|
.name = "AIC2",
|
||||||
|
.irq_mask = aic_irq_mask,
|
||||||
|
.irq_unmask = aic_irq_unmask,
|
||||||
|
.irq_eoi = aic_irq_eoi,
|
||||||
|
.irq_set_type = aic_irq_set_type,
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* FIQ irqchip
|
* FIQ irqchip
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static unsigned long aic_fiq_get_idx(struct irq_data *d)
|
static unsigned long aic_fiq_get_idx(struct irq_data *d)
|
||||||
{
|
{
|
||||||
struct aic_irq_chip *ic = irq_data_get_irq_chip_data(d);
|
return AIC_HWIRQ_IRQ(irqd_to_hwirq(d));
|
||||||
|
|
||||||
return irqd_to_hwirq(d) - ic->nr_hw;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void aic_fiq_set_mask(struct irq_data *d)
|
static void aic_fiq_set_mask(struct irq_data *d)
|
||||||
@ -380,17 +525,21 @@ static void __exception_irq_entry aic_handle_fiq(struct pt_regs *regs)
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
if (read_sysreg_s(SYS_IMP_APL_IPI_SR_EL1) & IPI_SR_PENDING) {
|
if (read_sysreg_s(SYS_IMP_APL_IPI_SR_EL1) & IPI_SR_PENDING) {
|
||||||
pr_err_ratelimited("Fast IPI fired. Acking.\n");
|
if (static_branch_likely(&use_fast_ipi)) {
|
||||||
write_sysreg_s(IPI_SR_PENDING, SYS_IMP_APL_IPI_SR_EL1);
|
aic_handle_ipi(regs);
|
||||||
|
} else {
|
||||||
|
pr_err_ratelimited("Fast IPI fired. Acking.\n");
|
||||||
|
write_sysreg_s(IPI_SR_PENDING, SYS_IMP_APL_IPI_SR_EL1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TIMER_FIRING(read_sysreg(cntp_ctl_el0)))
|
if (TIMER_FIRING(read_sysreg(cntp_ctl_el0)))
|
||||||
generic_handle_domain_irq(aic_irqc->hw_domain,
|
generic_handle_domain_irq(aic_irqc->hw_domain,
|
||||||
aic_irqc->nr_hw + AIC_TMR_EL0_PHYS);
|
AIC_FIQ_HWIRQ(AIC_TMR_EL0_PHYS));
|
||||||
|
|
||||||
if (TIMER_FIRING(read_sysreg(cntv_ctl_el0)))
|
if (TIMER_FIRING(read_sysreg(cntv_ctl_el0)))
|
||||||
generic_handle_domain_irq(aic_irqc->hw_domain,
|
generic_handle_domain_irq(aic_irqc->hw_domain,
|
||||||
aic_irqc->nr_hw + AIC_TMR_EL0_VIRT);
|
AIC_FIQ_HWIRQ(AIC_TMR_EL0_VIRT));
|
||||||
|
|
||||||
if (is_kernel_in_hyp_mode()) {
|
if (is_kernel_in_hyp_mode()) {
|
||||||
uint64_t enabled = read_sysreg_s(SYS_IMP_APL_VM_TMR_FIQ_ENA_EL2);
|
uint64_t enabled = read_sysreg_s(SYS_IMP_APL_VM_TMR_FIQ_ENA_EL2);
|
||||||
@ -398,12 +547,12 @@ static void __exception_irq_entry aic_handle_fiq(struct pt_regs *regs)
|
|||||||
if ((enabled & VM_TMR_FIQ_ENABLE_P) &&
|
if ((enabled & VM_TMR_FIQ_ENABLE_P) &&
|
||||||
TIMER_FIRING(read_sysreg_s(SYS_CNTP_CTL_EL02)))
|
TIMER_FIRING(read_sysreg_s(SYS_CNTP_CTL_EL02)))
|
||||||
generic_handle_domain_irq(aic_irqc->hw_domain,
|
generic_handle_domain_irq(aic_irqc->hw_domain,
|
||||||
aic_irqc->nr_hw + AIC_TMR_EL02_PHYS);
|
AIC_FIQ_HWIRQ(AIC_TMR_EL02_PHYS));
|
||||||
|
|
||||||
if ((enabled & VM_TMR_FIQ_ENABLE_V) &&
|
if ((enabled & VM_TMR_FIQ_ENABLE_V) &&
|
||||||
TIMER_FIRING(read_sysreg_s(SYS_CNTV_CTL_EL02)))
|
TIMER_FIRING(read_sysreg_s(SYS_CNTV_CTL_EL02)))
|
||||||
generic_handle_domain_irq(aic_irqc->hw_domain,
|
generic_handle_domain_irq(aic_irqc->hw_domain,
|
||||||
aic_irqc->nr_hw + AIC_TMR_EL02_VIRT);
|
AIC_FIQ_HWIRQ(AIC_TMR_EL02_VIRT));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (read_sysreg_s(SYS_IMP_APL_PMCR0_EL1) & PMCR0_IACT) {
|
if (read_sysreg_s(SYS_IMP_APL_PMCR0_EL1) & PMCR0_IACT) {
|
||||||
@ -414,7 +563,7 @@ static void __exception_irq_entry aic_handle_fiq(struct pt_regs *regs)
|
|||||||
else
|
else
|
||||||
irq = AIC_CPU_PMU_E;
|
irq = AIC_CPU_PMU_E;
|
||||||
generic_handle_domain_irq(aic_irqc->hw_domain,
|
generic_handle_domain_irq(aic_irqc->hw_domain,
|
||||||
aic_irqc->nr_hw + irq);
|
AIC_FIQ_HWIRQ(irq));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FIELD_GET(UPMCR0_IMODE, read_sysreg_s(SYS_IMP_APL_UPMCR0_EL1)) == UPMCR0_IMODE_FIQ &&
|
if (FIELD_GET(UPMCR0_IMODE, read_sysreg_s(SYS_IMP_APL_UPMCR0_EL1)) == UPMCR0_IMODE_FIQ &&
|
||||||
@ -448,13 +597,18 @@ static int aic_irq_domain_map(struct irq_domain *id, unsigned int irq,
|
|||||||
irq_hw_number_t hw)
|
irq_hw_number_t hw)
|
||||||
{
|
{
|
||||||
struct aic_irq_chip *ic = id->host_data;
|
struct aic_irq_chip *ic = id->host_data;
|
||||||
|
u32 type = FIELD_GET(AIC_EVENT_TYPE, hw);
|
||||||
|
struct irq_chip *chip = &aic_chip;
|
||||||
|
|
||||||
if (hw < ic->nr_hw) {
|
if (ic->info.version == 2)
|
||||||
irq_domain_set_info(id, irq, hw, &aic_chip, id->host_data,
|
chip = &aic2_chip;
|
||||||
|
|
||||||
|
if (type == AIC_EVENT_TYPE_IRQ) {
|
||||||
|
irq_domain_set_info(id, irq, hw, chip, id->host_data,
|
||||||
handle_fasteoi_irq, NULL, NULL);
|
handle_fasteoi_irq, NULL, NULL);
|
||||||
irqd_set_single_target(irq_desc_get_irq_data(irq_to_desc(irq)));
|
irqd_set_single_target(irq_desc_get_irq_data(irq_to_desc(irq)));
|
||||||
} else {
|
} else {
|
||||||
int fiq = hw - ic->nr_hw;
|
int fiq = FIELD_GET(AIC_EVENT_NUM, hw);
|
||||||
|
|
||||||
switch (fiq) {
|
switch (fiq) {
|
||||||
case AIC_CPU_PMU_P:
|
case AIC_CPU_PMU_P:
|
||||||
@ -479,32 +633,46 @@ static int aic_irq_domain_translate(struct irq_domain *id,
|
|||||||
unsigned int *type)
|
unsigned int *type)
|
||||||
{
|
{
|
||||||
struct aic_irq_chip *ic = id->host_data;
|
struct aic_irq_chip *ic = id->host_data;
|
||||||
|
u32 *args;
|
||||||
|
u32 die = 0;
|
||||||
|
|
||||||
if (fwspec->param_count != 3 || !is_of_node(fwspec->fwnode))
|
if (fwspec->param_count < 3 || fwspec->param_count > 4 ||
|
||||||
|
!is_of_node(fwspec->fwnode))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
args = &fwspec->param[1];
|
||||||
|
|
||||||
|
if (fwspec->param_count == 4) {
|
||||||
|
die = args[0];
|
||||||
|
args++;
|
||||||
|
}
|
||||||
|
|
||||||
switch (fwspec->param[0]) {
|
switch (fwspec->param[0]) {
|
||||||
case AIC_IRQ:
|
case AIC_IRQ:
|
||||||
if (fwspec->param[1] >= ic->nr_hw)
|
if (die >= ic->nr_die)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
*hwirq = fwspec->param[1];
|
if (args[0] >= ic->nr_irq)
|
||||||
|
return -EINVAL;
|
||||||
|
*hwirq = AIC_IRQ_HWIRQ(die, args[0]);
|
||||||
break;
|
break;
|
||||||
case AIC_FIQ:
|
case AIC_FIQ:
|
||||||
if (fwspec->param[1] >= AIC_NR_FIQ)
|
if (die != 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
*hwirq = ic->nr_hw + fwspec->param[1];
|
if (args[0] >= AIC_NR_FIQ)
|
||||||
|
return -EINVAL;
|
||||||
|
*hwirq = AIC_FIQ_HWIRQ(args[0]);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* In EL1 the non-redirected registers are the guest's,
|
* In EL1 the non-redirected registers are the guest's,
|
||||||
* not EL2's, so remap the hwirqs to match.
|
* not EL2's, so remap the hwirqs to match.
|
||||||
*/
|
*/
|
||||||
if (!is_kernel_in_hyp_mode()) {
|
if (!is_kernel_in_hyp_mode()) {
|
||||||
switch (fwspec->param[1]) {
|
switch (args[0]) {
|
||||||
case AIC_TMR_GUEST_PHYS:
|
case AIC_TMR_GUEST_PHYS:
|
||||||
*hwirq = ic->nr_hw + AIC_TMR_EL0_PHYS;
|
*hwirq = AIC_FIQ_HWIRQ(AIC_TMR_EL0_PHYS);
|
||||||
break;
|
break;
|
||||||
case AIC_TMR_GUEST_VIRT:
|
case AIC_TMR_GUEST_VIRT:
|
||||||
*hwirq = ic->nr_hw + AIC_TMR_EL0_VIRT;
|
*hwirq = AIC_FIQ_HWIRQ(AIC_TMR_EL0_VIRT);
|
||||||
break;
|
break;
|
||||||
case AIC_TMR_HV_PHYS:
|
case AIC_TMR_HV_PHYS:
|
||||||
case AIC_TMR_HV_VIRT:
|
case AIC_TMR_HV_VIRT:
|
||||||
@ -518,7 +686,7 @@ static int aic_irq_domain_translate(struct irq_domain *id,
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
*type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
|
*type = args[1] & IRQ_TYPE_SENSE_MASK;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -567,6 +735,22 @@ static const struct irq_domain_ops aic_irq_domain_ops = {
|
|||||||
* IPI irqchip
|
* IPI irqchip
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
static void aic_ipi_send_fast(int cpu)
|
||||||
|
{
|
||||||
|
u64 mpidr = cpu_logical_map(cpu);
|
||||||
|
u64 my_mpidr = read_cpuid_mpidr();
|
||||||
|
u64 cluster = MPIDR_CLUSTER(mpidr);
|
||||||
|
u64 idx = MPIDR_CPU(mpidr);
|
||||||
|
|
||||||
|
if (MPIDR_CLUSTER(my_mpidr) == cluster)
|
||||||
|
write_sysreg_s(FIELD_PREP(IPI_RR_CPU, idx),
|
||||||
|
SYS_IMP_APL_IPI_RR_LOCAL_EL1);
|
||||||
|
else
|
||||||
|
write_sysreg_s(FIELD_PREP(IPI_RR_CPU, idx) | FIELD_PREP(IPI_RR_CLUSTER, cluster),
|
||||||
|
SYS_IMP_APL_IPI_RR_GLOBAL_EL1);
|
||||||
|
isb();
|
||||||
|
}
|
||||||
|
|
||||||
static void aic_ipi_mask(struct irq_data *d)
|
static void aic_ipi_mask(struct irq_data *d)
|
||||||
{
|
{
|
||||||
u32 irq_bit = BIT(irqd_to_hwirq(d));
|
u32 irq_bit = BIT(irqd_to_hwirq(d));
|
||||||
@ -592,8 +776,12 @@ static void aic_ipi_unmask(struct irq_data *d)
|
|||||||
* If a pending vIPI was unmasked, raise a HW IPI to ourselves.
|
* If a pending vIPI was unmasked, raise a HW IPI to ourselves.
|
||||||
* No barriers needed here since this is a self-IPI.
|
* No barriers needed here since this is a self-IPI.
|
||||||
*/
|
*/
|
||||||
if (atomic_read(this_cpu_ptr(&aic_vipi_flag)) & irq_bit)
|
if (atomic_read(this_cpu_ptr(&aic_vipi_flag)) & irq_bit) {
|
||||||
aic_ic_write(ic, AIC_IPI_SEND, AIC_IPI_SEND_CPU(smp_processor_id()));
|
if (static_branch_likely(&use_fast_ipi))
|
||||||
|
aic_ipi_send_fast(smp_processor_id());
|
||||||
|
else
|
||||||
|
aic_ic_write(ic, AIC_IPI_SEND, AIC_IPI_SEND_CPU(smp_processor_id()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void aic_ipi_send_mask(struct irq_data *d, const struct cpumask *mask)
|
static void aic_ipi_send_mask(struct irq_data *d, const struct cpumask *mask)
|
||||||
@ -621,8 +809,12 @@ static void aic_ipi_send_mask(struct irq_data *d, const struct cpumask *mask)
|
|||||||
smp_mb__after_atomic();
|
smp_mb__after_atomic();
|
||||||
|
|
||||||
if (!(pending & irq_bit) &&
|
if (!(pending & irq_bit) &&
|
||||||
(atomic_read(per_cpu_ptr(&aic_vipi_enable, cpu)) & irq_bit))
|
(atomic_read(per_cpu_ptr(&aic_vipi_enable, cpu)) & irq_bit)) {
|
||||||
send |= AIC_IPI_SEND_CPU(cpu);
|
if (static_branch_likely(&use_fast_ipi))
|
||||||
|
aic_ipi_send_fast(cpu);
|
||||||
|
else
|
||||||
|
send |= AIC_IPI_SEND_CPU(cpu);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -654,8 +846,16 @@ static void aic_handle_ipi(struct pt_regs *regs)
|
|||||||
/*
|
/*
|
||||||
* Ack the IPI. We need to order this after the AIC event read, but
|
* Ack the IPI. We need to order this after the AIC event read, but
|
||||||
* that is enforced by normal MMIO ordering guarantees.
|
* that is enforced by normal MMIO ordering guarantees.
|
||||||
|
*
|
||||||
|
* For the Fast IPI case, this needs to be ordered before the vIPI
|
||||||
|
* handling below, so we need to isb();
|
||||||
*/
|
*/
|
||||||
aic_ic_write(aic_irqc, AIC_IPI_ACK, AIC_IPI_OTHER);
|
if (static_branch_likely(&use_fast_ipi)) {
|
||||||
|
write_sysreg_s(IPI_SR_PENDING, SYS_IMP_APL_IPI_SR_EL1);
|
||||||
|
isb();
|
||||||
|
} else {
|
||||||
|
aic_ic_write(aic_irqc, AIC_IPI_ACK, AIC_IPI_OTHER);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The mask read does not need to be ordered. Only we can change
|
* The mask read does not need to be ordered. Only we can change
|
||||||
@ -683,7 +883,8 @@ static void aic_handle_ipi(struct pt_regs *regs)
|
|||||||
* No ordering needed here; at worst this just changes the timing of
|
* No ordering needed here; at worst this just changes the timing of
|
||||||
* when the next IPI will be delivered.
|
* when the next IPI will be delivered.
|
||||||
*/
|
*/
|
||||||
aic_ic_write(aic_irqc, AIC_IPI_MASK_CLR, AIC_IPI_OTHER);
|
if (!static_branch_likely(&use_fast_ipi))
|
||||||
|
aic_ic_write(aic_irqc, AIC_IPI_MASK_CLR, AIC_IPI_OTHER);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int aic_ipi_alloc(struct irq_domain *d, unsigned int virq,
|
static int aic_ipi_alloc(struct irq_domain *d, unsigned int virq,
|
||||||
@ -770,20 +971,27 @@ static int aic_init_cpu(unsigned int cpu)
|
|||||||
/* Commit all of the above */
|
/* Commit all of the above */
|
||||||
isb();
|
isb();
|
||||||
|
|
||||||
/*
|
if (aic_irqc->info.version == 1) {
|
||||||
* Make sure the kernel's idea of logical CPU order is the same as AIC's
|
/*
|
||||||
* If we ever end up with a mismatch here, we will have to introduce
|
* Make sure the kernel's idea of logical CPU order is the same as AIC's
|
||||||
* a mapping table similar to what other irqchip drivers do.
|
* If we ever end up with a mismatch here, we will have to introduce
|
||||||
*/
|
* a mapping table similar to what other irqchip drivers do.
|
||||||
WARN_ON(aic_ic_read(aic_irqc, AIC_WHOAMI) != smp_processor_id());
|
*/
|
||||||
|
WARN_ON(aic_ic_read(aic_irqc, AIC_WHOAMI) != smp_processor_id());
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Always keep IPIs unmasked at the hardware level (except auto-masking
|
* Always keep IPIs unmasked at the hardware level (except auto-masking
|
||||||
* by AIC during processing). We manage masks at the vIPI level.
|
* by AIC during processing). We manage masks at the vIPI level.
|
||||||
*/
|
* These registers only exist on AICv1, AICv2 always uses fast IPIs.
|
||||||
aic_ic_write(aic_irqc, AIC_IPI_ACK, AIC_IPI_SELF | AIC_IPI_OTHER);
|
*/
|
||||||
aic_ic_write(aic_irqc, AIC_IPI_MASK_SET, AIC_IPI_SELF);
|
aic_ic_write(aic_irqc, AIC_IPI_ACK, AIC_IPI_SELF | AIC_IPI_OTHER);
|
||||||
aic_ic_write(aic_irqc, AIC_IPI_MASK_CLR, AIC_IPI_OTHER);
|
if (static_branch_likely(&use_fast_ipi)) {
|
||||||
|
aic_ic_write(aic_irqc, AIC_IPI_MASK_SET, AIC_IPI_SELF | AIC_IPI_OTHER);
|
||||||
|
} else {
|
||||||
|
aic_ic_write(aic_irqc, AIC_IPI_MASK_SET, AIC_IPI_SELF);
|
||||||
|
aic_ic_write(aic_irqc, AIC_IPI_MASK_CLR, AIC_IPI_OTHER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Initialize the local mask state */
|
/* Initialize the local mask state */
|
||||||
__this_cpu_write(aic_fiq_unmasked, 0);
|
__this_cpu_write(aic_fiq_unmasked, 0);
|
||||||
@ -810,7 +1018,7 @@ static void build_fiq_affinity(struct aic_irq_chip *ic, struct device_node *aff)
|
|||||||
if (WARN_ON(n < 0))
|
if (WARN_ON(n < 0))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ic->fiq_aff[fiq] = kzalloc(sizeof(ic->fiq_aff[fiq]), GFP_KERNEL);
|
ic->fiq_aff[fiq] = kzalloc(sizeof(*ic->fiq_aff[fiq]), GFP_KERNEL);
|
||||||
if (!ic->fiq_aff[fiq])
|
if (!ic->fiq_aff[fiq])
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -836,43 +1044,97 @@ static void build_fiq_affinity(struct aic_irq_chip *ic, struct device_node *aff)
|
|||||||
|
|
||||||
static int __init aic_of_ic_init(struct device_node *node, struct device_node *parent)
|
static int __init aic_of_ic_init(struct device_node *node, struct device_node *parent)
|
||||||
{
|
{
|
||||||
int i;
|
int i, die;
|
||||||
|
u32 off, start_off;
|
||||||
void __iomem *regs;
|
void __iomem *regs;
|
||||||
u32 info;
|
|
||||||
struct aic_irq_chip *irqc;
|
struct aic_irq_chip *irqc;
|
||||||
struct device_node *affs;
|
struct device_node *affs;
|
||||||
|
const struct of_device_id *match;
|
||||||
|
|
||||||
regs = of_iomap(node, 0);
|
regs = of_iomap(node, 0);
|
||||||
if (WARN_ON(!regs))
|
if (WARN_ON(!regs))
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
|
||||||
irqc = kzalloc(sizeof(*irqc), GFP_KERNEL);
|
irqc = kzalloc(sizeof(*irqc), GFP_KERNEL);
|
||||||
if (!irqc)
|
if (!irqc) {
|
||||||
|
iounmap(regs);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
aic_irqc = irqc;
|
|
||||||
irqc->base = regs;
|
irqc->base = regs;
|
||||||
|
|
||||||
info = aic_ic_read(irqc, AIC_INFO);
|
match = of_match_node(aic_info_match, node);
|
||||||
irqc->nr_hw = FIELD_GET(AIC_INFO_NR_HW, info);
|
if (!match)
|
||||||
|
goto err_unmap;
|
||||||
|
|
||||||
irqc->hw_domain = irq_domain_create_linear(of_node_to_fwnode(node),
|
irqc->info = *(struct aic_info *)match->data;
|
||||||
irqc->nr_hw + AIC_NR_FIQ,
|
|
||||||
&aic_irq_domain_ops, irqc);
|
aic_irqc = irqc;
|
||||||
if (WARN_ON(!irqc->hw_domain)) {
|
|
||||||
iounmap(irqc->base);
|
switch (irqc->info.version) {
|
||||||
kfree(irqc);
|
case 1: {
|
||||||
return -ENODEV;
|
u32 info;
|
||||||
|
|
||||||
|
info = aic_ic_read(irqc, AIC_INFO);
|
||||||
|
irqc->nr_irq = FIELD_GET(AIC_INFO_NR_IRQ, info);
|
||||||
|
irqc->max_irq = AIC_MAX_IRQ;
|
||||||
|
irqc->nr_die = irqc->max_die = 1;
|
||||||
|
|
||||||
|
off = start_off = irqc->info.target_cpu;
|
||||||
|
off += sizeof(u32) * irqc->max_irq; /* TARGET_CPU */
|
||||||
|
|
||||||
|
irqc->event = irqc->base;
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
case 2: {
|
||||||
|
u32 info1, info3;
|
||||||
|
|
||||||
|
info1 = aic_ic_read(irqc, AIC2_INFO1);
|
||||||
|
info3 = aic_ic_read(irqc, AIC2_INFO3);
|
||||||
|
|
||||||
|
irqc->nr_irq = FIELD_GET(AIC2_INFO1_NR_IRQ, info1);
|
||||||
|
irqc->max_irq = FIELD_GET(AIC2_INFO3_MAX_IRQ, info3);
|
||||||
|
irqc->nr_die = FIELD_GET(AIC2_INFO1_LAST_DIE, info1) + 1;
|
||||||
|
irqc->max_die = FIELD_GET(AIC2_INFO3_MAX_DIE, info3);
|
||||||
|
|
||||||
|
off = start_off = irqc->info.irq_cfg;
|
||||||
|
off += sizeof(u32) * irqc->max_irq; /* IRQ_CFG */
|
||||||
|
|
||||||
|
irqc->event = of_iomap(node, 1);
|
||||||
|
if (WARN_ON(!irqc->event))
|
||||||
|
goto err_unmap;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
irqc->info.sw_set = off;
|
||||||
|
off += sizeof(u32) * (irqc->max_irq >> 5); /* SW_SET */
|
||||||
|
irqc->info.sw_clr = off;
|
||||||
|
off += sizeof(u32) * (irqc->max_irq >> 5); /* SW_CLR */
|
||||||
|
irqc->info.mask_set = off;
|
||||||
|
off += sizeof(u32) * (irqc->max_irq >> 5); /* MASK_SET */
|
||||||
|
irqc->info.mask_clr = off;
|
||||||
|
off += sizeof(u32) * (irqc->max_irq >> 5); /* MASK_CLR */
|
||||||
|
off += sizeof(u32) * (irqc->max_irq >> 5); /* HW_STATE */
|
||||||
|
|
||||||
|
if (irqc->info.fast_ipi)
|
||||||
|
static_branch_enable(&use_fast_ipi);
|
||||||
|
else
|
||||||
|
static_branch_disable(&use_fast_ipi);
|
||||||
|
|
||||||
|
irqc->info.die_stride = off - start_off;
|
||||||
|
|
||||||
|
irqc->hw_domain = irq_domain_create_tree(of_node_to_fwnode(node),
|
||||||
|
&aic_irq_domain_ops, irqc);
|
||||||
|
if (WARN_ON(!irqc->hw_domain))
|
||||||
|
goto err_unmap;
|
||||||
|
|
||||||
irq_domain_update_bus_token(irqc->hw_domain, DOMAIN_BUS_WIRED);
|
irq_domain_update_bus_token(irqc->hw_domain, DOMAIN_BUS_WIRED);
|
||||||
|
|
||||||
if (aic_init_smp(irqc, node)) {
|
if (aic_init_smp(irqc, node))
|
||||||
irq_domain_remove(irqc->hw_domain);
|
goto err_remove_domain;
|
||||||
iounmap(irqc->base);
|
|
||||||
kfree(irqc);
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
|
|
||||||
affs = of_get_child_by_name(node, "affinities");
|
affs = of_get_child_by_name(node, "affinities");
|
||||||
if (affs) {
|
if (affs) {
|
||||||
@ -885,26 +1147,51 @@ static int __init aic_of_ic_init(struct device_node *node, struct device_node *p
|
|||||||
set_handle_irq(aic_handle_irq);
|
set_handle_irq(aic_handle_irq);
|
||||||
set_handle_fiq(aic_handle_fiq);
|
set_handle_fiq(aic_handle_fiq);
|
||||||
|
|
||||||
for (i = 0; i < BITS_TO_U32(irqc->nr_hw); i++)
|
off = 0;
|
||||||
aic_ic_write(irqc, AIC_MASK_SET + i * 4, U32_MAX);
|
for (die = 0; die < irqc->nr_die; die++) {
|
||||||
for (i = 0; i < BITS_TO_U32(irqc->nr_hw); i++)
|
for (i = 0; i < BITS_TO_U32(irqc->nr_irq); i++)
|
||||||
aic_ic_write(irqc, AIC_SW_CLR + i * 4, U32_MAX);
|
aic_ic_write(irqc, irqc->info.mask_set + off + i * 4, U32_MAX);
|
||||||
for (i = 0; i < irqc->nr_hw; i++)
|
for (i = 0; i < BITS_TO_U32(irqc->nr_irq); i++)
|
||||||
aic_ic_write(irqc, AIC_TARGET_CPU + i * 4, 1);
|
aic_ic_write(irqc, irqc->info.sw_clr + off + i * 4, U32_MAX);
|
||||||
|
if (irqc->info.target_cpu)
|
||||||
|
for (i = 0; i < irqc->nr_irq; i++)
|
||||||
|
aic_ic_write(irqc, irqc->info.target_cpu + off + i * 4, 1);
|
||||||
|
off += irqc->info.die_stride;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (irqc->info.version == 2) {
|
||||||
|
u32 config = aic_ic_read(irqc, AIC2_CONFIG);
|
||||||
|
|
||||||
|
config |= AIC2_CONFIG_ENABLE;
|
||||||
|
aic_ic_write(irqc, AIC2_CONFIG, config);
|
||||||
|
}
|
||||||
|
|
||||||
if (!is_kernel_in_hyp_mode())
|
if (!is_kernel_in_hyp_mode())
|
||||||
pr_info("Kernel running in EL1, mapping interrupts");
|
pr_info("Kernel running in EL1, mapping interrupts");
|
||||||
|
|
||||||
|
if (static_branch_likely(&use_fast_ipi))
|
||||||
|
pr_info("Using Fast IPIs");
|
||||||
|
|
||||||
cpuhp_setup_state(CPUHP_AP_IRQ_APPLE_AIC_STARTING,
|
cpuhp_setup_state(CPUHP_AP_IRQ_APPLE_AIC_STARTING,
|
||||||
"irqchip/apple-aic/ipi:starting",
|
"irqchip/apple-aic/ipi:starting",
|
||||||
aic_init_cpu, NULL);
|
aic_init_cpu, NULL);
|
||||||
|
|
||||||
vgic_set_kvm_info(&vgic_info);
|
vgic_set_kvm_info(&vgic_info);
|
||||||
|
|
||||||
pr_info("Initialized with %d IRQs, %d FIQs, %d vIPIs\n",
|
pr_info("Initialized with %d/%d IRQs * %d/%d die(s), %d FIQs, %d vIPIs",
|
||||||
irqc->nr_hw, AIC_NR_FIQ, AIC_NR_SWIPI);
|
irqc->nr_irq, irqc->max_irq, irqc->nr_die, irqc->max_die, AIC_NR_FIQ, AIC_NR_SWIPI);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
err_remove_domain:
|
||||||
|
irq_domain_remove(irqc->hw_domain);
|
||||||
|
err_unmap:
|
||||||
|
if (irqc->event && irqc->event != irqc->base)
|
||||||
|
iounmap(irqc->event);
|
||||||
|
iounmap(irqc->base);
|
||||||
|
kfree(irqc);
|
||||||
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
IRQCHIP_DECLARE(apple_m1_aic, "apple,aic", aic_of_ic_init);
|
IRQCHIP_DECLARE(apple_aic, "apple,aic", aic_of_ic_init);
|
||||||
|
IRQCHIP_DECLARE(apple_aic2, "apple,aic2", aic_of_ic_init);
|
||||||
|
@ -11,7 +11,6 @@
|
|||||||
#include <linux/irq.h>
|
#include <linux/irq.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/irqchip.h>
|
#include <linux/irqchip.h>
|
||||||
#include <linux/irqchip/versatile-fpga.h>
|
|
||||||
#include <linux/irqdomain.h>
|
#include <linux/irqdomain.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
|
@ -1211,7 +1211,7 @@ static void gic_ipi_send_mask(struct irq_data *d, const struct cpumask *mask)
|
|||||||
* Ensure that stores to Normal memory are visible to the
|
* Ensure that stores to Normal memory are visible to the
|
||||||
* other CPUs before issuing the IPI.
|
* other CPUs before issuing the IPI.
|
||||||
*/
|
*/
|
||||||
wmb();
|
dsb(ishst);
|
||||||
|
|
||||||
for_each_cpu(cpu, mask) {
|
for_each_cpu(cpu, mask) {
|
||||||
u64 cluster_id = MPIDR_TO_SGI_CLUSTER_ID(cpu_logical_map(cpu));
|
u64 cluster_id = MPIDR_TO_SGI_CLUSTER_ID(cpu_logical_map(cpu));
|
||||||
|
@ -34,6 +34,7 @@
|
|||||||
#include <linux/irqdomain.h>
|
#include <linux/irqdomain.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/percpu.h>
|
#include <linux/percpu.h>
|
||||||
|
#include <linux/seq_file.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/irqchip.h>
|
#include <linux/irqchip.h>
|
||||||
#include <linux/irqchip/chained_irq.h>
|
#include <linux/irqchip/chained_irq.h>
|
||||||
@ -66,7 +67,6 @@ union gic_base {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct gic_chip_data {
|
struct gic_chip_data {
|
||||||
struct irq_chip chip;
|
|
||||||
union gic_base dist_base;
|
union gic_base dist_base;
|
||||||
union gic_base cpu_base;
|
union gic_base cpu_base;
|
||||||
void __iomem *raw_dist_base;
|
void __iomem *raw_dist_base;
|
||||||
@ -397,18 +397,15 @@ static void gic_handle_cascade_irq(struct irq_desc *desc)
|
|||||||
chained_irq_exit(chip, desc);
|
chained_irq_exit(chip, desc);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct irq_chip gic_chip = {
|
static void gic_irq_print_chip(struct irq_data *d, struct seq_file *p)
|
||||||
.irq_mask = gic_mask_irq,
|
{
|
||||||
.irq_unmask = gic_unmask_irq,
|
struct gic_chip_data *gic = irq_data_get_irq_chip_data(d);
|
||||||
.irq_eoi = gic_eoi_irq,
|
|
||||||
.irq_set_type = gic_set_type,
|
if (gic->domain->dev)
|
||||||
.irq_retrigger = gic_retrigger,
|
seq_printf(p, gic->domain->dev->of_node->name);
|
||||||
.irq_get_irqchip_state = gic_irq_get_irqchip_state,
|
else
|
||||||
.irq_set_irqchip_state = gic_irq_set_irqchip_state,
|
seq_printf(p, "GIC-%d", (int)(gic - &gic_data[0]));
|
||||||
.flags = IRQCHIP_SET_TYPE_MASKED |
|
}
|
||||||
IRQCHIP_SKIP_SET_WAKE |
|
|
||||||
IRQCHIP_MASK_ON_SUSPEND,
|
|
||||||
};
|
|
||||||
|
|
||||||
void __init gic_cascade_irq(unsigned int gic_nr, unsigned int irq)
|
void __init gic_cascade_irq(unsigned int gic_nr, unsigned int irq)
|
||||||
{
|
{
|
||||||
@ -799,8 +796,12 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
|
|||||||
bool force)
|
bool force)
|
||||||
{
|
{
|
||||||
void __iomem *reg = gic_dist_base(d) + GIC_DIST_TARGET + gic_irq(d);
|
void __iomem *reg = gic_dist_base(d) + GIC_DIST_TARGET + gic_irq(d);
|
||||||
|
struct gic_chip_data *gic = irq_data_get_irq_chip_data(d);
|
||||||
unsigned int cpu;
|
unsigned int cpu;
|
||||||
|
|
||||||
|
if (unlikely(gic != &gic_data[0]))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
if (!force)
|
if (!force)
|
||||||
cpu = cpumask_any_and(mask_val, cpu_online_mask);
|
cpu = cpumask_any_and(mask_val, cpu_online_mask);
|
||||||
else
|
else
|
||||||
@ -880,6 +881,39 @@ static __init void gic_smp_init(void)
|
|||||||
#define gic_ipi_send_mask NULL
|
#define gic_ipi_send_mask NULL
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static const struct irq_chip gic_chip = {
|
||||||
|
.irq_mask = gic_mask_irq,
|
||||||
|
.irq_unmask = gic_unmask_irq,
|
||||||
|
.irq_eoi = gic_eoi_irq,
|
||||||
|
.irq_set_type = gic_set_type,
|
||||||
|
.irq_retrigger = gic_retrigger,
|
||||||
|
.irq_set_affinity = gic_set_affinity,
|
||||||
|
.ipi_send_mask = gic_ipi_send_mask,
|
||||||
|
.irq_get_irqchip_state = gic_irq_get_irqchip_state,
|
||||||
|
.irq_set_irqchip_state = gic_irq_set_irqchip_state,
|
||||||
|
.irq_print_chip = gic_irq_print_chip,
|
||||||
|
.flags = IRQCHIP_SET_TYPE_MASKED |
|
||||||
|
IRQCHIP_SKIP_SET_WAKE |
|
||||||
|
IRQCHIP_MASK_ON_SUSPEND,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct irq_chip gic_chip_mode1 = {
|
||||||
|
.name = "GICv2",
|
||||||
|
.irq_mask = gic_eoimode1_mask_irq,
|
||||||
|
.irq_unmask = gic_unmask_irq,
|
||||||
|
.irq_eoi = gic_eoimode1_eoi_irq,
|
||||||
|
.irq_set_type = gic_set_type,
|
||||||
|
.irq_retrigger = gic_retrigger,
|
||||||
|
.irq_set_affinity = gic_set_affinity,
|
||||||
|
.ipi_send_mask = gic_ipi_send_mask,
|
||||||
|
.irq_get_irqchip_state = gic_irq_get_irqchip_state,
|
||||||
|
.irq_set_irqchip_state = gic_irq_set_irqchip_state,
|
||||||
|
.irq_set_vcpu_affinity = gic_irq_set_vcpu_affinity,
|
||||||
|
.flags = IRQCHIP_SET_TYPE_MASKED |
|
||||||
|
IRQCHIP_SKIP_SET_WAKE |
|
||||||
|
IRQCHIP_MASK_ON_SUSPEND,
|
||||||
|
};
|
||||||
|
|
||||||
#ifdef CONFIG_BL_SWITCHER
|
#ifdef CONFIG_BL_SWITCHER
|
||||||
/*
|
/*
|
||||||
* gic_send_sgi - send a SGI directly to given CPU interface number
|
* gic_send_sgi - send a SGI directly to given CPU interface number
|
||||||
@ -1024,15 +1058,19 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,
|
|||||||
{
|
{
|
||||||
struct gic_chip_data *gic = d->host_data;
|
struct gic_chip_data *gic = d->host_data;
|
||||||
struct irq_data *irqd = irq_desc_get_irq_data(irq_to_desc(irq));
|
struct irq_data *irqd = irq_desc_get_irq_data(irq_to_desc(irq));
|
||||||
|
const struct irq_chip *chip;
|
||||||
|
|
||||||
|
chip = (static_branch_likely(&supports_deactivate_key) &&
|
||||||
|
gic == &gic_data[0]) ? &gic_chip_mode1 : &gic_chip;
|
||||||
|
|
||||||
switch (hw) {
|
switch (hw) {
|
||||||
case 0 ... 31:
|
case 0 ... 31:
|
||||||
irq_set_percpu_devid(irq);
|
irq_set_percpu_devid(irq);
|
||||||
irq_domain_set_info(d, irq, hw, &gic->chip, d->host_data,
|
irq_domain_set_info(d, irq, hw, chip, d->host_data,
|
||||||
handle_percpu_devid_irq, NULL, NULL);
|
handle_percpu_devid_irq, NULL, NULL);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
irq_domain_set_info(d, irq, hw, &gic->chip, d->host_data,
|
irq_domain_set_info(d, irq, hw, chip, d->host_data,
|
||||||
handle_fasteoi_irq, NULL, NULL);
|
handle_fasteoi_irq, NULL, NULL);
|
||||||
irq_set_probe(irq);
|
irq_set_probe(irq);
|
||||||
irqd_set_single_target(irqd);
|
irqd_set_single_target(irqd);
|
||||||
@ -1127,26 +1165,6 @@ static const struct irq_domain_ops gic_irq_domain_ops = {
|
|||||||
.unmap = gic_irq_domain_unmap,
|
.unmap = gic_irq_domain_unmap,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void gic_init_chip(struct gic_chip_data *gic, struct device *dev,
|
|
||||||
const char *name, bool use_eoimode1)
|
|
||||||
{
|
|
||||||
/* Initialize irq_chip */
|
|
||||||
gic->chip = gic_chip;
|
|
||||||
gic->chip.name = name;
|
|
||||||
gic->chip.parent_device = dev;
|
|
||||||
|
|
||||||
if (use_eoimode1) {
|
|
||||||
gic->chip.irq_mask = gic_eoimode1_mask_irq;
|
|
||||||
gic->chip.irq_eoi = gic_eoimode1_eoi_irq;
|
|
||||||
gic->chip.irq_set_vcpu_affinity = gic_irq_set_vcpu_affinity;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gic == &gic_data[0]) {
|
|
||||||
gic->chip.irq_set_affinity = gic_set_affinity;
|
|
||||||
gic->chip.ipi_send_mask = gic_ipi_send_mask;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int gic_init_bases(struct gic_chip_data *gic,
|
static int gic_init_bases(struct gic_chip_data *gic,
|
||||||
struct fwnode_handle *handle)
|
struct fwnode_handle *handle)
|
||||||
{
|
{
|
||||||
@ -1246,7 +1264,6 @@ error:
|
|||||||
static int __init __gic_init_bases(struct gic_chip_data *gic,
|
static int __init __gic_init_bases(struct gic_chip_data *gic,
|
||||||
struct fwnode_handle *handle)
|
struct fwnode_handle *handle)
|
||||||
{
|
{
|
||||||
char *name;
|
|
||||||
int i, ret;
|
int i, ret;
|
||||||
|
|
||||||
if (WARN_ON(!gic || gic->domain))
|
if (WARN_ON(!gic || gic->domain))
|
||||||
@ -1266,18 +1283,8 @@ static int __init __gic_init_bases(struct gic_chip_data *gic,
|
|||||||
pr_info("GIC: Using split EOI/Deactivate mode\n");
|
pr_info("GIC: Using split EOI/Deactivate mode\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (static_branch_likely(&supports_deactivate_key) && gic == &gic_data[0]) {
|
|
||||||
name = kasprintf(GFP_KERNEL, "GICv2");
|
|
||||||
gic_init_chip(gic, NULL, name, true);
|
|
||||||
} else {
|
|
||||||
name = kasprintf(GFP_KERNEL, "GIC-%d", (int)(gic-&gic_data[0]));
|
|
||||||
gic_init_chip(gic, NULL, name, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = gic_init_bases(gic, handle);
|
ret = gic_init_bases(gic, handle);
|
||||||
if (ret)
|
if (gic == &gic_data[0])
|
||||||
kfree(name);
|
|
||||||
else if (gic == &gic_data[0])
|
|
||||||
gic_smp_init();
|
gic_smp_init();
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@ -1460,8 +1467,6 @@ int gic_of_init_child(struct device *dev, struct gic_chip_data **gic, int irq)
|
|||||||
if (!*gic)
|
if (!*gic)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
gic_init_chip(*gic, dev, dev->of_node->name, false);
|
|
||||||
|
|
||||||
ret = gic_of_setup(*gic, dev->of_node);
|
ret = gic_of_setup(*gic, dev->of_node);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
@ -1472,6 +1477,7 @@ int gic_of_init_child(struct device *dev, struct gic_chip_data **gic, int irq)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
irq_domain_set_pm_device((*gic)->domain, dev);
|
||||||
irq_set_chained_handler_and_data(irq, gic_handle_cascade_irq, *gic);
|
irq_set_chained_handler_and_data(irq, gic_handle_cascade_irq, *gic);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -61,7 +61,6 @@
|
|||||||
#define CHAN_MAX_NUM 0x8
|
#define CHAN_MAX_NUM 0x8
|
||||||
|
|
||||||
struct intmux_irqchip_data {
|
struct intmux_irqchip_data {
|
||||||
struct irq_chip chip;
|
|
||||||
u32 saved_reg;
|
u32 saved_reg;
|
||||||
int chanidx;
|
int chanidx;
|
||||||
int irq;
|
int irq;
|
||||||
@ -114,7 +113,7 @@ static void imx_intmux_irq_unmask(struct irq_data *d)
|
|||||||
raw_spin_unlock_irqrestore(&data->lock, flags);
|
raw_spin_unlock_irqrestore(&data->lock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct irq_chip imx_intmux_irq_chip = {
|
static struct irq_chip imx_intmux_irq_chip __ro_after_init = {
|
||||||
.name = "intmux",
|
.name = "intmux",
|
||||||
.irq_mask = imx_intmux_irq_mask,
|
.irq_mask = imx_intmux_irq_mask,
|
||||||
.irq_unmask = imx_intmux_irq_unmask,
|
.irq_unmask = imx_intmux_irq_unmask,
|
||||||
@ -126,7 +125,7 @@ static int imx_intmux_irq_map(struct irq_domain *h, unsigned int irq,
|
|||||||
struct intmux_irqchip_data *data = h->host_data;
|
struct intmux_irqchip_data *data = h->host_data;
|
||||||
|
|
||||||
irq_set_chip_data(irq, data);
|
irq_set_chip_data(irq, data);
|
||||||
irq_set_chip_and_handler(irq, &data->chip, handle_level_irq);
|
irq_set_chip_and_handler(irq, &imx_intmux_irq_chip, handle_level_irq);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -241,8 +240,6 @@ static int imx_intmux_probe(struct platform_device *pdev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < channum; i++) {
|
for (i = 0; i < channum; i++) {
|
||||||
data->irqchip_data[i].chip = imx_intmux_irq_chip;
|
|
||||||
data->irqchip_data[i].chip.parent_device = &pdev->dev;
|
|
||||||
data->irqchip_data[i].chanidx = i;
|
data->irqchip_data[i].chanidx = i;
|
||||||
|
|
||||||
data->irqchip_data[i].irq = irq_of_parse_and_map(np, i);
|
data->irqchip_data[i].irq = irq_of_parse_and_map(np, i);
|
||||||
@ -260,6 +257,7 @@ static int imx_intmux_probe(struct platform_device *pdev)
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
data->irqchip_data[i].domain = domain;
|
data->irqchip_data[i].domain = domain;
|
||||||
|
irq_domain_set_pm_device(domain, &pdev->dev);
|
||||||
|
|
||||||
/* disable all interrupt sources of this channel firstly */
|
/* disable all interrupt sources of this channel firstly */
|
||||||
writel_relaxed(0, data->regs + CHANIER(i));
|
writel_relaxed(0, data->regs + CHANIER(i));
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include <linux/of_address.h>
|
#include <linux/of_address.h>
|
||||||
#include <linux/of_irq.h>
|
#include <linux/of_irq.h>
|
||||||
#include <linux/of_platform.h>
|
#include <linux/of_platform.h>
|
||||||
|
#include <linux/seq_file.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <asm/exception.h>
|
#include <asm/exception.h>
|
||||||
|
|
||||||
@ -25,8 +26,8 @@
|
|||||||
|
|
||||||
struct lpc32xx_irq_chip {
|
struct lpc32xx_irq_chip {
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
|
phys_addr_t addr;
|
||||||
struct irq_domain *domain;
|
struct irq_domain *domain;
|
||||||
struct irq_chip chip;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct lpc32xx_irq_chip *lpc32xx_mic_irqc;
|
static struct lpc32xx_irq_chip *lpc32xx_mic_irqc;
|
||||||
@ -118,6 +119,24 @@ static int lpc32xx_irq_set_type(struct irq_data *d, unsigned int type)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void lpc32xx_irq_print_chip(struct irq_data *d, struct seq_file *p)
|
||||||
|
{
|
||||||
|
struct lpc32xx_irq_chip *ic = irq_data_get_irq_chip_data(d);
|
||||||
|
|
||||||
|
if (ic == lpc32xx_mic_irqc)
|
||||||
|
seq_printf(p, "%08x.mic", ic->addr);
|
||||||
|
else
|
||||||
|
seq_printf(p, "%08x.sic", ic->addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct irq_chip lpc32xx_chip = {
|
||||||
|
.irq_ack = lpc32xx_irq_ack,
|
||||||
|
.irq_mask = lpc32xx_irq_mask,
|
||||||
|
.irq_unmask = lpc32xx_irq_unmask,
|
||||||
|
.irq_set_type = lpc32xx_irq_set_type,
|
||||||
|
.irq_print_chip = lpc32xx_irq_print_chip,
|
||||||
|
};
|
||||||
|
|
||||||
static void __exception_irq_entry lpc32xx_handle_irq(struct pt_regs *regs)
|
static void __exception_irq_entry lpc32xx_handle_irq(struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
struct lpc32xx_irq_chip *ic = lpc32xx_mic_irqc;
|
struct lpc32xx_irq_chip *ic = lpc32xx_mic_irqc;
|
||||||
@ -153,7 +172,7 @@ static int lpc32xx_irq_domain_map(struct irq_domain *id, unsigned int virq,
|
|||||||
struct lpc32xx_irq_chip *ic = id->host_data;
|
struct lpc32xx_irq_chip *ic = id->host_data;
|
||||||
|
|
||||||
irq_set_chip_data(virq, ic);
|
irq_set_chip_data(virq, ic);
|
||||||
irq_set_chip_and_handler(virq, &ic->chip, handle_level_irq);
|
irq_set_chip_and_handler(virq, &lpc32xx_chip, handle_level_irq);
|
||||||
irq_set_status_flags(virq, IRQ_LEVEL);
|
irq_set_status_flags(virq, IRQ_LEVEL);
|
||||||
irq_set_noprobe(virq);
|
irq_set_noprobe(virq);
|
||||||
|
|
||||||
@ -183,6 +202,7 @@ static int __init lpc32xx_of_ic_init(struct device_node *node,
|
|||||||
if (!irqc)
|
if (!irqc)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
irqc->addr = addr;
|
||||||
irqc->base = of_iomap(node, 0);
|
irqc->base = of_iomap(node, 0);
|
||||||
if (!irqc->base) {
|
if (!irqc->base) {
|
||||||
pr_err("%pOF: unable to map registers\n", node);
|
pr_err("%pOF: unable to map registers\n", node);
|
||||||
@ -190,21 +210,11 @@ static int __init lpc32xx_of_ic_init(struct device_node *node,
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
irqc->chip.irq_ack = lpc32xx_irq_ack;
|
|
||||||
irqc->chip.irq_mask = lpc32xx_irq_mask;
|
|
||||||
irqc->chip.irq_unmask = lpc32xx_irq_unmask;
|
|
||||||
irqc->chip.irq_set_type = lpc32xx_irq_set_type;
|
|
||||||
if (is_mic)
|
|
||||||
irqc->chip.name = kasprintf(GFP_KERNEL, "%08x.mic", addr);
|
|
||||||
else
|
|
||||||
irqc->chip.name = kasprintf(GFP_KERNEL, "%08x.sic", addr);
|
|
||||||
|
|
||||||
irqc->domain = irq_domain_add_linear(node, NR_LPC32XX_IC_IRQS,
|
irqc->domain = irq_domain_add_linear(node, NR_LPC32XX_IC_IRQS,
|
||||||
&lpc32xx_irq_domain_ops, irqc);
|
&lpc32xx_irq_domain_ops, irqc);
|
||||||
if (!irqc->domain) {
|
if (!irqc->domain) {
|
||||||
pr_err("unable to add irq domain\n");
|
pr_err("unable to add irq domain\n");
|
||||||
iounmap(irqc->base);
|
iounmap(irqc->base);
|
||||||
kfree(irqc->chip.name);
|
|
||||||
kfree(irqc);
|
kfree(irqc);
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_address.h>
|
#include <linux/of_address.h>
|
||||||
|
|
||||||
#define NUM_CHANNEL 8
|
#define MAX_NUM_CHANNEL 64
|
||||||
#define MAX_INPUT_MUX 256
|
#define MAX_INPUT_MUX 256
|
||||||
|
|
||||||
#define REG_EDGE_POL 0x00
|
#define REG_EDGE_POL 0x00
|
||||||
@ -26,6 +26,8 @@
|
|||||||
|
|
||||||
/* use for A1 like chips */
|
/* use for A1 like chips */
|
||||||
#define REG_PIN_A1_SEL 0x04
|
#define REG_PIN_A1_SEL 0x04
|
||||||
|
/* Used for s4 chips */
|
||||||
|
#define REG_EDGE_POL_S4 0x1c
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Note: The S905X3 datasheet reports that BOTH_EDGE is controlled by
|
* Note: The S905X3 datasheet reports that BOTH_EDGE is controlled by
|
||||||
@ -51,15 +53,22 @@ static void meson_a1_gpio_irq_sel_pin(struct meson_gpio_irq_controller *ctl,
|
|||||||
unsigned int channel,
|
unsigned int channel,
|
||||||
unsigned long hwirq);
|
unsigned long hwirq);
|
||||||
static void meson_a1_gpio_irq_init(struct meson_gpio_irq_controller *ctl);
|
static void meson_a1_gpio_irq_init(struct meson_gpio_irq_controller *ctl);
|
||||||
|
static int meson8_gpio_irq_set_type(struct meson_gpio_irq_controller *ctl,
|
||||||
|
unsigned int type, u32 *channel_hwirq);
|
||||||
|
static int meson_s4_gpio_irq_set_type(struct meson_gpio_irq_controller *ctl,
|
||||||
|
unsigned int type, u32 *channel_hwirq);
|
||||||
|
|
||||||
struct irq_ctl_ops {
|
struct irq_ctl_ops {
|
||||||
void (*gpio_irq_sel_pin)(struct meson_gpio_irq_controller *ctl,
|
void (*gpio_irq_sel_pin)(struct meson_gpio_irq_controller *ctl,
|
||||||
unsigned int channel, unsigned long hwirq);
|
unsigned int channel, unsigned long hwirq);
|
||||||
void (*gpio_irq_init)(struct meson_gpio_irq_controller *ctl);
|
void (*gpio_irq_init)(struct meson_gpio_irq_controller *ctl);
|
||||||
|
int (*gpio_irq_set_type)(struct meson_gpio_irq_controller *ctl,
|
||||||
|
unsigned int type, u32 *channel_hwirq);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct meson_gpio_irq_params {
|
struct meson_gpio_irq_params {
|
||||||
unsigned int nr_hwirq;
|
unsigned int nr_hwirq;
|
||||||
|
unsigned int nr_channels;
|
||||||
bool support_edge_both;
|
bool support_edge_both;
|
||||||
unsigned int edge_both_offset;
|
unsigned int edge_both_offset;
|
||||||
unsigned int edge_single_offset;
|
unsigned int edge_single_offset;
|
||||||
@ -68,28 +77,44 @@ struct meson_gpio_irq_params {
|
|||||||
struct irq_ctl_ops ops;
|
struct irq_ctl_ops ops;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define INIT_MESON_COMMON(irqs, init, sel) \
|
#define INIT_MESON_COMMON(irqs, init, sel, type) \
|
||||||
.nr_hwirq = irqs, \
|
.nr_hwirq = irqs, \
|
||||||
.ops = { \
|
.ops = { \
|
||||||
.gpio_irq_init = init, \
|
.gpio_irq_init = init, \
|
||||||
.gpio_irq_sel_pin = sel, \
|
.gpio_irq_sel_pin = sel, \
|
||||||
|
.gpio_irq_set_type = type, \
|
||||||
},
|
},
|
||||||
|
|
||||||
#define INIT_MESON8_COMMON_DATA(irqs) \
|
#define INIT_MESON8_COMMON_DATA(irqs) \
|
||||||
INIT_MESON_COMMON(irqs, meson_gpio_irq_init_dummy, \
|
INIT_MESON_COMMON(irqs, meson_gpio_irq_init_dummy, \
|
||||||
meson8_gpio_irq_sel_pin) \
|
meson8_gpio_irq_sel_pin, \
|
||||||
|
meson8_gpio_irq_set_type) \
|
||||||
.edge_single_offset = 0, \
|
.edge_single_offset = 0, \
|
||||||
.pol_low_offset = 16, \
|
.pol_low_offset = 16, \
|
||||||
.pin_sel_mask = 0xff, \
|
.pin_sel_mask = 0xff, \
|
||||||
|
.nr_channels = 8, \
|
||||||
|
|
||||||
#define INIT_MESON_A1_COMMON_DATA(irqs) \
|
#define INIT_MESON_A1_COMMON_DATA(irqs) \
|
||||||
INIT_MESON_COMMON(irqs, meson_a1_gpio_irq_init, \
|
INIT_MESON_COMMON(irqs, meson_a1_gpio_irq_init, \
|
||||||
meson_a1_gpio_irq_sel_pin) \
|
meson_a1_gpio_irq_sel_pin, \
|
||||||
|
meson8_gpio_irq_set_type) \
|
||||||
.support_edge_both = true, \
|
.support_edge_both = true, \
|
||||||
.edge_both_offset = 16, \
|
.edge_both_offset = 16, \
|
||||||
.edge_single_offset = 8, \
|
.edge_single_offset = 8, \
|
||||||
.pol_low_offset = 0, \
|
.pol_low_offset = 0, \
|
||||||
.pin_sel_mask = 0x7f, \
|
.pin_sel_mask = 0x7f, \
|
||||||
|
.nr_channels = 8, \
|
||||||
|
|
||||||
|
#define INIT_MESON_S4_COMMON_DATA(irqs) \
|
||||||
|
INIT_MESON_COMMON(irqs, meson_a1_gpio_irq_init, \
|
||||||
|
meson_a1_gpio_irq_sel_pin, \
|
||||||
|
meson_s4_gpio_irq_set_type) \
|
||||||
|
.support_edge_both = true, \
|
||||||
|
.edge_both_offset = 0, \
|
||||||
|
.edge_single_offset = 12, \
|
||||||
|
.pol_low_offset = 0, \
|
||||||
|
.pin_sel_mask = 0xff, \
|
||||||
|
.nr_channels = 12, \
|
||||||
|
|
||||||
static const struct meson_gpio_irq_params meson8_params = {
|
static const struct meson_gpio_irq_params meson8_params = {
|
||||||
INIT_MESON8_COMMON_DATA(134)
|
INIT_MESON8_COMMON_DATA(134)
|
||||||
@ -121,6 +146,10 @@ static const struct meson_gpio_irq_params a1_params = {
|
|||||||
INIT_MESON_A1_COMMON_DATA(62)
|
INIT_MESON_A1_COMMON_DATA(62)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct meson_gpio_irq_params s4_params = {
|
||||||
|
INIT_MESON_S4_COMMON_DATA(82)
|
||||||
|
};
|
||||||
|
|
||||||
static const struct of_device_id meson_irq_gpio_matches[] = {
|
static const struct of_device_id meson_irq_gpio_matches[] = {
|
||||||
{ .compatible = "amlogic,meson8-gpio-intc", .data = &meson8_params },
|
{ .compatible = "amlogic,meson8-gpio-intc", .data = &meson8_params },
|
||||||
{ .compatible = "amlogic,meson8b-gpio-intc", .data = &meson8b_params },
|
{ .compatible = "amlogic,meson8b-gpio-intc", .data = &meson8b_params },
|
||||||
@ -130,14 +159,15 @@ static const struct of_device_id meson_irq_gpio_matches[] = {
|
|||||||
{ .compatible = "amlogic,meson-g12a-gpio-intc", .data = &axg_params },
|
{ .compatible = "amlogic,meson-g12a-gpio-intc", .data = &axg_params },
|
||||||
{ .compatible = "amlogic,meson-sm1-gpio-intc", .data = &sm1_params },
|
{ .compatible = "amlogic,meson-sm1-gpio-intc", .data = &sm1_params },
|
||||||
{ .compatible = "amlogic,meson-a1-gpio-intc", .data = &a1_params },
|
{ .compatible = "amlogic,meson-a1-gpio-intc", .data = &a1_params },
|
||||||
|
{ .compatible = "amlogic,meson-s4-gpio-intc", .data = &s4_params },
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
|
|
||||||
struct meson_gpio_irq_controller {
|
struct meson_gpio_irq_controller {
|
||||||
const struct meson_gpio_irq_params *params;
|
const struct meson_gpio_irq_params *params;
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
u32 channel_irqs[NUM_CHANNEL];
|
u32 channel_irqs[MAX_NUM_CHANNEL];
|
||||||
DECLARE_BITMAP(channel_map, NUM_CHANNEL);
|
DECLARE_BITMAP(channel_map, MAX_NUM_CHANNEL);
|
||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -207,8 +237,8 @@ meson_gpio_irq_request_channel(struct meson_gpio_irq_controller *ctl,
|
|||||||
spin_lock_irqsave(&ctl->lock, flags);
|
spin_lock_irqsave(&ctl->lock, flags);
|
||||||
|
|
||||||
/* Find a free channel */
|
/* Find a free channel */
|
||||||
idx = find_first_zero_bit(ctl->channel_map, NUM_CHANNEL);
|
idx = find_first_zero_bit(ctl->channel_map, ctl->params->nr_channels);
|
||||||
if (idx >= NUM_CHANNEL) {
|
if (idx >= ctl->params->nr_channels) {
|
||||||
spin_unlock_irqrestore(&ctl->lock, flags);
|
spin_unlock_irqrestore(&ctl->lock, flags);
|
||||||
pr_err("No channel available\n");
|
pr_err("No channel available\n");
|
||||||
return -ENOSPC;
|
return -ENOSPC;
|
||||||
@ -256,9 +286,8 @@ meson_gpio_irq_release_channel(struct meson_gpio_irq_controller *ctl,
|
|||||||
clear_bit(idx, ctl->channel_map);
|
clear_bit(idx, ctl->channel_map);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int meson_gpio_irq_type_setup(struct meson_gpio_irq_controller *ctl,
|
static int meson8_gpio_irq_set_type(struct meson_gpio_irq_controller *ctl,
|
||||||
unsigned int type,
|
unsigned int type, u32 *channel_hwirq)
|
||||||
u32 *channel_hwirq)
|
|
||||||
{
|
{
|
||||||
u32 val = 0;
|
u32 val = 0;
|
||||||
unsigned int idx;
|
unsigned int idx;
|
||||||
@ -299,6 +328,51 @@ static int meson_gpio_irq_type_setup(struct meson_gpio_irq_controller *ctl,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* gpio irq relative registers for s4
|
||||||
|
* -PADCTRL_GPIO_IRQ_CTRL0
|
||||||
|
* bit[31]: enable/disable all the irq lines
|
||||||
|
* bit[12-23]: single edge trigger
|
||||||
|
* bit[0-11]: polarity trigger
|
||||||
|
*
|
||||||
|
* -PADCTRL_GPIO_IRQ_CTRL[X]
|
||||||
|
* bit[0-16]: 7 bits to choose gpio source for irq line 2*[X] - 2
|
||||||
|
* bit[16-22]:7 bits to choose gpio source for irq line 2*[X] - 1
|
||||||
|
* where X = 1-6
|
||||||
|
*
|
||||||
|
* -PADCTRL_GPIO_IRQ_CTRL[7]
|
||||||
|
* bit[0-11]: both edge trigger
|
||||||
|
*/
|
||||||
|
static int meson_s4_gpio_irq_set_type(struct meson_gpio_irq_controller *ctl,
|
||||||
|
unsigned int type, u32 *channel_hwirq)
|
||||||
|
{
|
||||||
|
u32 val = 0;
|
||||||
|
unsigned int idx;
|
||||||
|
|
||||||
|
idx = meson_gpio_irq_get_channel_idx(ctl, channel_hwirq);
|
||||||
|
|
||||||
|
type &= IRQ_TYPE_SENSE_MASK;
|
||||||
|
|
||||||
|
meson_gpio_irq_update_bits(ctl, REG_EDGE_POL_S4, BIT(idx), 0);
|
||||||
|
|
||||||
|
if (type == IRQ_TYPE_EDGE_BOTH) {
|
||||||
|
val |= BIT(ctl->params->edge_both_offset + idx);
|
||||||
|
meson_gpio_irq_update_bits(ctl, REG_EDGE_POL_S4,
|
||||||
|
BIT(ctl->params->edge_both_offset + idx), val);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_EDGE_FALLING))
|
||||||
|
val |= BIT(ctl->params->pol_low_offset + idx);
|
||||||
|
|
||||||
|
if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING))
|
||||||
|
val |= BIT(ctl->params->edge_single_offset + idx);
|
||||||
|
|
||||||
|
meson_gpio_irq_update_bits(ctl, REG_EDGE_POL,
|
||||||
|
BIT(idx) | BIT(12 + idx), val);
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
static unsigned int meson_gpio_irq_type_output(unsigned int type)
|
static unsigned int meson_gpio_irq_type_output(unsigned int type)
|
||||||
{
|
{
|
||||||
unsigned int sense = type & IRQ_TYPE_SENSE_MASK;
|
unsigned int sense = type & IRQ_TYPE_SENSE_MASK;
|
||||||
@ -323,7 +397,7 @@ static int meson_gpio_irq_set_type(struct irq_data *data, unsigned int type)
|
|||||||
u32 *channel_hwirq = irq_data_get_irq_chip_data(data);
|
u32 *channel_hwirq = irq_data_get_irq_chip_data(data);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = meson_gpio_irq_type_setup(ctl, type, channel_hwirq);
|
ret = ctl->params->ops.gpio_irq_set_type(ctl, type, channel_hwirq);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
@ -450,10 +524,10 @@ static int meson_gpio_irq_parse_dt(struct device_node *node, struct meson_gpio_i
|
|||||||
ret = of_property_read_variable_u32_array(node,
|
ret = of_property_read_variable_u32_array(node,
|
||||||
"amlogic,channel-interrupts",
|
"amlogic,channel-interrupts",
|
||||||
ctl->channel_irqs,
|
ctl->channel_irqs,
|
||||||
NUM_CHANNEL,
|
ctl->params->nr_channels,
|
||||||
NUM_CHANNEL);
|
ctl->params->nr_channels);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
pr_err("can't get %d channel interrupts\n", NUM_CHANNEL);
|
pr_err("can't get %d channel interrupts\n", ctl->params->nr_channels);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -507,7 +581,7 @@ static int meson_gpio_irq_of_init(struct device_node *node, struct device_node *
|
|||||||
}
|
}
|
||||||
|
|
||||||
pr_info("%d to %d gpio interrupt mux initialized\n",
|
pr_info("%d to %d gpio interrupt mux initialized\n",
|
||||||
ctl->params->nr_hwirq, NUM_CHANNEL);
|
ctl->params->nr_hwirq, ctl->params->nr_channels);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of_irq.h>
|
#include <linux/of_irq.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/seq_file.h>
|
||||||
|
|
||||||
#define PIC_CAUSE 0x0
|
#define PIC_CAUSE 0x0
|
||||||
#define PIC_MASK 0x4
|
#define PIC_MASK 0x4
|
||||||
@ -29,7 +30,7 @@ struct mvebu_pic {
|
|||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
u32 parent_irq;
|
u32 parent_irq;
|
||||||
struct irq_domain *domain;
|
struct irq_domain *domain;
|
||||||
struct irq_chip irq_chip;
|
struct platform_device *pdev;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void mvebu_pic_reset(struct mvebu_pic *pic)
|
static void mvebu_pic_reset(struct mvebu_pic *pic)
|
||||||
@ -66,6 +67,20 @@ static void mvebu_pic_unmask_irq(struct irq_data *d)
|
|||||||
writel(reg, pic->base + PIC_MASK);
|
writel(reg, pic->base + PIC_MASK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void mvebu_pic_print_chip(struct irq_data *d, struct seq_file *p)
|
||||||
|
{
|
||||||
|
struct mvebu_pic *pic = irq_data_get_irq_chip_data(d);
|
||||||
|
|
||||||
|
seq_printf(p, dev_name(&pic->pdev->dev));
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct irq_chip mvebu_pic_chip = {
|
||||||
|
.irq_mask = mvebu_pic_mask_irq,
|
||||||
|
.irq_unmask = mvebu_pic_unmask_irq,
|
||||||
|
.irq_eoi = mvebu_pic_eoi_irq,
|
||||||
|
.irq_print_chip = mvebu_pic_print_chip,
|
||||||
|
};
|
||||||
|
|
||||||
static int mvebu_pic_irq_map(struct irq_domain *domain, unsigned int virq,
|
static int mvebu_pic_irq_map(struct irq_domain *domain, unsigned int virq,
|
||||||
irq_hw_number_t hwirq)
|
irq_hw_number_t hwirq)
|
||||||
{
|
{
|
||||||
@ -73,8 +88,7 @@ static int mvebu_pic_irq_map(struct irq_domain *domain, unsigned int virq,
|
|||||||
|
|
||||||
irq_set_percpu_devid(virq);
|
irq_set_percpu_devid(virq);
|
||||||
irq_set_chip_data(virq, pic);
|
irq_set_chip_data(virq, pic);
|
||||||
irq_set_chip_and_handler(virq, &pic->irq_chip,
|
irq_set_chip_and_handler(virq, &mvebu_pic_chip, handle_percpu_devid_irq);
|
||||||
handle_percpu_devid_irq);
|
|
||||||
irq_set_status_flags(virq, IRQ_LEVEL);
|
irq_set_status_flags(virq, IRQ_LEVEL);
|
||||||
irq_set_probe(virq);
|
irq_set_probe(virq);
|
||||||
|
|
||||||
@ -120,22 +134,16 @@ static int mvebu_pic_probe(struct platform_device *pdev)
|
|||||||
{
|
{
|
||||||
struct device_node *node = pdev->dev.of_node;
|
struct device_node *node = pdev->dev.of_node;
|
||||||
struct mvebu_pic *pic;
|
struct mvebu_pic *pic;
|
||||||
struct irq_chip *irq_chip;
|
|
||||||
|
|
||||||
pic = devm_kzalloc(&pdev->dev, sizeof(struct mvebu_pic), GFP_KERNEL);
|
pic = devm_kzalloc(&pdev->dev, sizeof(struct mvebu_pic), GFP_KERNEL);
|
||||||
if (!pic)
|
if (!pic)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
pic->pdev = pdev;
|
||||||
pic->base = devm_platform_ioremap_resource(pdev, 0);
|
pic->base = devm_platform_ioremap_resource(pdev, 0);
|
||||||
if (IS_ERR(pic->base))
|
if (IS_ERR(pic->base))
|
||||||
return PTR_ERR(pic->base);
|
return PTR_ERR(pic->base);
|
||||||
|
|
||||||
irq_chip = &pic->irq_chip;
|
|
||||||
irq_chip->name = dev_name(&pdev->dev);
|
|
||||||
irq_chip->irq_mask = mvebu_pic_mask_irq;
|
|
||||||
irq_chip->irq_unmask = mvebu_pic_unmask_irq;
|
|
||||||
irq_chip->irq_eoi = mvebu_pic_eoi_irq;
|
|
||||||
|
|
||||||
pic->parent_irq = irq_of_parse_and_map(node, 0);
|
pic->parent_irq = irq_of_parse_and_map(node, 0);
|
||||||
if (pic->parent_irq <= 0) {
|
if (pic->parent_irq <= 0) {
|
||||||
dev_err(&pdev->dev, "Failed to parse parent interrupt\n");
|
dev_err(&pdev->dev, "Failed to parse parent interrupt\n");
|
||||||
|
@ -107,6 +107,7 @@ static int __init nvic_of_init(struct device_node *node,
|
|||||||
|
|
||||||
if (!nvic_irq_domain) {
|
if (!nvic_irq_domain) {
|
||||||
pr_warn("Failed to allocate irq domain\n");
|
pr_warn("Failed to allocate irq domain\n");
|
||||||
|
iounmap(nvic_base);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,6 +117,7 @@ static int __init nvic_of_init(struct device_node *node,
|
|||||||
if (ret) {
|
if (ret) {
|
||||||
pr_warn("Failed to allocate irq chips\n");
|
pr_warn("Failed to allocate irq chips\n");
|
||||||
irq_domain_remove(nvic_irq_domain);
|
irq_domain_remove(nvic_irq_domain);
|
||||||
|
iounmap(nvic_base);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
461
drivers/irqchip/irq-qcom-mpm.c
Normal file
461
drivers/irqchip/irq-qcom-mpm.c
Normal file
@ -0,0 +1,461 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021, Linaro Limited
|
||||||
|
* Copyright (c) 2010-2020, The Linux Foundation. All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/irqchip.h>
|
||||||
|
#include <linux/irqdomain.h>
|
||||||
|
#include <linux/mailbox_client.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/pm_domain.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/soc/qcom/irq.h>
|
||||||
|
#include <linux/spinlock.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is the driver for Qualcomm MPM (MSM Power Manager) interrupt controller,
|
||||||
|
* which is commonly found on Qualcomm SoCs built on the RPM architecture.
|
||||||
|
* Sitting in always-on domain, MPM monitors the wakeup interrupts when SoC is
|
||||||
|
* asleep, and wakes up the AP when one of those interrupts occurs. This driver
|
||||||
|
* doesn't directly access physical MPM registers though. Instead, the access
|
||||||
|
* is bridged via a piece of internal memory (SRAM) that is accessible to both
|
||||||
|
* AP and RPM. This piece of memory is called 'vMPM' in the driver.
|
||||||
|
*
|
||||||
|
* When SoC is awake, the vMPM is owned by AP and the register setup by this
|
||||||
|
* driver all happens on vMPM. When AP is about to get power collapsed, the
|
||||||
|
* driver sends a mailbox notification to RPM, which will take over the vMPM
|
||||||
|
* ownership and dump vMPM into physical MPM registers. On wakeup, AP is woken
|
||||||
|
* up by a MPM pin/interrupt, and RPM will copy STATUS registers into vMPM.
|
||||||
|
* Then AP start owning vMPM again.
|
||||||
|
*
|
||||||
|
* vMPM register map:
|
||||||
|
*
|
||||||
|
* 31 0
|
||||||
|
* +--------------------------------+
|
||||||
|
* | TIMER0 | 0x00
|
||||||
|
* +--------------------------------+
|
||||||
|
* | TIMER1 | 0x04
|
||||||
|
* +--------------------------------+
|
||||||
|
* | ENABLE0 | 0x08
|
||||||
|
* +--------------------------------+
|
||||||
|
* | ... | ...
|
||||||
|
* +--------------------------------+
|
||||||
|
* | ENABLEn |
|
||||||
|
* +--------------------------------+
|
||||||
|
* | FALLING_EDGE0 |
|
||||||
|
* +--------------------------------+
|
||||||
|
* | ... |
|
||||||
|
* +--------------------------------+
|
||||||
|
* | STATUSn |
|
||||||
|
* +--------------------------------+
|
||||||
|
*
|
||||||
|
* n = DIV_ROUND_UP(pin_cnt, 32)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define MPM_REG_ENABLE 0
|
||||||
|
#define MPM_REG_FALLING_EDGE 1
|
||||||
|
#define MPM_REG_RISING_EDGE 2
|
||||||
|
#define MPM_REG_POLARITY 3
|
||||||
|
#define MPM_REG_STATUS 4
|
||||||
|
|
||||||
|
/* MPM pin map to GIC hwirq */
|
||||||
|
struct mpm_gic_map {
|
||||||
|
int pin;
|
||||||
|
irq_hw_number_t hwirq;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct qcom_mpm_priv {
|
||||||
|
void __iomem *base;
|
||||||
|
raw_spinlock_t lock;
|
||||||
|
struct mbox_client mbox_client;
|
||||||
|
struct mbox_chan *mbox_chan;
|
||||||
|
struct mpm_gic_map *maps;
|
||||||
|
unsigned int map_cnt;
|
||||||
|
unsigned int reg_stride;
|
||||||
|
struct irq_domain *domain;
|
||||||
|
struct generic_pm_domain genpd;
|
||||||
|
};
|
||||||
|
|
||||||
|
static u32 qcom_mpm_read(struct qcom_mpm_priv *priv, unsigned int reg,
|
||||||
|
unsigned int index)
|
||||||
|
{
|
||||||
|
unsigned int offset = (reg * priv->reg_stride + index + 2) * 4;
|
||||||
|
|
||||||
|
return readl_relaxed(priv->base + offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void qcom_mpm_write(struct qcom_mpm_priv *priv, unsigned int reg,
|
||||||
|
unsigned int index, u32 val)
|
||||||
|
{
|
||||||
|
unsigned int offset = (reg * priv->reg_stride + index + 2) * 4;
|
||||||
|
|
||||||
|
writel_relaxed(val, priv->base + offset);
|
||||||
|
|
||||||
|
/* Ensure the write is completed */
|
||||||
|
wmb();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void qcom_mpm_enable_irq(struct irq_data *d, bool en)
|
||||||
|
{
|
||||||
|
struct qcom_mpm_priv *priv = d->chip_data;
|
||||||
|
int pin = d->hwirq;
|
||||||
|
unsigned int index = pin / 32;
|
||||||
|
unsigned int shift = pin % 32;
|
||||||
|
unsigned long flags, val;
|
||||||
|
|
||||||
|
raw_spin_lock_irqsave(&priv->lock, flags);
|
||||||
|
|
||||||
|
val = qcom_mpm_read(priv, MPM_REG_ENABLE, index);
|
||||||
|
__assign_bit(shift, &val, en);
|
||||||
|
qcom_mpm_write(priv, MPM_REG_ENABLE, index, val);
|
||||||
|
|
||||||
|
raw_spin_unlock_irqrestore(&priv->lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void qcom_mpm_mask(struct irq_data *d)
|
||||||
|
{
|
||||||
|
qcom_mpm_enable_irq(d, false);
|
||||||
|
|
||||||
|
if (d->parent_data)
|
||||||
|
irq_chip_mask_parent(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void qcom_mpm_unmask(struct irq_data *d)
|
||||||
|
{
|
||||||
|
qcom_mpm_enable_irq(d, true);
|
||||||
|
|
||||||
|
if (d->parent_data)
|
||||||
|
irq_chip_unmask_parent(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mpm_set_type(struct qcom_mpm_priv *priv, bool set, unsigned int reg,
|
||||||
|
unsigned int index, unsigned int shift)
|
||||||
|
{
|
||||||
|
unsigned long flags, val;
|
||||||
|
|
||||||
|
raw_spin_lock_irqsave(&priv->lock, flags);
|
||||||
|
|
||||||
|
val = qcom_mpm_read(priv, reg, index);
|
||||||
|
__assign_bit(shift, &val, set);
|
||||||
|
qcom_mpm_write(priv, reg, index, val);
|
||||||
|
|
||||||
|
raw_spin_unlock_irqrestore(&priv->lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int qcom_mpm_set_type(struct irq_data *d, unsigned int type)
|
||||||
|
{
|
||||||
|
struct qcom_mpm_priv *priv = d->chip_data;
|
||||||
|
int pin = d->hwirq;
|
||||||
|
unsigned int index = pin / 32;
|
||||||
|
unsigned int shift = pin % 32;
|
||||||
|
|
||||||
|
if (type & IRQ_TYPE_EDGE_RISING)
|
||||||
|
mpm_set_type(priv, true, MPM_REG_RISING_EDGE, index, shift);
|
||||||
|
else
|
||||||
|
mpm_set_type(priv, false, MPM_REG_RISING_EDGE, index, shift);
|
||||||
|
|
||||||
|
if (type & IRQ_TYPE_EDGE_FALLING)
|
||||||
|
mpm_set_type(priv, true, MPM_REG_FALLING_EDGE, index, shift);
|
||||||
|
else
|
||||||
|
mpm_set_type(priv, false, MPM_REG_FALLING_EDGE, index, shift);
|
||||||
|
|
||||||
|
if (type & IRQ_TYPE_LEVEL_HIGH)
|
||||||
|
mpm_set_type(priv, true, MPM_REG_POLARITY, index, shift);
|
||||||
|
else
|
||||||
|
mpm_set_type(priv, false, MPM_REG_POLARITY, index, shift);
|
||||||
|
|
||||||
|
if (!d->parent_data)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (type & IRQ_TYPE_EDGE_BOTH)
|
||||||
|
type = IRQ_TYPE_EDGE_RISING;
|
||||||
|
|
||||||
|
if (type & IRQ_TYPE_LEVEL_MASK)
|
||||||
|
type = IRQ_TYPE_LEVEL_HIGH;
|
||||||
|
|
||||||
|
return irq_chip_set_type_parent(d, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct irq_chip qcom_mpm_chip = {
|
||||||
|
.name = "mpm",
|
||||||
|
.irq_eoi = irq_chip_eoi_parent,
|
||||||
|
.irq_mask = qcom_mpm_mask,
|
||||||
|
.irq_unmask = qcom_mpm_unmask,
|
||||||
|
.irq_retrigger = irq_chip_retrigger_hierarchy,
|
||||||
|
.irq_set_type = qcom_mpm_set_type,
|
||||||
|
.irq_set_affinity = irq_chip_set_affinity_parent,
|
||||||
|
.flags = IRQCHIP_MASK_ON_SUSPEND |
|
||||||
|
IRQCHIP_SKIP_SET_WAKE,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct mpm_gic_map *get_mpm_gic_map(struct qcom_mpm_priv *priv, int pin)
|
||||||
|
{
|
||||||
|
struct mpm_gic_map *maps = priv->maps;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < priv->map_cnt; i++) {
|
||||||
|
if (maps[i].pin == pin)
|
||||||
|
return &maps[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int qcom_mpm_alloc(struct irq_domain *domain, unsigned int virq,
|
||||||
|
unsigned int nr_irqs, void *data)
|
||||||
|
{
|
||||||
|
struct qcom_mpm_priv *priv = domain->host_data;
|
||||||
|
struct irq_fwspec *fwspec = data;
|
||||||
|
struct irq_fwspec parent_fwspec;
|
||||||
|
struct mpm_gic_map *map;
|
||||||
|
irq_hw_number_t pin;
|
||||||
|
unsigned int type;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = irq_domain_translate_twocell(domain, fwspec, &pin, &type);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = irq_domain_set_hwirq_and_chip(domain, virq, pin,
|
||||||
|
&qcom_mpm_chip, priv);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
map = get_mpm_gic_map(priv, pin);
|
||||||
|
if (map == NULL)
|
||||||
|
return irq_domain_disconnect_hierarchy(domain->parent, virq);
|
||||||
|
|
||||||
|
if (type & IRQ_TYPE_EDGE_BOTH)
|
||||||
|
type = IRQ_TYPE_EDGE_RISING;
|
||||||
|
|
||||||
|
if (type & IRQ_TYPE_LEVEL_MASK)
|
||||||
|
type = IRQ_TYPE_LEVEL_HIGH;
|
||||||
|
|
||||||
|
parent_fwspec.fwnode = domain->parent->fwnode;
|
||||||
|
parent_fwspec.param_count = 3;
|
||||||
|
parent_fwspec.param[0] = 0;
|
||||||
|
parent_fwspec.param[1] = map->hwirq;
|
||||||
|
parent_fwspec.param[2] = type;
|
||||||
|
|
||||||
|
return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs,
|
||||||
|
&parent_fwspec);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct irq_domain_ops qcom_mpm_ops = {
|
||||||
|
.alloc = qcom_mpm_alloc,
|
||||||
|
.free = irq_domain_free_irqs_common,
|
||||||
|
.translate = irq_domain_translate_twocell,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Triggered by RPM when system resumes from deep sleep */
|
||||||
|
static irqreturn_t qcom_mpm_handler(int irq, void *dev_id)
|
||||||
|
{
|
||||||
|
struct qcom_mpm_priv *priv = dev_id;
|
||||||
|
unsigned long enable, pending;
|
||||||
|
irqreturn_t ret = IRQ_NONE;
|
||||||
|
unsigned long flags;
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
for (i = 0; i < priv->reg_stride; i++) {
|
||||||
|
raw_spin_lock_irqsave(&priv->lock, flags);
|
||||||
|
enable = qcom_mpm_read(priv, MPM_REG_ENABLE, i);
|
||||||
|
pending = qcom_mpm_read(priv, MPM_REG_STATUS, i);
|
||||||
|
pending &= enable;
|
||||||
|
raw_spin_unlock_irqrestore(&priv->lock, flags);
|
||||||
|
|
||||||
|
for_each_set_bit(j, &pending, 32) {
|
||||||
|
unsigned int pin = 32 * i + j;
|
||||||
|
struct irq_desc *desc = irq_resolve_mapping(priv->domain, pin);
|
||||||
|
struct irq_data *d = &desc->irq_data;
|
||||||
|
|
||||||
|
if (!irqd_is_level_type(d))
|
||||||
|
irq_set_irqchip_state(d->irq,
|
||||||
|
IRQCHIP_STATE_PENDING, true);
|
||||||
|
ret = IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mpm_pd_power_off(struct generic_pm_domain *genpd)
|
||||||
|
{
|
||||||
|
struct qcom_mpm_priv *priv = container_of(genpd, struct qcom_mpm_priv,
|
||||||
|
genpd);
|
||||||
|
int i, ret;
|
||||||
|
|
||||||
|
for (i = 0; i < priv->reg_stride; i++)
|
||||||
|
qcom_mpm_write(priv, MPM_REG_STATUS, i, 0);
|
||||||
|
|
||||||
|
/* Notify RPM to write vMPM into HW */
|
||||||
|
ret = mbox_send_message(priv->mbox_chan, NULL);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool gic_hwirq_is_mapped(struct mpm_gic_map *maps, int cnt, u32 hwirq)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < cnt; i++)
|
||||||
|
if (maps[i].hwirq == hwirq)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int qcom_mpm_init(struct device_node *np, struct device_node *parent)
|
||||||
|
{
|
||||||
|
struct platform_device *pdev = of_find_device_by_node(np);
|
||||||
|
struct device *dev = &pdev->dev;
|
||||||
|
struct irq_domain *parent_domain;
|
||||||
|
struct generic_pm_domain *genpd;
|
||||||
|
struct qcom_mpm_priv *priv;
|
||||||
|
unsigned int pin_cnt;
|
||||||
|
int i, irq;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||||
|
if (!priv)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
ret = of_property_read_u32(np, "qcom,mpm-pin-count", &pin_cnt);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "failed to read qcom,mpm-pin-count: %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
priv->reg_stride = DIV_ROUND_UP(pin_cnt, 32);
|
||||||
|
|
||||||
|
ret = of_property_count_u32_elems(np, "qcom,mpm-pin-map");
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(dev, "failed to read qcom,mpm-pin-map: %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret % 2) {
|
||||||
|
dev_err(dev, "invalid qcom,mpm-pin-map\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
priv->map_cnt = ret / 2;
|
||||||
|
priv->maps = devm_kcalloc(dev, priv->map_cnt, sizeof(*priv->maps),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!priv->maps)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
for (i = 0; i < priv->map_cnt; i++) {
|
||||||
|
u32 pin, hwirq;
|
||||||
|
|
||||||
|
of_property_read_u32_index(np, "qcom,mpm-pin-map", i * 2, &pin);
|
||||||
|
of_property_read_u32_index(np, "qcom,mpm-pin-map", i * 2 + 1, &hwirq);
|
||||||
|
|
||||||
|
if (gic_hwirq_is_mapped(priv->maps, i, hwirq)) {
|
||||||
|
dev_warn(dev, "failed to map pin %d as GIC hwirq %d is already mapped\n",
|
||||||
|
pin, hwirq);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
priv->maps[i].pin = pin;
|
||||||
|
priv->maps[i].hwirq = hwirq;
|
||||||
|
}
|
||||||
|
|
||||||
|
raw_spin_lock_init(&priv->lock);
|
||||||
|
|
||||||
|
priv->base = devm_platform_ioremap_resource(pdev, 0);
|
||||||
|
if (!priv->base)
|
||||||
|
return PTR_ERR(priv->base);
|
||||||
|
|
||||||
|
for (i = 0; i < priv->reg_stride; i++) {
|
||||||
|
qcom_mpm_write(priv, MPM_REG_ENABLE, i, 0);
|
||||||
|
qcom_mpm_write(priv, MPM_REG_FALLING_EDGE, i, 0);
|
||||||
|
qcom_mpm_write(priv, MPM_REG_RISING_EDGE, i, 0);
|
||||||
|
qcom_mpm_write(priv, MPM_REG_POLARITY, i, 0);
|
||||||
|
qcom_mpm_write(priv, MPM_REG_STATUS, i, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
irq = platform_get_irq(pdev, 0);
|
||||||
|
if (irq < 0)
|
||||||
|
return irq;
|
||||||
|
|
||||||
|
genpd = &priv->genpd;
|
||||||
|
genpd->flags = GENPD_FLAG_IRQ_SAFE;
|
||||||
|
genpd->power_off = mpm_pd_power_off;
|
||||||
|
|
||||||
|
genpd->name = devm_kasprintf(dev, GFP_KERNEL, "%s", dev_name(dev));
|
||||||
|
if (!genpd->name)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
ret = pm_genpd_init(genpd, NULL, false);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "failed to init genpd: %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = of_genpd_add_provider_simple(np, genpd);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "failed to add genpd provider: %d\n", ret);
|
||||||
|
goto remove_genpd;
|
||||||
|
}
|
||||||
|
|
||||||
|
priv->mbox_client.dev = dev;
|
||||||
|
priv->mbox_chan = mbox_request_channel(&priv->mbox_client, 0);
|
||||||
|
if (IS_ERR(priv->mbox_chan)) {
|
||||||
|
ret = PTR_ERR(priv->mbox_chan);
|
||||||
|
dev_err(dev, "failed to acquire IPC channel: %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
parent_domain = irq_find_host(parent);
|
||||||
|
if (!parent_domain) {
|
||||||
|
dev_err(dev, "failed to find MPM parent domain\n");
|
||||||
|
ret = -ENXIO;
|
||||||
|
goto free_mbox;
|
||||||
|
}
|
||||||
|
|
||||||
|
priv->domain = irq_domain_create_hierarchy(parent_domain,
|
||||||
|
IRQ_DOMAIN_FLAG_QCOM_MPM_WAKEUP, pin_cnt,
|
||||||
|
of_node_to_fwnode(np), &qcom_mpm_ops, priv);
|
||||||
|
if (!priv->domain) {
|
||||||
|
dev_err(dev, "failed to create MPM domain\n");
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto free_mbox;
|
||||||
|
}
|
||||||
|
|
||||||
|
irq_domain_update_bus_token(priv->domain, DOMAIN_BUS_WAKEUP);
|
||||||
|
|
||||||
|
ret = devm_request_irq(dev, irq, qcom_mpm_handler, IRQF_NO_SUSPEND,
|
||||||
|
"qcom_mpm", priv);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "failed to request irq: %d\n", ret);
|
||||||
|
goto remove_domain;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
remove_domain:
|
||||||
|
irq_domain_remove(priv->domain);
|
||||||
|
free_mbox:
|
||||||
|
mbox_free_channel(priv->mbox_chan);
|
||||||
|
remove_genpd:
|
||||||
|
pm_genpd_remove(genpd);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
IRQCHIP_PLATFORM_DRIVER_BEGIN(qcom_mpm)
|
||||||
|
IRQCHIP_MATCH("qcom,mpm", qcom_mpm_init)
|
||||||
|
IRQCHIP_PLATFORM_DRIVER_END(qcom_mpm)
|
||||||
|
MODULE_DESCRIPTION("Qualcomm Technologies, Inc. MSM Power Manager");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
@ -508,7 +508,6 @@ static int intc_irqpin_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
irq_chip = &p->irq_chip;
|
irq_chip = &p->irq_chip;
|
||||||
irq_chip->name = "intc-irqpin";
|
irq_chip->name = "intc-irqpin";
|
||||||
irq_chip->parent_device = dev;
|
|
||||||
irq_chip->irq_mask = disable_fn;
|
irq_chip->irq_mask = disable_fn;
|
||||||
irq_chip->irq_unmask = enable_fn;
|
irq_chip->irq_unmask = enable_fn;
|
||||||
irq_chip->irq_set_type = intc_irqpin_irq_set_type;
|
irq_chip->irq_set_type = intc_irqpin_irq_set_type;
|
||||||
@ -523,6 +522,8 @@ static int intc_irqpin_probe(struct platform_device *pdev)
|
|||||||
goto err0;
|
goto err0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
irq_domain_set_pm_device(p->irq_domain, dev);
|
||||||
|
|
||||||
if (p->shared_irqs) {
|
if (p->shared_irqs) {
|
||||||
/* request one shared interrupt */
|
/* request one shared interrupt */
|
||||||
if (devm_request_irq(dev, p->irq[0].requested_irq,
|
if (devm_request_irq(dev, p->irq[0].requested_irq,
|
||||||
|
@ -188,13 +188,14 @@ static int irqc_probe(struct platform_device *pdev)
|
|||||||
p->gc->reg_base = p->cpu_int_base;
|
p->gc->reg_base = p->cpu_int_base;
|
||||||
p->gc->chip_types[0].regs.enable = IRQC_EN_SET;
|
p->gc->chip_types[0].regs.enable = IRQC_EN_SET;
|
||||||
p->gc->chip_types[0].regs.disable = IRQC_EN_STS;
|
p->gc->chip_types[0].regs.disable = IRQC_EN_STS;
|
||||||
p->gc->chip_types[0].chip.parent_device = dev;
|
|
||||||
p->gc->chip_types[0].chip.irq_mask = irq_gc_mask_disable_reg;
|
p->gc->chip_types[0].chip.irq_mask = irq_gc_mask_disable_reg;
|
||||||
p->gc->chip_types[0].chip.irq_unmask = irq_gc_unmask_enable_reg;
|
p->gc->chip_types[0].chip.irq_unmask = irq_gc_unmask_enable_reg;
|
||||||
p->gc->chip_types[0].chip.irq_set_type = irqc_irq_set_type;
|
p->gc->chip_types[0].chip.irq_set_type = irqc_irq_set_type;
|
||||||
p->gc->chip_types[0].chip.irq_set_wake = irqc_irq_set_wake;
|
p->gc->chip_types[0].chip.irq_set_wake = irqc_irq_set_wake;
|
||||||
p->gc->chip_types[0].chip.flags = IRQCHIP_MASK_ON_SUSPEND;
|
p->gc->chip_types[0].chip.flags = IRQCHIP_MASK_ON_SUSPEND;
|
||||||
|
|
||||||
|
irq_domain_set_pm_device(p->irq_domain, dev);
|
||||||
|
|
||||||
/* request interrupts one by one */
|
/* request interrupts one by one */
|
||||||
for (k = 0; k < p->number_of_irqs; k++) {
|
for (k = 0; k < p->number_of_irqs; k++) {
|
||||||
if (devm_request_irq(dev, p->irq[k].requested_irq,
|
if (devm_request_irq(dev, p->irq[k].requested_irq,
|
||||||
|
@ -44,8 +44,8 @@
|
|||||||
* Each hart context has a vector of interrupt enable bits associated with it.
|
* Each hart context has a vector of interrupt enable bits associated with it.
|
||||||
* There's one bit for each interrupt source.
|
* There's one bit for each interrupt source.
|
||||||
*/
|
*/
|
||||||
#define ENABLE_BASE 0x2000
|
#define CONTEXT_ENABLE_BASE 0x2000
|
||||||
#define ENABLE_PER_HART 0x80
|
#define CONTEXT_ENABLE_SIZE 0x80
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Each hart context has a set of control registers associated with it. Right
|
* Each hart context has a set of control registers associated with it. Right
|
||||||
@ -53,7 +53,7 @@
|
|||||||
* take an interrupt, and a register to claim interrupts.
|
* take an interrupt, and a register to claim interrupts.
|
||||||
*/
|
*/
|
||||||
#define CONTEXT_BASE 0x200000
|
#define CONTEXT_BASE 0x200000
|
||||||
#define CONTEXT_PER_HART 0x1000
|
#define CONTEXT_SIZE 0x1000
|
||||||
#define CONTEXT_THRESHOLD 0x00
|
#define CONTEXT_THRESHOLD 0x00
|
||||||
#define CONTEXT_CLAIM 0x04
|
#define CONTEXT_CLAIM 0x04
|
||||||
|
|
||||||
@ -81,17 +81,21 @@ static int plic_parent_irq __ro_after_init;
|
|||||||
static bool plic_cpuhp_setup_done __ro_after_init;
|
static bool plic_cpuhp_setup_done __ro_after_init;
|
||||||
static DEFINE_PER_CPU(struct plic_handler, plic_handlers);
|
static DEFINE_PER_CPU(struct plic_handler, plic_handlers);
|
||||||
|
|
||||||
static inline void plic_toggle(struct plic_handler *handler,
|
static void __plic_toggle(void __iomem *enable_base, int hwirq, int enable)
|
||||||
int hwirq, int enable)
|
|
||||||
{
|
{
|
||||||
u32 __iomem *reg = handler->enable_base + (hwirq / 32) * sizeof(u32);
|
u32 __iomem *reg = enable_base + (hwirq / 32) * sizeof(u32);
|
||||||
u32 hwirq_mask = 1 << (hwirq % 32);
|
u32 hwirq_mask = 1 << (hwirq % 32);
|
||||||
|
|
||||||
raw_spin_lock(&handler->enable_lock);
|
|
||||||
if (enable)
|
if (enable)
|
||||||
writel(readl(reg) | hwirq_mask, reg);
|
writel(readl(reg) | hwirq_mask, reg);
|
||||||
else
|
else
|
||||||
writel(readl(reg) & ~hwirq_mask, reg);
|
writel(readl(reg) & ~hwirq_mask, reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void plic_toggle(struct plic_handler *handler, int hwirq, int enable)
|
||||||
|
{
|
||||||
|
raw_spin_lock(&handler->enable_lock);
|
||||||
|
__plic_toggle(handler->enable_base, hwirq, enable);
|
||||||
raw_spin_unlock(&handler->enable_lock);
|
raw_spin_unlock(&handler->enable_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -324,8 +328,18 @@ static int __init plic_init(struct device_node *node,
|
|||||||
* Skip contexts other than external interrupts for our
|
* Skip contexts other than external interrupts for our
|
||||||
* privilege level.
|
* privilege level.
|
||||||
*/
|
*/
|
||||||
if (parent.args[0] != RV_IRQ_EXT)
|
if (parent.args[0] != RV_IRQ_EXT) {
|
||||||
|
/* Disable S-mode enable bits if running in M-mode. */
|
||||||
|
if (IS_ENABLED(CONFIG_RISCV_M_MODE)) {
|
||||||
|
void __iomem *enable_base = priv->regs +
|
||||||
|
CONTEXT_ENABLE_BASE +
|
||||||
|
i * CONTEXT_ENABLE_SIZE;
|
||||||
|
|
||||||
|
for (hwirq = 1; hwirq <= nr_irqs; hwirq++)
|
||||||
|
__plic_toggle(enable_base, hwirq, 0);
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
hartid = riscv_of_parent_hartid(parent.np);
|
hartid = riscv_of_parent_hartid(parent.np);
|
||||||
if (hartid < 0) {
|
if (hartid < 0) {
|
||||||
@ -361,11 +375,11 @@ static int __init plic_init(struct device_node *node,
|
|||||||
|
|
||||||
cpumask_set_cpu(cpu, &priv->lmask);
|
cpumask_set_cpu(cpu, &priv->lmask);
|
||||||
handler->present = true;
|
handler->present = true;
|
||||||
handler->hart_base =
|
handler->hart_base = priv->regs + CONTEXT_BASE +
|
||||||
priv->regs + CONTEXT_BASE + i * CONTEXT_PER_HART;
|
i * CONTEXT_SIZE;
|
||||||
raw_spin_lock_init(&handler->enable_lock);
|
raw_spin_lock_init(&handler->enable_lock);
|
||||||
handler->enable_base =
|
handler->enable_base = priv->regs + CONTEXT_ENABLE_BASE +
|
||||||
priv->regs + ENABLE_BASE + i * ENABLE_PER_HART;
|
i * CONTEXT_ENABLE_SIZE;
|
||||||
handler->priv = priv;
|
handler->priv = priv;
|
||||||
done:
|
done:
|
||||||
for (hwirq = 1; hwirq <= nr_irqs; hwirq++)
|
for (hwirq = 1; hwirq <= nr_irqs; hwirq++)
|
||||||
|
@ -214,6 +214,48 @@ static const struct stm32_desc_irq stm32mp1_desc_irq[] = {
|
|||||||
{ .exti = 73, .irq_parent = 129, .chip = &stm32_exti_h_chip },
|
{ .exti = 73, .irq_parent = 129, .chip = &stm32_exti_h_chip },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct stm32_desc_irq stm32mp13_desc_irq[] = {
|
||||||
|
{ .exti = 0, .irq_parent = 6, .chip = &stm32_exti_h_chip },
|
||||||
|
{ .exti = 1, .irq_parent = 7, .chip = &stm32_exti_h_chip },
|
||||||
|
{ .exti = 2, .irq_parent = 8, .chip = &stm32_exti_h_chip },
|
||||||
|
{ .exti = 3, .irq_parent = 9, .chip = &stm32_exti_h_chip },
|
||||||
|
{ .exti = 4, .irq_parent = 10, .chip = &stm32_exti_h_chip },
|
||||||
|
{ .exti = 5, .irq_parent = 24, .chip = &stm32_exti_h_chip },
|
||||||
|
{ .exti = 6, .irq_parent = 65, .chip = &stm32_exti_h_chip },
|
||||||
|
{ .exti = 7, .irq_parent = 66, .chip = &stm32_exti_h_chip },
|
||||||
|
{ .exti = 8, .irq_parent = 67, .chip = &stm32_exti_h_chip },
|
||||||
|
{ .exti = 9, .irq_parent = 68, .chip = &stm32_exti_h_chip },
|
||||||
|
{ .exti = 10, .irq_parent = 41, .chip = &stm32_exti_h_chip },
|
||||||
|
{ .exti = 11, .irq_parent = 43, .chip = &stm32_exti_h_chip },
|
||||||
|
{ .exti = 12, .irq_parent = 77, .chip = &stm32_exti_h_chip },
|
||||||
|
{ .exti = 13, .irq_parent = 78, .chip = &stm32_exti_h_chip },
|
||||||
|
{ .exti = 14, .irq_parent = 106, .chip = &stm32_exti_h_chip },
|
||||||
|
{ .exti = 15, .irq_parent = 109, .chip = &stm32_exti_h_chip },
|
||||||
|
{ .exti = 16, .irq_parent = 1, .chip = &stm32_exti_h_chip },
|
||||||
|
{ .exti = 19, .irq_parent = 3, .chip = &stm32_exti_h_chip_direct },
|
||||||
|
{ .exti = 21, .irq_parent = 32, .chip = &stm32_exti_h_chip_direct },
|
||||||
|
{ .exti = 22, .irq_parent = 34, .chip = &stm32_exti_h_chip_direct },
|
||||||
|
{ .exti = 23, .irq_parent = 73, .chip = &stm32_exti_h_chip_direct },
|
||||||
|
{ .exti = 24, .irq_parent = 93, .chip = &stm32_exti_h_chip_direct },
|
||||||
|
{ .exti = 25, .irq_parent = 114, .chip = &stm32_exti_h_chip_direct },
|
||||||
|
{ .exti = 26, .irq_parent = 38, .chip = &stm32_exti_h_chip_direct },
|
||||||
|
{ .exti = 27, .irq_parent = 39, .chip = &stm32_exti_h_chip_direct },
|
||||||
|
{ .exti = 28, .irq_parent = 40, .chip = &stm32_exti_h_chip_direct },
|
||||||
|
{ .exti = 29, .irq_parent = 72, .chip = &stm32_exti_h_chip_direct },
|
||||||
|
{ .exti = 30, .irq_parent = 53, .chip = &stm32_exti_h_chip_direct },
|
||||||
|
{ .exti = 31, .irq_parent = 54, .chip = &stm32_exti_h_chip_direct },
|
||||||
|
{ .exti = 32, .irq_parent = 83, .chip = &stm32_exti_h_chip_direct },
|
||||||
|
{ .exti = 33, .irq_parent = 84, .chip = &stm32_exti_h_chip_direct },
|
||||||
|
{ .exti = 44, .irq_parent = 96, .chip = &stm32_exti_h_chip_direct },
|
||||||
|
{ .exti = 47, .irq_parent = 92, .chip = &stm32_exti_h_chip_direct },
|
||||||
|
{ .exti = 48, .irq_parent = 116, .chip = &stm32_exti_h_chip_direct },
|
||||||
|
{ .exti = 50, .irq_parent = 117, .chip = &stm32_exti_h_chip_direct },
|
||||||
|
{ .exti = 52, .irq_parent = 118, .chip = &stm32_exti_h_chip_direct },
|
||||||
|
{ .exti = 53, .irq_parent = 119, .chip = &stm32_exti_h_chip_direct },
|
||||||
|
{ .exti = 68, .irq_parent = 63, .chip = &stm32_exti_h_chip_direct },
|
||||||
|
{ .exti = 70, .irq_parent = 98, .chip = &stm32_exti_h_chip_direct },
|
||||||
|
};
|
||||||
|
|
||||||
static const struct stm32_exti_drv_data stm32mp1_drv_data = {
|
static const struct stm32_exti_drv_data stm32mp1_drv_data = {
|
||||||
.exti_banks = stm32mp1_exti_banks,
|
.exti_banks = stm32mp1_exti_banks,
|
||||||
.bank_nr = ARRAY_SIZE(stm32mp1_exti_banks),
|
.bank_nr = ARRAY_SIZE(stm32mp1_exti_banks),
|
||||||
@ -221,6 +263,13 @@ static const struct stm32_exti_drv_data stm32mp1_drv_data = {
|
|||||||
.irq_nr = ARRAY_SIZE(stm32mp1_desc_irq),
|
.irq_nr = ARRAY_SIZE(stm32mp1_desc_irq),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct stm32_exti_drv_data stm32mp13_drv_data = {
|
||||||
|
.exti_banks = stm32mp1_exti_banks,
|
||||||
|
.bank_nr = ARRAY_SIZE(stm32mp1_exti_banks),
|
||||||
|
.desc_irqs = stm32mp13_desc_irq,
|
||||||
|
.irq_nr = ARRAY_SIZE(stm32mp13_desc_irq),
|
||||||
|
};
|
||||||
|
|
||||||
static const struct
|
static const struct
|
||||||
stm32_desc_irq *stm32_exti_get_desc(const struct stm32_exti_drv_data *drv_data,
|
stm32_desc_irq *stm32_exti_get_desc(const struct stm32_exti_drv_data *drv_data,
|
||||||
irq_hw_number_t hwirq)
|
irq_hw_number_t hwirq)
|
||||||
@ -922,6 +971,7 @@ static int stm32_exti_probe(struct platform_device *pdev)
|
|||||||
/* platform driver only for MP1 */
|
/* platform driver only for MP1 */
|
||||||
static const struct of_device_id stm32_exti_ids[] = {
|
static const struct of_device_id stm32_exti_ids[] = {
|
||||||
{ .compatible = "st,stm32mp1-exti", .data = &stm32mp1_drv_data},
|
{ .compatible = "st,stm32mp1-exti", .data = &stm32mp1_drv_data},
|
||||||
|
{ .compatible = "st,stm32mp13-exti", .data = &stm32mp13_drv_data},
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, stm32_exti_ids);
|
MODULE_DEVICE_TABLE(of, stm32_exti_ids);
|
||||||
|
@ -19,14 +19,15 @@
|
|||||||
#include <linux/of_address.h>
|
#include <linux/of_address.h>
|
||||||
#include <linux/of_irq.h>
|
#include <linux/of_irq.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/seq_file.h>
|
||||||
|
|
||||||
#define IRQ_MASK 0x4
|
#define IRQ_MASK 0x4
|
||||||
#define IRQ_STATUS 0x8
|
#define IRQ_STATUS 0x8
|
||||||
|
|
||||||
struct ts4800_irq_data {
|
struct ts4800_irq_data {
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
|
struct platform_device *pdev;
|
||||||
struct irq_domain *domain;
|
struct irq_domain *domain;
|
||||||
struct irq_chip irq_chip;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static void ts4800_irq_mask(struct irq_data *d)
|
static void ts4800_irq_mask(struct irq_data *d)
|
||||||
@ -47,12 +48,25 @@ static void ts4800_irq_unmask(struct irq_data *d)
|
|||||||
writew(reg & ~mask, data->base + IRQ_MASK);
|
writew(reg & ~mask, data->base + IRQ_MASK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ts4800_irq_print_chip(struct irq_data *d, struct seq_file *p)
|
||||||
|
{
|
||||||
|
struct ts4800_irq_data *data = irq_data_get_irq_chip_data(d);
|
||||||
|
|
||||||
|
seq_printf(p, "%s", dev_name(&data->pdev->dev));
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct irq_chip ts4800_chip = {
|
||||||
|
.irq_mask = ts4800_irq_mask,
|
||||||
|
.irq_unmask = ts4800_irq_unmask,
|
||||||
|
.irq_print_chip = ts4800_irq_print_chip,
|
||||||
|
};
|
||||||
|
|
||||||
static int ts4800_irqdomain_map(struct irq_domain *d, unsigned int irq,
|
static int ts4800_irqdomain_map(struct irq_domain *d, unsigned int irq,
|
||||||
irq_hw_number_t hwirq)
|
irq_hw_number_t hwirq)
|
||||||
{
|
{
|
||||||
struct ts4800_irq_data *data = d->host_data;
|
struct ts4800_irq_data *data = d->host_data;
|
||||||
|
|
||||||
irq_set_chip_and_handler(irq, &data->irq_chip, handle_simple_irq);
|
irq_set_chip_and_handler(irq, &ts4800_chip, handle_simple_irq);
|
||||||
irq_set_chip_data(irq, data);
|
irq_set_chip_data(irq, data);
|
||||||
irq_set_noprobe(irq);
|
irq_set_noprobe(irq);
|
||||||
|
|
||||||
@ -92,13 +106,13 @@ static int ts4800_ic_probe(struct platform_device *pdev)
|
|||||||
{
|
{
|
||||||
struct device_node *node = pdev->dev.of_node;
|
struct device_node *node = pdev->dev.of_node;
|
||||||
struct ts4800_irq_data *data;
|
struct ts4800_irq_data *data;
|
||||||
struct irq_chip *irq_chip;
|
|
||||||
int parent_irq;
|
int parent_irq;
|
||||||
|
|
||||||
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
|
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
|
||||||
if (!data)
|
if (!data)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
data->pdev = pdev;
|
||||||
data->base = devm_platform_ioremap_resource(pdev, 0);
|
data->base = devm_platform_ioremap_resource(pdev, 0);
|
||||||
if (IS_ERR(data->base))
|
if (IS_ERR(data->base))
|
||||||
return PTR_ERR(data->base);
|
return PTR_ERR(data->base);
|
||||||
@ -111,11 +125,6 @@ static int ts4800_ic_probe(struct platform_device *pdev)
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
irq_chip = &data->irq_chip;
|
|
||||||
irq_chip->name = dev_name(&pdev->dev);
|
|
||||||
irq_chip->irq_mask = ts4800_irq_mask;
|
|
||||||
irq_chip->irq_unmask = ts4800_irq_unmask;
|
|
||||||
|
|
||||||
data->domain = irq_domain_add_linear(node, 8, &ts4800_ic_ops, data);
|
data->domain = irq_domain_add_linear(node, 8, &ts4800_ic_ops, data);
|
||||||
if (!data->domain) {
|
if (!data->domain) {
|
||||||
dev_err(&pdev->dev, "cannot add IRQ domain\n");
|
dev_err(&pdev->dev, "cannot add IRQ domain\n");
|
||||||
|
@ -7,12 +7,12 @@
|
|||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/irqchip.h>
|
#include <linux/irqchip.h>
|
||||||
#include <linux/irqchip/chained_irq.h>
|
#include <linux/irqchip/chained_irq.h>
|
||||||
#include <linux/irqchip/versatile-fpga.h>
|
|
||||||
#include <linux/irqdomain.h>
|
#include <linux/irqdomain.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_address.h>
|
#include <linux/of_address.h>
|
||||||
#include <linux/of_irq.h>
|
#include <linux/of_irq.h>
|
||||||
|
#include <linux/seq_file.h>
|
||||||
|
|
||||||
#include <asm/exception.h>
|
#include <asm/exception.h>
|
||||||
#include <asm/mach/irq.h>
|
#include <asm/mach/irq.h>
|
||||||
@ -34,14 +34,12 @@
|
|||||||
/**
|
/**
|
||||||
* struct fpga_irq_data - irq data container for the FPGA IRQ controller
|
* struct fpga_irq_data - irq data container for the FPGA IRQ controller
|
||||||
* @base: memory offset in virtual memory
|
* @base: memory offset in virtual memory
|
||||||
* @chip: chip container for this instance
|
|
||||||
* @domain: IRQ domain for this instance
|
* @domain: IRQ domain for this instance
|
||||||
* @valid: mask for valid IRQs on this controller
|
* @valid: mask for valid IRQs on this controller
|
||||||
* @used_irqs: number of active IRQs on this controller
|
* @used_irqs: number of active IRQs on this controller
|
||||||
*/
|
*/
|
||||||
struct fpga_irq_data {
|
struct fpga_irq_data {
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
struct irq_chip chip;
|
|
||||||
u32 valid;
|
u32 valid;
|
||||||
struct irq_domain *domain;
|
struct irq_domain *domain;
|
||||||
u8 used_irqs;
|
u8 used_irqs;
|
||||||
@ -67,6 +65,20 @@ static void fpga_irq_unmask(struct irq_data *d)
|
|||||||
writel(mask, f->base + IRQ_ENABLE_SET);
|
writel(mask, f->base + IRQ_ENABLE_SET);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void fpga_irq_print_chip(struct irq_data *d, struct seq_file *p)
|
||||||
|
{
|
||||||
|
struct fpga_irq_data *f = irq_data_get_irq_chip_data(d);
|
||||||
|
|
||||||
|
seq_printf(p, irq_domain_get_of_node(f->domain)->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct irq_chip fpga_chip = {
|
||||||
|
.irq_ack = fpga_irq_mask,
|
||||||
|
.irq_mask = fpga_irq_mask,
|
||||||
|
.irq_unmask = fpga_irq_unmask,
|
||||||
|
.irq_print_chip = fpga_irq_print_chip,
|
||||||
|
};
|
||||||
|
|
||||||
static void fpga_irq_handle(struct irq_desc *desc)
|
static void fpga_irq_handle(struct irq_desc *desc)
|
||||||
{
|
{
|
||||||
struct irq_chip *chip = irq_desc_get_chip(desc);
|
struct irq_chip *chip = irq_desc_get_chip(desc);
|
||||||
@ -116,7 +128,7 @@ static int handle_one_fpga(struct fpga_irq_data *f, struct pt_regs *regs)
|
|||||||
* Keep iterating over all registered FPGA IRQ controllers until there are
|
* Keep iterating over all registered FPGA IRQ controllers until there are
|
||||||
* no pending interrupts.
|
* no pending interrupts.
|
||||||
*/
|
*/
|
||||||
asmlinkage void __exception_irq_entry fpga_handle_irq(struct pt_regs *regs)
|
static asmlinkage void __exception_irq_entry fpga_handle_irq(struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
int i, handled;
|
int i, handled;
|
||||||
|
|
||||||
@ -135,8 +147,7 @@ static int fpga_irqdomain_map(struct irq_domain *d, unsigned int irq,
|
|||||||
if (!(f->valid & BIT(hwirq)))
|
if (!(f->valid & BIT(hwirq)))
|
||||||
return -EPERM;
|
return -EPERM;
|
||||||
irq_set_chip_data(irq, f);
|
irq_set_chip_data(irq, f);
|
||||||
irq_set_chip_and_handler(irq, &f->chip,
|
irq_set_chip_and_handler(irq, &fpga_chip, handle_level_irq);
|
||||||
handle_level_irq);
|
|
||||||
irq_set_probe(irq);
|
irq_set_probe(irq);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -146,8 +157,8 @@ static const struct irq_domain_ops fpga_irqdomain_ops = {
|
|||||||
.xlate = irq_domain_xlate_onetwocell,
|
.xlate = irq_domain_xlate_onetwocell,
|
||||||
};
|
};
|
||||||
|
|
||||||
void __init fpga_irq_init(void __iomem *base, const char *name, int irq_start,
|
static void __init fpga_irq_init(void __iomem *base, int parent_irq,
|
||||||
int parent_irq, u32 valid, struct device_node *node)
|
u32 valid, struct device_node *node)
|
||||||
{
|
{
|
||||||
struct fpga_irq_data *f;
|
struct fpga_irq_data *f;
|
||||||
int i;
|
int i;
|
||||||
@ -158,10 +169,6 @@ void __init fpga_irq_init(void __iomem *base, const char *name, int irq_start,
|
|||||||
}
|
}
|
||||||
f = &fpga_irq_devices[fpga_irq_id];
|
f = &fpga_irq_devices[fpga_irq_id];
|
||||||
f->base = base;
|
f->base = base;
|
||||||
f->chip.name = name;
|
|
||||||
f->chip.irq_ack = fpga_irq_mask;
|
|
||||||
f->chip.irq_mask = fpga_irq_mask;
|
|
||||||
f->chip.irq_unmask = fpga_irq_unmask;
|
|
||||||
f->valid = valid;
|
f->valid = valid;
|
||||||
|
|
||||||
if (parent_irq != -1) {
|
if (parent_irq != -1) {
|
||||||
@ -169,20 +176,19 @@ void __init fpga_irq_init(void __iomem *base, const char *name, int irq_start,
|
|||||||
f);
|
f);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This will also allocate irq descriptors */
|
f->domain = irq_domain_add_linear(node, fls(valid),
|
||||||
f->domain = irq_domain_add_simple(node, fls(valid), irq_start,
|
|
||||||
&fpga_irqdomain_ops, f);
|
&fpga_irqdomain_ops, f);
|
||||||
|
|
||||||
/* This will allocate all valid descriptors in the linear case */
|
/* This will allocate all valid descriptors in the linear case */
|
||||||
for (i = 0; i < fls(valid); i++)
|
for (i = 0; i < fls(valid); i++)
|
||||||
if (valid & BIT(i)) {
|
if (valid & BIT(i)) {
|
||||||
if (!irq_start)
|
/* Is this still required? */
|
||||||
irq_create_mapping(f->domain, i);
|
irq_create_mapping(f->domain, i);
|
||||||
f->used_irqs++;
|
f->used_irqs++;
|
||||||
}
|
}
|
||||||
|
|
||||||
pr_info("FPGA IRQ chip %d \"%s\" @ %p, %u irqs",
|
pr_info("FPGA IRQ chip %d \"%s\" @ %p, %u irqs",
|
||||||
fpga_irq_id, name, base, f->used_irqs);
|
fpga_irq_id, node->name, base, f->used_irqs);
|
||||||
if (parent_irq != -1)
|
if (parent_irq != -1)
|
||||||
pr_cont(", parent IRQ: %d\n", parent_irq);
|
pr_cont(", parent IRQ: %d\n", parent_irq);
|
||||||
else
|
else
|
||||||
@ -192,8 +198,8 @@ void __init fpga_irq_init(void __iomem *base, const char *name, int irq_start,
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_OF
|
#ifdef CONFIG_OF
|
||||||
int __init fpga_irq_of_init(struct device_node *node,
|
static int __init fpga_irq_of_init(struct device_node *node,
|
||||||
struct device_node *parent)
|
struct device_node *parent)
|
||||||
{
|
{
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
u32 clear_mask;
|
u32 clear_mask;
|
||||||
@ -222,7 +228,7 @@ int __init fpga_irq_of_init(struct device_node *node,
|
|||||||
parent_irq = -1;
|
parent_irq = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
fpga_irq_init(base, node->name, 0, parent_irq, valid_mask, node);
|
fpga_irq_init(base, parent_irq, valid_mask, node);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* On Versatile AB/PB, some secondary interrupts have a direct
|
* On Versatile AB/PB, some secondary interrupts have a direct
|
||||||
|
@ -32,6 +32,8 @@
|
|||||||
#define MER_ME (1<<0)
|
#define MER_ME (1<<0)
|
||||||
#define MER_HIE (1<<1)
|
#define MER_HIE (1<<1)
|
||||||
|
|
||||||
|
#define SPURIOUS_IRQ (-1U)
|
||||||
|
|
||||||
static DEFINE_STATIC_KEY_FALSE(xintc_is_be);
|
static DEFINE_STATIC_KEY_FALSE(xintc_is_be);
|
||||||
|
|
||||||
struct xintc_irq_chip {
|
struct xintc_irq_chip {
|
||||||
@ -110,20 +112,6 @@ static struct irq_chip intc_dev = {
|
|||||||
.irq_mask_ack = intc_mask_ack,
|
.irq_mask_ack = intc_mask_ack,
|
||||||
};
|
};
|
||||||
|
|
||||||
unsigned int xintc_get_irq(void)
|
|
||||||
{
|
|
||||||
unsigned int irq = -1;
|
|
||||||
u32 hwirq;
|
|
||||||
|
|
||||||
hwirq = xintc_read(primary_intc, IVR);
|
|
||||||
if (hwirq != -1U)
|
|
||||||
irq = irq_find_mapping(primary_intc->root_domain, hwirq);
|
|
||||||
|
|
||||||
pr_debug("irq-xilinx: hwirq=%d, irq=%d\n", hwirq, irq);
|
|
||||||
|
|
||||||
return irq;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int xintc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
|
static int xintc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
|
||||||
{
|
{
|
||||||
struct xintc_irq_chip *irqc = d->host_data;
|
struct xintc_irq_chip *irqc = d->host_data;
|
||||||
@ -164,6 +152,19 @@ static void xil_intc_irq_handler(struct irq_desc *desc)
|
|||||||
chained_irq_exit(chip, desc);
|
chained_irq_exit(chip, desc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void xil_intc_handle_irq(struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
u32 hwirq;
|
||||||
|
|
||||||
|
do {
|
||||||
|
hwirq = xintc_read(primary_intc, IVR);
|
||||||
|
if (unlikely(hwirq == SPURIOUS_IRQ))
|
||||||
|
break;
|
||||||
|
|
||||||
|
generic_handle_domain_irq(primary_intc->root_domain, hwirq);
|
||||||
|
} while (true);
|
||||||
|
}
|
||||||
|
|
||||||
static int __init xilinx_intc_of_init(struct device_node *intc,
|
static int __init xilinx_intc_of_init(struct device_node *intc,
|
||||||
struct device_node *parent)
|
struct device_node *parent)
|
||||||
{
|
{
|
||||||
@ -233,6 +234,7 @@ static int __init xilinx_intc_of_init(struct device_node *intc,
|
|||||||
} else {
|
} else {
|
||||||
primary_intc = irqc;
|
primary_intc = irqc;
|
||||||
irq_set_default_host(primary_intc->root_domain);
|
irq_set_default_host(primary_intc->root_domain);
|
||||||
|
set_handle_irq(xil_intc_handle_irq);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -21,23 +21,19 @@
|
|||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
|
|
||||||
#define PDC_MAX_IRQS 168
|
|
||||||
#define PDC_MAX_GPIO_IRQS 256
|
#define PDC_MAX_GPIO_IRQS 256
|
||||||
|
|
||||||
#define CLEAR_INTR(reg, intr) (reg & ~(1 << intr))
|
|
||||||
#define ENABLE_INTR(reg, intr) (reg | (1 << intr))
|
|
||||||
|
|
||||||
#define IRQ_ENABLE_BANK 0x10
|
#define IRQ_ENABLE_BANK 0x10
|
||||||
#define IRQ_i_CFG 0x110
|
#define IRQ_i_CFG 0x110
|
||||||
|
|
||||||
#define PDC_NO_PARENT_IRQ ~0UL
|
|
||||||
|
|
||||||
struct pdc_pin_region {
|
struct pdc_pin_region {
|
||||||
u32 pin_base;
|
u32 pin_base;
|
||||||
u32 parent_base;
|
u32 parent_base;
|
||||||
u32 cnt;
|
u32 cnt;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define pin_to_hwirq(r, p) ((r)->parent_base + (p) - (r)->pin_base)
|
||||||
|
|
||||||
static DEFINE_RAW_SPINLOCK(pdc_lock);
|
static DEFINE_RAW_SPINLOCK(pdc_lock);
|
||||||
static void __iomem *pdc_base;
|
static void __iomem *pdc_base;
|
||||||
static struct pdc_pin_region *pdc_region;
|
static struct pdc_pin_region *pdc_region;
|
||||||
@ -56,17 +52,18 @@ static u32 pdc_reg_read(int reg, u32 i)
|
|||||||
static void pdc_enable_intr(struct irq_data *d, bool on)
|
static void pdc_enable_intr(struct irq_data *d, bool on)
|
||||||
{
|
{
|
||||||
int pin_out = d->hwirq;
|
int pin_out = d->hwirq;
|
||||||
|
unsigned long enable;
|
||||||
|
unsigned long flags;
|
||||||
u32 index, mask;
|
u32 index, mask;
|
||||||
u32 enable;
|
|
||||||
|
|
||||||
index = pin_out / 32;
|
index = pin_out / 32;
|
||||||
mask = pin_out % 32;
|
mask = pin_out % 32;
|
||||||
|
|
||||||
raw_spin_lock(&pdc_lock);
|
raw_spin_lock_irqsave(&pdc_lock, flags);
|
||||||
enable = pdc_reg_read(IRQ_ENABLE_BANK, index);
|
enable = pdc_reg_read(IRQ_ENABLE_BANK, index);
|
||||||
enable = on ? ENABLE_INTR(enable, mask) : CLEAR_INTR(enable, mask);
|
__assign_bit(mask, &enable, on);
|
||||||
pdc_reg_write(IRQ_ENABLE_BANK, index, enable);
|
pdc_reg_write(IRQ_ENABLE_BANK, index, enable);
|
||||||
raw_spin_unlock(&pdc_lock);
|
raw_spin_unlock_irqrestore(&pdc_lock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qcom_pdc_gic_disable(struct irq_data *d)
|
static void qcom_pdc_gic_disable(struct irq_data *d)
|
||||||
@ -186,34 +183,17 @@ static struct irq_chip qcom_pdc_gic_chip = {
|
|||||||
.irq_set_affinity = irq_chip_set_affinity_parent,
|
.irq_set_affinity = irq_chip_set_affinity_parent,
|
||||||
};
|
};
|
||||||
|
|
||||||
static irq_hw_number_t get_parent_hwirq(int pin)
|
static struct pdc_pin_region *get_pin_region(int pin)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
struct pdc_pin_region *region;
|
|
||||||
|
|
||||||
for (i = 0; i < pdc_region_cnt; i++) {
|
for (i = 0; i < pdc_region_cnt; i++) {
|
||||||
region = &pdc_region[i];
|
if (pin >= pdc_region[i].pin_base &&
|
||||||
if (pin >= region->pin_base &&
|
pin < pdc_region[i].pin_base + pdc_region[i].cnt)
|
||||||
pin < region->pin_base + region->cnt)
|
return &pdc_region[i];
|
||||||
return (region->parent_base + pin - region->pin_base);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return PDC_NO_PARENT_IRQ;
|
return NULL;
|
||||||
}
|
|
||||||
|
|
||||||
static int qcom_pdc_translate(struct irq_domain *d, struct irq_fwspec *fwspec,
|
|
||||||
unsigned long *hwirq, unsigned int *type)
|
|
||||||
{
|
|
||||||
if (is_of_node(fwspec->fwnode)) {
|
|
||||||
if (fwspec->param_count != 2)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
*hwirq = fwspec->param[0];
|
|
||||||
*type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int qcom_pdc_alloc(struct irq_domain *domain, unsigned int virq,
|
static int qcom_pdc_alloc(struct irq_domain *domain, unsigned int virq,
|
||||||
@ -221,55 +201,12 @@ static int qcom_pdc_alloc(struct irq_domain *domain, unsigned int virq,
|
|||||||
{
|
{
|
||||||
struct irq_fwspec *fwspec = data;
|
struct irq_fwspec *fwspec = data;
|
||||||
struct irq_fwspec parent_fwspec;
|
struct irq_fwspec parent_fwspec;
|
||||||
irq_hw_number_t hwirq, parent_hwirq;
|
struct pdc_pin_region *region;
|
||||||
|
irq_hw_number_t hwirq;
|
||||||
unsigned int type;
|
unsigned int type;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = qcom_pdc_translate(domain, fwspec, &hwirq, &type);
|
ret = irq_domain_translate_twocell(domain, fwspec, &hwirq, &type);
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
ret = irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
|
|
||||||
&qcom_pdc_gic_chip, NULL);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
parent_hwirq = get_parent_hwirq(hwirq);
|
|
||||||
if (parent_hwirq == PDC_NO_PARENT_IRQ)
|
|
||||||
return irq_domain_disconnect_hierarchy(domain->parent, virq);
|
|
||||||
|
|
||||||
if (type & IRQ_TYPE_EDGE_BOTH)
|
|
||||||
type = IRQ_TYPE_EDGE_RISING;
|
|
||||||
|
|
||||||
if (type & IRQ_TYPE_LEVEL_MASK)
|
|
||||||
type = IRQ_TYPE_LEVEL_HIGH;
|
|
||||||
|
|
||||||
parent_fwspec.fwnode = domain->parent->fwnode;
|
|
||||||
parent_fwspec.param_count = 3;
|
|
||||||
parent_fwspec.param[0] = 0;
|
|
||||||
parent_fwspec.param[1] = parent_hwirq;
|
|
||||||
parent_fwspec.param[2] = type;
|
|
||||||
|
|
||||||
return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs,
|
|
||||||
&parent_fwspec);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct irq_domain_ops qcom_pdc_ops = {
|
|
||||||
.translate = qcom_pdc_translate,
|
|
||||||
.alloc = qcom_pdc_alloc,
|
|
||||||
.free = irq_domain_free_irqs_common,
|
|
||||||
};
|
|
||||||
|
|
||||||
static int qcom_pdc_gpio_alloc(struct irq_domain *domain, unsigned int virq,
|
|
||||||
unsigned int nr_irqs, void *data)
|
|
||||||
{
|
|
||||||
struct irq_fwspec *fwspec = data;
|
|
||||||
struct irq_fwspec parent_fwspec;
|
|
||||||
irq_hw_number_t hwirq, parent_hwirq;
|
|
||||||
unsigned int type;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = qcom_pdc_translate(domain, fwspec, &hwirq, &type);
|
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
@ -281,8 +218,8 @@ static int qcom_pdc_gpio_alloc(struct irq_domain *domain, unsigned int virq,
|
|||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
parent_hwirq = get_parent_hwirq(hwirq);
|
region = get_pin_region(hwirq);
|
||||||
if (parent_hwirq == PDC_NO_PARENT_IRQ)
|
if (!region)
|
||||||
return irq_domain_disconnect_hierarchy(domain->parent, virq);
|
return irq_domain_disconnect_hierarchy(domain->parent, virq);
|
||||||
|
|
||||||
if (type & IRQ_TYPE_EDGE_BOTH)
|
if (type & IRQ_TYPE_EDGE_BOTH)
|
||||||
@ -294,23 +231,16 @@ static int qcom_pdc_gpio_alloc(struct irq_domain *domain, unsigned int virq,
|
|||||||
parent_fwspec.fwnode = domain->parent->fwnode;
|
parent_fwspec.fwnode = domain->parent->fwnode;
|
||||||
parent_fwspec.param_count = 3;
|
parent_fwspec.param_count = 3;
|
||||||
parent_fwspec.param[0] = 0;
|
parent_fwspec.param[0] = 0;
|
||||||
parent_fwspec.param[1] = parent_hwirq;
|
parent_fwspec.param[1] = pin_to_hwirq(region, hwirq);
|
||||||
parent_fwspec.param[2] = type;
|
parent_fwspec.param[2] = type;
|
||||||
|
|
||||||
return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs,
|
return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs,
|
||||||
&parent_fwspec);
|
&parent_fwspec);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int qcom_pdc_gpio_domain_select(struct irq_domain *d,
|
static const struct irq_domain_ops qcom_pdc_ops = {
|
||||||
struct irq_fwspec *fwspec,
|
.translate = irq_domain_translate_twocell,
|
||||||
enum irq_domain_bus_token bus_token)
|
.alloc = qcom_pdc_alloc,
|
||||||
{
|
|
||||||
return bus_token == DOMAIN_BUS_WAKEUP;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct irq_domain_ops qcom_pdc_gpio_ops = {
|
|
||||||
.select = qcom_pdc_gpio_domain_select,
|
|
||||||
.alloc = qcom_pdc_gpio_alloc,
|
|
||||||
.free = irq_domain_free_irqs_common,
|
.free = irq_domain_free_irqs_common,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -361,7 +291,7 @@ static int pdc_setup_pin_mapping(struct device_node *np)
|
|||||||
|
|
||||||
static int qcom_pdc_init(struct device_node *node, struct device_node *parent)
|
static int qcom_pdc_init(struct device_node *node, struct device_node *parent)
|
||||||
{
|
{
|
||||||
struct irq_domain *parent_domain, *pdc_domain, *pdc_gpio_domain;
|
struct irq_domain *parent_domain, *pdc_domain;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
pdc_base = of_iomap(node, 0);
|
pdc_base = of_iomap(node, 0);
|
||||||
@ -383,32 +313,21 @@ static int qcom_pdc_init(struct device_node *node, struct device_node *parent)
|
|||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
pdc_domain = irq_domain_create_hierarchy(parent_domain, 0, PDC_MAX_IRQS,
|
pdc_domain = irq_domain_create_hierarchy(parent_domain,
|
||||||
of_fwnode_handle(node),
|
IRQ_DOMAIN_FLAG_QCOM_PDC_WAKEUP,
|
||||||
&qcom_pdc_ops, NULL);
|
PDC_MAX_GPIO_IRQS,
|
||||||
|
of_fwnode_handle(node),
|
||||||
|
&qcom_pdc_ops, NULL);
|
||||||
if (!pdc_domain) {
|
if (!pdc_domain) {
|
||||||
pr_err("%pOF: GIC domain add failed\n", node);
|
pr_err("%pOF: PDC domain add failed\n", node);
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
pdc_gpio_domain = irq_domain_create_hierarchy(parent_domain,
|
irq_domain_update_bus_token(pdc_domain, DOMAIN_BUS_WAKEUP);
|
||||||
IRQ_DOMAIN_FLAG_QCOM_PDC_WAKEUP,
|
|
||||||
PDC_MAX_GPIO_IRQS,
|
|
||||||
of_fwnode_handle(node),
|
|
||||||
&qcom_pdc_gpio_ops, NULL);
|
|
||||||
if (!pdc_gpio_domain) {
|
|
||||||
pr_err("%pOF: PDC domain add failed for GPIO domain\n", node);
|
|
||||||
ret = -ENOMEM;
|
|
||||||
goto remove;
|
|
||||||
}
|
|
||||||
|
|
||||||
irq_domain_update_bus_token(pdc_gpio_domain, DOMAIN_BUS_WAKEUP);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
remove:
|
|
||||||
irq_domain_remove(pdc_domain);
|
|
||||||
fail:
|
fail:
|
||||||
kfree(pdc_region);
|
kfree(pdc_region);
|
||||||
iounmap(pdc_base);
|
iounmap(pdc_base);
|
||||||
|
@ -193,13 +193,11 @@ static void pcap_isr_work(struct work_struct *work)
|
|||||||
ezx_pcap_write(pcap, PCAP_REG_MSR, isr | msr);
|
ezx_pcap_write(pcap, PCAP_REG_MSR, isr | msr);
|
||||||
ezx_pcap_write(pcap, PCAP_REG_ISR, isr);
|
ezx_pcap_write(pcap, PCAP_REG_ISR, isr);
|
||||||
|
|
||||||
local_irq_disable();
|
|
||||||
service = isr & ~msr;
|
service = isr & ~msr;
|
||||||
for (irq = pcap->irq_base; service; service >>= 1, irq++) {
|
for (irq = pcap->irq_base; service; service >>= 1, irq++) {
|
||||||
if (service & 1)
|
if (service & 1)
|
||||||
generic_handle_irq(irq);
|
generic_handle_irq_safe(irq);
|
||||||
}
|
}
|
||||||
local_irq_enable();
|
|
||||||
ezx_pcap_write(pcap, PCAP_REG_MSR, pcap->msr);
|
ezx_pcap_write(pcap, PCAP_REG_MSR, pcap->msr);
|
||||||
} while (gpio_get_value(pdata->gpio));
|
} while (gpio_get_value(pdata->gpio));
|
||||||
}
|
}
|
||||||
|
@ -117,8 +117,8 @@ static irqreturn_t hi6421v600_irq_handler(int irq, void *__priv)
|
|||||||
* If both powerkey down and up IRQs are received,
|
* If both powerkey down and up IRQs are received,
|
||||||
* handle them at the right order
|
* handle them at the right order
|
||||||
*/
|
*/
|
||||||
generic_handle_irq(priv->irqs[POWERKEY_DOWN]);
|
generic_handle_irq_safe(priv->irqs[POWERKEY_DOWN]);
|
||||||
generic_handle_irq(priv->irqs[POWERKEY_UP]);
|
generic_handle_irq_safe(priv->irqs[POWERKEY_UP]);
|
||||||
pending &= ~HISI_IRQ_POWERKEY_UP_DOWN;
|
pending &= ~HISI_IRQ_POWERKEY_UP_DOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,7 +126,7 @@ static irqreturn_t hi6421v600_irq_handler(int irq, void *__priv)
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
for_each_set_bit(offset, &pending, BITS_PER_BYTE) {
|
for_each_set_bit(offset, &pending, BITS_PER_BYTE) {
|
||||||
generic_handle_irq(priv->irqs[offset + i * BITS_PER_BYTE]);
|
generic_handle_irq_safe(priv->irqs[offset + i * BITS_PER_BYTE]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1537,11 +1537,8 @@ static void lan78xx_status(struct lan78xx_net *dev, struct urb *urb)
|
|||||||
netif_dbg(dev, link, dev->net, "PHY INTR: 0x%08x\n", intdata);
|
netif_dbg(dev, link, dev->net, "PHY INTR: 0x%08x\n", intdata);
|
||||||
lan78xx_defer_kevent(dev, EVENT_LINK_RESET);
|
lan78xx_defer_kevent(dev, EVENT_LINK_RESET);
|
||||||
|
|
||||||
if (dev->domain_data.phyirq > 0) {
|
if (dev->domain_data.phyirq > 0)
|
||||||
local_irq_disable();
|
generic_handle_irq_safe(dev->domain_data.phyirq);
|
||||||
generic_handle_irq(dev->domain_data.phyirq);
|
|
||||||
local_irq_enable();
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
netdev_warn(dev->net,
|
netdev_warn(dev->net,
|
||||||
"unexpected interrupt: 0x%08x\n", intdata);
|
"unexpected interrupt: 0x%08x\n", intdata);
|
||||||
|
@ -219,7 +219,7 @@ static int apple_msi_domain_alloc(struct irq_domain *domain, unsigned int virq,
|
|||||||
if (hwirq < 0)
|
if (hwirq < 0)
|
||||||
return -ENOSPC;
|
return -ENOSPC;
|
||||||
|
|
||||||
fwspec.param[1] += hwirq;
|
fwspec.param[fwspec.param_count - 2] += hwirq;
|
||||||
|
|
||||||
ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &fwspec);
|
ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &fwspec);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
@ -78,7 +78,6 @@ struct npcm7xx_gpio {
|
|||||||
struct gpio_chip gc;
|
struct gpio_chip gc;
|
||||||
int irqbase;
|
int irqbase;
|
||||||
int irq;
|
int irq;
|
||||||
void *priv;
|
|
||||||
struct irq_chip irq_chip;
|
struct irq_chip irq_chip;
|
||||||
u32 pinctrl_id;
|
u32 pinctrl_id;
|
||||||
int (*direction_input)(struct gpio_chip *chip, unsigned offset);
|
int (*direction_input)(struct gpio_chip *chip, unsigned offset);
|
||||||
@ -226,7 +225,7 @@ static void npcmgpio_irq_handler(struct irq_desc *desc)
|
|||||||
chained_irq_enter(chip, desc);
|
chained_irq_enter(chip, desc);
|
||||||
sts = ioread32(bank->base + NPCM7XX_GP_N_EVST);
|
sts = ioread32(bank->base + NPCM7XX_GP_N_EVST);
|
||||||
en = ioread32(bank->base + NPCM7XX_GP_N_EVEN);
|
en = ioread32(bank->base + NPCM7XX_GP_N_EVEN);
|
||||||
dev_dbg(chip->parent_device, "==> got irq sts %.8x %.8x\n", sts,
|
dev_dbg(bank->gc.parent, "==> got irq sts %.8x %.8x\n", sts,
|
||||||
en);
|
en);
|
||||||
|
|
||||||
sts &= en;
|
sts &= en;
|
||||||
@ -241,33 +240,33 @@ static int npcmgpio_set_irq_type(struct irq_data *d, unsigned int type)
|
|||||||
gpiochip_get_data(irq_data_get_irq_chip_data(d));
|
gpiochip_get_data(irq_data_get_irq_chip_data(d));
|
||||||
unsigned int gpio = BIT(d->hwirq);
|
unsigned int gpio = BIT(d->hwirq);
|
||||||
|
|
||||||
dev_dbg(d->chip->parent_device, "setirqtype: %u.%u = %u\n", gpio,
|
dev_dbg(bank->gc.parent, "setirqtype: %u.%u = %u\n", gpio,
|
||||||
d->irq, type);
|
d->irq, type);
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case IRQ_TYPE_EDGE_RISING:
|
case IRQ_TYPE_EDGE_RISING:
|
||||||
dev_dbg(d->chip->parent_device, "edge.rising\n");
|
dev_dbg(bank->gc.parent, "edge.rising\n");
|
||||||
npcm_gpio_clr(&bank->gc, bank->base + NPCM7XX_GP_N_EVBE, gpio);
|
npcm_gpio_clr(&bank->gc, bank->base + NPCM7XX_GP_N_EVBE, gpio);
|
||||||
npcm_gpio_clr(&bank->gc, bank->base + NPCM7XX_GP_N_POL, gpio);
|
npcm_gpio_clr(&bank->gc, bank->base + NPCM7XX_GP_N_POL, gpio);
|
||||||
break;
|
break;
|
||||||
case IRQ_TYPE_EDGE_FALLING:
|
case IRQ_TYPE_EDGE_FALLING:
|
||||||
dev_dbg(d->chip->parent_device, "edge.falling\n");
|
dev_dbg(bank->gc.parent, "edge.falling\n");
|
||||||
npcm_gpio_clr(&bank->gc, bank->base + NPCM7XX_GP_N_EVBE, gpio);
|
npcm_gpio_clr(&bank->gc, bank->base + NPCM7XX_GP_N_EVBE, gpio);
|
||||||
npcm_gpio_set(&bank->gc, bank->base + NPCM7XX_GP_N_POL, gpio);
|
npcm_gpio_set(&bank->gc, bank->base + NPCM7XX_GP_N_POL, gpio);
|
||||||
break;
|
break;
|
||||||
case IRQ_TYPE_EDGE_BOTH:
|
case IRQ_TYPE_EDGE_BOTH:
|
||||||
dev_dbg(d->chip->parent_device, "edge.both\n");
|
dev_dbg(bank->gc.parent, "edge.both\n");
|
||||||
npcm_gpio_set(&bank->gc, bank->base + NPCM7XX_GP_N_EVBE, gpio);
|
npcm_gpio_set(&bank->gc, bank->base + NPCM7XX_GP_N_EVBE, gpio);
|
||||||
break;
|
break;
|
||||||
case IRQ_TYPE_LEVEL_LOW:
|
case IRQ_TYPE_LEVEL_LOW:
|
||||||
dev_dbg(d->chip->parent_device, "level.low\n");
|
dev_dbg(bank->gc.parent, "level.low\n");
|
||||||
npcm_gpio_set(&bank->gc, bank->base + NPCM7XX_GP_N_POL, gpio);
|
npcm_gpio_set(&bank->gc, bank->base + NPCM7XX_GP_N_POL, gpio);
|
||||||
break;
|
break;
|
||||||
case IRQ_TYPE_LEVEL_HIGH:
|
case IRQ_TYPE_LEVEL_HIGH:
|
||||||
dev_dbg(d->chip->parent_device, "level.high\n");
|
dev_dbg(bank->gc.parent, "level.high\n");
|
||||||
npcm_gpio_clr(&bank->gc, bank->base + NPCM7XX_GP_N_POL, gpio);
|
npcm_gpio_clr(&bank->gc, bank->base + NPCM7XX_GP_N_POL, gpio);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
dev_dbg(d->chip->parent_device, "invalid irq type\n");
|
dev_dbg(bank->gc.parent, "invalid irq type\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -289,7 +288,7 @@ static void npcmgpio_irq_ack(struct irq_data *d)
|
|||||||
gpiochip_get_data(irq_data_get_irq_chip_data(d));
|
gpiochip_get_data(irq_data_get_irq_chip_data(d));
|
||||||
unsigned int gpio = d->hwirq;
|
unsigned int gpio = d->hwirq;
|
||||||
|
|
||||||
dev_dbg(d->chip->parent_device, "irq_ack: %u.%u\n", gpio, d->irq);
|
dev_dbg(bank->gc.parent, "irq_ack: %u.%u\n", gpio, d->irq);
|
||||||
iowrite32(BIT(gpio), bank->base + NPCM7XX_GP_N_EVST);
|
iowrite32(BIT(gpio), bank->base + NPCM7XX_GP_N_EVST);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -301,7 +300,7 @@ static void npcmgpio_irq_mask(struct irq_data *d)
|
|||||||
unsigned int gpio = d->hwirq;
|
unsigned int gpio = d->hwirq;
|
||||||
|
|
||||||
/* Clear events */
|
/* Clear events */
|
||||||
dev_dbg(d->chip->parent_device, "irq_mask: %u.%u\n", gpio, d->irq);
|
dev_dbg(bank->gc.parent, "irq_mask: %u.%u\n", gpio, d->irq);
|
||||||
iowrite32(BIT(gpio), bank->base + NPCM7XX_GP_N_EVENC);
|
iowrite32(BIT(gpio), bank->base + NPCM7XX_GP_N_EVENC);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -313,7 +312,7 @@ static void npcmgpio_irq_unmask(struct irq_data *d)
|
|||||||
unsigned int gpio = d->hwirq;
|
unsigned int gpio = d->hwirq;
|
||||||
|
|
||||||
/* Enable events */
|
/* Enable events */
|
||||||
dev_dbg(d->chip->parent_device, "irq_unmask: %u.%u\n", gpio, d->irq);
|
dev_dbg(bank->gc.parent, "irq_unmask: %u.%u\n", gpio, d->irq);
|
||||||
iowrite32(BIT(gpio), bank->base + NPCM7XX_GP_N_EVENS);
|
iowrite32(BIT(gpio), bank->base + NPCM7XX_GP_N_EVENS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -323,7 +322,7 @@ static unsigned int npcmgpio_irq_startup(struct irq_data *d)
|
|||||||
unsigned int gpio = d->hwirq;
|
unsigned int gpio = d->hwirq;
|
||||||
|
|
||||||
/* active-high, input, clear interrupt, enable interrupt */
|
/* active-high, input, clear interrupt, enable interrupt */
|
||||||
dev_dbg(d->chip->parent_device, "startup: %u.%u\n", gpio, d->irq);
|
dev_dbg(gc->parent, "startup: %u.%u\n", gpio, d->irq);
|
||||||
npcmgpio_direction_input(gc, gpio);
|
npcmgpio_direction_input(gc, gpio);
|
||||||
npcmgpio_irq_ack(d);
|
npcmgpio_irq_ack(d);
|
||||||
npcmgpio_irq_unmask(d);
|
npcmgpio_irq_unmask(d);
|
||||||
|
@ -1308,8 +1308,6 @@ static int starfive_probe(struct platform_device *pdev)
|
|||||||
sfp->gc.base = -1;
|
sfp->gc.base = -1;
|
||||||
sfp->gc.ngpio = NR_GPIOS;
|
sfp->gc.ngpio = NR_GPIOS;
|
||||||
|
|
||||||
starfive_irq_chip.parent_device = dev;
|
|
||||||
|
|
||||||
sfp->gc.irq.chip = &starfive_irq_chip;
|
sfp->gc.irq.chip = &starfive_irq_chip;
|
||||||
sfp->gc.irq.parent_handler = starfive_gpio_irq_handler;
|
sfp->gc.irq.parent_handler = starfive_gpio_irq_handler;
|
||||||
sfp->gc.irq.num_parents = 1;
|
sfp->gc.irq.num_parents = 1;
|
||||||
@ -1330,6 +1328,8 @@ static int starfive_probe(struct platform_device *pdev)
|
|||||||
if (ret)
|
if (ret)
|
||||||
return dev_err_probe(dev, ret, "could not register gpiochip\n");
|
return dev_err_probe(dev, ret, "could not register gpiochip\n");
|
||||||
|
|
||||||
|
irq_domain_set_pm_device(sfp->gc.irq.domain, dev);
|
||||||
|
|
||||||
out_pinctrl_enable:
|
out_pinctrl_enable:
|
||||||
return pinctrl_enable(sfp->pctl);
|
return pinctrl_enable(sfp->pctl);
|
||||||
}
|
}
|
||||||
|
@ -391,10 +391,7 @@ static int gb_gpio_request_handler(struct gb_operation *op)
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
local_irq_disable();
|
ret = generic_handle_irq_safe(irq);
|
||||||
ret = generic_handle_irq(irq);
|
|
||||||
local_irq_enable();
|
|
||||||
|
|
||||||
if (ret)
|
if (ret)
|
||||||
dev_err(dev, "failed to invoke irq handler\n");
|
dev_err(dev, "failed to invoke irq handler\n");
|
||||||
|
|
||||||
|
@ -456,7 +456,6 @@ static inline irq_hw_number_t irqd_to_hwirq(struct irq_data *d)
|
|||||||
/**
|
/**
|
||||||
* struct irq_chip - hardware interrupt chip descriptor
|
* struct irq_chip - hardware interrupt chip descriptor
|
||||||
*
|
*
|
||||||
* @parent_device: pointer to parent device for irqchip
|
|
||||||
* @name: name for /proc/interrupts
|
* @name: name for /proc/interrupts
|
||||||
* @irq_startup: start up the interrupt (defaults to ->enable if NULL)
|
* @irq_startup: start up the interrupt (defaults to ->enable if NULL)
|
||||||
* @irq_shutdown: shut down the interrupt (defaults to ->disable if NULL)
|
* @irq_shutdown: shut down the interrupt (defaults to ->disable if NULL)
|
||||||
@ -503,7 +502,6 @@ static inline irq_hw_number_t irqd_to_hwirq(struct irq_data *d)
|
|||||||
* @flags: chip specific flags
|
* @flags: chip specific flags
|
||||||
*/
|
*/
|
||||||
struct irq_chip {
|
struct irq_chip {
|
||||||
struct device *parent_device;
|
|
||||||
const char *name;
|
const char *name;
|
||||||
unsigned int (*irq_startup)(struct irq_data *data);
|
unsigned int (*irq_startup)(struct irq_data *data);
|
||||||
void (*irq_shutdown)(struct irq_data *data);
|
void (*irq_shutdown)(struct irq_data *data);
|
||||||
@ -712,10 +710,11 @@ extern struct irq_chip no_irq_chip;
|
|||||||
extern struct irq_chip dummy_irq_chip;
|
extern struct irq_chip dummy_irq_chip;
|
||||||
|
|
||||||
extern void
|
extern void
|
||||||
irq_set_chip_and_handler_name(unsigned int irq, struct irq_chip *chip,
|
irq_set_chip_and_handler_name(unsigned int irq, const struct irq_chip *chip,
|
||||||
irq_flow_handler_t handle, const char *name);
|
irq_flow_handler_t handle, const char *name);
|
||||||
|
|
||||||
static inline void irq_set_chip_and_handler(unsigned int irq, struct irq_chip *chip,
|
static inline void irq_set_chip_and_handler(unsigned int irq,
|
||||||
|
const struct irq_chip *chip,
|
||||||
irq_flow_handler_t handle)
|
irq_flow_handler_t handle)
|
||||||
{
|
{
|
||||||
irq_set_chip_and_handler_name(irq, chip, handle, NULL);
|
irq_set_chip_and_handler_name(irq, chip, handle, NULL);
|
||||||
@ -805,7 +804,7 @@ static inline void irq_set_percpu_devid_flags(unsigned int irq)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Set/get chip/data for an IRQ: */
|
/* Set/get chip/data for an IRQ: */
|
||||||
extern int irq_set_chip(unsigned int irq, struct irq_chip *chip);
|
extern int irq_set_chip(unsigned int irq, const struct irq_chip *chip);
|
||||||
extern int irq_set_handler_data(unsigned int irq, void *data);
|
extern int irq_set_handler_data(unsigned int irq, void *data);
|
||||||
extern int irq_set_chip_data(unsigned int irq, void *data);
|
extern int irq_set_chip_data(unsigned int irq, void *data);
|
||||||
extern int irq_set_irq_type(unsigned int irq, unsigned int type);
|
extern int irq_set_irq_type(unsigned int irq, unsigned int type);
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
/* SPDX-License-Identifier: GPL-2.0 */
|
|
||||||
#ifndef PLAT_FPGA_IRQ_H
|
|
||||||
#define PLAT_FPGA_IRQ_H
|
|
||||||
|
|
||||||
struct device_node;
|
|
||||||
struct pt_regs;
|
|
||||||
|
|
||||||
void fpga_handle_irq(struct pt_regs *regs);
|
|
||||||
void fpga_irq_init(void __iomem *, const char *, int, int, u32,
|
|
||||||
struct device_node *node);
|
|
||||||
int fpga_irq_of_init(struct device_node *node,
|
|
||||||
struct device_node *parent);
|
|
||||||
|
|
||||||
#endif
|
|
@ -160,6 +160,7 @@ static inline void generic_handle_irq_desc(struct irq_desc *desc)
|
|||||||
|
|
||||||
int handle_irq_desc(struct irq_desc *desc);
|
int handle_irq_desc(struct irq_desc *desc);
|
||||||
int generic_handle_irq(unsigned int irq);
|
int generic_handle_irq(unsigned int irq);
|
||||||
|
int generic_handle_irq_safe(unsigned int irq);
|
||||||
|
|
||||||
#ifdef CONFIG_IRQ_DOMAIN
|
#ifdef CONFIG_IRQ_DOMAIN
|
||||||
/*
|
/*
|
||||||
|
@ -151,6 +151,8 @@ struct irq_domain_chip_generic;
|
|||||||
* @gc: Pointer to a list of generic chips. There is a helper function for
|
* @gc: Pointer to a list of generic chips. There is a helper function for
|
||||||
* setting up one or more generic chips for interrupt controllers
|
* setting up one or more generic chips for interrupt controllers
|
||||||
* drivers using the generic chip library which uses this pointer.
|
* drivers using the generic chip library which uses this pointer.
|
||||||
|
* @dev: Pointer to a device that the domain represent, and that will be
|
||||||
|
* used for power management purposes.
|
||||||
* @parent: Pointer to parent irq_domain to support hierarchy irq_domains
|
* @parent: Pointer to parent irq_domain to support hierarchy irq_domains
|
||||||
*
|
*
|
||||||
* Revmap data, used internally by irq_domain
|
* Revmap data, used internally by irq_domain
|
||||||
@ -171,6 +173,7 @@ struct irq_domain {
|
|||||||
struct fwnode_handle *fwnode;
|
struct fwnode_handle *fwnode;
|
||||||
enum irq_domain_bus_token bus_token;
|
enum irq_domain_bus_token bus_token;
|
||||||
struct irq_domain_chip_generic *gc;
|
struct irq_domain_chip_generic *gc;
|
||||||
|
struct device *dev;
|
||||||
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
|
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
|
||||||
struct irq_domain *parent;
|
struct irq_domain *parent;
|
||||||
#endif
|
#endif
|
||||||
@ -226,6 +229,13 @@ static inline struct device_node *irq_domain_get_of_node(struct irq_domain *d)
|
|||||||
return to_of_node(d->fwnode);
|
return to_of_node(d->fwnode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void irq_domain_set_pm_device(struct irq_domain *d,
|
||||||
|
struct device *dev)
|
||||||
|
{
|
||||||
|
if (d)
|
||||||
|
d->dev = dev;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_IRQ_DOMAIN
|
#ifdef CONFIG_IRQ_DOMAIN
|
||||||
struct fwnode_handle *__irq_domain_alloc_fwnode(unsigned int type, int id,
|
struct fwnode_handle *__irq_domain_alloc_fwnode(unsigned int type, int id,
|
||||||
const char *name, phys_addr_t *pa);
|
const char *name, phys_addr_t *pa);
|
||||||
@ -469,7 +479,8 @@ int irq_destroy_ipi(unsigned int irq, const struct cpumask *dest);
|
|||||||
extern struct irq_data *irq_domain_get_irq_data(struct irq_domain *domain,
|
extern struct irq_data *irq_domain_get_irq_data(struct irq_domain *domain,
|
||||||
unsigned int virq);
|
unsigned int virq);
|
||||||
extern void irq_domain_set_info(struct irq_domain *domain, unsigned int virq,
|
extern void irq_domain_set_info(struct irq_domain *domain, unsigned int virq,
|
||||||
irq_hw_number_t hwirq, struct irq_chip *chip,
|
irq_hw_number_t hwirq,
|
||||||
|
const struct irq_chip *chip,
|
||||||
void *chip_data, irq_flow_handler_t handler,
|
void *chip_data, irq_flow_handler_t handler,
|
||||||
void *handler_data, const char *handler_name);
|
void *handler_data, const char *handler_name);
|
||||||
extern void irq_domain_reset_irq_data(struct irq_data *irq_data);
|
extern void irq_domain_reset_irq_data(struct irq_data *irq_data);
|
||||||
@ -512,7 +523,7 @@ extern int irq_domain_alloc_irqs_hierarchy(struct irq_domain *domain,
|
|||||||
extern int irq_domain_set_hwirq_and_chip(struct irq_domain *domain,
|
extern int irq_domain_set_hwirq_and_chip(struct irq_domain *domain,
|
||||||
unsigned int virq,
|
unsigned int virq,
|
||||||
irq_hw_number_t hwirq,
|
irq_hw_number_t hwirq,
|
||||||
struct irq_chip *chip,
|
const struct irq_chip *chip,
|
||||||
void *chip_data);
|
void *chip_data);
|
||||||
extern void irq_domain_free_irqs_common(struct irq_domain *domain,
|
extern void irq_domain_free_irqs_common(struct irq_domain *domain,
|
||||||
unsigned int virq,
|
unsigned int virq,
|
||||||
|
@ -38,7 +38,7 @@ struct irqaction chained_action = {
|
|||||||
* @irq: irq number
|
* @irq: irq number
|
||||||
* @chip: pointer to irq chip description structure
|
* @chip: pointer to irq chip description structure
|
||||||
*/
|
*/
|
||||||
int irq_set_chip(unsigned int irq, struct irq_chip *chip)
|
int irq_set_chip(unsigned int irq, const struct irq_chip *chip)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0);
|
struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0);
|
||||||
@ -46,10 +46,7 @@ int irq_set_chip(unsigned int irq, struct irq_chip *chip)
|
|||||||
if (!desc)
|
if (!desc)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (!chip)
|
desc->irq_data.chip = (struct irq_chip *)(chip ?: &no_irq_chip);
|
||||||
chip = &no_irq_chip;
|
|
||||||
|
|
||||||
desc->irq_data.chip = chip;
|
|
||||||
irq_put_desc_unlock(desc, flags);
|
irq_put_desc_unlock(desc, flags);
|
||||||
/*
|
/*
|
||||||
* For !CONFIG_SPARSE_IRQ make the irq show up in
|
* For !CONFIG_SPARSE_IRQ make the irq show up in
|
||||||
@ -1073,7 +1070,7 @@ irq_set_chained_handler_and_data(unsigned int irq, irq_flow_handler_t handle,
|
|||||||
EXPORT_SYMBOL_GPL(irq_set_chained_handler_and_data);
|
EXPORT_SYMBOL_GPL(irq_set_chained_handler_and_data);
|
||||||
|
|
||||||
void
|
void
|
||||||
irq_set_chip_and_handler_name(unsigned int irq, struct irq_chip *chip,
|
irq_set_chip_and_handler_name(unsigned int irq, const struct irq_chip *chip,
|
||||||
irq_flow_handler_t handle, const char *name)
|
irq_flow_handler_t handle, const char *name)
|
||||||
{
|
{
|
||||||
irq_set_chip(irq, chip);
|
irq_set_chip(irq, chip);
|
||||||
@ -1558,6 +1555,14 @@ int irq_chip_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct device *irq_get_parent_device(struct irq_data *data)
|
||||||
|
{
|
||||||
|
if (data->domain)
|
||||||
|
return data->domain->dev;
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* irq_chip_pm_get - Enable power for an IRQ chip
|
* irq_chip_pm_get - Enable power for an IRQ chip
|
||||||
* @data: Pointer to interrupt specific data
|
* @data: Pointer to interrupt specific data
|
||||||
@ -1567,12 +1572,13 @@ int irq_chip_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
|
|||||||
*/
|
*/
|
||||||
int irq_chip_pm_get(struct irq_data *data)
|
int irq_chip_pm_get(struct irq_data *data)
|
||||||
{
|
{
|
||||||
|
struct device *dev = irq_get_parent_device(data);
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
if (IS_ENABLED(CONFIG_PM) && data->chip->parent_device) {
|
if (IS_ENABLED(CONFIG_PM) && dev) {
|
||||||
retval = pm_runtime_get_sync(data->chip->parent_device);
|
retval = pm_runtime_get_sync(dev);
|
||||||
if (retval < 0) {
|
if (retval < 0) {
|
||||||
pm_runtime_put_noidle(data->chip->parent_device);
|
pm_runtime_put_noidle(dev);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1590,10 +1596,11 @@ int irq_chip_pm_get(struct irq_data *data)
|
|||||||
*/
|
*/
|
||||||
int irq_chip_pm_put(struct irq_data *data)
|
int irq_chip_pm_put(struct irq_data *data)
|
||||||
{
|
{
|
||||||
|
struct device *dev = irq_get_parent_device(data);
|
||||||
int retval = 0;
|
int retval = 0;
|
||||||
|
|
||||||
if (IS_ENABLED(CONFIG_PM) && data->chip->parent_device)
|
if (IS_ENABLED(CONFIG_PM) && dev)
|
||||||
retval = pm_runtime_put(data->chip->parent_device);
|
retval = pm_runtime_put(dev);
|
||||||
|
|
||||||
return (retval < 0) ? retval : 0;
|
return (retval < 0) ? retval : 0;
|
||||||
}
|
}
|
||||||
|
@ -69,8 +69,12 @@ irq_debug_show_chip(struct seq_file *m, struct irq_data *data, int ind)
|
|||||||
seq_printf(m, "chip: None\n");
|
seq_printf(m, "chip: None\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
seq_printf(m, "%*schip: %s\n", ind, "", chip->name);
|
seq_printf(m, "%*schip: ", ind, "");
|
||||||
seq_printf(m, "%*sflags: 0x%lx\n", ind + 1, "", chip->flags);
|
if (chip->irq_print_chip)
|
||||||
|
chip->irq_print_chip(data, m);
|
||||||
|
else
|
||||||
|
seq_printf(m, "%s", chip->name);
|
||||||
|
seq_printf(m, "\n%*sflags: 0x%lx\n", ind + 1, "", chip->flags);
|
||||||
irq_debug_show_bits(m, ind, chip->flags, irqchip_flags,
|
irq_debug_show_bits(m, ind, chip->flags, irqchip_flags,
|
||||||
ARRAY_SIZE(irqchip_flags));
|
ARRAY_SIZE(irqchip_flags));
|
||||||
}
|
}
|
||||||
|
@ -640,7 +640,7 @@ int handle_irq_desc(struct irq_desc *desc)
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
data = irq_desc_get_irq_data(desc);
|
data = irq_desc_get_irq_data(desc);
|
||||||
if (WARN_ON_ONCE(!in_irq() && handle_enforce_irqctx(data)))
|
if (WARN_ON_ONCE(!in_hardirq() && handle_enforce_irqctx(data)))
|
||||||
return -EPERM;
|
return -EPERM;
|
||||||
|
|
||||||
generic_handle_irq_desc(desc);
|
generic_handle_irq_desc(desc);
|
||||||
@ -662,6 +662,29 @@ int generic_handle_irq(unsigned int irq)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(generic_handle_irq);
|
EXPORT_SYMBOL_GPL(generic_handle_irq);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* generic_handle_irq_safe - Invoke the handler for a particular irq from any
|
||||||
|
* context.
|
||||||
|
* @irq: The irq number to handle
|
||||||
|
*
|
||||||
|
* Returns: 0 on success, a negative value on error.
|
||||||
|
*
|
||||||
|
* This function can be called from any context (IRQ or process context). It
|
||||||
|
* will report an error if not invoked from IRQ context and the irq has been
|
||||||
|
* marked to enforce IRQ-context only.
|
||||||
|
*/
|
||||||
|
int generic_handle_irq_safe(unsigned int irq)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
local_irq_save(flags);
|
||||||
|
ret = handle_irq_desc(irq_to_desc(irq));
|
||||||
|
local_irq_restore(flags);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(generic_handle_irq_safe);
|
||||||
|
|
||||||
#ifdef CONFIG_IRQ_DOMAIN
|
#ifdef CONFIG_IRQ_DOMAIN
|
||||||
/**
|
/**
|
||||||
* generic_handle_domain_irq - Invoke the handler for a HW irq belonging
|
* generic_handle_domain_irq - Invoke the handler for a HW irq belonging
|
||||||
@ -676,7 +699,7 @@ EXPORT_SYMBOL_GPL(generic_handle_irq);
|
|||||||
*/
|
*/
|
||||||
int generic_handle_domain_irq(struct irq_domain *domain, unsigned int hwirq)
|
int generic_handle_domain_irq(struct irq_domain *domain, unsigned int hwirq)
|
||||||
{
|
{
|
||||||
WARN_ON_ONCE(!in_irq());
|
WARN_ON_ONCE(!in_hardirq());
|
||||||
return handle_irq_desc(irq_resolve_mapping(domain, hwirq));
|
return handle_irq_desc(irq_resolve_mapping(domain, hwirq));
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(generic_handle_domain_irq);
|
EXPORT_SYMBOL_GPL(generic_handle_domain_irq);
|
||||||
|
@ -1319,7 +1319,8 @@ EXPORT_SYMBOL_GPL(irq_domain_get_irq_data);
|
|||||||
* @chip_data: The associated chip data
|
* @chip_data: The associated chip data
|
||||||
*/
|
*/
|
||||||
int irq_domain_set_hwirq_and_chip(struct irq_domain *domain, unsigned int virq,
|
int irq_domain_set_hwirq_and_chip(struct irq_domain *domain, unsigned int virq,
|
||||||
irq_hw_number_t hwirq, struct irq_chip *chip,
|
irq_hw_number_t hwirq,
|
||||||
|
const struct irq_chip *chip,
|
||||||
void *chip_data)
|
void *chip_data)
|
||||||
{
|
{
|
||||||
struct irq_data *irq_data = irq_domain_get_irq_data(domain, virq);
|
struct irq_data *irq_data = irq_domain_get_irq_data(domain, virq);
|
||||||
@ -1328,7 +1329,7 @@ int irq_domain_set_hwirq_and_chip(struct irq_domain *domain, unsigned int virq,
|
|||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
|
|
||||||
irq_data->hwirq = hwirq;
|
irq_data->hwirq = hwirq;
|
||||||
irq_data->chip = chip ? chip : &no_irq_chip;
|
irq_data->chip = (struct irq_chip *)(chip ? chip : &no_irq_chip);
|
||||||
irq_data->chip_data = chip_data;
|
irq_data->chip_data = chip_data;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -1347,7 +1348,7 @@ EXPORT_SYMBOL_GPL(irq_domain_set_hwirq_and_chip);
|
|||||||
* @handler_name: The interrupt handler name
|
* @handler_name: The interrupt handler name
|
||||||
*/
|
*/
|
||||||
void irq_domain_set_info(struct irq_domain *domain, unsigned int virq,
|
void irq_domain_set_info(struct irq_domain *domain, unsigned int virq,
|
||||||
irq_hw_number_t hwirq, struct irq_chip *chip,
|
irq_hw_number_t hwirq, const struct irq_chip *chip,
|
||||||
void *chip_data, irq_flow_handler_t handler,
|
void *chip_data, irq_flow_handler_t handler,
|
||||||
void *handler_data, const char *handler_name)
|
void *handler_data, const char *handler_name)
|
||||||
{
|
{
|
||||||
@ -1853,7 +1854,7 @@ EXPORT_SYMBOL_GPL(irq_domain_get_irq_data);
|
|||||||
* @handler_name: The interrupt handler name
|
* @handler_name: The interrupt handler name
|
||||||
*/
|
*/
|
||||||
void irq_domain_set_info(struct irq_domain *domain, unsigned int virq,
|
void irq_domain_set_info(struct irq_domain *domain, unsigned int virq,
|
||||||
irq_hw_number_t hwirq, struct irq_chip *chip,
|
irq_hw_number_t hwirq, const struct irq_chip *chip,
|
||||||
void *chip_data, irq_flow_handler_t handler,
|
void *chip_data, irq_flow_handler_t handler,
|
||||||
void *handler_data, const char *handler_name)
|
void *handler_data, const char *handler_name)
|
||||||
{
|
{
|
||||||
|
@ -222,7 +222,7 @@ void __local_bh_enable_ip(unsigned long ip, unsigned int cnt)
|
|||||||
u32 pending;
|
u32 pending;
|
||||||
int curcnt;
|
int curcnt;
|
||||||
|
|
||||||
WARN_ON_ONCE(in_irq());
|
WARN_ON_ONCE(in_hardirq());
|
||||||
lockdep_assert_irqs_enabled();
|
lockdep_assert_irqs_enabled();
|
||||||
|
|
||||||
local_irq_save(flags);
|
local_irq_save(flags);
|
||||||
@ -305,7 +305,7 @@ void __local_bh_disable_ip(unsigned long ip, unsigned int cnt)
|
|||||||
{
|
{
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
WARN_ON_ONCE(in_irq());
|
WARN_ON_ONCE(in_hardirq());
|
||||||
|
|
||||||
raw_local_irq_save(flags);
|
raw_local_irq_save(flags);
|
||||||
/*
|
/*
|
||||||
@ -352,14 +352,14 @@ static void __local_bh_enable(unsigned int cnt)
|
|||||||
*/
|
*/
|
||||||
void _local_bh_enable(void)
|
void _local_bh_enable(void)
|
||||||
{
|
{
|
||||||
WARN_ON_ONCE(in_irq());
|
WARN_ON_ONCE(in_hardirq());
|
||||||
__local_bh_enable(SOFTIRQ_DISABLE_OFFSET);
|
__local_bh_enable(SOFTIRQ_DISABLE_OFFSET);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(_local_bh_enable);
|
EXPORT_SYMBOL(_local_bh_enable);
|
||||||
|
|
||||||
void __local_bh_enable_ip(unsigned long ip, unsigned int cnt)
|
void __local_bh_enable_ip(unsigned long ip, unsigned int cnt)
|
||||||
{
|
{
|
||||||
WARN_ON_ONCE(in_irq());
|
WARN_ON_ONCE(in_hardirq());
|
||||||
lockdep_assert_irqs_enabled();
|
lockdep_assert_irqs_enabled();
|
||||||
#ifdef CONFIG_TRACE_IRQFLAGS
|
#ifdef CONFIG_TRACE_IRQFLAGS
|
||||||
local_irq_disable();
|
local_irq_disable();
|
||||||
@ -618,7 +618,7 @@ static inline void tick_irq_exit(void)
|
|||||||
|
|
||||||
/* Make sure that timer wheel updates are propagated */
|
/* Make sure that timer wheel updates are propagated */
|
||||||
if ((idle_cpu(cpu) && !need_resched()) || tick_nohz_full_cpu(cpu)) {
|
if ((idle_cpu(cpu) && !need_resched()) || tick_nohz_full_cpu(cpu)) {
|
||||||
if (!in_irq())
|
if (!in_hardirq())
|
||||||
tick_nohz_irq_exit();
|
tick_nohz_irq_exit();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
x
Reference in New Issue
Block a user