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:
Linus Torvalds 2022-03-21 13:03:00 -07:00
commit 93287e28bc
48 changed files with 1511 additions and 450 deletions

View file

@ -18,6 +18,7 @@ Required properties:
"amlogic,meson-g12a-gpio-intc" for G12A SoCs (S905D2, S905X2, S905Y2)
"amlogic,meson-sm1-gpio-intc" for SM1 SoCs (S905D3, S905X3, S905Y3)
"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.
- interrupt-controller : Identifies the node as an interrupt controller.
- #interrupt-cells : Specifies the number of cells needed to encode an

View file

@ -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";
};
};

View file

@ -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>;
};

View file

@ -20,6 +20,7 @@ properties:
- items:
- enum:
- st,stm32mp1-exti
- st,stm32mp13-exti
- const: syscon
"#interrupt-cells":

View file

@ -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/*
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/pci/apple,pcie.yaml
F: Documentation/devicetree/bindings/pinctrl/apple,pinctrl.yaml

View file

@ -45,6 +45,8 @@ config MICROBLAZE
select SET_FS
select ZONE_DMA
select TRACE_IRQFLAGS_SUPPORT
select GENERIC_IRQ_MULTI_HANDLER
select HANDLE_DOMAIN_IRQ
# Endianness selection
choice

View file

@ -11,7 +11,4 @@
struct pt_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 */

View file

@ -20,27 +20,13 @@
#include <linux/irqchip.h>
#include <linux/of_irq.h>
static u32 concurrent_irq;
void __irq_entry do_IRQ(struct pt_regs *regs)
{
unsigned int irq;
struct pt_regs *old_regs = set_irq_regs(regs);
trace_hardirqs_off();
irq_enter();
irq = xintc_get_irq();
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;
}
handle_arch_irq(regs);
irq_exit();
set_irq_regs(old_regs);
trace_hardirqs_on();

View file

@ -239,7 +239,6 @@ mediatek_gpio_bank_probe(struct device *dev, int bank)
rg->chip.offset = bank * MTK_BANK_WIDTH;
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_mask = mediatek_gpio_irq_mask;
rg->irq_chip.irq_mask_ack = mediatek_gpio_irq_mask;

View file

@ -986,7 +986,8 @@ static void omap_gpio_mod_init(struct gpio_bank *bank)
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;
static int gpio;
@ -1052,6 +1053,7 @@ static int omap_gpio_chip_init(struct gpio_bank *bank, struct irq_chip *irqc)
if (ret)
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,
omap_gpio_irq_handler,
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->name = dev_name(&pdev->dev);
irqc->flags = IRQCHIP_MASK_ON_SUSPEND;
irqc->parent_device = dev;
bank->irq = platform_get_irq(pdev, 0);
if (bank->irq <= 0) {
@ -1466,7 +1467,7 @@ static int omap_gpio_probe(struct platform_device *pdev)
omap_gpio_mod_init(bank);
ret = omap_gpio_chip_init(bank, irqc);
ret = omap_gpio_chip_init(bank, irqc, dev);
if (ret) {
pm_runtime_put_sync(dev);
pm_runtime_disable(dev);

View file

@ -530,7 +530,6 @@ static int gpio_rcar_probe(struct platform_device *pdev)
irq_chip = &p->irq_chip;
irq_chip->name = "gpio-rcar";
irq_chip->parent_device = dev;
irq_chip->irq_mask = gpio_rcar_irq_disable;
irq_chip->irq_unmask = gpio_rcar_irq_enable;
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;
}
irq_domain_set_pm_device(gpio_chip->irq.domain, dev);
ret = devm_request_irq(dev, p->irq_parent, gpio_rcar_irq_handler,
IRQF_SHARED, name, p);
if (ret) {

View file

@ -281,7 +281,6 @@ static int tqmx86_gpio_probe(struct platform_device *pdev)
u8 irq_status;
irq_chip->name = chip->label;
irq_chip->parent_device = &pdev->dev;
irq_chip->irq_mask = tqmx86_gpio_irq_mask;
irq_chip->irq_unmask = tqmx86_gpio_irq_unmask;
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;
}
irq_domain_set_pm_device(girq->domain, dev);
dev_info(dev, "GPIO functionality initialized with %d pins\n",
chip->ngpio);

View file

@ -430,6 +430,14 @@ config QCOM_PDC
Power Domain Controller driver to manage and configure wakeup
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
bool
depends on CSKY

View file

@ -94,6 +94,7 @@ obj-$(CONFIG_MESON_IRQ_GPIO) += irq-meson-gpio.o
obj-$(CONFIG_GOLDFISH_PIC) += irq-goldfish-pic.o
obj-$(CONFIG_NDS32) += irq-ativic32.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_APB_INTC) += irq-csky-apb-intc.o
obj-$(CONFIG_RISCV_INTC) += irq-riscv-intc.o

View file

@ -24,7 +24,7 @@
* - Default "this CPU" register view and explicit per-CPU views
*
* 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).
*
* Implementation notes:
@ -52,10 +52,12 @@
#include <linux/irqchip.h>
#include <linux/irqchip/arm-vgic-info.h>
#include <linux/irqdomain.h>
#include <linux/jump_label.h>
#include <linux/limits.h>
#include <linux/of_address.h>
#include <linux/slab.h>
#include <asm/apple_m1_pmu.h>
#include <asm/cputype.h>
#include <asm/exception.h>
#include <asm/sysreg.h>
#include <asm/virt.h>
@ -63,20 +65,22 @@
#include <dt-bindings/interrupt-controller/apple-aic.h>
/*
* AIC registers (MMIO)
* AIC v1 registers (MMIO)
*/
#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_WHOAMI 0x2000
#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_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_IPI_OTHER 1
#define AIC_EVENT_IPI_SELF 2
@ -92,22 +96,71 @@
#define AIC_IPI_SELF BIT(31)
#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_CLR(cpu) (0x500c + ((cpu) << 7))
#define AIC_CPU_IPI_MASK_SET(cpu) (0x5024 + ((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_BIT(x) BIT((x) & GENMASK(4, 0))
/*
* IMP-DEF sysregs that control FIQ sources
* Note: sysreg-based IPIs are not supported yet.
*/
/* IPI request registers */
@ -146,6 +199,17 @@
#define SYS_IMP_APL_UPMSR_EL1 sys_reg(3, 7, 15, 6, 4)
#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_SWIPI 32
@ -164,14 +228,81 @@
#define AIC_TMR_EL02_PHYS AIC_TMR_GUEST_PHYS
#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 {
void __iomem *base;
void __iomem *event;
struct irq_domain *hw_domain;
struct irq_domain *ipi_domain;
struct {
cpumask_t aff;
} *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);
@ -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)
{
irq_hw_number_t hwirq = irqd_to_hwirq(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)),
MASK_BIT(irqd_to_hwirq(d)));
u32 off = AIC_HWIRQ_DIE(hwirq) * ic->info.die_stride;
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)
{
irq_hw_number_t hwirq = irqd_to_hwirq(d);
struct aic_irq_chip *ic = irq_data_get_irq_chip_data(d);
aic_ic_write(ic, AIC_MASK_CLR + MASK_REG(d->hwirq),
MASK_BIT(irqd_to_hwirq(d)));
u32 off = AIC_HWIRQ_DIE(hwirq) * ic->info.die_stride;
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)
@ -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
* 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);
irq = FIELD_GET(AIC_EVENT_NUM, event);
if (type == AIC_EVENT_TYPE_HW)
generic_handle_domain_irq(aic_irqc->hw_domain, irq);
if (type == AIC_EVENT_TYPE_IRQ)
generic_handle_domain_irq(aic_irqc->hw_domain, event);
else if (type == AIC_EVENT_TYPE_IPI && irq == 1)
aic_handle_ipi(regs);
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);
int cpu;
BUG_ON(!ic->info.target_cpu);
if (force)
cpu = cpumask_first(mask_val);
else
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));
return IRQ_SET_MASK_OK;
@ -294,15 +433,21 @@ static struct irq_chip aic_chip = {
.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
*/
static unsigned long aic_fiq_get_idx(struct irq_data *d)
{
struct aic_irq_chip *ic = irq_data_get_irq_chip_data(d);
return irqd_to_hwirq(d) - ic->nr_hw;
return AIC_HWIRQ_IRQ(irqd_to_hwirq(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) {
pr_err_ratelimited("Fast IPI fired. Acking.\n");
write_sysreg_s(IPI_SR_PENDING, SYS_IMP_APL_IPI_SR_EL1);
if (static_branch_likely(&use_fast_ipi)) {
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)))
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)))
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()) {
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) &&
TIMER_FIRING(read_sysreg_s(SYS_CNTP_CTL_EL02)))
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) &&
TIMER_FIRING(read_sysreg_s(SYS_CNTV_CTL_EL02)))
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) {
@ -414,7 +563,7 @@ static void __exception_irq_entry aic_handle_fiq(struct pt_regs *regs)
else
irq = AIC_CPU_PMU_E;
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 &&
@ -448,13 +597,18 @@ static int aic_irq_domain_map(struct irq_domain *id, unsigned int irq,
irq_hw_number_t hw)
{
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) {
irq_domain_set_info(id, irq, hw, &aic_chip, id->host_data,
if (ic->info.version == 2)
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);
irqd_set_single_target(irq_desc_get_irq_data(irq_to_desc(irq)));
} else {
int fiq = hw - ic->nr_hw;
int fiq = FIELD_GET(AIC_EVENT_NUM, hw);
switch (fiq) {
case AIC_CPU_PMU_P:
@ -479,32 +633,46 @@ static int aic_irq_domain_translate(struct irq_domain *id,
unsigned int *type)
{
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;
args = &fwspec->param[1];
if (fwspec->param_count == 4) {
die = args[0];
args++;
}
switch (fwspec->param[0]) {
case AIC_IRQ:
if (fwspec->param[1] >= ic->nr_hw)
if (die >= ic->nr_die)
return -EINVAL;
*hwirq = fwspec->param[1];
if (args[0] >= ic->nr_irq)
return -EINVAL;
*hwirq = AIC_IRQ_HWIRQ(die, args[0]);
break;
case AIC_FIQ:
if (fwspec->param[1] >= AIC_NR_FIQ)
if (die != 0)
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,
* not EL2's, so remap the hwirqs to match.
*/
if (!is_kernel_in_hyp_mode()) {
switch (fwspec->param[1]) {
switch (args[0]) {
case AIC_TMR_GUEST_PHYS:
*hwirq = ic->nr_hw + AIC_TMR_EL0_PHYS;
*hwirq = AIC_FIQ_HWIRQ(AIC_TMR_EL0_PHYS);
break;
case AIC_TMR_GUEST_VIRT:
*hwirq = ic->nr_hw + AIC_TMR_EL0_VIRT;
*hwirq = AIC_FIQ_HWIRQ(AIC_TMR_EL0_VIRT);
break;
case AIC_TMR_HV_PHYS:
case AIC_TMR_HV_VIRT:
@ -518,7 +686,7 @@ static int aic_irq_domain_translate(struct irq_domain *id,
return -EINVAL;
}
*type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
*type = args[1] & IRQ_TYPE_SENSE_MASK;
return 0;
}
@ -567,6 +735,22 @@ static const struct irq_domain_ops aic_irq_domain_ops = {
* 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)
{
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.
* No barriers needed here since this is a self-IPI.
*/
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 (atomic_read(this_cpu_ptr(&aic_vipi_flag)) & irq_bit) {
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)
@ -621,8 +809,12 @@ static void aic_ipi_send_mask(struct irq_data *d, const struct cpumask *mask)
smp_mb__after_atomic();
if (!(pending & irq_bit) &&
(atomic_read(per_cpu_ptr(&aic_vipi_enable, cpu)) & irq_bit))
send |= AIC_IPI_SEND_CPU(cpu);
(atomic_read(per_cpu_ptr(&aic_vipi_enable, cpu)) & irq_bit)) {
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
* 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
@ -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
* 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,
@ -770,20 +971,27 @@ static int aic_init_cpu(unsigned int cpu)
/* Commit all of the above */
isb();
/*
* 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
* a mapping table similar to what other irqchip drivers do.
*/
WARN_ON(aic_ic_read(aic_irqc, AIC_WHOAMI) != smp_processor_id());
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
* a mapping table similar to what other irqchip drivers do.
*/
WARN_ON(aic_ic_read(aic_irqc, AIC_WHOAMI) != smp_processor_id());
/*
* Always keep IPIs unmasked at the hardware level (except auto-masking
* by AIC during processing). We manage masks at the vIPI level.
*/
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_MASK_CLR, AIC_IPI_OTHER);
/*
* Always keep IPIs unmasked at the hardware level (except auto-masking
* 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);
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 */
__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))
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])
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)
{
int i;
int i, die;
u32 off, start_off;
void __iomem *regs;
u32 info;
struct aic_irq_chip *irqc;
struct device_node *affs;
const struct of_device_id *match;
regs = of_iomap(node, 0);
if (WARN_ON(!regs))
return -EIO;
irqc = kzalloc(sizeof(*irqc), GFP_KERNEL);
if (!irqc)
if (!irqc) {
iounmap(regs);
return -ENOMEM;
}
aic_irqc = irqc;
irqc->base = regs;
info = aic_ic_read(irqc, AIC_INFO);
irqc->nr_hw = FIELD_GET(AIC_INFO_NR_HW, info);
match = of_match_node(aic_info_match, node);
if (!match)
goto err_unmap;
irqc->hw_domain = irq_domain_create_linear(of_node_to_fwnode(node),
irqc->nr_hw + AIC_NR_FIQ,
&aic_irq_domain_ops, irqc);
if (WARN_ON(!irqc->hw_domain)) {
iounmap(irqc->base);
kfree(irqc);
return -ENODEV;
irqc->info = *(struct aic_info *)match->data;
aic_irqc = irqc;
switch (irqc->info.version) {
case 1: {
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);
if (aic_init_smp(irqc, node)) {
irq_domain_remove(irqc->hw_domain);
iounmap(irqc->base);
kfree(irqc);
return -ENODEV;
}
if (aic_init_smp(irqc, node))
goto err_remove_domain;
affs = of_get_child_by_name(node, "affinities");
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_fiq(aic_handle_fiq);
for (i = 0; i < BITS_TO_U32(irqc->nr_hw); i++)
aic_ic_write(irqc, AIC_MASK_SET + i * 4, U32_MAX);
for (i = 0; i < BITS_TO_U32(irqc->nr_hw); i++)
aic_ic_write(irqc, AIC_SW_CLR + i * 4, U32_MAX);
for (i = 0; i < irqc->nr_hw; i++)
aic_ic_write(irqc, AIC_TARGET_CPU + i * 4, 1);
off = 0;
for (die = 0; die < irqc->nr_die; die++) {
for (i = 0; i < BITS_TO_U32(irqc->nr_irq); i++)
aic_ic_write(irqc, irqc->info.mask_set + off + i * 4, U32_MAX);
for (i = 0; i < BITS_TO_U32(irqc->nr_irq); i++)
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())
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,
"irqchip/apple-aic/ipi:starting",
aic_init_cpu, NULL);
vgic_set_kvm_info(&vgic_info);
pr_info("Initialized with %d IRQs, %d FIQs, %d vIPIs\n",
irqc->nr_hw, AIC_NR_FIQ, AIC_NR_SWIPI);
pr_info("Initialized with %d/%d IRQs * %d/%d die(s), %d FIQs, %d vIPIs",
irqc->nr_irq, irqc->max_irq, irqc->nr_die, irqc->max_die, AIC_NR_FIQ, AIC_NR_SWIPI);
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);

View file

@ -11,7 +11,6 @@
#include <linux/irq.h>
#include <linux/io.h>
#include <linux/irqchip.h>
#include <linux/irqchip/versatile-fpga.h>
#include <linux/irqdomain.h>
#include <linux/module.h>
#include <linux/of.h>

View file

@ -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
* other CPUs before issuing the IPI.
*/
wmb();
dsb(ishst);
for_each_cpu(cpu, mask) {
u64 cluster_id = MPIDR_TO_SGI_CLUSTER_ID(cpu_logical_map(cpu));

View file

@ -34,6 +34,7 @@
#include <linux/irqdomain.h>
#include <linux/interrupt.h>
#include <linux/percpu.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/irqchip.h>
#include <linux/irqchip/chained_irq.h>
@ -66,7 +67,6 @@ union gic_base {
};
struct gic_chip_data {
struct irq_chip chip;
union gic_base dist_base;
union gic_base cpu_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);
}
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_get_irqchip_state = gic_irq_get_irqchip_state,
.irq_set_irqchip_state = gic_irq_set_irqchip_state,
.flags = IRQCHIP_SET_TYPE_MASKED |
IRQCHIP_SKIP_SET_WAKE |
IRQCHIP_MASK_ON_SUSPEND,
};
static void gic_irq_print_chip(struct irq_data *d, struct seq_file *p)
{
struct gic_chip_data *gic = irq_data_get_irq_chip_data(d);
if (gic->domain->dev)
seq_printf(p, gic->domain->dev->of_node->name);
else
seq_printf(p, "GIC-%d", (int)(gic - &gic_data[0]));
}
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)
{
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;
if (unlikely(gic != &gic_data[0]))
return -EINVAL;
if (!force)
cpu = cpumask_any_and(mask_val, cpu_online_mask);
else
@ -880,6 +881,39 @@ static __init void gic_smp_init(void)
#define gic_ipi_send_mask NULL
#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
/*
* 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 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) {
case 0 ... 31:
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);
break;
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);
irq_set_probe(irq);
irqd_set_single_target(irqd);
@ -1127,26 +1165,6 @@ static const struct irq_domain_ops gic_irq_domain_ops = {
.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,
struct fwnode_handle *handle)
{
@ -1246,7 +1264,6 @@ static int gic_init_bases(struct gic_chip_data *gic,
static int __init __gic_init_bases(struct gic_chip_data *gic,
struct fwnode_handle *handle)
{
char *name;
int i, ret;
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");
}
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);
if (ret)
kfree(name);
else if (gic == &gic_data[0])
if (gic == &gic_data[0])
gic_smp_init();
return ret;
@ -1460,8 +1467,6 @@ int gic_of_init_child(struct device *dev, struct gic_chip_data **gic, int irq)
if (!*gic)
return -ENOMEM;
gic_init_chip(*gic, dev, dev->of_node->name, false);
ret = gic_of_setup(*gic, dev->of_node);
if (ret)
return ret;
@ -1472,6 +1477,7 @@ int gic_of_init_child(struct device *dev, struct gic_chip_data **gic, int irq)
return ret;
}
irq_domain_set_pm_device((*gic)->domain, dev);
irq_set_chained_handler_and_data(irq, gic_handle_cascade_irq, *gic);
return 0;

View file

@ -61,7 +61,6 @@
#define CHAN_MAX_NUM 0x8
struct intmux_irqchip_data {
struct irq_chip chip;
u32 saved_reg;
int chanidx;
int irq;
@ -114,7 +113,7 @@ static void imx_intmux_irq_unmask(struct irq_data *d)
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",
.irq_mask = imx_intmux_irq_mask,
.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;
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;
}
@ -241,8 +240,6 @@ static int imx_intmux_probe(struct platform_device *pdev)
}
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].irq = irq_of_parse_and_map(np, i);
@ -260,6 +257,7 @@ static int imx_intmux_probe(struct platform_device *pdev)
goto out;
}
data->irqchip_data[i].domain = domain;
irq_domain_set_pm_device(domain, &pdev->dev);
/* disable all interrupt sources of this channel firstly */
writel_relaxed(0, data->regs + CHANIER(i));

View file

@ -11,6 +11,7 @@
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <asm/exception.h>
@ -25,8 +26,8 @@
struct lpc32xx_irq_chip {
void __iomem *base;
phys_addr_t addr;
struct irq_domain *domain;
struct irq_chip chip;
};
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;
}
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)
{
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;
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_noprobe(virq);
@ -183,6 +202,7 @@ static int __init lpc32xx_of_ic_init(struct device_node *node,
if (!irqc)
return -ENOMEM;
irqc->addr = addr;
irqc->base = of_iomap(node, 0);
if (!irqc->base) {
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;
}
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,
&lpc32xx_irq_domain_ops, irqc);
if (!irqc->domain) {
pr_err("unable to add irq domain\n");
iounmap(irqc->base);
kfree(irqc->chip.name);
kfree(irqc);
return -ENODEV;
}

View file

@ -16,7 +16,7 @@
#include <linux/of.h>
#include <linux/of_address.h>
#define NUM_CHANNEL 8
#define MAX_NUM_CHANNEL 64
#define MAX_INPUT_MUX 256
#define REG_EDGE_POL 0x00
@ -26,6 +26,8 @@
/* use for A1 like chips */
#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
@ -51,15 +53,22 @@ static void meson_a1_gpio_irq_sel_pin(struct meson_gpio_irq_controller *ctl,
unsigned int channel,
unsigned long hwirq);
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 {
void (*gpio_irq_sel_pin)(struct meson_gpio_irq_controller *ctl,
unsigned int channel, unsigned long hwirq);
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 {
unsigned int nr_hwirq;
unsigned int nr_channels;
bool support_edge_both;
unsigned int edge_both_offset;
unsigned int edge_single_offset;
@ -68,28 +77,44 @@ struct meson_gpio_irq_params {
struct irq_ctl_ops ops;
};
#define INIT_MESON_COMMON(irqs, init, sel) \
#define INIT_MESON_COMMON(irqs, init, sel, type) \
.nr_hwirq = irqs, \
.ops = { \
.gpio_irq_init = init, \
.gpio_irq_sel_pin = sel, \
.gpio_irq_set_type = type, \
},
#define INIT_MESON8_COMMON_DATA(irqs) \
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, \
.pol_low_offset = 16, \
.pin_sel_mask = 0xff, \
.nr_channels = 8, \
#define INIT_MESON_A1_COMMON_DATA(irqs) \
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, \
.edge_both_offset = 16, \
.edge_single_offset = 8, \
.pol_low_offset = 0, \
.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 = {
INIT_MESON8_COMMON_DATA(134)
@ -121,6 +146,10 @@ static const struct meson_gpio_irq_params a1_params = {
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[] = {
{ .compatible = "amlogic,meson8-gpio-intc", .data = &meson8_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-sm1-gpio-intc", .data = &sm1_params },
{ .compatible = "amlogic,meson-a1-gpio-intc", .data = &a1_params },
{ .compatible = "amlogic,meson-s4-gpio-intc", .data = &s4_params },
{ }
};
struct meson_gpio_irq_controller {
const struct meson_gpio_irq_params *params;
void __iomem *base;
u32 channel_irqs[NUM_CHANNEL];
DECLARE_BITMAP(channel_map, NUM_CHANNEL);
u32 channel_irqs[MAX_NUM_CHANNEL];
DECLARE_BITMAP(channel_map, MAX_NUM_CHANNEL);
spinlock_t lock;
};
@ -207,8 +237,8 @@ meson_gpio_irq_request_channel(struct meson_gpio_irq_controller *ctl,
spin_lock_irqsave(&ctl->lock, flags);
/* Find a free channel */
idx = find_first_zero_bit(ctl->channel_map, NUM_CHANNEL);
if (idx >= NUM_CHANNEL) {
idx = find_first_zero_bit(ctl->channel_map, ctl->params->nr_channels);
if (idx >= ctl->params->nr_channels) {
spin_unlock_irqrestore(&ctl->lock, flags);
pr_err("No channel available\n");
return -ENOSPC;
@ -256,9 +286,8 @@ meson_gpio_irq_release_channel(struct meson_gpio_irq_controller *ctl,
clear_bit(idx, ctl->channel_map);
}
static int meson_gpio_irq_type_setup(struct meson_gpio_irq_controller *ctl,
unsigned int type,
u32 *channel_hwirq)
static int meson8_gpio_irq_set_type(struct meson_gpio_irq_controller *ctl,
unsigned int type, u32 *channel_hwirq)
{
u32 val = 0;
unsigned int idx;
@ -299,6 +328,51 @@ static int meson_gpio_irq_type_setup(struct meson_gpio_irq_controller *ctl,
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)
{
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);
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)
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,
"amlogic,channel-interrupts",
ctl->channel_irqs,
NUM_CHANNEL,
NUM_CHANNEL);
ctl->params->nr_channels,
ctl->params->nr_channels);
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;
}
@ -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",
ctl->params->nr_hwirq, NUM_CHANNEL);
ctl->params->nr_hwirq, ctl->params->nr_channels);
return 0;

View file

@ -18,6 +18,7 @@
#include <linux/module.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/seq_file.h>
#define PIC_CAUSE 0x0
#define PIC_MASK 0x4
@ -29,7 +30,7 @@ struct mvebu_pic {
void __iomem *base;
u32 parent_irq;
struct irq_domain *domain;
struct irq_chip irq_chip;
struct platform_device *pdev;
};
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);
}
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,
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_chip_data(virq, pic);
irq_set_chip_and_handler(virq, &pic->irq_chip,
handle_percpu_devid_irq);
irq_set_chip_and_handler(virq, &mvebu_pic_chip, handle_percpu_devid_irq);
irq_set_status_flags(virq, IRQ_LEVEL);
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 mvebu_pic *pic;
struct irq_chip *irq_chip;
pic = devm_kzalloc(&pdev->dev, sizeof(struct mvebu_pic), GFP_KERNEL);
if (!pic)
return -ENOMEM;
pic->pdev = pdev;
pic->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_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);
if (pic->parent_irq <= 0) {
dev_err(&pdev->dev, "Failed to parse parent interrupt\n");

View file

@ -107,6 +107,7 @@ static int __init nvic_of_init(struct device_node *node,
if (!nvic_irq_domain) {
pr_warn("Failed to allocate irq domain\n");
iounmap(nvic_base);
return -ENOMEM;
}
@ -116,6 +117,7 @@ static int __init nvic_of_init(struct device_node *node,
if (ret) {
pr_warn("Failed to allocate irq chips\n");
irq_domain_remove(nvic_irq_domain);
iounmap(nvic_base);
return ret;
}

View 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");

View file

@ -508,7 +508,6 @@ static int intc_irqpin_probe(struct platform_device *pdev)
irq_chip = &p->irq_chip;
irq_chip->name = "intc-irqpin";
irq_chip->parent_device = dev;
irq_chip->irq_mask = disable_fn;
irq_chip->irq_unmask = enable_fn;
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;
}
irq_domain_set_pm_device(p->irq_domain, dev);
if (p->shared_irqs) {
/* request one shared interrupt */
if (devm_request_irq(dev, p->irq[0].requested_irq,

View file

@ -188,13 +188,14 @@ static int irqc_probe(struct platform_device *pdev)
p->gc->reg_base = p->cpu_int_base;
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].chip.parent_device = dev;
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_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.flags = IRQCHIP_MASK_ON_SUSPEND;
irq_domain_set_pm_device(p->irq_domain, dev);
/* request interrupts one by one */
for (k = 0; k < p->number_of_irqs; k++) {
if (devm_request_irq(dev, p->irq[k].requested_irq,

View file

@ -44,8 +44,8 @@
* Each hart context has a vector of interrupt enable bits associated with it.
* There's one bit for each interrupt source.
*/
#define ENABLE_BASE 0x2000
#define ENABLE_PER_HART 0x80
#define CONTEXT_ENABLE_BASE 0x2000
#define CONTEXT_ENABLE_SIZE 0x80
/*
* 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.
*/
#define CONTEXT_BASE 0x200000
#define CONTEXT_PER_HART 0x1000
#define CONTEXT_SIZE 0x1000
#define CONTEXT_THRESHOLD 0x00
#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 DEFINE_PER_CPU(struct plic_handler, plic_handlers);
static inline void plic_toggle(struct plic_handler *handler,
int hwirq, int enable)
static void __plic_toggle(void __iomem *enable_base, 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);
raw_spin_lock(&handler->enable_lock);
if (enable)
writel(readl(reg) | hwirq_mask, reg);
else
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);
}
@ -324,8 +328,18 @@ static int __init plic_init(struct device_node *node,
* Skip contexts other than external interrupts for our
* 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;
}
hartid = riscv_of_parent_hartid(parent.np);
if (hartid < 0) {
@ -361,11 +375,11 @@ static int __init plic_init(struct device_node *node,
cpumask_set_cpu(cpu, &priv->lmask);
handler->present = true;
handler->hart_base =
priv->regs + CONTEXT_BASE + i * CONTEXT_PER_HART;
handler->hart_base = priv->regs + CONTEXT_BASE +
i * CONTEXT_SIZE;
raw_spin_lock_init(&handler->enable_lock);
handler->enable_base =
priv->regs + ENABLE_BASE + i * ENABLE_PER_HART;
handler->enable_base = priv->regs + CONTEXT_ENABLE_BASE +
i * CONTEXT_ENABLE_SIZE;
handler->priv = priv;
done:
for (hwirq = 1; hwirq <= nr_irqs; hwirq++)

View file

@ -214,6 +214,48 @@ static const struct stm32_desc_irq stm32mp1_desc_irq[] = {
{ .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 = {
.exti_banks = 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),
};
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
stm32_desc_irq *stm32_exti_get_desc(const struct stm32_exti_drv_data *drv_data,
irq_hw_number_t hwirq)
@ -922,6 +971,7 @@ static int stm32_exti_probe(struct platform_device *pdev)
/* platform driver only for MP1 */
static const struct of_device_id stm32_exti_ids[] = {
{ .compatible = "st,stm32mp1-exti", .data = &stm32mp1_drv_data},
{ .compatible = "st,stm32mp13-exti", .data = &stm32mp13_drv_data},
{},
};
MODULE_DEVICE_TABLE(of, stm32_exti_ids);

View file

@ -19,14 +19,15 @@
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/seq_file.h>
#define IRQ_MASK 0x4
#define IRQ_STATUS 0x8
struct ts4800_irq_data {
void __iomem *base;
struct platform_device *pdev;
struct irq_domain *domain;
struct irq_chip irq_chip;
};
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);
}
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,
irq_hw_number_t hwirq)
{
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_noprobe(irq);
@ -92,13 +106,13 @@ static int ts4800_ic_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
struct ts4800_irq_data *data;
struct irq_chip *irq_chip;
int parent_irq;
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->pdev = pdev;
data->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(data->base))
return PTR_ERR(data->base);
@ -111,11 +125,6 @@ static int ts4800_ic_probe(struct platform_device *pdev)
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);
if (!data->domain) {
dev_err(&pdev->dev, "cannot add IRQ domain\n");

View file

@ -7,12 +7,12 @@
#include <linux/io.h>
#include <linux/irqchip.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/irqchip/versatile-fpga.h>
#include <linux/irqdomain.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/seq_file.h>
#include <asm/exception.h>
#include <asm/mach/irq.h>
@ -34,14 +34,12 @@
/**
* struct fpga_irq_data - irq data container for the FPGA IRQ controller
* @base: memory offset in virtual memory
* @chip: chip container for this instance
* @domain: IRQ domain for this instance
* @valid: mask for valid IRQs on this controller
* @used_irqs: number of active IRQs on this controller
*/
struct fpga_irq_data {
void __iomem *base;
struct irq_chip chip;
u32 valid;
struct irq_domain *domain;
u8 used_irqs;
@ -67,6 +65,20 @@ static void fpga_irq_unmask(struct irq_data *d)
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)
{
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
* 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;
@ -135,8 +147,7 @@ static int fpga_irqdomain_map(struct irq_domain *d, unsigned int irq,
if (!(f->valid & BIT(hwirq)))
return -EPERM;
irq_set_chip_data(irq, f);
irq_set_chip_and_handler(irq, &f->chip,
handle_level_irq);
irq_set_chip_and_handler(irq, &fpga_chip, handle_level_irq);
irq_set_probe(irq);
return 0;
}
@ -146,8 +157,8 @@ static const struct irq_domain_ops fpga_irqdomain_ops = {
.xlate = irq_domain_xlate_onetwocell,
};
void __init fpga_irq_init(void __iomem *base, const char *name, int irq_start,
int parent_irq, u32 valid, struct device_node *node)
static void __init fpga_irq_init(void __iomem *base, int parent_irq,
u32 valid, struct device_node *node)
{
struct fpga_irq_data *f;
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->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;
if (parent_irq != -1) {
@ -169,20 +176,19 @@ void __init fpga_irq_init(void __iomem *base, const char *name, int irq_start,
f);
}
/* This will also allocate irq descriptors */
f->domain = irq_domain_add_simple(node, fls(valid), irq_start,
f->domain = irq_domain_add_linear(node, fls(valid),
&fpga_irqdomain_ops, f);
/* This will allocate all valid descriptors in the linear case */
for (i = 0; i < fls(valid); i++)
if (valid & BIT(i)) {
if (!irq_start)
irq_create_mapping(f->domain, i);
/* Is this still required? */
irq_create_mapping(f->domain, i);
f->used_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)
pr_cont(", parent IRQ: %d\n", parent_irq);
else
@ -192,8 +198,8 @@ void __init fpga_irq_init(void __iomem *base, const char *name, int irq_start,
}
#ifdef CONFIG_OF
int __init fpga_irq_of_init(struct device_node *node,
struct device_node *parent)
static int __init fpga_irq_of_init(struct device_node *node,
struct device_node *parent)
{
void __iomem *base;
u32 clear_mask;
@ -222,7 +228,7 @@ int __init fpga_irq_of_init(struct device_node *node,
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

View file

@ -32,6 +32,8 @@
#define MER_ME (1<<0)
#define MER_HIE (1<<1)
#define SPURIOUS_IRQ (-1U)
static DEFINE_STATIC_KEY_FALSE(xintc_is_be);
struct xintc_irq_chip {
@ -110,20 +112,6 @@ static struct irq_chip intc_dev = {
.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)
{
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);
}
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,
struct device_node *parent)
{
@ -233,6 +234,7 @@ static int __init xilinx_intc_of_init(struct device_node *intc,
} else {
primary_intc = irqc;
irq_set_default_host(primary_intc->root_domain);
set_handle_irq(xil_intc_handle_irq);
}
return 0;

View file

@ -21,23 +21,19 @@
#include <linux/slab.h>
#include <linux/types.h>
#define PDC_MAX_IRQS 168
#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_i_CFG 0x110
#define PDC_NO_PARENT_IRQ ~0UL
struct pdc_pin_region {
u32 pin_base;
u32 parent_base;
u32 cnt;
};
#define pin_to_hwirq(r, p) ((r)->parent_base + (p) - (r)->pin_base)
static DEFINE_RAW_SPINLOCK(pdc_lock);
static void __iomem *pdc_base;
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)
{
int pin_out = d->hwirq;
unsigned long enable;
unsigned long flags;
u32 index, mask;
u32 enable;
index = 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 = on ? ENABLE_INTR(enable, mask) : CLEAR_INTR(enable, mask);
__assign_bit(mask, &enable, on);
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)
@ -186,34 +183,17 @@ static struct irq_chip qcom_pdc_gic_chip = {
.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;
struct pdc_pin_region *region;
for (i = 0; i < pdc_region_cnt; i++) {
region = &pdc_region[i];
if (pin >= region->pin_base &&
pin < region->pin_base + region->cnt)
return (region->parent_base + pin - region->pin_base);
if (pin >= pdc_region[i].pin_base &&
pin < pdc_region[i].pin_base + pdc_region[i].cnt)
return &pdc_region[i];
}
return PDC_NO_PARENT_IRQ;
}
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;
return NULL;
}
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 parent_fwspec;
irq_hw_number_t hwirq, parent_hwirq;
struct pdc_pin_region *region;
irq_hw_number_t hwirq;
unsigned int type;
int ret;
ret = qcom_pdc_translate(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);
ret = irq_domain_translate_twocell(domain, fwspec, &hwirq, &type);
if (ret)
return ret;
@ -281,8 +218,8 @@ static int qcom_pdc_gpio_alloc(struct irq_domain *domain, unsigned int virq,
if (ret)
return ret;
parent_hwirq = get_parent_hwirq(hwirq);
if (parent_hwirq == PDC_NO_PARENT_IRQ)
region = get_pin_region(hwirq);
if (!region)
return irq_domain_disconnect_hierarchy(domain->parent, virq);
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.param_count = 3;
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;
return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs,
&parent_fwspec);
}
static int qcom_pdc_gpio_domain_select(struct irq_domain *d,
struct irq_fwspec *fwspec,
enum irq_domain_bus_token bus_token)
{
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,
static const struct irq_domain_ops qcom_pdc_ops = {
.translate = irq_domain_translate_twocell,
.alloc = qcom_pdc_alloc,
.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)
{
struct irq_domain *parent_domain, *pdc_domain, *pdc_gpio_domain;
struct irq_domain *parent_domain, *pdc_domain;
int ret;
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;
}
pdc_domain = irq_domain_create_hierarchy(parent_domain, 0, PDC_MAX_IRQS,
of_fwnode_handle(node),
&qcom_pdc_ops, NULL);
pdc_domain = irq_domain_create_hierarchy(parent_domain,
IRQ_DOMAIN_FLAG_QCOM_PDC_WAKEUP,
PDC_MAX_GPIO_IRQS,
of_fwnode_handle(node),
&qcom_pdc_ops, NULL);
if (!pdc_domain) {
pr_err("%pOF: GIC domain add failed\n", node);
pr_err("%pOF: PDC domain add failed\n", node);
ret = -ENOMEM;
goto fail;
}
pdc_gpio_domain = irq_domain_create_hierarchy(parent_domain,
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);
irq_domain_update_bus_token(pdc_domain, DOMAIN_BUS_WAKEUP);
return 0;
remove:
irq_domain_remove(pdc_domain);
fail:
kfree(pdc_region);
iounmap(pdc_base);

View file

@ -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_ISR, isr);
local_irq_disable();
service = isr & ~msr;
for (irq = pcap->irq_base; service; service >>= 1, irq++) {
if (service & 1)
generic_handle_irq(irq);
generic_handle_irq_safe(irq);
}
local_irq_enable();
ezx_pcap_write(pcap, PCAP_REG_MSR, pcap->msr);
} while (gpio_get_value(pdata->gpio));
}

View file

@ -117,8 +117,8 @@ static irqreturn_t hi6421v600_irq_handler(int irq, void *__priv)
* If both powerkey down and up IRQs are received,
* handle them at the right order
*/
generic_handle_irq(priv->irqs[POWERKEY_DOWN]);
generic_handle_irq(priv->irqs[POWERKEY_UP]);
generic_handle_irq_safe(priv->irqs[POWERKEY_DOWN]);
generic_handle_irq_safe(priv->irqs[POWERKEY_UP]);
pending &= ~HISI_IRQ_POWERKEY_UP_DOWN;
}
@ -126,7 +126,7 @@ static irqreturn_t hi6421v600_irq_handler(int irq, void *__priv)
continue;
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]);
}
}

View file

@ -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);
lan78xx_defer_kevent(dev, EVENT_LINK_RESET);
if (dev->domain_data.phyirq > 0) {
local_irq_disable();
generic_handle_irq(dev->domain_data.phyirq);
local_irq_enable();
}
if (dev->domain_data.phyirq > 0)
generic_handle_irq_safe(dev->domain_data.phyirq);
} else {
netdev_warn(dev->net,
"unexpected interrupt: 0x%08x\n", intdata);

View file

@ -219,7 +219,7 @@ static int apple_msi_domain_alloc(struct irq_domain *domain, unsigned int virq,
if (hwirq < 0)
return -ENOSPC;
fwspec.param[1] += hwirq;
fwspec.param[fwspec.param_count - 2] += hwirq;
ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &fwspec);
if (ret)

View file

@ -78,7 +78,6 @@ struct npcm7xx_gpio {
struct gpio_chip gc;
int irqbase;
int irq;
void *priv;
struct irq_chip irq_chip;
u32 pinctrl_id;
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);
sts = ioread32(bank->base + NPCM7XX_GP_N_EVST);
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);
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));
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);
switch (type) {
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_POL, gpio);
break;
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_set(&bank->gc, bank->base + NPCM7XX_GP_N_POL, gpio);
break;
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);
break;
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);
break;
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);
break;
default:
dev_dbg(d->chip->parent_device, "invalid irq type\n");
dev_dbg(bank->gc.parent, "invalid irq type\n");
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));
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);
}
@ -301,7 +300,7 @@ static void npcmgpio_irq_mask(struct irq_data *d)
unsigned int gpio = d->hwirq;
/* 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);
}
@ -313,7 +312,7 @@ static void npcmgpio_irq_unmask(struct irq_data *d)
unsigned int gpio = d->hwirq;
/* 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);
}
@ -323,7 +322,7 @@ static unsigned int npcmgpio_irq_startup(struct irq_data *d)
unsigned int gpio = d->hwirq;
/* 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_irq_ack(d);
npcmgpio_irq_unmask(d);

View file

@ -1308,8 +1308,6 @@ static int starfive_probe(struct platform_device *pdev)
sfp->gc.base = -1;
sfp->gc.ngpio = NR_GPIOS;
starfive_irq_chip.parent_device = dev;
sfp->gc.irq.chip = &starfive_irq_chip;
sfp->gc.irq.parent_handler = starfive_gpio_irq_handler;
sfp->gc.irq.num_parents = 1;
@ -1330,6 +1328,8 @@ static int starfive_probe(struct platform_device *pdev)
if (ret)
return dev_err_probe(dev, ret, "could not register gpiochip\n");
irq_domain_set_pm_device(sfp->gc.irq.domain, dev);
out_pinctrl_enable:
return pinctrl_enable(sfp->pctl);
}

View file

@ -391,10 +391,7 @@ static int gb_gpio_request_handler(struct gb_operation *op)
return -EINVAL;
}
local_irq_disable();
ret = generic_handle_irq(irq);
local_irq_enable();
ret = generic_handle_irq_safe(irq);
if (ret)
dev_err(dev, "failed to invoke irq handler\n");

View file

@ -456,7 +456,6 @@ static inline irq_hw_number_t irqd_to_hwirq(struct irq_data *d)
/**
* struct irq_chip - hardware interrupt chip descriptor
*
* @parent_device: pointer to parent device for irqchip
* @name: name for /proc/interrupts
* @irq_startup: start up the interrupt (defaults to ->enable 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
*/
struct irq_chip {
struct device *parent_device;
const char *name;
unsigned int (*irq_startup)(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 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);
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_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: */
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_chip_data(unsigned int irq, void *data);
extern int irq_set_irq_type(unsigned int irq, unsigned int type);

View file

@ -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

View file

@ -160,6 +160,7 @@ static inline void generic_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_safe(unsigned int irq);
#ifdef CONFIG_IRQ_DOMAIN
/*

View file

@ -151,6 +151,8 @@ struct irq_domain_chip_generic;
* @gc: Pointer to a list of generic chips. There is a helper function for
* setting up one or more generic chips for interrupt controllers
* 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
*
* Revmap data, used internally by irq_domain
@ -171,6 +173,7 @@ struct irq_domain {
struct fwnode_handle *fwnode;
enum irq_domain_bus_token bus_token;
struct irq_domain_chip_generic *gc;
struct device *dev;
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
struct irq_domain *parent;
#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);
}
static inline void irq_domain_set_pm_device(struct irq_domain *d,
struct device *dev)
{
if (d)
d->dev = dev;
}
#ifdef CONFIG_IRQ_DOMAIN
struct fwnode_handle *__irq_domain_alloc_fwnode(unsigned int type, int id,
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,
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 *handler_data, const char *handler_name);
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,
unsigned int virq,
irq_hw_number_t hwirq,
struct irq_chip *chip,
const struct irq_chip *chip,
void *chip_data);
extern void irq_domain_free_irqs_common(struct irq_domain *domain,
unsigned int virq,

View file

@ -38,7 +38,7 @@ struct irqaction chained_action = {
* @irq: irq number
* @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;
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)
return -EINVAL;
if (!chip)
chip = &no_irq_chip;
desc->irq_data.chip = chip;
desc->irq_data.chip = (struct irq_chip *)(chip ?: &no_irq_chip);
irq_put_desc_unlock(desc, flags);
/*
* 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);
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_set_chip(irq, chip);
@ -1558,6 +1555,14 @@ int irq_chip_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
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
* @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)
{
struct device *dev = irq_get_parent_device(data);
int retval;
if (IS_ENABLED(CONFIG_PM) && data->chip->parent_device) {
retval = pm_runtime_get_sync(data->chip->parent_device);
if (IS_ENABLED(CONFIG_PM) && dev) {
retval = pm_runtime_get_sync(dev);
if (retval < 0) {
pm_runtime_put_noidle(data->chip->parent_device);
pm_runtime_put_noidle(dev);
return retval;
}
}
@ -1590,10 +1596,11 @@ int irq_chip_pm_get(struct irq_data *data)
*/
int irq_chip_pm_put(struct irq_data *data)
{
struct device *dev = irq_get_parent_device(data);
int retval = 0;
if (IS_ENABLED(CONFIG_PM) && data->chip->parent_device)
retval = pm_runtime_put(data->chip->parent_device);
if (IS_ENABLED(CONFIG_PM) && dev)
retval = pm_runtime_put(dev);
return (retval < 0) ? retval : 0;
}

View file

@ -69,8 +69,12 @@ irq_debug_show_chip(struct seq_file *m, struct irq_data *data, int ind)
seq_printf(m, "chip: None\n");
return;
}
seq_printf(m, "%*schip: %s\n", ind, "", chip->name);
seq_printf(m, "%*sflags: 0x%lx\n", ind + 1, "", chip->flags);
seq_printf(m, "%*schip: ", ind, "");
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,
ARRAY_SIZE(irqchip_flags));
}

View file

@ -640,7 +640,7 @@ int handle_irq_desc(struct irq_desc *desc)
return -EINVAL;
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;
generic_handle_irq_desc(desc);
@ -662,6 +662,29 @@ int generic_handle_irq(unsigned int 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
/**
* 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)
{
WARN_ON_ONCE(!in_irq());
WARN_ON_ONCE(!in_hardirq());
return handle_irq_desc(irq_resolve_mapping(domain, hwirq));
}
EXPORT_SYMBOL_GPL(generic_handle_domain_irq);

View file

@ -1319,7 +1319,8 @@ EXPORT_SYMBOL_GPL(irq_domain_get_irq_data);
* @chip_data: The associated chip data
*/
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)
{
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;
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;
return 0;
@ -1347,7 +1348,7 @@ EXPORT_SYMBOL_GPL(irq_domain_set_hwirq_and_chip);
* @handler_name: The interrupt handler name
*/
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 *handler_data, const char *handler_name)
{
@ -1853,7 +1854,7 @@ EXPORT_SYMBOL_GPL(irq_domain_get_irq_data);
* @handler_name: The interrupt handler name
*/
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 *handler_data, const char *handler_name)
{

View file

@ -222,7 +222,7 @@ void __local_bh_enable_ip(unsigned long ip, unsigned int cnt)
u32 pending;
int curcnt;
WARN_ON_ONCE(in_irq());
WARN_ON_ONCE(in_hardirq());
lockdep_assert_irqs_enabled();
local_irq_save(flags);
@ -305,7 +305,7 @@ void __local_bh_disable_ip(unsigned long ip, unsigned int cnt)
{
unsigned long flags;
WARN_ON_ONCE(in_irq());
WARN_ON_ONCE(in_hardirq());
raw_local_irq_save(flags);
/*
@ -352,14 +352,14 @@ static void __local_bh_enable(unsigned int cnt)
*/
void _local_bh_enable(void)
{
WARN_ON_ONCE(in_irq());
WARN_ON_ONCE(in_hardirq());
__local_bh_enable(SOFTIRQ_DISABLE_OFFSET);
}
EXPORT_SYMBOL(_local_bh_enable);
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();
#ifdef CONFIG_TRACE_IRQFLAGS
local_irq_disable();
@ -618,7 +618,7 @@ static inline void tick_irq_exit(void)
/* Make sure that timer wheel updates are propagated */
if ((idle_cpu(cpu) && !need_resched()) || tick_nohz_full_cpu(cpu)) {
if (!in_irq())
if (!in_hardirq())
tick_nohz_irq_exit();
}
#endif