Updates for interrupt core and drivers:
core: - Fix a few inconsistencies between UP and SMP vs. interrupt affinities - Small updates and cleanups all over the place drivers: - New driver for the LoongArch interrupt controller - New driver for the Renesas RZ/G2L interrupt controller - Hotpath optimization for SiFive PLIC - Workaround for broken PLIC edge triggered interrupts - Simall cleanups and improvements as usual -----BEGIN PGP SIGNATURE----- iQJHBAABCgAxFiEEQp8+kY+LLUocC4bMphj1TA10mKEFAmLn5agTHHRnbHhAbGlu dXRyb25peC5kZQAKCRCmGPVMDXSYoV2HD/4u0+09Fd8Awt1Knnb4CInmwFihZ/bu EiS1Air+MEJ/fyFb5sT/Dn8YdUWYA6a3ifpLMGBwrKCcb5pMwPEtI8uqjSmtgsN/ 2Z4o3N5v6EgM25CtrHNBrXK0E9Rz5Py49gm5p3K7+h4g63z9JwrM4G0Bvr8+krLS EV9IZU6kVmGC6gnG/MspkArsLk1rCM0PU0SJ2lEPsWd1fjhVSDfunvy/qnnzXRzz wjrcAf+a2Kgb1TMnpL6tx9d2Xx8KrKfODZTdOmPHrdv58F0EbJzapJnAVkYZDPtR LE2kQc2Qhdawx0kgNNNhvu9P6oZtpnK9N7KAhDQdw17sgrRygINjAMSEe2RykYL1 lK+lJOIzfyd2JkEuC/8w1ZezL88S0EaTNawrkxjJ8L3fa7WDbwilCC+1w95QydCv sQB137OaLKgWetcRsht9PLWFb4ujkWzxoPf2cPPsm81EzCicNtBuNPLReBTcNrWJ X2VPpbaqRK8t8bnkXRqhahbq7f8c86feoICHfA4c7T4eZUp/Oq6T8aNvf6WPgjae I2/FO6kxZj3CQqFkhJGhiZRtGZdx6HLCsL84A+2Ktsra+D8+/qecZCnkHYtz0Vo6 aFuGg+Wj+zuc2QfdaWwG8Dh5dijbxgHGHhzbh9znsWzytN9gfoBxuvBejf65i6sC In63mEkv35ttfA== =OnhF -----END PGP SIGNATURE----- Merge tag 'irq-core-2022-08-01' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip Pull irq updates from Thomas Gleixner: "Updates for interrupt core and drivers: Core: - Fix a few inconsistencies between UP and SMP vs interrupt affinities - Small updates and cleanups all over the place New drivers: - LoongArch interrupt controller - Renesas RZ/G2L interrupt controller Updates: - Hotpath optimization for SiFive PLIC - Workaround for broken PLIC edge triggered interrupts - Simall cleanups and improvements as usual" * tag 'irq-core-2022-08-01' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (52 commits) irqchip/mmp: Declare init functions in common header file irqchip/mips-gic: Check the return value of ioremap() in gic_of_init() genirq: Use for_each_action_of_desc in actions_show() irqchip / ACPI: Introduce ACPI_IRQ_MODEL_LPIC for LoongArch irqchip: Add LoongArch CPU interrupt controller support irqchip: Add Loongson Extended I/O interrupt controller support irqchip/loongson-liointc: Add ACPI init support irqchip/loongson-pch-msi: Add ACPI init support irqchip/loongson-pch-pic: Add ACPI init support irqchip: Add Loongson PCH LPC controller support LoongArch: Prepare to support multiple pch-pic and pch-msi irqdomain LoongArch: Use ACPI_GENERIC_GSI for gsi handling genirq/generic_chip: Export irq_unmap_generic_chip ACPI: irq: Allow acpi_gsi_to_irq() to have an arch-specific fallback APCI: irq: Add support for multiple GSI domains LoongArch: Provisionally add ACPICA data structures irqdomain: Use hwirq_max instead of revmap_size for NOMAP domains irqdomain: Report irq number for NOMAP domains irqchip/gic-v3: Fix comment typo dt-bindings: interrupt-controller: renesas,rzg2l-irqc: Document RZ/V2L SoC ...
This commit is contained in:
commit
9de1f9c8ca
@ -0,0 +1,134 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/interrupt-controller/renesas,rzg2l-irqc.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Renesas RZ/G2L (and alike SoC's) Interrupt Controller (IA55)
|
||||
|
||||
maintainers:
|
||||
- Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
|
||||
- Geert Uytterhoeven <geert+renesas@glider.be>
|
||||
|
||||
description: |
|
||||
IA55 performs various interrupt controls including synchronization for the external
|
||||
interrupts of NMI, IRQ, and GPIOINT and the interrupts of the built-in peripheral
|
||||
interrupts output by each IP. And it notifies the interrupt to the GIC
|
||||
- IRQ sense select for 8 external interrupts, mapped to 8 GIC SPI interrupts
|
||||
- GPIO pins used as external interrupt input pins, mapped to 32 GIC SPI interrupts
|
||||
- NMI edge select (NMI is not treated as NMI exception and supports fall edge and
|
||||
stand-up edge detection interrupts)
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/interrupt-controller.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- renesas,r9a07g044-irqc # RZ/G2{L,LC}
|
||||
- renesas,r9a07g054-irqc # RZ/V2L
|
||||
- const: renesas,rzg2l-irqc
|
||||
|
||||
'#interrupt-cells':
|
||||
description: The first cell should contain external interrupt number (IRQ0-7) and the
|
||||
second cell is used to specify the flag.
|
||||
const: 2
|
||||
|
||||
'#address-cells':
|
||||
const: 0
|
||||
|
||||
interrupt-controller: true
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 41
|
||||
|
||||
clocks:
|
||||
maxItems: 2
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: clk
|
||||
- const: pclk
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- '#interrupt-cells'
|
||||
- '#address-cells'
|
||||
- interrupt-controller
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- clock-names
|
||||
- power-domains
|
||||
- resets
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/clock/r9a07g044-cpg.h>
|
||||
|
||||
irqc: interrupt-controller@110a0000 {
|
||||
compatible = "renesas,r9a07g044-irqc", "renesas,rzg2l-irqc";
|
||||
reg = <0x110a0000 0x10000>;
|
||||
#interrupt-cells = <2>;
|
||||
#address-cells = <0>;
|
||||
interrupt-controller;
|
||||
interrupts = <GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 444 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 445 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 446 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 447 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 448 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 449 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 450 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 451 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 452 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 453 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 454 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 455 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 456 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 457 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 458 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 459 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 460 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 461 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 462 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 463 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 464 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 465 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 466 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 467 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 468 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 469 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 470 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 471 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 472 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 473 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 474 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 475 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&cpg CPG_MOD R9A07G044_IA55_CLK>,
|
||||
<&cpg CPG_MOD R9A07G044_IA55_PCLK>;
|
||||
clock-names = "clk", "pclk";
|
||||
power-domains = <&cpg>;
|
||||
resets = <&cpg R9A07G044_IA55_RESETN>;
|
||||
};
|
@ -26,9 +26,14 @@ description:
|
||||
with priority below this threshold will not cause the PLIC to raise its
|
||||
interrupt line leading to the context.
|
||||
|
||||
While the PLIC supports both edge-triggered and level-triggered interrupts,
|
||||
interrupt handlers are oblivious to this distinction and therefore it is not
|
||||
specified in the PLIC device-tree binding.
|
||||
The PLIC supports both edge-triggered and level-triggered interrupts. For
|
||||
edge-triggered interrupts, the RISC-V PLIC spec allows two responses to edges
|
||||
seen while an interrupt handler is active; the PLIC may either queue them or
|
||||
ignore them. In the first case, handlers are oblivious to the trigger type, so
|
||||
it is not included in the interrupt specifier. In the second case, software
|
||||
needs to know the trigger type, so it can reorder the interrupt flow to avoid
|
||||
missing interrupts. This special handling is needed by at least the Renesas
|
||||
RZ/Five SoC (AX45MP AndesCore with a NCEPLIC100) and the T-HEAD C900 PLIC.
|
||||
|
||||
While the RISC-V ISA doesn't specify a memory layout for the PLIC, the
|
||||
"sifive,plic-1.0.0" device is a concrete implementation of the PLIC that
|
||||
@ -47,6 +52,10 @@ maintainers:
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- items:
|
||||
- enum:
|
||||
- renesas,r9a07g043-plic
|
||||
- const: andestech,nceplic100
|
||||
- items:
|
||||
- enum:
|
||||
- sifive,fu540-c000-plic
|
||||
@ -64,8 +73,7 @@ properties:
|
||||
'#address-cells':
|
||||
const: 0
|
||||
|
||||
'#interrupt-cells':
|
||||
const: 1
|
||||
'#interrupt-cells': true
|
||||
|
||||
interrupt-controller: true
|
||||
|
||||
@ -82,6 +90,12 @@ properties:
|
||||
description:
|
||||
Specifies how many external interrupts are supported by this controller.
|
||||
|
||||
clocks: true
|
||||
|
||||
power-domains: true
|
||||
|
||||
resets: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- '#address-cells'
|
||||
@ -91,6 +105,47 @@ required:
|
||||
- interrupts-extended
|
||||
- riscv,ndev
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- andestech,nceplic100
|
||||
- thead,c900-plic
|
||||
|
||||
then:
|
||||
properties:
|
||||
'#interrupt-cells':
|
||||
const: 2
|
||||
|
||||
else:
|
||||
properties:
|
||||
'#interrupt-cells':
|
||||
const: 1
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: renesas,r9a07g043-plic
|
||||
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- clocks
|
||||
- power-domains
|
||||
- resets
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
|
@ -47,6 +47,17 @@ properties:
|
||||
gpio-ranges:
|
||||
maxItems: 1
|
||||
|
||||
interrupt-controller: true
|
||||
|
||||
'#interrupt-cells':
|
||||
const: 2
|
||||
description:
|
||||
The first cell contains the global GPIO port index, constructed using the
|
||||
RZG2L_GPIO() helper macro in <dt-bindings/pinctrl/rzg2l-pinctrl.h> and the
|
||||
second cell is used to specify the flag.
|
||||
E.g. "interrupts = <RZG2L_GPIO(43, 0) IRQ_TYPE_EDGE_FALLING>;" if P43_0 is
|
||||
being used as an interrupt.
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
@ -110,6 +121,8 @@ required:
|
||||
- gpio-controller
|
||||
- '#gpio-cells'
|
||||
- gpio-ranges
|
||||
- interrupt-controller
|
||||
- '#interrupt-cells'
|
||||
- clocks
|
||||
- power-domains
|
||||
- resets
|
||||
@ -126,6 +139,8 @@ examples:
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
gpio-ranges = <&pinctrl 0 0 392>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
clocks = <&cpg CPG_MOD R9A07G044_GPIO_HCLK>;
|
||||
resets = <&cpg R9A07G044_GPIO_RSTN>,
|
||||
<&cpg R9A07G044_GPIO_PORT_RESETN>,
|
||||
|
@ -60,7 +60,7 @@ int irq_select_affinity(unsigned int irq)
|
||||
cpu = (cpu < (NR_CPUS-1) ? cpu + 1 : 0);
|
||||
last_cpu = cpu;
|
||||
|
||||
cpumask_copy(irq_data_get_affinity_mask(data), cpumask_of(cpu));
|
||||
irq_data_update_affinity(data, cpumask_of(cpu));
|
||||
chip->irq_set_affinity(data, cpumask_of(cpu), false);
|
||||
return 0;
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ config ARCH_HIP04
|
||||
select HAVE_ARM_ARCH_TIMER
|
||||
select MCPM if SMP
|
||||
select MCPM_QUAD_CLUSTER if SMP
|
||||
select GENERIC_IRQ_EFFECTIVE_AFF_MASK
|
||||
select GENERIC_IRQ_EFFECTIVE_AFF_MASK if SMP
|
||||
help
|
||||
Support for Hisilicon HiP04 SoC family
|
||||
|
||||
|
@ -5,13 +5,13 @@
|
||||
#include <linux/platform_data/pxa_sdhci.h>
|
||||
|
||||
extern void mmp2_timer_init(void);
|
||||
extern void __init mmp2_init_icu(void);
|
||||
extern void __init mmp2_init_irq(void);
|
||||
extern void mmp2_clear_pmic_int(void);
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/platform_data/i2c-pxa.h>
|
||||
#include <linux/platform_data/dma-mmp_tdma.h>
|
||||
#include <linux/irqchip/mmp.h>
|
||||
|
||||
#include "devices.h"
|
||||
|
||||
|
@ -5,7 +5,6 @@
|
||||
#include <linux/reboot.h>
|
||||
|
||||
extern void pxa168_timer_init(void);
|
||||
extern void __init icu_init_irq(void);
|
||||
extern void __init pxa168_init_irq(void);
|
||||
extern void pxa168_restart(enum reboot_mode, const char *);
|
||||
extern void pxa168_clear_keypad_wakeup(void);
|
||||
@ -18,6 +17,7 @@ extern void pxa168_clear_keypad_wakeup(void);
|
||||
#include <linux/pxa168_eth.h>
|
||||
#include <linux/platform_data/mv_usb.h>
|
||||
#include <linux/soc/mmp/cputype.h>
|
||||
#include <linux/irqchip/mmp.h>
|
||||
|
||||
#include "devices.h"
|
||||
|
||||
|
@ -3,13 +3,13 @@
|
||||
#define __ASM_MACH_PXA910_H
|
||||
|
||||
extern void pxa910_timer_init(void);
|
||||
extern void __init icu_init_irq(void);
|
||||
extern void __init pxa910_init_irq(void);
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/platform_data/i2c-pxa.h>
|
||||
#include <linux/platform_data/mtd-nand-pxa3xx.h>
|
||||
#include <video/mmp_disp.h>
|
||||
#include <linux/irqchip/mmp.h>
|
||||
|
||||
#include "devices.h"
|
||||
|
||||
|
@ -834,7 +834,7 @@ iosapic_unregister_intr (unsigned int gsi)
|
||||
if (iosapic_intr_info[irq].count == 0) {
|
||||
#ifdef CONFIG_SMP
|
||||
/* Clear affinity */
|
||||
cpumask_setall(irq_get_affinity_mask(irq));
|
||||
irq_data_update_affinity(irq_get_irq_data(irq), cpu_all_mask);
|
||||
#endif
|
||||
/* Clear the interrupt information */
|
||||
iosapic_intr_info[irq].dest = 0;
|
||||
|
@ -57,8 +57,8 @@ static char irq_redir [NR_IRQS]; // = { [0 ... NR_IRQS-1] = 1 };
|
||||
void set_irq_affinity_info (unsigned int irq, int hwid, int redir)
|
||||
{
|
||||
if (irq < NR_IRQS) {
|
||||
cpumask_copy(irq_get_affinity_mask(irq),
|
||||
cpumask_of(cpu_logical_id(hwid)));
|
||||
irq_data_update_affinity(irq_get_irq_data(irq),
|
||||
cpumask_of(cpu_logical_id(hwid)));
|
||||
irq_redir[irq] = (char) (redir & 0xff);
|
||||
}
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ static int ia64_set_msi_irq_affinity(struct irq_data *idata,
|
||||
msg.data = data;
|
||||
|
||||
pci_write_msi_msg(irq, &msg);
|
||||
cpumask_copy(irq_data_get_affinity_mask(idata), cpumask_of(cpu));
|
||||
irq_data_update_affinity(idata, cpumask_of(cpu));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -132,7 +132,7 @@ static int dmar_msi_set_affinity(struct irq_data *data,
|
||||
msg.address_lo |= MSI_ADDR_DEST_ID_CPU(cpu_physical_id(cpu));
|
||||
|
||||
dmar_msi_write(irq, &msg);
|
||||
cpumask_copy(irq_data_get_affinity_mask(data), mask);
|
||||
irq_data_update_affinity(data, mask);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
config LOONGARCH
|
||||
bool
|
||||
default y
|
||||
select ACPI_GENERIC_GSI if ACPI
|
||||
select ACPI_SYSTEM_POWER_STATES_SUPPORT if ACPI
|
||||
select ARCH_BINFMT_ELF_STATE
|
||||
select ARCH_ENABLE_MEMORY_HOTPLUG
|
||||
|
@ -31,6 +31,148 @@ static inline bool acpi_has_cpu_in_madt(void)
|
||||
|
||||
extern struct list_head acpi_wakeup_device_list;
|
||||
|
||||
/*
|
||||
* Temporary definitions until the core ACPICA code gets updated (see
|
||||
* 1656837932-18257-1-git-send-email-lvjianmin@loongson.cn and its
|
||||
* follow-ups for the "rationale").
|
||||
*
|
||||
* Once the "legal reasons" are cleared and that the code is merged,
|
||||
* this can be dropped entierely.
|
||||
*/
|
||||
#if (ACPI_CA_VERSION == 0x20220331 && !defined(LOONGARCH_ACPICA_EXT))
|
||||
|
||||
#define LOONGARCH_ACPICA_EXT 1
|
||||
|
||||
#define ACPI_MADT_TYPE_CORE_PIC 17
|
||||
#define ACPI_MADT_TYPE_LIO_PIC 18
|
||||
#define ACPI_MADT_TYPE_HT_PIC 19
|
||||
#define ACPI_MADT_TYPE_EIO_PIC 20
|
||||
#define ACPI_MADT_TYPE_MSI_PIC 21
|
||||
#define ACPI_MADT_TYPE_BIO_PIC 22
|
||||
#define ACPI_MADT_TYPE_LPC_PIC 23
|
||||
|
||||
/* Values for Version field above */
|
||||
|
||||
enum acpi_madt_core_pic_version {
|
||||
ACPI_MADT_CORE_PIC_VERSION_NONE = 0,
|
||||
ACPI_MADT_CORE_PIC_VERSION_V1 = 1,
|
||||
ACPI_MADT_CORE_PIC_VERSION_RESERVED = 2 /* 2 and greater are reserved */
|
||||
};
|
||||
|
||||
enum acpi_madt_lio_pic_version {
|
||||
ACPI_MADT_LIO_PIC_VERSION_NONE = 0,
|
||||
ACPI_MADT_LIO_PIC_VERSION_V1 = 1,
|
||||
ACPI_MADT_LIO_PIC_VERSION_RESERVED = 2 /* 2 and greater are reserved */
|
||||
};
|
||||
|
||||
enum acpi_madt_eio_pic_version {
|
||||
ACPI_MADT_EIO_PIC_VERSION_NONE = 0,
|
||||
ACPI_MADT_EIO_PIC_VERSION_V1 = 1,
|
||||
ACPI_MADT_EIO_PIC_VERSION_RESERVED = 2 /* 2 and greater are reserved */
|
||||
};
|
||||
|
||||
enum acpi_madt_ht_pic_version {
|
||||
ACPI_MADT_HT_PIC_VERSION_NONE = 0,
|
||||
ACPI_MADT_HT_PIC_VERSION_V1 = 1,
|
||||
ACPI_MADT_HT_PIC_VERSION_RESERVED = 2 /* 2 and greater are reserved */
|
||||
};
|
||||
|
||||
enum acpi_madt_bio_pic_version {
|
||||
ACPI_MADT_BIO_PIC_VERSION_NONE = 0,
|
||||
ACPI_MADT_BIO_PIC_VERSION_V1 = 1,
|
||||
ACPI_MADT_BIO_PIC_VERSION_RESERVED = 2 /* 2 and greater are reserved */
|
||||
};
|
||||
|
||||
enum acpi_madt_msi_pic_version {
|
||||
ACPI_MADT_MSI_PIC_VERSION_NONE = 0,
|
||||
ACPI_MADT_MSI_PIC_VERSION_V1 = 1,
|
||||
ACPI_MADT_MSI_PIC_VERSION_RESERVED = 2 /* 2 and greater are reserved */
|
||||
};
|
||||
|
||||
enum acpi_madt_lpc_pic_version {
|
||||
ACPI_MADT_LPC_PIC_VERSION_NONE = 0,
|
||||
ACPI_MADT_LPC_PIC_VERSION_V1 = 1,
|
||||
ACPI_MADT_LPC_PIC_VERSION_RESERVED = 2 /* 2 and greater are reserved */
|
||||
};
|
||||
|
||||
#pragma pack(1)
|
||||
|
||||
/* Core Interrupt Controller */
|
||||
|
||||
struct acpi_madt_core_pic {
|
||||
struct acpi_subtable_header header;
|
||||
u8 version;
|
||||
u32 processor_id;
|
||||
u32 core_id;
|
||||
u32 flags;
|
||||
};
|
||||
|
||||
/* Legacy I/O Interrupt Controller */
|
||||
|
||||
struct acpi_madt_lio_pic {
|
||||
struct acpi_subtable_header header;
|
||||
u8 version;
|
||||
u64 address;
|
||||
u16 size;
|
||||
u8 cascade[2];
|
||||
u32 cascade_map[2];
|
||||
};
|
||||
|
||||
/* Extend I/O Interrupt Controller */
|
||||
|
||||
struct acpi_madt_eio_pic {
|
||||
struct acpi_subtable_header header;
|
||||
u8 version;
|
||||
u8 cascade;
|
||||
u8 node;
|
||||
u64 node_map;
|
||||
};
|
||||
|
||||
/* HT Interrupt Controller */
|
||||
|
||||
struct acpi_madt_ht_pic {
|
||||
struct acpi_subtable_header header;
|
||||
u8 version;
|
||||
u64 address;
|
||||
u16 size;
|
||||
u8 cascade[8];
|
||||
};
|
||||
|
||||
/* Bridge I/O Interrupt Controller */
|
||||
|
||||
struct acpi_madt_bio_pic {
|
||||
struct acpi_subtable_header header;
|
||||
u8 version;
|
||||
u64 address;
|
||||
u16 size;
|
||||
u16 id;
|
||||
u16 gsi_base;
|
||||
};
|
||||
|
||||
/* MSI Interrupt Controller */
|
||||
|
||||
struct acpi_madt_msi_pic {
|
||||
struct acpi_subtable_header header;
|
||||
u8 version;
|
||||
u64 msg_address;
|
||||
u32 start;
|
||||
u32 count;
|
||||
};
|
||||
|
||||
/* LPC Interrupt Controller */
|
||||
|
||||
struct acpi_madt_lpc_pic {
|
||||
struct acpi_subtable_header header;
|
||||
u8 version;
|
||||
u64 address;
|
||||
u16 size;
|
||||
u8 cascade;
|
||||
};
|
||||
|
||||
#pragma pack()
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* !CONFIG_ACPI */
|
||||
|
||||
#define ACPI_TABLE_UPGRADE_MAX_PHYS ARCH_LOW_ADDRESS_LIMIT
|
||||
|
@ -35,9 +35,6 @@ static inline bool on_irq_stack(int cpu, unsigned long sp)
|
||||
return (low <= sp && sp <= high);
|
||||
}
|
||||
|
||||
int get_ipi_irq(void);
|
||||
int get_pmc_irq(void);
|
||||
int get_timer_irq(void);
|
||||
void spurious_interrupt(void);
|
||||
|
||||
#define NR_IRQS_LEGACY 16
|
||||
@ -48,6 +45,14 @@ void arch_trigger_cpumask_backtrace(const struct cpumask *mask, bool exclude_sel
|
||||
#define MAX_IO_PICS 2
|
||||
#define NR_IRQS (64 + (256 * MAX_IO_PICS))
|
||||
|
||||
struct acpi_vector_group {
|
||||
int node;
|
||||
int pci_segment;
|
||||
struct irq_domain *parent;
|
||||
};
|
||||
extern struct acpi_vector_group pch_group[MAX_IO_PICS];
|
||||
extern struct acpi_vector_group msi_group[MAX_IO_PICS];
|
||||
|
||||
#define CORES_PER_EIO_NODE 4
|
||||
|
||||
#define LOONGSON_CPU_UART0_VEC 10 /* CPU UART0 */
|
||||
@ -79,15 +84,6 @@ void arch_trigger_cpumask_backtrace(const struct cpumask *mask, bool exclude_sel
|
||||
extern int find_pch_pic(u32 gsi);
|
||||
extern int eiointc_get_node(int id);
|
||||
|
||||
static inline void eiointc_enable(void)
|
||||
{
|
||||
uint64_t misc;
|
||||
|
||||
misc = iocsr_read64(LOONGARCH_IOCSR_MISC_FUNC);
|
||||
misc |= IOCSR_MISC_FUNC_EXT_IOI_EN;
|
||||
iocsr_write64(misc, LOONGARCH_IOCSR_MISC_FUNC);
|
||||
}
|
||||
|
||||
struct acpi_madt_lio_pic;
|
||||
struct acpi_madt_eio_pic;
|
||||
struct acpi_madt_ht_pic;
|
||||
@ -95,21 +91,29 @@ struct acpi_madt_bio_pic;
|
||||
struct acpi_madt_msi_pic;
|
||||
struct acpi_madt_lpc_pic;
|
||||
|
||||
struct irq_domain *loongarch_cpu_irq_init(void);
|
||||
|
||||
struct irq_domain *liointc_acpi_init(struct irq_domain *parent,
|
||||
int liointc_acpi_init(struct irq_domain *parent,
|
||||
struct acpi_madt_lio_pic *acpi_liointc);
|
||||
struct irq_domain *eiointc_acpi_init(struct irq_domain *parent,
|
||||
int eiointc_acpi_init(struct irq_domain *parent,
|
||||
struct acpi_madt_eio_pic *acpi_eiointc);
|
||||
|
||||
struct irq_domain *htvec_acpi_init(struct irq_domain *parent,
|
||||
struct acpi_madt_ht_pic *acpi_htvec);
|
||||
struct irq_domain *pch_lpc_acpi_init(struct irq_domain *parent,
|
||||
int pch_lpc_acpi_init(struct irq_domain *parent,
|
||||
struct acpi_madt_lpc_pic *acpi_pchlpc);
|
||||
struct irq_domain *pch_msi_acpi_init(struct irq_domain *parent,
|
||||
#if IS_ENABLED(CONFIG_LOONGSON_PCH_MSI)
|
||||
int pch_msi_acpi_init(struct irq_domain *parent,
|
||||
struct acpi_madt_msi_pic *acpi_pchmsi);
|
||||
struct irq_domain *pch_pic_acpi_init(struct irq_domain *parent,
|
||||
#else
|
||||
static inline int pch_msi_acpi_init(struct irq_domain *parent,
|
||||
struct acpi_madt_msi_pic *acpi_pchmsi)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
int pch_pic_acpi_init(struct irq_domain *parent,
|
||||
struct acpi_madt_bio_pic *acpi_pchpic);
|
||||
int find_pch_pic(u32 gsi);
|
||||
struct fwnode_handle *get_pch_msi_handle(int pci_segment);
|
||||
|
||||
extern struct acpi_madt_lio_pic *acpi_liointc;
|
||||
extern struct acpi_madt_eio_pic *acpi_eiointc[MAX_IO_PICS];
|
||||
@ -119,11 +123,10 @@ extern struct acpi_madt_lpc_pic *acpi_pchlpc;
|
||||
extern struct acpi_madt_msi_pic *acpi_pchmsi[MAX_IO_PICS];
|
||||
extern struct acpi_madt_bio_pic *acpi_pchpic[MAX_IO_PICS];
|
||||
|
||||
extern struct irq_domain *cpu_domain;
|
||||
extern struct irq_domain *liointc_domain;
|
||||
extern struct irq_domain *pch_lpc_domain;
|
||||
extern struct irq_domain *pch_msi_domain[MAX_IO_PICS];
|
||||
extern struct irq_domain *pch_pic_domain[MAX_IO_PICS];
|
||||
extern struct fwnode_handle *cpuintc_handle;
|
||||
extern struct fwnode_handle *liointc_handle;
|
||||
extern struct fwnode_handle *pch_lpc_handle;
|
||||
extern struct fwnode_handle *pch_pic_handle[MAX_IO_PICS];
|
||||
|
||||
extern irqreturn_t loongson3_ipi_interrupt(int irq, void *dev);
|
||||
|
||||
|
@ -25,7 +25,6 @@ EXPORT_SYMBOL(acpi_pci_disabled);
|
||||
int acpi_strict = 1; /* We have no workarounds on LoongArch */
|
||||
int num_processors;
|
||||
int disabled_cpus;
|
||||
enum acpi_irq_model_id acpi_irq_model = ACPI_IRQ_MODEL_PLATFORM;
|
||||
|
||||
u64 acpi_saved_sp;
|
||||
|
||||
@ -33,70 +32,6 @@ u64 acpi_saved_sp;
|
||||
|
||||
#define PREFIX "ACPI: "
|
||||
|
||||
int acpi_gsi_to_irq(u32 gsi, unsigned int *irqp)
|
||||
{
|
||||
if (irqp != NULL)
|
||||
*irqp = acpi_register_gsi(NULL, gsi, -1, -1);
|
||||
return (*irqp >= 0) ? 0 : -EINVAL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_gsi_to_irq);
|
||||
|
||||
int acpi_isa_irq_to_gsi(unsigned int isa_irq, u32 *gsi)
|
||||
{
|
||||
if (gsi)
|
||||
*gsi = isa_irq;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* success: return IRQ number (>=0)
|
||||
* failure: return < 0
|
||||
*/
|
||||
int acpi_register_gsi(struct device *dev, u32 gsi, int trigger, int polarity)
|
||||
{
|
||||
struct irq_fwspec fwspec;
|
||||
|
||||
switch (gsi) {
|
||||
case GSI_MIN_CPU_IRQ ... GSI_MAX_CPU_IRQ:
|
||||
fwspec.fwnode = liointc_domain->fwnode;
|
||||
fwspec.param[0] = gsi - GSI_MIN_CPU_IRQ;
|
||||
fwspec.param_count = 1;
|
||||
|
||||
return irq_create_fwspec_mapping(&fwspec);
|
||||
|
||||
case GSI_MIN_LPC_IRQ ... GSI_MAX_LPC_IRQ:
|
||||
if (!pch_lpc_domain)
|
||||
return -EINVAL;
|
||||
|
||||
fwspec.fwnode = pch_lpc_domain->fwnode;
|
||||
fwspec.param[0] = gsi - GSI_MIN_LPC_IRQ;
|
||||
fwspec.param[1] = acpi_dev_get_irq_type(trigger, polarity);
|
||||
fwspec.param_count = 2;
|
||||
|
||||
return irq_create_fwspec_mapping(&fwspec);
|
||||
|
||||
case GSI_MIN_PCH_IRQ ... GSI_MAX_PCH_IRQ:
|
||||
if (!pch_pic_domain[0])
|
||||
return -EINVAL;
|
||||
|
||||
fwspec.fwnode = pch_pic_domain[0]->fwnode;
|
||||
fwspec.param[0] = gsi - GSI_MIN_PCH_IRQ;
|
||||
fwspec.param[1] = IRQ_TYPE_LEVEL_HIGH;
|
||||
fwspec.param_count = 2;
|
||||
|
||||
return irq_create_fwspec_mapping(&fwspec);
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_register_gsi);
|
||||
|
||||
void acpi_unregister_gsi(u32 gsi)
|
||||
{
|
||||
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_unregister_gsi);
|
||||
|
||||
void __init __iomem * __acpi_map_table(unsigned long phys, unsigned long size)
|
||||
{
|
||||
|
||||
|
@ -25,12 +25,8 @@ DEFINE_PER_CPU(unsigned long, irq_stack);
|
||||
DEFINE_PER_CPU_SHARED_ALIGNED(irq_cpustat_t, irq_stat);
|
||||
EXPORT_PER_CPU_SYMBOL(irq_stat);
|
||||
|
||||
struct irq_domain *cpu_domain;
|
||||
struct irq_domain *liointc_domain;
|
||||
struct irq_domain *pch_lpc_domain;
|
||||
struct irq_domain *pch_msi_domain[MAX_IO_PICS];
|
||||
struct irq_domain *pch_pic_domain[MAX_IO_PICS];
|
||||
|
||||
struct acpi_vector_group pch_group[MAX_IO_PICS];
|
||||
struct acpi_vector_group msi_group[MAX_IO_PICS];
|
||||
/*
|
||||
* 'what should we do if we get a hw irq event on an illegal vector'.
|
||||
* each architecture has to answer this themselves.
|
||||
@ -56,6 +52,51 @@ int arch_show_interrupts(struct seq_file *p, int prec)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init early_pci_mcfg_parse(struct acpi_table_header *header)
|
||||
{
|
||||
struct acpi_table_mcfg *mcfg;
|
||||
struct acpi_mcfg_allocation *mptr;
|
||||
int i, n;
|
||||
|
||||
if (header->length < sizeof(struct acpi_table_mcfg))
|
||||
return -EINVAL;
|
||||
|
||||
n = (header->length - sizeof(struct acpi_table_mcfg)) /
|
||||
sizeof(struct acpi_mcfg_allocation);
|
||||
mcfg = (struct acpi_table_mcfg *)header;
|
||||
mptr = (struct acpi_mcfg_allocation *) &mcfg[1];
|
||||
|
||||
for (i = 0; i < n; i++, mptr++) {
|
||||
msi_group[i].pci_segment = mptr->pci_segment;
|
||||
pch_group[i].node = msi_group[i].node = (mptr->address >> 44) & 0xf;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __init init_vec_parent_group(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_IO_PICS; i++) {
|
||||
msi_group[i].pci_segment = -1;
|
||||
msi_group[i].node = -1;
|
||||
pch_group[i].node = -1;
|
||||
}
|
||||
|
||||
acpi_table_parse(ACPI_SIG_MCFG, early_pci_mcfg_parse);
|
||||
}
|
||||
|
||||
static int __init get_ipi_irq(void)
|
||||
{
|
||||
struct irq_domain *d = irq_find_matching_fwnode(cpuintc_handle, DOMAIN_BUS_ANY);
|
||||
|
||||
if (d)
|
||||
return irq_create_mapping(d, EXCCODE_IPI - EXCCODE_INT_START);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
void __init init_IRQ(void)
|
||||
{
|
||||
int i;
|
||||
@ -69,9 +110,12 @@ void __init init_IRQ(void)
|
||||
clear_csr_ecfg(ECFG0_IM);
|
||||
clear_csr_estat(ESTATF_IP);
|
||||
|
||||
init_vec_parent_group();
|
||||
irqchip_init();
|
||||
#ifdef CONFIG_SMP
|
||||
ipi_irq = EXCCODE_IPI - EXCCODE_INT_START;
|
||||
ipi_irq = get_ipi_irq();
|
||||
if (ipi_irq < 0)
|
||||
panic("IPI IRQ mapping failed\n");
|
||||
irq_set_percpu_devid(ipi_irq);
|
||||
r = request_percpu_irq(ipi_irq, loongson3_ipi_interrupt, "IPI", &ipi_dummy_dev);
|
||||
if (r < 0)
|
||||
|
@ -123,6 +123,16 @@ void sync_counter(void)
|
||||
csr_write64(-init_timeval, LOONGARCH_CSR_CNTC);
|
||||
}
|
||||
|
||||
static int get_timer_irq(void)
|
||||
{
|
||||
struct irq_domain *d = irq_find_matching_fwnode(cpuintc_handle, DOMAIN_BUS_ANY);
|
||||
|
||||
if (d)
|
||||
return irq_create_mapping(d, EXCCODE_TIMER - EXCCODE_INT_START);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int constant_clockevent_init(void)
|
||||
{
|
||||
unsigned int irq;
|
||||
@ -132,7 +142,9 @@ int constant_clockevent_init(void)
|
||||
struct clock_event_device *cd;
|
||||
static int timer_irq_installed = 0;
|
||||
|
||||
irq = EXCCODE_TIMER - EXCCODE_INT_START;
|
||||
irq = get_timer_irq();
|
||||
if (irq < 0)
|
||||
pr_err("Failed to map irq %d (timer)\n", irq);
|
||||
|
||||
cd = &per_cpu(constant_clockevent_device, cpu);
|
||||
|
||||
|
@ -263,7 +263,7 @@ static int next_cpu_for_irq(struct irq_data *data)
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
int cpu;
|
||||
struct cpumask *mask = irq_data_get_affinity_mask(data);
|
||||
const struct cpumask *mask = irq_data_get_affinity_mask(data);
|
||||
int weight = cpumask_weight(mask);
|
||||
struct octeon_ciu_chip_data *cd = irq_data_get_irq_chip_data(data);
|
||||
|
||||
@ -758,7 +758,7 @@ static void octeon_irq_cpu_offline_ciu(struct irq_data *data)
|
||||
{
|
||||
int cpu = smp_processor_id();
|
||||
cpumask_t new_affinity;
|
||||
struct cpumask *mask = irq_data_get_affinity_mask(data);
|
||||
const struct cpumask *mask = irq_data_get_affinity_mask(data);
|
||||
|
||||
if (!cpumask_test_cpu(cpu, mask))
|
||||
return;
|
||||
|
@ -7,8 +7,9 @@
|
||||
#define NR_MIPS_CPU_IRQS 8
|
||||
#define NR_MAX_CHAINED_IRQS 40 /* Chained IRQs means those not directly used by devices */
|
||||
#define NR_IRQS (NR_IRQS_LEGACY + NR_MIPS_CPU_IRQS + NR_MAX_CHAINED_IRQS + 256)
|
||||
|
||||
#define MAX_IO_PICS 1
|
||||
#define MIPS_CPU_IRQ_BASE NR_IRQS_LEGACY
|
||||
#define GSI_MIN_CPU_IRQ 0
|
||||
|
||||
#include <asm/mach-generic/irq.h>
|
||||
|
||||
|
@ -315,7 +315,7 @@ unsigned long txn_affinity_addr(unsigned int irq, int cpu)
|
||||
{
|
||||
#ifdef CONFIG_SMP
|
||||
struct irq_data *d = irq_get_irq_data(irq);
|
||||
cpumask_copy(irq_data_get_affinity_mask(d), cpumask_of(cpu));
|
||||
irq_data_update_affinity(d, cpumask_of(cpu));
|
||||
#endif
|
||||
|
||||
return per_cpu(cpu_data, cpu).txn_addr;
|
||||
|
@ -230,16 +230,17 @@ void migrate_irqs(void)
|
||||
struct irq_data *data = irq_get_irq_data(irq);
|
||||
|
||||
if (irq_data_get_node(data) == cpu) {
|
||||
struct cpumask *mask = irq_data_get_affinity_mask(data);
|
||||
const struct cpumask *mask = irq_data_get_affinity_mask(data);
|
||||
unsigned int newcpu = cpumask_any_and(mask,
|
||||
cpu_online_mask);
|
||||
if (newcpu >= nr_cpu_ids) {
|
||||
pr_info_ratelimited("IRQ%u no longer affine to CPU%u\n",
|
||||
irq, cpu);
|
||||
|
||||
cpumask_setall(mask);
|
||||
irq_set_affinity(irq, cpu_all_mask);
|
||||
} else {
|
||||
irq_set_affinity(irq, mask);
|
||||
}
|
||||
irq_set_affinity(irq, mask);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -192,7 +192,7 @@ static void hv_irq_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
|
||||
struct pci_dev *dev;
|
||||
struct hv_interrupt_entry out_entry, *stored_entry;
|
||||
struct irq_cfg *cfg = irqd_cfg(data);
|
||||
cpumask_t *affinity;
|
||||
const cpumask_t *affinity;
|
||||
int cpu;
|
||||
u64 status;
|
||||
|
||||
|
@ -169,7 +169,7 @@ void migrate_irqs(void)
|
||||
|
||||
for_each_active_irq(i) {
|
||||
struct irq_data *data = irq_get_irq_data(i);
|
||||
struct cpumask *mask;
|
||||
const struct cpumask *mask;
|
||||
unsigned int newcpu;
|
||||
|
||||
if (irqd_is_per_cpu(data))
|
||||
@ -185,9 +185,10 @@ void migrate_irqs(void)
|
||||
pr_info_ratelimited("IRQ%u no longer affine to CPU%u\n",
|
||||
i, cpu);
|
||||
|
||||
cpumask_setall(mask);
|
||||
irq_set_affinity(i, cpu_all_mask);
|
||||
} else {
|
||||
irq_set_affinity(i, mask);
|
||||
}
|
||||
irq_set_affinity(i, mask);
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_HOTPLUG_CPU */
|
||||
|
@ -1144,6 +1144,9 @@ static int __init acpi_bus_init_irq(void)
|
||||
case ACPI_IRQ_MODEL_PLATFORM:
|
||||
message = "platform specific model";
|
||||
break;
|
||||
case ACPI_IRQ_MODEL_LPIC:
|
||||
message = "LPIC";
|
||||
break;
|
||||
default:
|
||||
pr_info("Unknown interrupt routing model\n");
|
||||
return -ENODEV;
|
||||
|
@ -12,7 +12,8 @@
|
||||
|
||||
enum acpi_irq_model_id acpi_irq_model;
|
||||
|
||||
static struct fwnode_handle *acpi_gsi_domain_id;
|
||||
static struct fwnode_handle *(*acpi_get_gsi_domain_id)(u32 gsi);
|
||||
static u32 (*acpi_gsi_to_irq_fallback)(u32 gsi);
|
||||
|
||||
/**
|
||||
* acpi_gsi_to_irq() - Retrieve the linux irq number for a given GSI
|
||||
@ -26,14 +27,18 @@ static struct fwnode_handle *acpi_gsi_domain_id;
|
||||
*/
|
||||
int acpi_gsi_to_irq(u32 gsi, unsigned int *irq)
|
||||
{
|
||||
struct irq_domain *d = irq_find_matching_fwnode(acpi_gsi_domain_id,
|
||||
DOMAIN_BUS_ANY);
|
||||
struct irq_domain *d;
|
||||
|
||||
d = irq_find_matching_fwnode(acpi_get_gsi_domain_id(gsi),
|
||||
DOMAIN_BUS_ANY);
|
||||
*irq = irq_find_mapping(d, gsi);
|
||||
/*
|
||||
* *irq == 0 means no mapping, that should
|
||||
* be reported as a failure
|
||||
* *irq == 0 means no mapping, that should be reported as a
|
||||
* failure, unless there is an arch-specific fallback handler.
|
||||
*/
|
||||
if (!*irq && acpi_gsi_to_irq_fallback)
|
||||
*irq = acpi_gsi_to_irq_fallback(gsi);
|
||||
|
||||
return (*irq > 0) ? 0 : -EINVAL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_gsi_to_irq);
|
||||
@ -53,12 +58,12 @@ int acpi_register_gsi(struct device *dev, u32 gsi, int trigger,
|
||||
{
|
||||
struct irq_fwspec fwspec;
|
||||
|
||||
if (WARN_ON(!acpi_gsi_domain_id)) {
|
||||
fwspec.fwnode = acpi_get_gsi_domain_id(gsi);
|
||||
if (WARN_ON(!fwspec.fwnode)) {
|
||||
pr_warn("GSI: No registered irqchip, giving up\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
fwspec.fwnode = acpi_gsi_domain_id;
|
||||
fwspec.param[0] = gsi;
|
||||
fwspec.param[1] = acpi_dev_get_irq_type(trigger, polarity);
|
||||
fwspec.param_count = 2;
|
||||
@ -73,13 +78,14 @@ EXPORT_SYMBOL_GPL(acpi_register_gsi);
|
||||
*/
|
||||
void acpi_unregister_gsi(u32 gsi)
|
||||
{
|
||||
struct irq_domain *d = irq_find_matching_fwnode(acpi_gsi_domain_id,
|
||||
DOMAIN_BUS_ANY);
|
||||
struct irq_domain *d;
|
||||
int irq;
|
||||
|
||||
if (WARN_ON(acpi_irq_model == ACPI_IRQ_MODEL_GIC && gsi < 16))
|
||||
return;
|
||||
|
||||
d = irq_find_matching_fwnode(acpi_get_gsi_domain_id(gsi),
|
||||
DOMAIN_BUS_ANY);
|
||||
irq = irq_find_mapping(d, gsi);
|
||||
irq_dispose_mapping(irq);
|
||||
}
|
||||
@ -97,7 +103,8 @@ EXPORT_SYMBOL_GPL(acpi_unregister_gsi);
|
||||
* The referenced device fwhandle or NULL on failure
|
||||
*/
|
||||
static struct fwnode_handle *
|
||||
acpi_get_irq_source_fwhandle(const struct acpi_resource_source *source)
|
||||
acpi_get_irq_source_fwhandle(const struct acpi_resource_source *source,
|
||||
u32 gsi)
|
||||
{
|
||||
struct fwnode_handle *result;
|
||||
struct acpi_device *device;
|
||||
@ -105,7 +112,7 @@ acpi_get_irq_source_fwhandle(const struct acpi_resource_source *source)
|
||||
acpi_status status;
|
||||
|
||||
if (!source->string_length)
|
||||
return acpi_gsi_domain_id;
|
||||
return acpi_get_gsi_domain_id(gsi);
|
||||
|
||||
status = acpi_get_handle(NULL, source->string_ptr, &handle);
|
||||
if (WARN_ON(ACPI_FAILURE(status)))
|
||||
@ -194,7 +201,7 @@ static acpi_status acpi_irq_parse_one_cb(struct acpi_resource *ares,
|
||||
ctx->index -= irq->interrupt_count;
|
||||
return AE_OK;
|
||||
}
|
||||
fwnode = acpi_gsi_domain_id;
|
||||
fwnode = acpi_get_gsi_domain_id(irq->interrupts[ctx->index]);
|
||||
acpi_irq_parse_one_match(fwnode, irq->interrupts[ctx->index],
|
||||
irq->triggering, irq->polarity,
|
||||
irq->shareable, ctx);
|
||||
@ -207,7 +214,8 @@ static acpi_status acpi_irq_parse_one_cb(struct acpi_resource *ares,
|
||||
ctx->index -= eirq->interrupt_count;
|
||||
return AE_OK;
|
||||
}
|
||||
fwnode = acpi_get_irq_source_fwhandle(&eirq->resource_source);
|
||||
fwnode = acpi_get_irq_source_fwhandle(&eirq->resource_source,
|
||||
eirq->interrupts[ctx->index]);
|
||||
acpi_irq_parse_one_match(fwnode, eirq->interrupts[ctx->index],
|
||||
eirq->triggering, eirq->polarity,
|
||||
eirq->shareable, ctx);
|
||||
@ -291,10 +299,20 @@ EXPORT_SYMBOL_GPL(acpi_irq_get);
|
||||
* GSI interrupts
|
||||
*/
|
||||
void __init acpi_set_irq_model(enum acpi_irq_model_id model,
|
||||
struct fwnode_handle *fwnode)
|
||||
struct fwnode_handle *(*fn)(u32))
|
||||
{
|
||||
acpi_irq_model = model;
|
||||
acpi_gsi_domain_id = fwnode;
|
||||
acpi_get_gsi_domain_id = fn;
|
||||
}
|
||||
|
||||
/**
|
||||
* acpi_set_gsi_to_irq_fallback - Register a GSI transfer
|
||||
* callback to fallback to arch specified implementation.
|
||||
* @fn: arch-specific fallback handler
|
||||
*/
|
||||
void __init acpi_set_gsi_to_irq_fallback(u32 (*fn)(u32))
|
||||
{
|
||||
acpi_gsi_to_irq_fallback = fn;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -312,8 +330,14 @@ struct irq_domain *acpi_irq_create_hierarchy(unsigned int flags,
|
||||
const struct irq_domain_ops *ops,
|
||||
void *host_data)
|
||||
{
|
||||
struct irq_domain *d = irq_find_matching_fwnode(acpi_gsi_domain_id,
|
||||
DOMAIN_BUS_ANY);
|
||||
struct irq_domain *d;
|
||||
|
||||
/* This only works for the GIC model... */
|
||||
if (acpi_irq_model != ACPI_IRQ_MODEL_GIC)
|
||||
return NULL;
|
||||
|
||||
d = irq_find_matching_fwnode(acpi_get_gsi_domain_id(0),
|
||||
DOMAIN_BUS_ANY);
|
||||
|
||||
if (!d)
|
||||
return NULL;
|
||||
|
@ -550,15 +550,12 @@ static struct irq_chip msc313_gpio_irqchip = {
|
||||
* so we need to provide the fwspec. Essentially gpiochip_populate_parent_fwspec_twocell
|
||||
* that puts GIC_SPI into the first cell.
|
||||
*/
|
||||
static void *msc313_gpio_populate_parent_fwspec(struct gpio_chip *gc,
|
||||
unsigned int parent_hwirq,
|
||||
unsigned int parent_type)
|
||||
static int msc313_gpio_populate_parent_fwspec(struct gpio_chip *gc,
|
||||
union gpio_irq_fwspec *gfwspec,
|
||||
unsigned int parent_hwirq,
|
||||
unsigned int parent_type)
|
||||
{
|
||||
struct irq_fwspec *fwspec;
|
||||
|
||||
fwspec = kmalloc(sizeof(*fwspec), GFP_KERNEL);
|
||||
if (!fwspec)
|
||||
return NULL;
|
||||
struct irq_fwspec *fwspec = &gfwspec->fwspec;
|
||||
|
||||
fwspec->fwnode = gc->irq.parent_domain->fwnode;
|
||||
fwspec->param_count = 3;
|
||||
@ -566,7 +563,7 @@ static void *msc313_gpio_populate_parent_fwspec(struct gpio_chip *gc,
|
||||
fwspec->param[1] = parent_hwirq;
|
||||
fwspec->param[2] = parent_type;
|
||||
|
||||
return fwspec;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msc313e_gpio_child_to_parent_hwirq(struct gpio_chip *chip,
|
||||
|
@ -443,15 +443,12 @@ static int tegra_gpio_child_to_parent_hwirq(struct gpio_chip *chip,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *tegra_gpio_populate_parent_fwspec(struct gpio_chip *chip,
|
||||
unsigned int parent_hwirq,
|
||||
unsigned int parent_type)
|
||||
static int tegra_gpio_populate_parent_fwspec(struct gpio_chip *chip,
|
||||
union gpio_irq_fwspec *gfwspec,
|
||||
unsigned int parent_hwirq,
|
||||
unsigned int parent_type)
|
||||
{
|
||||
struct irq_fwspec *fwspec;
|
||||
|
||||
fwspec = kmalloc(sizeof(*fwspec), GFP_KERNEL);
|
||||
if (!fwspec)
|
||||
return NULL;
|
||||
struct irq_fwspec *fwspec = &gfwspec->fwspec;
|
||||
|
||||
fwspec->fwnode = chip->irq.parent_domain->fwnode;
|
||||
fwspec->param_count = 3;
|
||||
@ -459,7 +456,7 @@ static void *tegra_gpio_populate_parent_fwspec(struct gpio_chip *chip,
|
||||
fwspec->param[1] = parent_hwirq;
|
||||
fwspec->param[2] = parent_type;
|
||||
|
||||
return fwspec;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
@ -621,16 +621,13 @@ static int tegra186_gpio_irq_domain_translate(struct irq_domain *domain,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *tegra186_gpio_populate_parent_fwspec(struct gpio_chip *chip,
|
||||
unsigned int parent_hwirq,
|
||||
unsigned int parent_type)
|
||||
static int tegra186_gpio_populate_parent_fwspec(struct gpio_chip *chip,
|
||||
union gpio_irq_fwspec *gfwspec,
|
||||
unsigned int parent_hwirq,
|
||||
unsigned int parent_type)
|
||||
{
|
||||
struct tegra_gpio *gpio = gpiochip_get_data(chip);
|
||||
struct irq_fwspec *fwspec;
|
||||
|
||||
fwspec = kmalloc(sizeof(*fwspec), GFP_KERNEL);
|
||||
if (!fwspec)
|
||||
return NULL;
|
||||
struct irq_fwspec *fwspec = &gfwspec->fwspec;
|
||||
|
||||
fwspec->fwnode = chip->irq.parent_domain->fwnode;
|
||||
fwspec->param_count = 3;
|
||||
@ -638,7 +635,7 @@ static void *tegra186_gpio_populate_parent_fwspec(struct gpio_chip *chip,
|
||||
fwspec->param[1] = parent_hwirq;
|
||||
fwspec->param[2] = parent_type;
|
||||
|
||||
return fwspec;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra186_gpio_child_to_parent_hwirq(struct gpio_chip *chip,
|
||||
|
@ -15,8 +15,6 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <asm-generic/msi.h>
|
||||
|
||||
|
||||
#define GPIO_RX_DAT 0x0
|
||||
#define GPIO_TX_SET 0x8
|
||||
@ -408,18 +406,15 @@ static int thunderx_gpio_child_to_parent_hwirq(struct gpio_chip *gc,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *thunderx_gpio_populate_parent_alloc_info(struct gpio_chip *chip,
|
||||
unsigned int parent_hwirq,
|
||||
unsigned int parent_type)
|
||||
static int thunderx_gpio_populate_parent_alloc_info(struct gpio_chip *chip,
|
||||
union gpio_irq_fwspec *gfwspec,
|
||||
unsigned int parent_hwirq,
|
||||
unsigned int parent_type)
|
||||
{
|
||||
msi_alloc_info_t *info;
|
||||
|
||||
info = kmalloc(sizeof(*info), GFP_KERNEL);
|
||||
if (!info)
|
||||
return NULL;
|
||||
msi_alloc_info_t *info = &gfwspec->msiinfo;
|
||||
|
||||
info->hwirq = parent_hwirq;
|
||||
return info;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int thunderx_gpio_probe(struct pci_dev *pdev,
|
||||
|
@ -103,15 +103,12 @@ static int visconti_gpio_child_to_parent_hwirq(struct gpio_chip *gc,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void *visconti_gpio_populate_parent_fwspec(struct gpio_chip *chip,
|
||||
unsigned int parent_hwirq,
|
||||
unsigned int parent_type)
|
||||
static int visconti_gpio_populate_parent_fwspec(struct gpio_chip *chip,
|
||||
union gpio_irq_fwspec *gfwspec,
|
||||
unsigned int parent_hwirq,
|
||||
unsigned int parent_type)
|
||||
{
|
||||
struct irq_fwspec *fwspec;
|
||||
|
||||
fwspec = kmalloc(sizeof(*fwspec), GFP_KERNEL);
|
||||
if (!fwspec)
|
||||
return NULL;
|
||||
struct irq_fwspec *fwspec = &gfwspec->fwspec;
|
||||
|
||||
fwspec->fwnode = chip->irq.parent_domain->fwnode;
|
||||
fwspec->param_count = 3;
|
||||
@ -119,7 +116,7 @@ static void *visconti_gpio_populate_parent_fwspec(struct gpio_chip *chip,
|
||||
fwspec->param[1] = parent_hwirq;
|
||||
fwspec->param[2] = parent_type;
|
||||
|
||||
return fwspec;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int visconti_gpio_probe(struct platform_device *pdev)
|
||||
|
@ -1107,7 +1107,7 @@ static int gpiochip_hierarchy_irq_domain_alloc(struct irq_domain *d,
|
||||
irq_hw_number_t hwirq;
|
||||
unsigned int type = IRQ_TYPE_NONE;
|
||||
struct irq_fwspec *fwspec = data;
|
||||
void *parent_arg;
|
||||
union gpio_irq_fwspec gpio_parent_fwspec = {};
|
||||
unsigned int parent_hwirq;
|
||||
unsigned int parent_type;
|
||||
struct gpio_irq_chip *girq = &gc->irq;
|
||||
@ -1147,14 +1147,15 @@ static int gpiochip_hierarchy_irq_domain_alloc(struct irq_domain *d,
|
||||
irq_set_probe(irq);
|
||||
|
||||
/* This parent only handles asserted level IRQs */
|
||||
parent_arg = girq->populate_parent_alloc_arg(gc, parent_hwirq, parent_type);
|
||||
if (!parent_arg)
|
||||
return -ENOMEM;
|
||||
ret = girq->populate_parent_alloc_arg(gc, &gpio_parent_fwspec,
|
||||
parent_hwirq, parent_type);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
chip_dbg(gc, "alloc_irqs_parent for %d parent hwirq %d\n",
|
||||
irq, parent_hwirq);
|
||||
irq_set_lockdep_class(irq, gc->irq.lock_key, gc->irq.request_key);
|
||||
ret = irq_domain_alloc_irqs_parent(d, irq, 1, parent_arg);
|
||||
ret = irq_domain_alloc_irqs_parent(d, irq, 1, &gpio_parent_fwspec);
|
||||
/*
|
||||
* If the parent irqdomain is msi, the interrupts have already
|
||||
* been allocated, so the EEXIST is good.
|
||||
@ -1166,7 +1167,6 @@ static int gpiochip_hierarchy_irq_domain_alloc(struct irq_domain *d,
|
||||
"failed to allocate parent hwirq %d for hwirq %lu\n",
|
||||
parent_hwirq, hwirq);
|
||||
|
||||
kfree(parent_arg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1181,15 +1181,18 @@ static void gpiochip_hierarchy_setup_domain_ops(struct irq_domain_ops *ops)
|
||||
ops->activate = gpiochip_irq_domain_activate;
|
||||
ops->deactivate = gpiochip_irq_domain_deactivate;
|
||||
ops->alloc = gpiochip_hierarchy_irq_domain_alloc;
|
||||
ops->free = irq_domain_free_irqs_common;
|
||||
|
||||
/*
|
||||
* We only allow overriding the translate() function for
|
||||
* We only allow overriding the translate() and free() functions for
|
||||
* hierarchical chips, and this should only be done if the user
|
||||
* really need something other than 1:1 translation.
|
||||
* really need something other than 1:1 translation for translate()
|
||||
* callback and free if user wants to free up any resources which
|
||||
* were allocated during callbacks, for example populate_parent_alloc_arg.
|
||||
*/
|
||||
if (!ops->translate)
|
||||
ops->translate = gpiochip_hierarchy_irq_domain_translate;
|
||||
if (!ops->free)
|
||||
ops->free = irq_domain_free_irqs_common;
|
||||
}
|
||||
|
||||
static int gpiochip_hierarchy_add_domain(struct gpio_chip *gc)
|
||||
@ -1230,34 +1233,28 @@ static bool gpiochip_hierarchy_is_hierarchical(struct gpio_chip *gc)
|
||||
return !!gc->irq.parent_domain;
|
||||
}
|
||||
|
||||
void *gpiochip_populate_parent_fwspec_twocell(struct gpio_chip *gc,
|
||||
unsigned int parent_hwirq,
|
||||
unsigned int parent_type)
|
||||
int gpiochip_populate_parent_fwspec_twocell(struct gpio_chip *gc,
|
||||
union gpio_irq_fwspec *gfwspec,
|
||||
unsigned int parent_hwirq,
|
||||
unsigned int parent_type)
|
||||
{
|
||||
struct irq_fwspec *fwspec;
|
||||
|
||||
fwspec = kmalloc(sizeof(*fwspec), GFP_KERNEL);
|
||||
if (!fwspec)
|
||||
return NULL;
|
||||
struct irq_fwspec *fwspec = &gfwspec->fwspec;
|
||||
|
||||
fwspec->fwnode = gc->irq.parent_domain->fwnode;
|
||||
fwspec->param_count = 2;
|
||||
fwspec->param[0] = parent_hwirq;
|
||||
fwspec->param[1] = parent_type;
|
||||
|
||||
return fwspec;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gpiochip_populate_parent_fwspec_twocell);
|
||||
|
||||
void *gpiochip_populate_parent_fwspec_fourcell(struct gpio_chip *gc,
|
||||
unsigned int parent_hwirq,
|
||||
unsigned int parent_type)
|
||||
int gpiochip_populate_parent_fwspec_fourcell(struct gpio_chip *gc,
|
||||
union gpio_irq_fwspec *gfwspec,
|
||||
unsigned int parent_hwirq,
|
||||
unsigned int parent_type)
|
||||
{
|
||||
struct irq_fwspec *fwspec;
|
||||
|
||||
fwspec = kmalloc(sizeof(*fwspec), GFP_KERNEL);
|
||||
if (!fwspec)
|
||||
return NULL;
|
||||
struct irq_fwspec *fwspec = &gfwspec->fwspec;
|
||||
|
||||
fwspec->fwnode = gc->irq.parent_domain->fwnode;
|
||||
fwspec->param_count = 4;
|
||||
@ -1266,7 +1263,7 @@ void *gpiochip_populate_parent_fwspec_fourcell(struct gpio_chip *gc,
|
||||
fwspec->param[2] = 0;
|
||||
fwspec->param[3] = parent_type;
|
||||
|
||||
return fwspec;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gpiochip_populate_parent_fwspec_fourcell);
|
||||
|
||||
|
@ -194,7 +194,7 @@ hyperv_root_ir_compose_msi_msg(struct irq_data *irq_data, struct msi_msg *msg)
|
||||
u32 vector;
|
||||
struct irq_cfg *cfg;
|
||||
int ioapic_id;
|
||||
struct cpumask *affinity;
|
||||
const struct cpumask *affinity;
|
||||
int cpu;
|
||||
struct hv_interrupt_entry entry;
|
||||
struct hyperv_root_ir_data *data = irq_data->chip_data;
|
||||
|
@ -8,7 +8,7 @@ config IRQCHIP
|
||||
config ARM_GIC
|
||||
bool
|
||||
select IRQ_DOMAIN_HIERARCHY
|
||||
select GENERIC_IRQ_EFFECTIVE_AFF_MASK
|
||||
select GENERIC_IRQ_EFFECTIVE_AFF_MASK if SMP
|
||||
|
||||
config ARM_GIC_PM
|
||||
bool
|
||||
@ -34,7 +34,7 @@ config ARM_GIC_V3
|
||||
bool
|
||||
select IRQ_DOMAIN_HIERARCHY
|
||||
select PARTITION_PERCPU
|
||||
select GENERIC_IRQ_EFFECTIVE_AFF_MASK
|
||||
select GENERIC_IRQ_EFFECTIVE_AFF_MASK if SMP
|
||||
|
||||
config ARM_GIC_V3_ITS
|
||||
bool
|
||||
@ -76,7 +76,7 @@ config ARMADA_370_XP_IRQ
|
||||
bool
|
||||
select GENERIC_IRQ_CHIP
|
||||
select PCI_MSI if PCI
|
||||
select GENERIC_IRQ_EFFECTIVE_AFF_MASK
|
||||
select GENERIC_IRQ_EFFECTIVE_AFF_MASK if SMP
|
||||
|
||||
config ALPINE_MSI
|
||||
bool
|
||||
@ -112,7 +112,7 @@ config BCM6345_L1_IRQ
|
||||
bool
|
||||
select GENERIC_IRQ_CHIP
|
||||
select IRQ_DOMAIN
|
||||
select GENERIC_IRQ_EFFECTIVE_AFF_MASK
|
||||
select GENERIC_IRQ_EFFECTIVE_AFF_MASK if SMP
|
||||
|
||||
config BCM7038_L1_IRQ
|
||||
tristate "Broadcom STB 7038-style L1/L2 interrupt controller driver"
|
||||
@ -120,7 +120,7 @@ config BCM7038_L1_IRQ
|
||||
default ARCH_BRCMSTB || BMIPS_GENERIC
|
||||
select GENERIC_IRQ_CHIP
|
||||
select IRQ_DOMAIN
|
||||
select GENERIC_IRQ_EFFECTIVE_AFF_MASK
|
||||
select GENERIC_IRQ_EFFECTIVE_AFF_MASK if SMP
|
||||
|
||||
config BCM7120_L2_IRQ
|
||||
tristate "Broadcom STB 7120-style L2 interrupt controller driver"
|
||||
@ -177,9 +177,9 @@ config MADERA_IRQ
|
||||
config IRQ_MIPS_CPU
|
||||
bool
|
||||
select GENERIC_IRQ_CHIP
|
||||
select GENERIC_IRQ_IPI if SYS_SUPPORTS_MULTITHREADING
|
||||
select GENERIC_IRQ_IPI if SMP && SYS_SUPPORTS_MULTITHREADING
|
||||
select IRQ_DOMAIN
|
||||
select GENERIC_IRQ_EFFECTIVE_AFF_MASK
|
||||
select GENERIC_IRQ_EFFECTIVE_AFF_MASK if SMP
|
||||
|
||||
config CLPS711X_IRQCHIP
|
||||
bool
|
||||
@ -242,6 +242,14 @@ config RENESAS_RZA1_IRQC
|
||||
Enable support for the Renesas RZ/A1 Interrupt Controller, to use up
|
||||
to 8 external interrupts with configurable sense select.
|
||||
|
||||
config RENESAS_RZG2L_IRQC
|
||||
bool "Renesas RZ/G2L (and alike SoC) IRQC support" if COMPILE_TEST
|
||||
select GENERIC_IRQ_CHIP
|
||||
select IRQ_DOMAIN_HIERARCHY
|
||||
help
|
||||
Enable support for the Renesas RZ/G2L (and alike SoC) Interrupt Controller
|
||||
for external devices.
|
||||
|
||||
config SL28CPLD_INTC
|
||||
bool "Kontron sl28cpld IRQ controller"
|
||||
depends on MFD_SL28CPLD=y || COMPILE_TEST
|
||||
@ -294,7 +302,7 @@ config VERSATILE_FPGA_IRQ_NR
|
||||
config XTENSA_MX
|
||||
bool
|
||||
select IRQ_DOMAIN
|
||||
select GENERIC_IRQ_EFFECTIVE_AFF_MASK
|
||||
select GENERIC_IRQ_EFFECTIVE_AFF_MASK if SMP
|
||||
|
||||
config XILINX_INTC
|
||||
bool "Xilinx Interrupt Controller IP"
|
||||
@ -322,7 +330,8 @@ config KEYSTONE_IRQ
|
||||
|
||||
config MIPS_GIC
|
||||
bool
|
||||
select GENERIC_IRQ_IPI
|
||||
select GENERIC_IRQ_IPI if SMP
|
||||
select IRQ_DOMAIN_HIERARCHY
|
||||
select MIPS_CM
|
||||
|
||||
config INGENIC_IRQ
|
||||
@ -530,6 +539,7 @@ config SIFIVE_PLIC
|
||||
bool "SiFive Platform-Level Interrupt Controller"
|
||||
depends on RISCV
|
||||
select IRQ_DOMAIN_HIERARCHY
|
||||
select GENERIC_IRQ_EFFECTIVE_AFF_MASK if SMP
|
||||
help
|
||||
This enables support for the PLIC chip found in SiFive (and
|
||||
potentially other) RISC-V systems. The PLIC controls devices
|
||||
@ -546,6 +556,16 @@ config EXYNOS_IRQ_COMBINER
|
||||
Say yes here to add support for the IRQ combiner devices embedded
|
||||
in Samsung Exynos chips.
|
||||
|
||||
config IRQ_LOONGARCH_CPU
|
||||
bool
|
||||
select GENERIC_IRQ_CHIP
|
||||
select IRQ_DOMAIN
|
||||
select GENERIC_IRQ_EFFECTIVE_AFF_MASK
|
||||
help
|
||||
Support for the LoongArch CPU Interrupt Controller. For details of
|
||||
irq chip hierarchy on LoongArch platforms please read the document
|
||||
Documentation/loongarch/irq-chip-model.rst.
|
||||
|
||||
config LOONGSON_LIOINTC
|
||||
bool "Loongson Local I/O Interrupt Controller"
|
||||
depends on MACH_LOONGSON64
|
||||
@ -555,6 +575,16 @@ config LOONGSON_LIOINTC
|
||||
help
|
||||
Support for the Loongson Local I/O Interrupt Controller.
|
||||
|
||||
config LOONGSON_EIOINTC
|
||||
bool "Loongson Extend I/O Interrupt Controller"
|
||||
depends on LOONGARCH
|
||||
depends on MACH_LOONGSON64
|
||||
default MACH_LOONGSON64
|
||||
select IRQ_DOMAIN_HIERARCHY
|
||||
select GENERIC_IRQ_CHIP
|
||||
help
|
||||
Support for the Loongson3 Extend I/O Interrupt Vector Controller.
|
||||
|
||||
config LOONGSON_HTPIC
|
||||
bool "Loongson3 HyperTransport PIC Controller"
|
||||
depends on MACH_LOONGSON64 && MIPS
|
||||
@ -574,7 +604,7 @@ config LOONGSON_HTVEC
|
||||
|
||||
config LOONGSON_PCH_PIC
|
||||
bool "Loongson PCH PIC Controller"
|
||||
depends on MACH_LOONGSON64 || COMPILE_TEST
|
||||
depends on MACH_LOONGSON64
|
||||
default MACH_LOONGSON64
|
||||
select IRQ_DOMAIN_HIERARCHY
|
||||
select IRQ_FASTEOI_HIERARCHY_HANDLERS
|
||||
@ -583,7 +613,7 @@ config LOONGSON_PCH_PIC
|
||||
|
||||
config LOONGSON_PCH_MSI
|
||||
bool "Loongson PCH MSI Controller"
|
||||
depends on MACH_LOONGSON64 || COMPILE_TEST
|
||||
depends on MACH_LOONGSON64
|
||||
depends on PCI
|
||||
default MACH_LOONGSON64
|
||||
select IRQ_DOMAIN_HIERARCHY
|
||||
@ -591,6 +621,14 @@ config LOONGSON_PCH_MSI
|
||||
help
|
||||
Support for the Loongson PCH MSI Controller.
|
||||
|
||||
config LOONGSON_PCH_LPC
|
||||
bool "Loongson PCH LPC Controller"
|
||||
depends on MACH_LOONGSON64
|
||||
default (MACH_LOONGSON64 && LOONGARCH)
|
||||
select IRQ_DOMAIN_HIERARCHY
|
||||
help
|
||||
Support for the Loongson PCH LPC Controller.
|
||||
|
||||
config MST_IRQ
|
||||
bool "MStar Interrupt Controller"
|
||||
depends on ARCH_MEDIATEK || ARCH_MSTARV7 || COMPILE_TEST
|
||||
|
@ -51,6 +51,7 @@ obj-$(CONFIG_RDA_INTC) += irq-rda-intc.o
|
||||
obj-$(CONFIG_RENESAS_INTC_IRQPIN) += irq-renesas-intc-irqpin.o
|
||||
obj-$(CONFIG_RENESAS_IRQC) += irq-renesas-irqc.o
|
||||
obj-$(CONFIG_RENESAS_RZA1_IRQC) += irq-renesas-rza1.o
|
||||
obj-$(CONFIG_RENESAS_RZG2L_IRQC) += irq-renesas-rzg2l.o
|
||||
obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o
|
||||
obj-$(CONFIG_ARCH_NSPIRE) += irq-zevio.o
|
||||
obj-$(CONFIG_ARCH_VT8500) += irq-vt8500.o
|
||||
@ -103,11 +104,14 @@ obj-$(CONFIG_LS1X_IRQ) += irq-ls1x.o
|
||||
obj-$(CONFIG_TI_SCI_INTR_IRQCHIP) += irq-ti-sci-intr.o
|
||||
obj-$(CONFIG_TI_SCI_INTA_IRQCHIP) += irq-ti-sci-inta.o
|
||||
obj-$(CONFIG_TI_PRUSS_INTC) += irq-pruss-intc.o
|
||||
obj-$(CONFIG_IRQ_LOONGARCH_CPU) += irq-loongarch-cpu.o
|
||||
obj-$(CONFIG_LOONGSON_LIOINTC) += irq-loongson-liointc.o
|
||||
obj-$(CONFIG_LOONGSON_EIOINTC) += irq-loongson-eiointc.o
|
||||
obj-$(CONFIG_LOONGSON_HTPIC) += irq-loongson-htpic.o
|
||||
obj-$(CONFIG_LOONGSON_HTVEC) += irq-loongson-htvec.o
|
||||
obj-$(CONFIG_LOONGSON_PCH_PIC) += irq-loongson-pch-pic.o
|
||||
obj-$(CONFIG_LOONGSON_PCH_MSI) += irq-loongson-pch-msi.o
|
||||
obj-$(CONFIG_LOONGSON_PCH_LPC) += irq-loongson-pch-lpc.o
|
||||
obj-$(CONFIG_MST_IRQ) += irq-mst-intc.o
|
||||
obj-$(CONFIG_SL28CPLD_INTC) += irq-sl28cpld.o
|
||||
obj-$(CONFIG_MACH_REALTEK_RTL) += irq-realtek-rtl.o
|
||||
|
@ -216,11 +216,11 @@ static int bcm6345_l1_set_affinity(struct irq_data *d,
|
||||
enabled = intc->cpus[old_cpu]->enable_cache[word] & mask;
|
||||
if (enabled)
|
||||
__bcm6345_l1_mask(d);
|
||||
cpumask_copy(irq_data_get_affinity_mask(d), dest);
|
||||
irq_data_update_affinity(d, dest);
|
||||
if (enabled)
|
||||
__bcm6345_l1_unmask(d);
|
||||
} else {
|
||||
cpumask_copy(irq_data_get_affinity_mask(d), dest);
|
||||
irq_data_update_affinity(d, dest);
|
||||
}
|
||||
raw_spin_unlock_irqrestore(&intc->lock, flags);
|
||||
|
||||
|
@ -1783,7 +1783,7 @@ static void gic_enable_nmi_support(void)
|
||||
* the security state of the GIC (controlled by the GICD_CTRL.DS bit)
|
||||
* and if Group 0 interrupts can be delivered to Linux in the non-secure
|
||||
* world as FIQs (controlled by the SCR_EL3.FIQ bit). These affect the
|
||||
* the ICC_PMR_EL1 register and the priority that software assigns to
|
||||
* ICC_PMR_EL1 register and the priority that software assigns to
|
||||
* interrupts:
|
||||
*
|
||||
* GICD_CTRL.DS | SCR_EL3.FIQ | ICC_PMR_EL1 | Group 1 priority
|
||||
@ -2381,11 +2381,17 @@ static void __init gic_acpi_setup_kvm_info(void)
|
||||
vgic_set_kvm_info(&gic_v3_kvm_info);
|
||||
}
|
||||
|
||||
static struct fwnode_handle *gsi_domain_handle;
|
||||
|
||||
static struct fwnode_handle *gic_v3_get_gsi_domain_id(u32 gsi)
|
||||
{
|
||||
return gsi_domain_handle;
|
||||
}
|
||||
|
||||
static int __init
|
||||
gic_acpi_init(union acpi_subtable_headers *header, const unsigned long end)
|
||||
{
|
||||
struct acpi_madt_generic_distributor *dist;
|
||||
struct fwnode_handle *domain_handle;
|
||||
size_t size;
|
||||
int i, err;
|
||||
|
||||
@ -2417,18 +2423,18 @@ gic_acpi_init(union acpi_subtable_headers *header, const unsigned long end)
|
||||
if (err)
|
||||
goto out_redist_unmap;
|
||||
|
||||
domain_handle = irq_domain_alloc_fwnode(&dist->base_address);
|
||||
if (!domain_handle) {
|
||||
gsi_domain_handle = irq_domain_alloc_fwnode(&dist->base_address);
|
||||
if (!gsi_domain_handle) {
|
||||
err = -ENOMEM;
|
||||
goto out_redist_unmap;
|
||||
}
|
||||
|
||||
err = gic_init_bases(acpi_data.dist_base, acpi_data.redist_regs,
|
||||
acpi_data.nr_redist_regions, 0, domain_handle);
|
||||
acpi_data.nr_redist_regions, 0, gsi_domain_handle);
|
||||
if (err)
|
||||
goto out_fwhandle_free;
|
||||
|
||||
acpi_set_irq_model(ACPI_IRQ_MODEL_GIC, domain_handle);
|
||||
acpi_set_irq_model(ACPI_IRQ_MODEL_GIC, gic_v3_get_gsi_domain_id);
|
||||
|
||||
if (static_branch_likely(&supports_deactivate_key))
|
||||
gic_acpi_setup_kvm_info();
|
||||
@ -2436,7 +2442,7 @@ gic_acpi_init(union acpi_subtable_headers *header, const unsigned long end)
|
||||
return 0;
|
||||
|
||||
out_fwhandle_free:
|
||||
irq_domain_free_fwnode(domain_handle);
|
||||
irq_domain_free_fwnode(gsi_domain_handle);
|
||||
out_redist_unmap:
|
||||
for (i = 0; i < acpi_data.nr_redist_regions; i++)
|
||||
if (acpi_data.redist_regs[i].redist_base)
|
||||
|
@ -1682,11 +1682,17 @@ static void __init gic_acpi_setup_kvm_info(void)
|
||||
vgic_set_kvm_info(&gic_v2_kvm_info);
|
||||
}
|
||||
|
||||
static struct fwnode_handle *gsi_domain_handle;
|
||||
|
||||
static struct fwnode_handle *gic_v2_get_gsi_domain_id(u32 gsi)
|
||||
{
|
||||
return gsi_domain_handle;
|
||||
}
|
||||
|
||||
static int __init gic_v2_acpi_init(union acpi_subtable_headers *header,
|
||||
const unsigned long end)
|
||||
{
|
||||
struct acpi_madt_generic_distributor *dist;
|
||||
struct fwnode_handle *domain_handle;
|
||||
struct gic_chip_data *gic = &gic_data[0];
|
||||
int count, ret;
|
||||
|
||||
@ -1724,22 +1730,22 @@ static int __init gic_v2_acpi_init(union acpi_subtable_headers *header,
|
||||
/*
|
||||
* Initialize GIC instance zero (no multi-GIC support).
|
||||
*/
|
||||
domain_handle = irq_domain_alloc_fwnode(&dist->base_address);
|
||||
if (!domain_handle) {
|
||||
gsi_domain_handle = irq_domain_alloc_fwnode(&dist->base_address);
|
||||
if (!gsi_domain_handle) {
|
||||
pr_err("Unable to allocate domain handle\n");
|
||||
gic_teardown(gic);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = __gic_init_bases(gic, domain_handle);
|
||||
ret = __gic_init_bases(gic, gsi_domain_handle);
|
||||
if (ret) {
|
||||
pr_err("Failed to initialise GIC\n");
|
||||
irq_domain_free_fwnode(domain_handle);
|
||||
irq_domain_free_fwnode(gsi_domain_handle);
|
||||
gic_teardown(gic);
|
||||
return ret;
|
||||
}
|
||||
|
||||
acpi_set_irq_model(ACPI_IRQ_MODEL_GIC, domain_handle);
|
||||
acpi_set_irq_model(ACPI_IRQ_MODEL_GIC, gic_v2_get_gsi_domain_id);
|
||||
|
||||
if (IS_ENABLED(CONFIG_ARM_GIC_V2M))
|
||||
gicv2m_init(NULL, gic_data[0].domain);
|
||||
|
148
drivers/irqchip/irq-loongarch-cpu.c
Normal file
148
drivers/irqchip/irq-loongarch-cpu.c
Normal file
@ -0,0 +1,148 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqchip.h>
|
||||
#include <linux/irqdomain.h>
|
||||
|
||||
#include <asm/loongarch.h>
|
||||
#include <asm/setup.h>
|
||||
|
||||
static struct irq_domain *irq_domain;
|
||||
struct fwnode_handle *cpuintc_handle;
|
||||
|
||||
static u32 lpic_gsi_to_irq(u32 gsi)
|
||||
{
|
||||
/* Only pch irqdomain transferring is required for LoongArch. */
|
||||
if (gsi >= GSI_MIN_PCH_IRQ && gsi <= GSI_MAX_PCH_IRQ)
|
||||
return acpi_register_gsi(NULL, gsi, ACPI_LEVEL_SENSITIVE, ACPI_ACTIVE_HIGH);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct fwnode_handle *lpic_get_gsi_domain_id(u32 gsi)
|
||||
{
|
||||
int id;
|
||||
struct fwnode_handle *domain_handle = NULL;
|
||||
|
||||
switch (gsi) {
|
||||
case GSI_MIN_CPU_IRQ ... GSI_MAX_CPU_IRQ:
|
||||
if (liointc_handle)
|
||||
domain_handle = liointc_handle;
|
||||
break;
|
||||
|
||||
case GSI_MIN_LPC_IRQ ... GSI_MAX_LPC_IRQ:
|
||||
if (pch_lpc_handle)
|
||||
domain_handle = pch_lpc_handle;
|
||||
break;
|
||||
|
||||
case GSI_MIN_PCH_IRQ ... GSI_MAX_PCH_IRQ:
|
||||
id = find_pch_pic(gsi);
|
||||
if (id >= 0 && pch_pic_handle[id])
|
||||
domain_handle = pch_pic_handle[id];
|
||||
break;
|
||||
}
|
||||
|
||||
return domain_handle;
|
||||
}
|
||||
|
||||
static void mask_loongarch_irq(struct irq_data *d)
|
||||
{
|
||||
clear_csr_ecfg(ECFGF(d->hwirq));
|
||||
}
|
||||
|
||||
static void unmask_loongarch_irq(struct irq_data *d)
|
||||
{
|
||||
set_csr_ecfg(ECFGF(d->hwirq));
|
||||
}
|
||||
|
||||
static struct irq_chip cpu_irq_controller = {
|
||||
.name = "CPUINTC",
|
||||
.irq_mask = mask_loongarch_irq,
|
||||
.irq_unmask = unmask_loongarch_irq,
|
||||
};
|
||||
|
||||
static void handle_cpu_irq(struct pt_regs *regs)
|
||||
{
|
||||
int hwirq;
|
||||
unsigned int estat = read_csr_estat() & CSR_ESTAT_IS;
|
||||
|
||||
while ((hwirq = ffs(estat))) {
|
||||
estat &= ~BIT(hwirq - 1);
|
||||
generic_handle_domain_irq(irq_domain, hwirq - 1);
|
||||
}
|
||||
}
|
||||
|
||||
static int loongarch_cpu_intc_map(struct irq_domain *d, unsigned int irq,
|
||||
irq_hw_number_t hwirq)
|
||||
{
|
||||
irq_set_noprobe(irq);
|
||||
irq_set_chip_and_handler(irq, &cpu_irq_controller, handle_percpu_irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct irq_domain_ops loongarch_cpu_intc_irq_domain_ops = {
|
||||
.map = loongarch_cpu_intc_map,
|
||||
.xlate = irq_domain_xlate_onecell,
|
||||
};
|
||||
|
||||
static int __init
|
||||
liointc_parse_madt(union acpi_subtable_headers *header,
|
||||
const unsigned long end)
|
||||
{
|
||||
struct acpi_madt_lio_pic *liointc_entry = (struct acpi_madt_lio_pic *)header;
|
||||
|
||||
return liointc_acpi_init(irq_domain, liointc_entry);
|
||||
}
|
||||
|
||||
static int __init
|
||||
eiointc_parse_madt(union acpi_subtable_headers *header,
|
||||
const unsigned long end)
|
||||
{
|
||||
struct acpi_madt_eio_pic *eiointc_entry = (struct acpi_madt_eio_pic *)header;
|
||||
|
||||
return eiointc_acpi_init(irq_domain, eiointc_entry);
|
||||
}
|
||||
|
||||
static int __init acpi_cascade_irqdomain_init(void)
|
||||
{
|
||||
acpi_table_parse_madt(ACPI_MADT_TYPE_LIO_PIC,
|
||||
liointc_parse_madt, 0);
|
||||
acpi_table_parse_madt(ACPI_MADT_TYPE_EIO_PIC,
|
||||
eiointc_parse_madt, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init cpuintc_acpi_init(union acpi_subtable_headers *header,
|
||||
const unsigned long end)
|
||||
{
|
||||
if (irq_domain)
|
||||
return 0;
|
||||
|
||||
/* Mask interrupts. */
|
||||
clear_csr_ecfg(ECFG0_IM);
|
||||
clear_csr_estat(ESTATF_IP);
|
||||
|
||||
cpuintc_handle = irq_domain_alloc_fwnode(NULL);
|
||||
irq_domain = irq_domain_create_linear(cpuintc_handle, EXCCODE_INT_NUM,
|
||||
&loongarch_cpu_intc_irq_domain_ops, NULL);
|
||||
|
||||
if (!irq_domain)
|
||||
panic("Failed to add irqdomain for LoongArch CPU");
|
||||
|
||||
set_handle_irq(&handle_cpu_irq);
|
||||
acpi_set_irq_model(ACPI_IRQ_MODEL_LPIC, lpic_get_gsi_domain_id);
|
||||
acpi_set_gsi_to_irq_fallback(lpic_gsi_to_irq);
|
||||
acpi_cascade_irqdomain_init();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
IRQCHIP_ACPI_DECLARE(cpuintc_v1, ACPI_MADT_TYPE_CORE_PIC,
|
||||
NULL, ACPI_MADT_CORE_PIC_VERSION_V1, cpuintc_acpi_init);
|
395
drivers/irqchip/irq-loongson-eiointc.c
Normal file
395
drivers/irqchip/irq-loongson-eiointc.c
Normal file
@ -0,0 +1,395 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Loongson Extend I/O Interrupt Controller support
|
||||
*
|
||||
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "eiointc: " fmt
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqchip.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/irqchip/chained_irq.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_platform.h>
|
||||
|
||||
#define EIOINTC_REG_NODEMAP 0x14a0
|
||||
#define EIOINTC_REG_IPMAP 0x14c0
|
||||
#define EIOINTC_REG_ENABLE 0x1600
|
||||
#define EIOINTC_REG_BOUNCE 0x1680
|
||||
#define EIOINTC_REG_ISR 0x1800
|
||||
#define EIOINTC_REG_ROUTE 0x1c00
|
||||
|
||||
#define VEC_REG_COUNT 4
|
||||
#define VEC_COUNT_PER_REG 64
|
||||
#define VEC_COUNT (VEC_REG_COUNT * VEC_COUNT_PER_REG)
|
||||
#define VEC_REG_IDX(irq_id) ((irq_id) / VEC_COUNT_PER_REG)
|
||||
#define VEC_REG_BIT(irq_id) ((irq_id) % VEC_COUNT_PER_REG)
|
||||
#define EIOINTC_ALL_ENABLE 0xffffffff
|
||||
|
||||
#define MAX_EIO_NODES (NR_CPUS / CORES_PER_EIO_NODE)
|
||||
|
||||
static int nr_pics;
|
||||
|
||||
struct eiointc_priv {
|
||||
u32 node;
|
||||
nodemask_t node_map;
|
||||
cpumask_t cpuspan_map;
|
||||
struct fwnode_handle *domain_handle;
|
||||
struct irq_domain *eiointc_domain;
|
||||
};
|
||||
|
||||
static struct eiointc_priv *eiointc_priv[MAX_IO_PICS];
|
||||
|
||||
static void eiointc_enable(void)
|
||||
{
|
||||
uint64_t misc;
|
||||
|
||||
misc = iocsr_read64(LOONGARCH_IOCSR_MISC_FUNC);
|
||||
misc |= IOCSR_MISC_FUNC_EXT_IOI_EN;
|
||||
iocsr_write64(misc, LOONGARCH_IOCSR_MISC_FUNC);
|
||||
}
|
||||
|
||||
static int cpu_to_eio_node(int cpu)
|
||||
{
|
||||
return cpu_logical_map(cpu) / CORES_PER_EIO_NODE;
|
||||
}
|
||||
|
||||
static void eiointc_set_irq_route(int pos, unsigned int cpu, unsigned int mnode, nodemask_t *node_map)
|
||||
{
|
||||
int i, node, cpu_node, route_node;
|
||||
unsigned char coremap;
|
||||
uint32_t pos_off, data, data_byte, data_mask;
|
||||
|
||||
pos_off = pos & ~3;
|
||||
data_byte = pos & 3;
|
||||
data_mask = ~BIT_MASK(data_byte) & 0xf;
|
||||
|
||||
/* Calculate node and coremap of target irq */
|
||||
cpu_node = cpu_logical_map(cpu) / CORES_PER_EIO_NODE;
|
||||
coremap = BIT(cpu_logical_map(cpu) % CORES_PER_EIO_NODE);
|
||||
|
||||
for_each_online_cpu(i) {
|
||||
node = cpu_to_eio_node(i);
|
||||
if (!node_isset(node, *node_map))
|
||||
continue;
|
||||
|
||||
/* EIO node 0 is in charge of inter-node interrupt dispatch */
|
||||
route_node = (node == mnode) ? cpu_node : node;
|
||||
data = ((coremap | (route_node << 4)) << (data_byte * 8));
|
||||
csr_any_send(EIOINTC_REG_ROUTE + pos_off, data, data_mask, node * CORES_PER_EIO_NODE);
|
||||
}
|
||||
}
|
||||
|
||||
static DEFINE_RAW_SPINLOCK(affinity_lock);
|
||||
|
||||
static int eiointc_set_irq_affinity(struct irq_data *d, const struct cpumask *affinity, bool force)
|
||||
{
|
||||
unsigned int cpu;
|
||||
unsigned long flags;
|
||||
uint32_t vector, regaddr;
|
||||
struct cpumask intersect_affinity;
|
||||
struct eiointc_priv *priv = d->domain->host_data;
|
||||
|
||||
raw_spin_lock_irqsave(&affinity_lock, flags);
|
||||
|
||||
cpumask_and(&intersect_affinity, affinity, cpu_online_mask);
|
||||
cpumask_and(&intersect_affinity, &intersect_affinity, &priv->cpuspan_map);
|
||||
|
||||
if (cpumask_empty(&intersect_affinity)) {
|
||||
raw_spin_unlock_irqrestore(&affinity_lock, flags);
|
||||
return -EINVAL;
|
||||
}
|
||||
cpu = cpumask_first(&intersect_affinity);
|
||||
|
||||
vector = d->hwirq;
|
||||
regaddr = EIOINTC_REG_ENABLE + ((vector >> 5) << 2);
|
||||
|
||||
/* Mask target vector */
|
||||
csr_any_send(regaddr, EIOINTC_ALL_ENABLE & (~BIT(vector & 0x1F)), 0x0, 0);
|
||||
/* Set route for target vector */
|
||||
eiointc_set_irq_route(vector, cpu, priv->node, &priv->node_map);
|
||||
/* Unmask target vector */
|
||||
csr_any_send(regaddr, EIOINTC_ALL_ENABLE, 0x0, 0);
|
||||
|
||||
irq_data_update_effective_affinity(d, cpumask_of(cpu));
|
||||
|
||||
raw_spin_unlock_irqrestore(&affinity_lock, flags);
|
||||
|
||||
return IRQ_SET_MASK_OK;
|
||||
}
|
||||
|
||||
static int eiointc_index(int node)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nr_pics; i++) {
|
||||
if (node_isset(node, eiointc_priv[i]->node_map))
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int eiointc_router_init(unsigned int cpu)
|
||||
{
|
||||
int i, bit;
|
||||
uint32_t data;
|
||||
uint32_t node = cpu_to_eio_node(cpu);
|
||||
uint32_t index = eiointc_index(node);
|
||||
|
||||
if (index < 0) {
|
||||
pr_err("Error: invalid nodemap!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((cpu_logical_map(cpu) % CORES_PER_EIO_NODE) == 0) {
|
||||
eiointc_enable();
|
||||
|
||||
for (i = 0; i < VEC_COUNT / 32; i++) {
|
||||
data = (((1 << (i * 2 + 1)) << 16) | (1 << (i * 2)));
|
||||
iocsr_write32(data, EIOINTC_REG_NODEMAP + i * 4);
|
||||
}
|
||||
|
||||
for (i = 0; i < VEC_COUNT / 32 / 4; i++) {
|
||||
bit = BIT(1 + index); /* Route to IP[1 + index] */
|
||||
data = bit | (bit << 8) | (bit << 16) | (bit << 24);
|
||||
iocsr_write32(data, EIOINTC_REG_IPMAP + i * 4);
|
||||
}
|
||||
|
||||
for (i = 0; i < VEC_COUNT / 4; i++) {
|
||||
/* Route to Node-0 Core-0 */
|
||||
if (index == 0)
|
||||
bit = BIT(cpu_logical_map(0));
|
||||
else
|
||||
bit = (eiointc_priv[index]->node << 4) | 1;
|
||||
|
||||
data = bit | (bit << 8) | (bit << 16) | (bit << 24);
|
||||
iocsr_write32(data, EIOINTC_REG_ROUTE + i * 4);
|
||||
}
|
||||
|
||||
for (i = 0; i < VEC_COUNT / 32; i++) {
|
||||
data = 0xffffffff;
|
||||
iocsr_write32(data, EIOINTC_REG_ENABLE + i * 4);
|
||||
iocsr_write32(data, EIOINTC_REG_BOUNCE + i * 4);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void eiointc_irq_dispatch(struct irq_desc *desc)
|
||||
{
|
||||
int i;
|
||||
u64 pending;
|
||||
bool handled = false;
|
||||
struct irq_chip *chip = irq_desc_get_chip(desc);
|
||||
struct eiointc_priv *priv = irq_desc_get_handler_data(desc);
|
||||
|
||||
chained_irq_enter(chip, desc);
|
||||
|
||||
for (i = 0; i < VEC_REG_COUNT; i++) {
|
||||
pending = iocsr_read64(EIOINTC_REG_ISR + (i << 3));
|
||||
iocsr_write64(pending, EIOINTC_REG_ISR + (i << 3));
|
||||
while (pending) {
|
||||
int bit = __ffs(pending);
|
||||
int irq = bit + VEC_COUNT_PER_REG * i;
|
||||
|
||||
generic_handle_domain_irq(priv->eiointc_domain, irq);
|
||||
pending &= ~BIT(bit);
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!handled)
|
||||
spurious_interrupt();
|
||||
|
||||
chained_irq_exit(chip, desc);
|
||||
}
|
||||
|
||||
static void eiointc_ack_irq(struct irq_data *d)
|
||||
{
|
||||
}
|
||||
|
||||
static void eiointc_mask_irq(struct irq_data *d)
|
||||
{
|
||||
}
|
||||
|
||||
static void eiointc_unmask_irq(struct irq_data *d)
|
||||
{
|
||||
}
|
||||
|
||||
static struct irq_chip eiointc_irq_chip = {
|
||||
.name = "EIOINTC",
|
||||
.irq_ack = eiointc_ack_irq,
|
||||
.irq_mask = eiointc_mask_irq,
|
||||
.irq_unmask = eiointc_unmask_irq,
|
||||
.irq_set_affinity = eiointc_set_irq_affinity,
|
||||
};
|
||||
|
||||
static int eiointc_domain_alloc(struct irq_domain *domain, unsigned int virq,
|
||||
unsigned int nr_irqs, void *arg)
|
||||
{
|
||||
int ret;
|
||||
unsigned int i, type;
|
||||
unsigned long hwirq = 0;
|
||||
struct eiointc *priv = domain->host_data;
|
||||
|
||||
ret = irq_domain_translate_onecell(domain, arg, &hwirq, &type);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < nr_irqs; i++) {
|
||||
irq_domain_set_info(domain, virq + i, hwirq + i, &eiointc_irq_chip,
|
||||
priv, handle_edge_irq, NULL, NULL);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void eiointc_domain_free(struct irq_domain *domain, unsigned int virq,
|
||||
unsigned int nr_irqs)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nr_irqs; i++) {
|
||||
struct irq_data *d = irq_domain_get_irq_data(domain, virq + i);
|
||||
|
||||
irq_set_handler(virq + i, NULL);
|
||||
irq_domain_reset_irq_data(d);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct irq_domain_ops eiointc_domain_ops = {
|
||||
.translate = irq_domain_translate_onecell,
|
||||
.alloc = eiointc_domain_alloc,
|
||||
.free = eiointc_domain_free,
|
||||
};
|
||||
|
||||
static void acpi_set_vec_parent(int node, struct irq_domain *parent, struct acpi_vector_group *vec_group)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (cpu_has_flatmode)
|
||||
node = cpu_to_node(node * CORES_PER_EIO_NODE);
|
||||
|
||||
for (i = 0; i < MAX_IO_PICS; i++) {
|
||||
if (node == vec_group[i].node) {
|
||||
vec_group[i].parent = parent;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct irq_domain *acpi_get_vec_parent(int node, struct acpi_vector_group *vec_group)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_IO_PICS; i++) {
|
||||
if (node == vec_group[i].node)
|
||||
return vec_group[i].parent;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int __init
|
||||
pch_pic_parse_madt(union acpi_subtable_headers *header,
|
||||
const unsigned long end)
|
||||
{
|
||||
struct acpi_madt_bio_pic *pchpic_entry = (struct acpi_madt_bio_pic *)header;
|
||||
unsigned int node = (pchpic_entry->address >> 44) & 0xf;
|
||||
struct irq_domain *parent = acpi_get_vec_parent(node, pch_group);
|
||||
|
||||
if (parent)
|
||||
return pch_pic_acpi_init(parent, pchpic_entry);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int __init
|
||||
pch_msi_parse_madt(union acpi_subtable_headers *header,
|
||||
const unsigned long end)
|
||||
{
|
||||
struct acpi_madt_msi_pic *pchmsi_entry = (struct acpi_madt_msi_pic *)header;
|
||||
struct irq_domain *parent = acpi_get_vec_parent(eiointc_priv[nr_pics - 1]->node, msi_group);
|
||||
|
||||
if (parent)
|
||||
return pch_msi_acpi_init(parent, pchmsi_entry);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int __init acpi_cascade_irqdomain_init(void)
|
||||
{
|
||||
acpi_table_parse_madt(ACPI_MADT_TYPE_BIO_PIC,
|
||||
pch_pic_parse_madt, 0);
|
||||
acpi_table_parse_madt(ACPI_MADT_TYPE_MSI_PIC,
|
||||
pch_msi_parse_madt, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __init eiointc_acpi_init(struct irq_domain *parent,
|
||||
struct acpi_madt_eio_pic *acpi_eiointc)
|
||||
{
|
||||
int i, parent_irq;
|
||||
unsigned long node_map;
|
||||
struct eiointc_priv *priv;
|
||||
|
||||
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->domain_handle = irq_domain_alloc_fwnode((phys_addr_t *)acpi_eiointc);
|
||||
if (!priv->domain_handle) {
|
||||
pr_err("Unable to allocate domain handle\n");
|
||||
goto out_free_priv;
|
||||
}
|
||||
|
||||
priv->node = acpi_eiointc->node;
|
||||
node_map = acpi_eiointc->node_map ? : -1ULL;
|
||||
|
||||
for_each_possible_cpu(i) {
|
||||
if (node_map & (1ULL << cpu_to_eio_node(i))) {
|
||||
node_set(cpu_to_eio_node(i), priv->node_map);
|
||||
cpumask_or(&priv->cpuspan_map, &priv->cpuspan_map, cpumask_of(i));
|
||||
}
|
||||
}
|
||||
|
||||
/* Setup IRQ domain */
|
||||
priv->eiointc_domain = irq_domain_create_linear(priv->domain_handle, VEC_COUNT,
|
||||
&eiointc_domain_ops, priv);
|
||||
if (!priv->eiointc_domain) {
|
||||
pr_err("loongson-eiointc: cannot add IRQ domain\n");
|
||||
goto out_free_handle;
|
||||
}
|
||||
|
||||
eiointc_priv[nr_pics++] = priv;
|
||||
|
||||
eiointc_router_init(0);
|
||||
|
||||
parent_irq = irq_create_mapping(parent, acpi_eiointc->cascade);
|
||||
irq_set_chained_handler_and_data(parent_irq, eiointc_irq_dispatch, priv);
|
||||
|
||||
cpuhp_setup_state_nocalls(CPUHP_AP_IRQ_LOONGARCH_STARTING,
|
||||
"irqchip/loongarch/intc:starting",
|
||||
eiointc_router_init, NULL);
|
||||
|
||||
acpi_set_vec_parent(acpi_eiointc->node, priv->eiointc_domain, pch_group);
|
||||
acpi_set_vec_parent(acpi_eiointc->node, priv->eiointc_domain, msi_group);
|
||||
acpi_cascade_irqdomain_init();
|
||||
|
||||
return 0;
|
||||
|
||||
out_free_handle:
|
||||
irq_domain_free_fwnode(priv->domain_handle);
|
||||
priv->domain_handle = NULL;
|
||||
out_free_priv:
|
||||
kfree(priv);
|
||||
|
||||
return -ENOMEM;
|
||||
}
|
@ -23,7 +23,7 @@
|
||||
#endif
|
||||
|
||||
#define LIOINTC_CHIP_IRQ 32
|
||||
#define LIOINTC_NUM_PARENT 4
|
||||
#define LIOINTC_NUM_PARENT 4
|
||||
#define LIOINTC_NUM_CORES 4
|
||||
|
||||
#define LIOINTC_INTC_CHIP_START 0x20
|
||||
@ -58,6 +58,8 @@ struct liointc_priv {
|
||||
bool has_lpc_irq_errata;
|
||||
};
|
||||
|
||||
struct fwnode_handle *liointc_handle;
|
||||
|
||||
static void liointc_chained_handle_irq(struct irq_desc *desc)
|
||||
{
|
||||
struct liointc_handler_data *handler = irq_desc_get_handler_data(desc);
|
||||
@ -153,97 +155,79 @@ static void liointc_resume(struct irq_chip_generic *gc)
|
||||
irq_gc_unlock_irqrestore(gc, flags);
|
||||
}
|
||||
|
||||
static const char * const parent_names[] = {"int0", "int1", "int2", "int3"};
|
||||
static const char * const core_reg_names[] = {"isr0", "isr1", "isr2", "isr3"};
|
||||
static int parent_irq[LIOINTC_NUM_PARENT];
|
||||
static u32 parent_int_map[LIOINTC_NUM_PARENT];
|
||||
static const char *const parent_names[] = {"int0", "int1", "int2", "int3"};
|
||||
static const char *const core_reg_names[] = {"isr0", "isr1", "isr2", "isr3"};
|
||||
|
||||
static void __iomem *liointc_get_reg_byname(struct device_node *node,
|
||||
const char *name)
|
||||
static int liointc_domain_xlate(struct irq_domain *d, struct device_node *ctrlr,
|
||||
const u32 *intspec, unsigned int intsize,
|
||||
unsigned long *out_hwirq, unsigned int *out_type)
|
||||
{
|
||||
int index = of_property_match_string(node, "reg-names", name);
|
||||
|
||||
if (index < 0)
|
||||
return NULL;
|
||||
|
||||
return of_iomap(node, index);
|
||||
if (WARN_ON(intsize < 1))
|
||||
return -EINVAL;
|
||||
*out_hwirq = intspec[0] - GSI_MIN_CPU_IRQ;
|
||||
*out_type = IRQ_TYPE_NONE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init liointc_of_init(struct device_node *node,
|
||||
struct device_node *parent)
|
||||
static const struct irq_domain_ops acpi_irq_gc_ops = {
|
||||
.map = irq_map_generic_chip,
|
||||
.unmap = irq_unmap_generic_chip,
|
||||
.xlate = liointc_domain_xlate,
|
||||
};
|
||||
|
||||
static int liointc_init(phys_addr_t addr, unsigned long size, int revision,
|
||||
struct fwnode_handle *domain_handle, struct device_node *node)
|
||||
{
|
||||
int i, err;
|
||||
void __iomem *base;
|
||||
struct irq_chip_type *ct;
|
||||
struct irq_chip_generic *gc;
|
||||
struct irq_domain *domain;
|
||||
struct irq_chip_type *ct;
|
||||
struct liointc_priv *priv;
|
||||
void __iomem *base;
|
||||
u32 of_parent_int_map[LIOINTC_NUM_PARENT];
|
||||
int parent_irq[LIOINTC_NUM_PARENT];
|
||||
bool have_parent = FALSE;
|
||||
int sz, i, err = 0;
|
||||
|
||||
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
if (of_device_is_compatible(node, "loongson,liointc-2.0")) {
|
||||
base = liointc_get_reg_byname(node, "main");
|
||||
if (!base) {
|
||||
err = -ENODEV;
|
||||
goto out_free_priv;
|
||||
}
|
||||
base = ioremap(addr, size);
|
||||
if (!base)
|
||||
goto out_free_priv;
|
||||
|
||||
for (i = 0; i < LIOINTC_NUM_CORES; i++)
|
||||
priv->core_isr[i] = liointc_get_reg_byname(node, core_reg_names[i]);
|
||||
if (!priv->core_isr[0]) {
|
||||
err = -ENODEV;
|
||||
goto out_iounmap_base;
|
||||
}
|
||||
} else {
|
||||
base = of_iomap(node, 0);
|
||||
if (!base) {
|
||||
err = -ENODEV;
|
||||
goto out_free_priv;
|
||||
}
|
||||
|
||||
for (i = 0; i < LIOINTC_NUM_CORES; i++)
|
||||
priv->core_isr[i] = base + LIOINTC_REG_INTC_STATUS;
|
||||
}
|
||||
|
||||
for (i = 0; i < LIOINTC_NUM_PARENT; i++) {
|
||||
parent_irq[i] = of_irq_get_byname(node, parent_names[i]);
|
||||
if (parent_irq[i] > 0)
|
||||
have_parent = TRUE;
|
||||
}
|
||||
if (!have_parent) {
|
||||
err = -ENODEV;
|
||||
goto out_iounmap_isr;
|
||||
}
|
||||
|
||||
sz = of_property_read_variable_u32_array(node,
|
||||
"loongson,parent_int_map",
|
||||
&of_parent_int_map[0],
|
||||
LIOINTC_NUM_PARENT,
|
||||
LIOINTC_NUM_PARENT);
|
||||
if (sz < 4) {
|
||||
pr_err("loongson-liointc: No parent_int_map\n");
|
||||
err = -ENODEV;
|
||||
goto out_iounmap_isr;
|
||||
}
|
||||
for (i = 0; i < LIOINTC_NUM_CORES; i++)
|
||||
priv->core_isr[i] = base + LIOINTC_REG_INTC_STATUS;
|
||||
|
||||
for (i = 0; i < LIOINTC_NUM_PARENT; i++)
|
||||
priv->handler[i].parent_int_map = of_parent_int_map[i];
|
||||
priv->handler[i].parent_int_map = parent_int_map[i];
|
||||
|
||||
if (revision > 1) {
|
||||
for (i = 0; i < LIOINTC_NUM_CORES; i++) {
|
||||
int index = of_property_match_string(node,
|
||||
"reg-names", core_reg_names[i]);
|
||||
|
||||
if (index < 0)
|
||||
return -EINVAL;
|
||||
|
||||
priv->core_isr[i] = of_iomap(node, index);
|
||||
}
|
||||
}
|
||||
|
||||
/* Setup IRQ domain */
|
||||
domain = irq_domain_add_linear(node, 32,
|
||||
if (!acpi_disabled)
|
||||
domain = irq_domain_create_linear(domain_handle, LIOINTC_CHIP_IRQ,
|
||||
&acpi_irq_gc_ops, priv);
|
||||
else
|
||||
domain = irq_domain_create_linear(domain_handle, LIOINTC_CHIP_IRQ,
|
||||
&irq_generic_chip_ops, priv);
|
||||
if (!domain) {
|
||||
pr_err("loongson-liointc: cannot add IRQ domain\n");
|
||||
err = -EINVAL;
|
||||
goto out_iounmap_isr;
|
||||
goto out_iounmap;
|
||||
}
|
||||
|
||||
err = irq_alloc_domain_generic_chips(domain, 32, 1,
|
||||
node->full_name, handle_level_irq,
|
||||
IRQ_NOPROBE, 0, 0);
|
||||
err = irq_alloc_domain_generic_chips(domain, LIOINTC_CHIP_IRQ, 1,
|
||||
(node ? node->full_name : "LIOINTC"),
|
||||
handle_level_irq, 0, IRQ_NOPROBE, 0);
|
||||
if (err) {
|
||||
pr_err("loongson-liointc: unable to register IRQ domain\n");
|
||||
goto out_free_domain;
|
||||
@ -299,24 +283,93 @@ static int __init liointc_of_init(struct device_node *node,
|
||||
liointc_chained_handle_irq, &priv->handler[i]);
|
||||
}
|
||||
|
||||
liointc_handle = domain_handle;
|
||||
return 0;
|
||||
|
||||
out_free_domain:
|
||||
irq_domain_remove(domain);
|
||||
out_iounmap_isr:
|
||||
for (i = 0; i < LIOINTC_NUM_CORES; i++) {
|
||||
if (!priv->core_isr[i])
|
||||
continue;
|
||||
iounmap(priv->core_isr[i]);
|
||||
}
|
||||
out_iounmap_base:
|
||||
out_iounmap:
|
||||
iounmap(base);
|
||||
out_free_priv:
|
||||
kfree(priv);
|
||||
|
||||
return err;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
|
||||
static int __init liointc_of_init(struct device_node *node,
|
||||
struct device_node *parent)
|
||||
{
|
||||
bool have_parent = FALSE;
|
||||
int sz, i, index, revision, err = 0;
|
||||
struct resource res;
|
||||
|
||||
if (!of_device_is_compatible(node, "loongson,liointc-2.0")) {
|
||||
index = 0;
|
||||
revision = 1;
|
||||
} else {
|
||||
index = of_property_match_string(node, "reg-names", "main");
|
||||
revision = 2;
|
||||
}
|
||||
|
||||
if (of_address_to_resource(node, index, &res))
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < LIOINTC_NUM_PARENT; i++) {
|
||||
parent_irq[i] = of_irq_get_byname(node, parent_names[i]);
|
||||
if (parent_irq[i] > 0)
|
||||
have_parent = TRUE;
|
||||
}
|
||||
if (!have_parent)
|
||||
return -ENODEV;
|
||||
|
||||
sz = of_property_read_variable_u32_array(node,
|
||||
"loongson,parent_int_map",
|
||||
&parent_int_map[0],
|
||||
LIOINTC_NUM_PARENT,
|
||||
LIOINTC_NUM_PARENT);
|
||||
if (sz < 4) {
|
||||
pr_err("loongson-liointc: No parent_int_map\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
err = liointc_init(res.start, resource_size(&res),
|
||||
revision, of_node_to_fwnode(node), node);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
IRQCHIP_DECLARE(loongson_liointc_1_0, "loongson,liointc-1.0", liointc_of_init);
|
||||
IRQCHIP_DECLARE(loongson_liointc_1_0a, "loongson,liointc-1.0a", liointc_of_init);
|
||||
IRQCHIP_DECLARE(loongson_liointc_2_0, "loongson,liointc-2.0", liointc_of_init);
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
int __init liointc_acpi_init(struct irq_domain *parent, struct acpi_madt_lio_pic *acpi_liointc)
|
||||
{
|
||||
int ret;
|
||||
struct fwnode_handle *domain_handle;
|
||||
|
||||
parent_int_map[0] = acpi_liointc->cascade_map[0];
|
||||
parent_int_map[1] = acpi_liointc->cascade_map[1];
|
||||
|
||||
parent_irq[0] = irq_create_mapping(parent, acpi_liointc->cascade[0]);
|
||||
parent_irq[1] = irq_create_mapping(parent, acpi_liointc->cascade[1]);
|
||||
|
||||
domain_handle = irq_domain_alloc_fwnode((phys_addr_t *)acpi_liointc);
|
||||
if (!domain_handle) {
|
||||
pr_err("Unable to allocate domain handle\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
ret = liointc_init(acpi_liointc->address, acpi_liointc->size,
|
||||
1, domain_handle, NULL);
|
||||
if (ret)
|
||||
irq_domain_free_fwnode(domain_handle);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
205
drivers/irqchip/irq-loongson-pch-lpc.c
Normal file
205
drivers/irqchip/irq-loongson-pch-lpc.c
Normal file
@ -0,0 +1,205 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Loongson LPC Interrupt Controller support
|
||||
*
|
||||
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "lpc: " fmt
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqchip.h>
|
||||
#include <linux/irqchip/chained_irq.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/kernel.h>
|
||||
|
||||
/* Registers */
|
||||
#define LPC_INT_CTL 0x00
|
||||
#define LPC_INT_ENA 0x04
|
||||
#define LPC_INT_STS 0x08
|
||||
#define LPC_INT_CLR 0x0c
|
||||
#define LPC_INT_POL 0x10
|
||||
#define LPC_COUNT 16
|
||||
|
||||
/* LPC_INT_CTL */
|
||||
#define LPC_INT_CTL_EN BIT(31)
|
||||
|
||||
struct pch_lpc {
|
||||
void __iomem *base;
|
||||
struct irq_domain *lpc_domain;
|
||||
raw_spinlock_t lpc_lock;
|
||||
u32 saved_reg_ctl;
|
||||
u32 saved_reg_ena;
|
||||
u32 saved_reg_pol;
|
||||
};
|
||||
|
||||
struct fwnode_handle *pch_lpc_handle;
|
||||
|
||||
static void lpc_irq_ack(struct irq_data *d)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct pch_lpc *priv = d->domain->host_data;
|
||||
|
||||
raw_spin_lock_irqsave(&priv->lpc_lock, flags);
|
||||
writel(0x1 << d->hwirq, priv->base + LPC_INT_CLR);
|
||||
raw_spin_unlock_irqrestore(&priv->lpc_lock, flags);
|
||||
}
|
||||
|
||||
static void lpc_irq_mask(struct irq_data *d)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct pch_lpc *priv = d->domain->host_data;
|
||||
|
||||
raw_spin_lock_irqsave(&priv->lpc_lock, flags);
|
||||
writel(readl(priv->base + LPC_INT_ENA) & (~(0x1 << (d->hwirq))),
|
||||
priv->base + LPC_INT_ENA);
|
||||
raw_spin_unlock_irqrestore(&priv->lpc_lock, flags);
|
||||
}
|
||||
|
||||
static void lpc_irq_unmask(struct irq_data *d)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct pch_lpc *priv = d->domain->host_data;
|
||||
|
||||
raw_spin_lock_irqsave(&priv->lpc_lock, flags);
|
||||
writel(readl(priv->base + LPC_INT_ENA) | (0x1 << (d->hwirq)),
|
||||
priv->base + LPC_INT_ENA);
|
||||
raw_spin_unlock_irqrestore(&priv->lpc_lock, flags);
|
||||
}
|
||||
|
||||
static int lpc_irq_set_type(struct irq_data *d, unsigned int type)
|
||||
{
|
||||
u32 val;
|
||||
u32 mask = 0x1 << (d->hwirq);
|
||||
struct pch_lpc *priv = d->domain->host_data;
|
||||
|
||||
if (!(type & IRQ_TYPE_LEVEL_MASK))
|
||||
return 0;
|
||||
|
||||
val = readl(priv->base + LPC_INT_POL);
|
||||
|
||||
if (type == IRQ_TYPE_LEVEL_HIGH)
|
||||
val |= mask;
|
||||
else
|
||||
val &= ~mask;
|
||||
|
||||
writel(val, priv->base + LPC_INT_POL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct irq_chip pch_lpc_irq_chip = {
|
||||
.name = "PCH LPC",
|
||||
.irq_mask = lpc_irq_mask,
|
||||
.irq_unmask = lpc_irq_unmask,
|
||||
.irq_ack = lpc_irq_ack,
|
||||
.irq_set_type = lpc_irq_set_type,
|
||||
.flags = IRQCHIP_SKIP_SET_WAKE,
|
||||
};
|
||||
|
||||
static void lpc_irq_dispatch(struct irq_desc *desc)
|
||||
{
|
||||
u32 pending, bit;
|
||||
struct irq_chip *chip = irq_desc_get_chip(desc);
|
||||
struct pch_lpc *priv = irq_desc_get_handler_data(desc);
|
||||
|
||||
chained_irq_enter(chip, desc);
|
||||
|
||||
pending = readl(priv->base + LPC_INT_ENA);
|
||||
pending &= readl(priv->base + LPC_INT_STS);
|
||||
if (!pending)
|
||||
spurious_interrupt();
|
||||
|
||||
while (pending) {
|
||||
bit = __ffs(pending);
|
||||
|
||||
generic_handle_domain_irq(priv->lpc_domain, bit);
|
||||
pending &= ~BIT(bit);
|
||||
}
|
||||
chained_irq_exit(chip, desc);
|
||||
}
|
||||
|
||||
static int pch_lpc_map(struct irq_domain *d, unsigned int irq,
|
||||
irq_hw_number_t hw)
|
||||
{
|
||||
irq_set_chip_and_handler(irq, &pch_lpc_irq_chip, handle_level_irq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct irq_domain_ops pch_lpc_domain_ops = {
|
||||
.map = pch_lpc_map,
|
||||
.translate = irq_domain_translate_twocell,
|
||||
};
|
||||
|
||||
static void pch_lpc_reset(struct pch_lpc *priv)
|
||||
{
|
||||
/* Enable the LPC interrupt, bit31: en bit30: edge */
|
||||
writel(LPC_INT_CTL_EN, priv->base + LPC_INT_CTL);
|
||||
writel(0, priv->base + LPC_INT_ENA);
|
||||
/* Clear all 18-bit interrpt bit */
|
||||
writel(GENMASK(17, 0), priv->base + LPC_INT_CLR);
|
||||
}
|
||||
|
||||
static int pch_lpc_disabled(struct pch_lpc *priv)
|
||||
{
|
||||
return (readl(priv->base + LPC_INT_ENA) == 0xffffffff) &&
|
||||
(readl(priv->base + LPC_INT_STS) == 0xffffffff);
|
||||
}
|
||||
|
||||
int __init pch_lpc_acpi_init(struct irq_domain *parent,
|
||||
struct acpi_madt_lpc_pic *acpi_pchlpc)
|
||||
{
|
||||
int parent_irq;
|
||||
struct pch_lpc *priv;
|
||||
struct irq_fwspec fwspec;
|
||||
struct fwnode_handle *irq_handle;
|
||||
|
||||
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
raw_spin_lock_init(&priv->lpc_lock);
|
||||
|
||||
priv->base = ioremap(acpi_pchlpc->address, acpi_pchlpc->size);
|
||||
if (!priv->base)
|
||||
goto free_priv;
|
||||
|
||||
if (pch_lpc_disabled(priv)) {
|
||||
pr_err("Failed to get LPC status\n");
|
||||
goto iounmap_base;
|
||||
}
|
||||
|
||||
irq_handle = irq_domain_alloc_named_fwnode("lpcintc");
|
||||
if (!irq_handle) {
|
||||
pr_err("Unable to allocate domain handle\n");
|
||||
goto iounmap_base;
|
||||
}
|
||||
|
||||
priv->lpc_domain = irq_domain_create_linear(irq_handle, LPC_COUNT,
|
||||
&pch_lpc_domain_ops, priv);
|
||||
if (!priv->lpc_domain) {
|
||||
pr_err("Failed to create IRQ domain\n");
|
||||
goto free_irq_handle;
|
||||
}
|
||||
pch_lpc_reset(priv);
|
||||
|
||||
fwspec.fwnode = parent->fwnode;
|
||||
fwspec.param[0] = acpi_pchlpc->cascade + GSI_MIN_PCH_IRQ;
|
||||
fwspec.param[1] = IRQ_TYPE_LEVEL_HIGH;
|
||||
fwspec.param_count = 2;
|
||||
parent_irq = irq_create_fwspec_mapping(&fwspec);
|
||||
irq_set_chained_handler_and_data(parent_irq, lpc_irq_dispatch, priv);
|
||||
|
||||
pch_lpc_handle = irq_handle;
|
||||
return 0;
|
||||
|
||||
free_irq_handle:
|
||||
irq_domain_free_fwnode(irq_handle);
|
||||
iounmap_base:
|
||||
iounmap(priv->base);
|
||||
free_priv:
|
||||
kfree(priv);
|
||||
|
||||
return -ENOMEM;
|
||||
}
|
@ -15,6 +15,8 @@
|
||||
#include <linux/pci.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
static int nr_pics;
|
||||
|
||||
struct pch_msi_data {
|
||||
struct mutex msi_map_lock;
|
||||
phys_addr_t doorbell;
|
||||
@ -23,6 +25,8 @@ struct pch_msi_data {
|
||||
unsigned long *msi_map;
|
||||
};
|
||||
|
||||
static struct fwnode_handle *pch_msi_handle[MAX_IO_PICS];
|
||||
|
||||
static void pch_msi_mask_msi_irq(struct irq_data *d)
|
||||
{
|
||||
pci_msi_mask_irq(d);
|
||||
@ -154,12 +158,12 @@ static const struct irq_domain_ops pch_msi_middle_domain_ops = {
|
||||
};
|
||||
|
||||
static int pch_msi_init_domains(struct pch_msi_data *priv,
|
||||
struct device_node *node,
|
||||
struct irq_domain *parent)
|
||||
struct irq_domain *parent,
|
||||
struct fwnode_handle *domain_handle)
|
||||
{
|
||||
struct irq_domain *middle_domain, *msi_domain;
|
||||
|
||||
middle_domain = irq_domain_create_linear(of_node_to_fwnode(node),
|
||||
middle_domain = irq_domain_create_linear(domain_handle,
|
||||
priv->num_irqs,
|
||||
&pch_msi_middle_domain_ops,
|
||||
priv);
|
||||
@ -171,7 +175,7 @@ static int pch_msi_init_domains(struct pch_msi_data *priv,
|
||||
middle_domain->parent = parent;
|
||||
irq_domain_update_bus_token(middle_domain, DOMAIN_BUS_NEXUS);
|
||||
|
||||
msi_domain = pci_msi_create_irq_domain(of_node_to_fwnode(node),
|
||||
msi_domain = pci_msi_create_irq_domain(domain_handle,
|
||||
&pch_msi_domain_info,
|
||||
middle_domain);
|
||||
if (!msi_domain) {
|
||||
@ -183,19 +187,11 @@ static int pch_msi_init_domains(struct pch_msi_data *priv,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pch_msi_init(struct device_node *node,
|
||||
struct device_node *parent)
|
||||
static int pch_msi_init(phys_addr_t msg_address, int irq_base, int irq_count,
|
||||
struct irq_domain *parent_domain, struct fwnode_handle *domain_handle)
|
||||
{
|
||||
struct pch_msi_data *priv;
|
||||
struct irq_domain *parent_domain;
|
||||
struct resource res;
|
||||
int ret;
|
||||
|
||||
parent_domain = irq_find_host(parent);
|
||||
if (!parent_domain) {
|
||||
pr_err("Failed to find the parent domain\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
struct pch_msi_data *priv;
|
||||
|
||||
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
@ -203,48 +199,95 @@ static int pch_msi_init(struct device_node *node,
|
||||
|
||||
mutex_init(&priv->msi_map_lock);
|
||||
|
||||
ret = of_address_to_resource(node, 0, &res);
|
||||
if (ret) {
|
||||
pr_err("Failed to allocate resource\n");
|
||||
goto err_priv;
|
||||
}
|
||||
|
||||
priv->doorbell = res.start;
|
||||
|
||||
if (of_property_read_u32(node, "loongson,msi-base-vec",
|
||||
&priv->irq_first)) {
|
||||
pr_err("Unable to parse MSI vec base\n");
|
||||
ret = -EINVAL;
|
||||
goto err_priv;
|
||||
}
|
||||
|
||||
if (of_property_read_u32(node, "loongson,msi-num-vecs",
|
||||
&priv->num_irqs)) {
|
||||
pr_err("Unable to parse MSI vec number\n");
|
||||
ret = -EINVAL;
|
||||
goto err_priv;
|
||||
}
|
||||
priv->doorbell = msg_address;
|
||||
priv->irq_first = irq_base;
|
||||
priv->num_irqs = irq_count;
|
||||
|
||||
priv->msi_map = bitmap_zalloc(priv->num_irqs, GFP_KERNEL);
|
||||
if (!priv->msi_map) {
|
||||
ret = -ENOMEM;
|
||||
if (!priv->msi_map)
|
||||
goto err_priv;
|
||||
}
|
||||
|
||||
pr_debug("Registering %d MSIs, starting at %d\n",
|
||||
priv->num_irqs, priv->irq_first);
|
||||
|
||||
ret = pch_msi_init_domains(priv, node, parent_domain);
|
||||
ret = pch_msi_init_domains(priv, parent_domain, domain_handle);
|
||||
if (ret)
|
||||
goto err_map;
|
||||
|
||||
pch_msi_handle[nr_pics++] = domain_handle;
|
||||
return 0;
|
||||
|
||||
err_map:
|
||||
bitmap_free(priv->msi_map);
|
||||
err_priv:
|
||||
kfree(priv);
|
||||
return ret;
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
IRQCHIP_DECLARE(pch_msi, "loongson,pch-msi-1.0", pch_msi_init);
|
||||
#ifdef CONFIG_OF
|
||||
static int pch_msi_of_init(struct device_node *node, struct device_node *parent)
|
||||
{
|
||||
int err;
|
||||
int irq_base, irq_count;
|
||||
struct resource res;
|
||||
struct irq_domain *parent_domain;
|
||||
|
||||
parent_domain = irq_find_host(parent);
|
||||
if (!parent_domain) {
|
||||
pr_err("Failed to find the parent domain\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
if (of_address_to_resource(node, 0, &res)) {
|
||||
pr_err("Failed to allocate resource\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (of_property_read_u32(node, "loongson,msi-base-vec", &irq_base)) {
|
||||
pr_err("Unable to parse MSI vec base\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (of_property_read_u32(node, "loongson,msi-num-vecs", &irq_count)) {
|
||||
pr_err("Unable to parse MSI vec number\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = pch_msi_init(res.start, irq_base, irq_count, parent_domain, of_node_to_fwnode(node));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
IRQCHIP_DECLARE(pch_msi, "loongson,pch-msi-1.0", pch_msi_of_init);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
struct fwnode_handle *get_pch_msi_handle(int pci_segment)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_IO_PICS; i++) {
|
||||
if (msi_group[i].pci_segment == pci_segment)
|
||||
return pch_msi_handle[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int __init pch_msi_acpi_init(struct irq_domain *parent,
|
||||
struct acpi_madt_msi_pic *acpi_pchmsi)
|
||||
{
|
||||
int ret;
|
||||
struct fwnode_handle *domain_handle;
|
||||
|
||||
domain_handle = irq_domain_alloc_fwnode((phys_addr_t *)acpi_pchmsi);
|
||||
ret = pch_msi_init(acpi_pchmsi->msg_address, acpi_pchmsi->start,
|
||||
acpi_pchmsi->count, parent, domain_handle);
|
||||
if (ret < 0)
|
||||
irq_domain_free_fwnode(domain_handle);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
@ -33,13 +33,40 @@
|
||||
#define PIC_REG_IDX(irq_id) ((irq_id) / PIC_COUNT_PER_REG)
|
||||
#define PIC_REG_BIT(irq_id) ((irq_id) % PIC_COUNT_PER_REG)
|
||||
|
||||
static int nr_pics;
|
||||
|
||||
struct pch_pic {
|
||||
void __iomem *base;
|
||||
struct irq_domain *pic_domain;
|
||||
u32 ht_vec_base;
|
||||
raw_spinlock_t pic_lock;
|
||||
u32 vec_count;
|
||||
u32 gsi_base;
|
||||
};
|
||||
|
||||
static struct pch_pic *pch_pic_priv[MAX_IO_PICS];
|
||||
|
||||
struct fwnode_handle *pch_pic_handle[MAX_IO_PICS];
|
||||
|
||||
int find_pch_pic(u32 gsi)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Find the PCH_PIC that manages this GSI. */
|
||||
for (i = 0; i < MAX_IO_PICS; i++) {
|
||||
struct pch_pic *priv = pch_pic_priv[i];
|
||||
|
||||
if (!priv)
|
||||
return -1;
|
||||
|
||||
if (gsi >= priv->gsi_base && gsi < (priv->gsi_base + priv->vec_count))
|
||||
return i;
|
||||
}
|
||||
|
||||
pr_err("ERROR: Unable to locate PCH_PIC for GSI %d\n", gsi);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void pch_pic_bitset(struct pch_pic *priv, int offset, int bit)
|
||||
{
|
||||
u32 reg;
|
||||
@ -139,6 +166,28 @@ static struct irq_chip pch_pic_irq_chip = {
|
||||
.irq_set_type = pch_pic_set_type,
|
||||
};
|
||||
|
||||
static int pch_pic_domain_translate(struct irq_domain *d,
|
||||
struct irq_fwspec *fwspec,
|
||||
unsigned long *hwirq,
|
||||
unsigned int *type)
|
||||
{
|
||||
struct pch_pic *priv = d->host_data;
|
||||
struct device_node *of_node = to_of_node(fwspec->fwnode);
|
||||
|
||||
if (fwspec->param_count < 1)
|
||||
return -EINVAL;
|
||||
|
||||
if (of_node) {
|
||||
*hwirq = fwspec->param[0] + priv->ht_vec_base;
|
||||
*type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK;
|
||||
} else {
|
||||
*hwirq = fwspec->param[0] - priv->gsi_base;
|
||||
*type = IRQ_TYPE_NONE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pch_pic_alloc(struct irq_domain *domain, unsigned int virq,
|
||||
unsigned int nr_irqs, void *arg)
|
||||
{
|
||||
@ -149,13 +198,13 @@ static int pch_pic_alloc(struct irq_domain *domain, unsigned int virq,
|
||||
struct irq_fwspec parent_fwspec;
|
||||
struct pch_pic *priv = domain->host_data;
|
||||
|
||||
err = irq_domain_translate_twocell(domain, fwspec, &hwirq, &type);
|
||||
err = pch_pic_domain_translate(domain, fwspec, &hwirq, &type);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
parent_fwspec.fwnode = domain->parent->fwnode;
|
||||
parent_fwspec.param_count = 1;
|
||||
parent_fwspec.param[0] = hwirq + priv->ht_vec_base;
|
||||
parent_fwspec.param[0] = hwirq;
|
||||
|
||||
err = irq_domain_alloc_irqs_parent(domain, virq, 1, &parent_fwspec);
|
||||
if (err)
|
||||
@ -170,7 +219,7 @@ static int pch_pic_alloc(struct irq_domain *domain, unsigned int virq,
|
||||
}
|
||||
|
||||
static const struct irq_domain_ops pch_pic_domain_ops = {
|
||||
.translate = irq_domain_translate_twocell,
|
||||
.translate = pch_pic_domain_translate,
|
||||
.alloc = pch_pic_alloc,
|
||||
.free = irq_domain_free_irqs_parent,
|
||||
};
|
||||
@ -180,7 +229,7 @@ static void pch_pic_reset(struct pch_pic *priv)
|
||||
int i;
|
||||
|
||||
for (i = 0; i < PIC_COUNT; i++) {
|
||||
/* Write vectored ID */
|
||||
/* Write vector ID */
|
||||
writeb(priv->ht_vec_base + i, priv->base + PCH_INT_HTVEC(i));
|
||||
/* Hardcode route to HT0 Lo */
|
||||
writeb(1, priv->base + PCH_INT_ROUTE(i));
|
||||
@ -198,50 +247,37 @@ static void pch_pic_reset(struct pch_pic *priv)
|
||||
}
|
||||
}
|
||||
|
||||
static int pch_pic_of_init(struct device_node *node,
|
||||
struct device_node *parent)
|
||||
static int pch_pic_init(phys_addr_t addr, unsigned long size, int vec_base,
|
||||
struct irq_domain *parent_domain, struct fwnode_handle *domain_handle,
|
||||
u32 gsi_base)
|
||||
{
|
||||
struct pch_pic *priv;
|
||||
struct irq_domain *parent_domain;
|
||||
int err;
|
||||
|
||||
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
raw_spin_lock_init(&priv->pic_lock);
|
||||
priv->base = of_iomap(node, 0);
|
||||
if (!priv->base) {
|
||||
err = -ENOMEM;
|
||||
priv->base = ioremap(addr, size);
|
||||
if (!priv->base)
|
||||
goto free_priv;
|
||||
}
|
||||
|
||||
parent_domain = irq_find_host(parent);
|
||||
if (!parent_domain) {
|
||||
pr_err("Failed to find the parent domain\n");
|
||||
err = -ENXIO;
|
||||
goto iounmap_base;
|
||||
}
|
||||
|
||||
if (of_property_read_u32(node, "loongson,pic-base-vec",
|
||||
&priv->ht_vec_base)) {
|
||||
pr_err("Failed to determine pic-base-vec\n");
|
||||
err = -EINVAL;
|
||||
goto iounmap_base;
|
||||
}
|
||||
priv->ht_vec_base = vec_base;
|
||||
priv->vec_count = ((readq(priv->base) >> 48) & 0xff) + 1;
|
||||
priv->gsi_base = gsi_base;
|
||||
|
||||
priv->pic_domain = irq_domain_create_hierarchy(parent_domain, 0,
|
||||
PIC_COUNT,
|
||||
of_node_to_fwnode(node),
|
||||
&pch_pic_domain_ops,
|
||||
priv);
|
||||
priv->vec_count, domain_handle,
|
||||
&pch_pic_domain_ops, priv);
|
||||
|
||||
if (!priv->pic_domain) {
|
||||
pr_err("Failed to create IRQ domain\n");
|
||||
err = -ENOMEM;
|
||||
goto iounmap_base;
|
||||
}
|
||||
|
||||
pch_pic_reset(priv);
|
||||
pch_pic_handle[nr_pics] = domain_handle;
|
||||
pch_pic_priv[nr_pics++] = priv;
|
||||
|
||||
return 0;
|
||||
|
||||
@ -250,7 +286,86 @@ iounmap_base:
|
||||
free_priv:
|
||||
kfree(priv);
|
||||
|
||||
return err;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
|
||||
static int pch_pic_of_init(struct device_node *node,
|
||||
struct device_node *parent)
|
||||
{
|
||||
int err, vec_base;
|
||||
struct resource res;
|
||||
struct irq_domain *parent_domain;
|
||||
|
||||
if (of_address_to_resource(node, 0, &res))
|
||||
return -EINVAL;
|
||||
|
||||
parent_domain = irq_find_host(parent);
|
||||
if (!parent_domain) {
|
||||
pr_err("Failed to find the parent domain\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
if (of_property_read_u32(node, "loongson,pic-base-vec", &vec_base)) {
|
||||
pr_err("Failed to determine pic-base-vec\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = pch_pic_init(res.start, resource_size(&res), vec_base,
|
||||
parent_domain, of_node_to_fwnode(node), 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
IRQCHIP_DECLARE(pch_pic, "loongson,pch-pic-1.0", pch_pic_of_init);
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static int __init
|
||||
pch_lpc_parse_madt(union acpi_subtable_headers *header,
|
||||
const unsigned long end)
|
||||
{
|
||||
struct acpi_madt_lpc_pic *pchlpc_entry = (struct acpi_madt_lpc_pic *)header;
|
||||
|
||||
return pch_lpc_acpi_init(pch_pic_priv[0]->pic_domain, pchlpc_entry);
|
||||
}
|
||||
|
||||
static int __init acpi_cascade_irqdomain_init(void)
|
||||
{
|
||||
acpi_table_parse_madt(ACPI_MADT_TYPE_LPC_PIC,
|
||||
pch_lpc_parse_madt, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __init pch_pic_acpi_init(struct irq_domain *parent,
|
||||
struct acpi_madt_bio_pic *acpi_pchpic)
|
||||
{
|
||||
int ret, vec_base;
|
||||
struct fwnode_handle *domain_handle;
|
||||
|
||||
vec_base = acpi_pchpic->gsi_base - GSI_MIN_PCH_IRQ;
|
||||
|
||||
domain_handle = irq_domain_alloc_fwnode((phys_addr_t *)acpi_pchpic);
|
||||
if (!domain_handle) {
|
||||
pr_err("Unable to allocate domain handle\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = pch_pic_init(acpi_pchpic->address, acpi_pchpic->size,
|
||||
vec_base, parent, domain_handle, acpi_pchpic->gsi_base);
|
||||
|
||||
if (ret < 0) {
|
||||
irq_domain_free_fwnode(domain_handle);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (acpi_pchpic->id == 0)
|
||||
acpi_cascade_irqdomain_init();
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
@ -52,13 +52,15 @@ static DEFINE_PER_CPU_READ_MOSTLY(unsigned long[GIC_MAX_LONGS], pcpu_masks);
|
||||
|
||||
static DEFINE_SPINLOCK(gic_lock);
|
||||
static struct irq_domain *gic_irq_domain;
|
||||
static struct irq_domain *gic_ipi_domain;
|
||||
static int gic_shared_intrs;
|
||||
static unsigned int gic_cpu_pin;
|
||||
static unsigned int timer_cpu_pin;
|
||||
static struct irq_chip gic_level_irq_controller, gic_edge_irq_controller;
|
||||
|
||||
#ifdef CONFIG_GENERIC_IRQ_IPI
|
||||
static DECLARE_BITMAP(ipi_resrv, GIC_MAX_INTRS);
|
||||
static DECLARE_BITMAP(ipi_available, GIC_MAX_INTRS);
|
||||
#endif /* CONFIG_GENERIC_IRQ_IPI */
|
||||
|
||||
static struct gic_all_vpes_chip_data {
|
||||
u32 map;
|
||||
@ -472,9 +474,11 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int virq,
|
||||
u32 map;
|
||||
|
||||
if (hwirq >= GIC_SHARED_HWIRQ_BASE) {
|
||||
#ifdef CONFIG_GENERIC_IRQ_IPI
|
||||
/* verify that shared irqs don't conflict with an IPI irq */
|
||||
if (test_bit(GIC_HWIRQ_TO_SHARED(hwirq), ipi_resrv))
|
||||
return -EBUSY;
|
||||
#endif /* CONFIG_GENERIC_IRQ_IPI */
|
||||
|
||||
err = irq_domain_set_hwirq_and_chip(d, virq, hwirq,
|
||||
&gic_level_irq_controller,
|
||||
@ -567,6 +571,8 @@ static const struct irq_domain_ops gic_irq_domain_ops = {
|
||||
.map = gic_irq_domain_map,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_GENERIC_IRQ_IPI
|
||||
|
||||
static int gic_ipi_domain_xlate(struct irq_domain *d, struct device_node *ctrlr,
|
||||
const u32 *intspec, unsigned int intsize,
|
||||
irq_hw_number_t *out_hwirq,
|
||||
@ -670,6 +676,48 @@ static const struct irq_domain_ops gic_ipi_domain_ops = {
|
||||
.match = gic_ipi_domain_match,
|
||||
};
|
||||
|
||||
static int gic_register_ipi_domain(struct device_node *node)
|
||||
{
|
||||
struct irq_domain *gic_ipi_domain;
|
||||
unsigned int v[2], num_ipis;
|
||||
|
||||
gic_ipi_domain = irq_domain_add_hierarchy(gic_irq_domain,
|
||||
IRQ_DOMAIN_FLAG_IPI_PER_CPU,
|
||||
GIC_NUM_LOCAL_INTRS + gic_shared_intrs,
|
||||
node, &gic_ipi_domain_ops, NULL);
|
||||
if (!gic_ipi_domain) {
|
||||
pr_err("Failed to add IPI domain");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
irq_domain_update_bus_token(gic_ipi_domain, DOMAIN_BUS_IPI);
|
||||
|
||||
if (node &&
|
||||
!of_property_read_u32_array(node, "mti,reserved-ipi-vectors", v, 2)) {
|
||||
bitmap_set(ipi_resrv, v[0], v[1]);
|
||||
} else {
|
||||
/*
|
||||
* Reserve 2 interrupts per possible CPU/VP for use as IPIs,
|
||||
* meeting the requirements of arch/mips SMP.
|
||||
*/
|
||||
num_ipis = 2 * num_possible_cpus();
|
||||
bitmap_set(ipi_resrv, gic_shared_intrs - num_ipis, num_ipis);
|
||||
}
|
||||
|
||||
bitmap_copy(ipi_available, ipi_resrv, GIC_MAX_INTRS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else /* !CONFIG_GENERIC_IRQ_IPI */
|
||||
|
||||
static inline int gic_register_ipi_domain(struct device_node *node)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* !CONFIG_GENERIC_IRQ_IPI */
|
||||
|
||||
static int gic_cpu_startup(unsigned int cpu)
|
||||
{
|
||||
/* Enable or disable EIC */
|
||||
@ -688,11 +736,12 @@ static int gic_cpu_startup(unsigned int cpu)
|
||||
static int __init gic_of_init(struct device_node *node,
|
||||
struct device_node *parent)
|
||||
{
|
||||
unsigned int cpu_vec, i, gicconfig, v[2], num_ipis;
|
||||
unsigned int cpu_vec, i, gicconfig;
|
||||
unsigned long reserved;
|
||||
phys_addr_t gic_base;
|
||||
struct resource res;
|
||||
size_t gic_len;
|
||||
int ret;
|
||||
|
||||
/* Find the first available CPU vector. */
|
||||
i = 0;
|
||||
@ -734,6 +783,10 @@ static int __init gic_of_init(struct device_node *node,
|
||||
}
|
||||
|
||||
mips_gic_base = ioremap(gic_base, gic_len);
|
||||
if (!mips_gic_base) {
|
||||
pr_err("Failed to ioremap gic_base\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
gicconfig = read_gic_config();
|
||||
gic_shared_intrs = FIELD_GET(GIC_CONFIG_NUMINTERRUPTS, gicconfig);
|
||||
@ -780,30 +833,9 @@ static int __init gic_of_init(struct device_node *node,
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
gic_ipi_domain = irq_domain_add_hierarchy(gic_irq_domain,
|
||||
IRQ_DOMAIN_FLAG_IPI_PER_CPU,
|
||||
GIC_NUM_LOCAL_INTRS + gic_shared_intrs,
|
||||
node, &gic_ipi_domain_ops, NULL);
|
||||
if (!gic_ipi_domain) {
|
||||
pr_err("Failed to add IPI domain");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
irq_domain_update_bus_token(gic_ipi_domain, DOMAIN_BUS_IPI);
|
||||
|
||||
if (node &&
|
||||
!of_property_read_u32_array(node, "mti,reserved-ipi-vectors", v, 2)) {
|
||||
bitmap_set(ipi_resrv, v[0], v[1]);
|
||||
} else {
|
||||
/*
|
||||
* Reserve 2 interrupts per possible CPU/VP for use as IPIs,
|
||||
* meeting the requirements of arch/mips SMP.
|
||||
*/
|
||||
num_ipis = 2 * num_possible_cpus();
|
||||
bitmap_set(ipi_resrv, gic_shared_intrs - num_ipis, num_ipis);
|
||||
}
|
||||
|
||||
bitmap_copy(ipi_available, ipi_resrv, GIC_MAX_INTRS);
|
||||
ret = gic_register_ipi_domain(node);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
board_bind_eic_interrupt = &gic_bind_eic_interrupt;
|
||||
|
||||
|
393
drivers/irqchip/irq-renesas-rzg2l.c
Normal file
393
drivers/irqchip/irq-renesas-rzg2l.c
Normal file
@ -0,0 +1,393 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Renesas RZ/G2L IRQC Driver
|
||||
*
|
||||
* Copyright (C) 2022 Renesas Electronics Corporation.
|
||||
*
|
||||
* Author: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/irqchip.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#define IRQC_IRQ_START 1
|
||||
#define IRQC_IRQ_COUNT 8
|
||||
#define IRQC_TINT_START (IRQC_IRQ_START + IRQC_IRQ_COUNT)
|
||||
#define IRQC_TINT_COUNT 32
|
||||
#define IRQC_NUM_IRQ (IRQC_TINT_START + IRQC_TINT_COUNT)
|
||||
|
||||
#define ISCR 0x10
|
||||
#define IITSR 0x14
|
||||
#define TSCR 0x20
|
||||
#define TITSR0 0x24
|
||||
#define TITSR1 0x28
|
||||
#define TITSR0_MAX_INT 16
|
||||
#define TITSEL_WIDTH 0x2
|
||||
#define TSSR(n) (0x30 + ((n) * 4))
|
||||
#define TIEN BIT(7)
|
||||
#define TSSEL_SHIFT(n) (8 * (n))
|
||||
#define TSSEL_MASK GENMASK(7, 0)
|
||||
#define IRQ_MASK 0x3
|
||||
|
||||
#define TSSR_OFFSET(n) ((n) % 4)
|
||||
#define TSSR_INDEX(n) ((n) / 4)
|
||||
|
||||
#define TITSR_TITSEL_EDGE_RISING 0
|
||||
#define TITSR_TITSEL_EDGE_FALLING 1
|
||||
#define TITSR_TITSEL_LEVEL_HIGH 2
|
||||
#define TITSR_TITSEL_LEVEL_LOW 3
|
||||
|
||||
#define IITSR_IITSEL(n, sense) ((sense) << ((n) * 2))
|
||||
#define IITSR_IITSEL_LEVEL_LOW 0
|
||||
#define IITSR_IITSEL_EDGE_FALLING 1
|
||||
#define IITSR_IITSEL_EDGE_RISING 2
|
||||
#define IITSR_IITSEL_EDGE_BOTH 3
|
||||
#define IITSR_IITSEL_MASK(n) IITSR_IITSEL((n), 3)
|
||||
|
||||
#define TINT_EXTRACT_HWIRQ(x) FIELD_GET(GENMASK(15, 0), (x))
|
||||
#define TINT_EXTRACT_GPIOINT(x) FIELD_GET(GENMASK(31, 16), (x))
|
||||
|
||||
struct rzg2l_irqc_priv {
|
||||
void __iomem *base;
|
||||
struct irq_fwspec fwspec[IRQC_NUM_IRQ];
|
||||
raw_spinlock_t lock;
|
||||
};
|
||||
|
||||
static struct rzg2l_irqc_priv *irq_data_to_priv(struct irq_data *data)
|
||||
{
|
||||
return data->domain->host_data;
|
||||
}
|
||||
|
||||
static void rzg2l_irq_eoi(struct irq_data *d)
|
||||
{
|
||||
unsigned int hw_irq = irqd_to_hwirq(d) - IRQC_IRQ_START;
|
||||
struct rzg2l_irqc_priv *priv = irq_data_to_priv(d);
|
||||
u32 bit = BIT(hw_irq);
|
||||
u32 reg;
|
||||
|
||||
reg = readl_relaxed(priv->base + ISCR);
|
||||
if (reg & bit)
|
||||
writel_relaxed(reg & ~bit, priv->base + ISCR);
|
||||
}
|
||||
|
||||
static void rzg2l_tint_eoi(struct irq_data *d)
|
||||
{
|
||||
unsigned int hw_irq = irqd_to_hwirq(d) - IRQC_TINT_START;
|
||||
struct rzg2l_irqc_priv *priv = irq_data_to_priv(d);
|
||||
u32 bit = BIT(hw_irq);
|
||||
u32 reg;
|
||||
|
||||
reg = readl_relaxed(priv->base + TSCR);
|
||||
if (reg & bit)
|
||||
writel_relaxed(reg & ~bit, priv->base + TSCR);
|
||||
}
|
||||
|
||||
static void rzg2l_irqc_eoi(struct irq_data *d)
|
||||
{
|
||||
struct rzg2l_irqc_priv *priv = irq_data_to_priv(d);
|
||||
unsigned int hw_irq = irqd_to_hwirq(d);
|
||||
|
||||
raw_spin_lock(&priv->lock);
|
||||
if (hw_irq >= IRQC_IRQ_START && hw_irq <= IRQC_IRQ_COUNT)
|
||||
rzg2l_irq_eoi(d);
|
||||
else if (hw_irq >= IRQC_TINT_START && hw_irq < IRQC_NUM_IRQ)
|
||||
rzg2l_tint_eoi(d);
|
||||
raw_spin_unlock(&priv->lock);
|
||||
irq_chip_eoi_parent(d);
|
||||
}
|
||||
|
||||
static void rzg2l_irqc_irq_disable(struct irq_data *d)
|
||||
{
|
||||
unsigned int hw_irq = irqd_to_hwirq(d);
|
||||
|
||||
if (hw_irq >= IRQC_TINT_START && hw_irq < IRQC_NUM_IRQ) {
|
||||
struct rzg2l_irqc_priv *priv = irq_data_to_priv(d);
|
||||
u32 offset = hw_irq - IRQC_TINT_START;
|
||||
u32 tssr_offset = TSSR_OFFSET(offset);
|
||||
u8 tssr_index = TSSR_INDEX(offset);
|
||||
u32 reg;
|
||||
|
||||
raw_spin_lock(&priv->lock);
|
||||
reg = readl_relaxed(priv->base + TSSR(tssr_index));
|
||||
reg &= ~(TSSEL_MASK << tssr_offset);
|
||||
writel_relaxed(reg, priv->base + TSSR(tssr_index));
|
||||
raw_spin_unlock(&priv->lock);
|
||||
}
|
||||
irq_chip_disable_parent(d);
|
||||
}
|
||||
|
||||
static void rzg2l_irqc_irq_enable(struct irq_data *d)
|
||||
{
|
||||
unsigned int hw_irq = irqd_to_hwirq(d);
|
||||
|
||||
if (hw_irq >= IRQC_TINT_START && hw_irq < IRQC_NUM_IRQ) {
|
||||
struct rzg2l_irqc_priv *priv = irq_data_to_priv(d);
|
||||
unsigned long tint = (uintptr_t)d->chip_data;
|
||||
u32 offset = hw_irq - IRQC_TINT_START;
|
||||
u32 tssr_offset = TSSR_OFFSET(offset);
|
||||
u8 tssr_index = TSSR_INDEX(offset);
|
||||
u32 reg;
|
||||
|
||||
raw_spin_lock(&priv->lock);
|
||||
reg = readl_relaxed(priv->base + TSSR(tssr_index));
|
||||
reg |= (TIEN | tint) << TSSEL_SHIFT(tssr_offset);
|
||||
writel_relaxed(reg, priv->base + TSSR(tssr_index));
|
||||
raw_spin_unlock(&priv->lock);
|
||||
}
|
||||
irq_chip_enable_parent(d);
|
||||
}
|
||||
|
||||
static int rzg2l_irq_set_type(struct irq_data *d, unsigned int type)
|
||||
{
|
||||
unsigned int hw_irq = irqd_to_hwirq(d) - IRQC_IRQ_START;
|
||||
struct rzg2l_irqc_priv *priv = irq_data_to_priv(d);
|
||||
u16 sense, tmp;
|
||||
|
||||
switch (type & IRQ_TYPE_SENSE_MASK) {
|
||||
case IRQ_TYPE_LEVEL_LOW:
|
||||
sense = IITSR_IITSEL_LEVEL_LOW;
|
||||
break;
|
||||
|
||||
case IRQ_TYPE_EDGE_FALLING:
|
||||
sense = IITSR_IITSEL_EDGE_FALLING;
|
||||
break;
|
||||
|
||||
case IRQ_TYPE_EDGE_RISING:
|
||||
sense = IITSR_IITSEL_EDGE_RISING;
|
||||
break;
|
||||
|
||||
case IRQ_TYPE_EDGE_BOTH:
|
||||
sense = IITSR_IITSEL_EDGE_BOTH;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
raw_spin_lock(&priv->lock);
|
||||
tmp = readl_relaxed(priv->base + IITSR);
|
||||
tmp &= ~IITSR_IITSEL_MASK(hw_irq);
|
||||
tmp |= IITSR_IITSEL(hw_irq, sense);
|
||||
writel_relaxed(tmp, priv->base + IITSR);
|
||||
raw_spin_unlock(&priv->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rzg2l_tint_set_edge(struct irq_data *d, unsigned int type)
|
||||
{
|
||||
struct rzg2l_irqc_priv *priv = irq_data_to_priv(d);
|
||||
unsigned int hwirq = irqd_to_hwirq(d);
|
||||
u32 titseln = hwirq - IRQC_TINT_START;
|
||||
u32 offset;
|
||||
u8 sense;
|
||||
u32 reg;
|
||||
|
||||
switch (type & IRQ_TYPE_SENSE_MASK) {
|
||||
case IRQ_TYPE_EDGE_RISING:
|
||||
sense = TITSR_TITSEL_EDGE_RISING;
|
||||
break;
|
||||
|
||||
case IRQ_TYPE_EDGE_FALLING:
|
||||
sense = TITSR_TITSEL_EDGE_FALLING;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
offset = TITSR0;
|
||||
if (titseln >= TITSR0_MAX_INT) {
|
||||
titseln -= TITSR0_MAX_INT;
|
||||
offset = TITSR1;
|
||||
}
|
||||
|
||||
raw_spin_lock(&priv->lock);
|
||||
reg = readl_relaxed(priv->base + offset);
|
||||
reg &= ~(IRQ_MASK << (titseln * TITSEL_WIDTH));
|
||||
reg |= sense << (titseln * TITSEL_WIDTH);
|
||||
writel_relaxed(reg, priv->base + offset);
|
||||
raw_spin_unlock(&priv->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rzg2l_irqc_set_type(struct irq_data *d, unsigned int type)
|
||||
{
|
||||
unsigned int hw_irq = irqd_to_hwirq(d);
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (hw_irq >= IRQC_IRQ_START && hw_irq <= IRQC_IRQ_COUNT)
|
||||
ret = rzg2l_irq_set_type(d, type);
|
||||
else if (hw_irq >= IRQC_TINT_START && hw_irq < IRQC_NUM_IRQ)
|
||||
ret = rzg2l_tint_set_edge(d, type);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return irq_chip_set_type_parent(d, IRQ_TYPE_LEVEL_HIGH);
|
||||
}
|
||||
|
||||
static const struct irq_chip irqc_chip = {
|
||||
.name = "rzg2l-irqc",
|
||||
.irq_eoi = rzg2l_irqc_eoi,
|
||||
.irq_mask = irq_chip_mask_parent,
|
||||
.irq_unmask = irq_chip_unmask_parent,
|
||||
.irq_disable = rzg2l_irqc_irq_disable,
|
||||
.irq_enable = rzg2l_irqc_irq_enable,
|
||||
.irq_get_irqchip_state = irq_chip_get_parent_state,
|
||||
.irq_set_irqchip_state = irq_chip_set_parent_state,
|
||||
.irq_retrigger = irq_chip_retrigger_hierarchy,
|
||||
.irq_set_type = rzg2l_irqc_set_type,
|
||||
.flags = IRQCHIP_MASK_ON_SUSPEND |
|
||||
IRQCHIP_SET_TYPE_MASKED |
|
||||
IRQCHIP_SKIP_SET_WAKE,
|
||||
};
|
||||
|
||||
static int rzg2l_irqc_alloc(struct irq_domain *domain, unsigned int virq,
|
||||
unsigned int nr_irqs, void *arg)
|
||||
{
|
||||
struct rzg2l_irqc_priv *priv = domain->host_data;
|
||||
unsigned long tint = 0;
|
||||
irq_hw_number_t hwirq;
|
||||
unsigned int type;
|
||||
int ret;
|
||||
|
||||
ret = irq_domain_translate_twocell(domain, arg, &hwirq, &type);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* For TINT interrupts ie where pinctrl driver is child of irqc domain
|
||||
* the hwirq and TINT are encoded in fwspec->param[0].
|
||||
* hwirq for TINT range from 9-40, hwirq is embedded 0-15 bits and TINT
|
||||
* from 16-31 bits. TINT from the pinctrl driver needs to be programmed
|
||||
* in IRQC registers to enable a given gpio pin as interrupt.
|
||||
*/
|
||||
if (hwirq > IRQC_IRQ_COUNT) {
|
||||
tint = TINT_EXTRACT_GPIOINT(hwirq);
|
||||
hwirq = TINT_EXTRACT_HWIRQ(hwirq);
|
||||
|
||||
if (hwirq < IRQC_TINT_START)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (hwirq > (IRQC_NUM_IRQ - 1))
|
||||
return -EINVAL;
|
||||
|
||||
ret = irq_domain_set_hwirq_and_chip(domain, virq, hwirq, &irqc_chip,
|
||||
(void *)(uintptr_t)tint);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &priv->fwspec[hwirq]);
|
||||
}
|
||||
|
||||
static const struct irq_domain_ops rzg2l_irqc_domain_ops = {
|
||||
.alloc = rzg2l_irqc_alloc,
|
||||
.free = irq_domain_free_irqs_common,
|
||||
.translate = irq_domain_translate_twocell,
|
||||
};
|
||||
|
||||
static int rzg2l_irqc_parse_interrupts(struct rzg2l_irqc_priv *priv,
|
||||
struct device_node *np)
|
||||
{
|
||||
struct of_phandle_args map;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
for (i = 0; i < IRQC_NUM_IRQ; i++) {
|
||||
ret = of_irq_parse_one(np, i, &map);
|
||||
if (ret)
|
||||
return ret;
|
||||
of_phandle_args_to_fwspec(np, map.args, map.args_count,
|
||||
&priv->fwspec[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rzg2l_irqc_init(struct device_node *node, struct device_node *parent)
|
||||
{
|
||||
struct irq_domain *irq_domain, *parent_domain;
|
||||
struct platform_device *pdev;
|
||||
struct reset_control *resetn;
|
||||
struct rzg2l_irqc_priv *priv;
|
||||
int ret;
|
||||
|
||||
pdev = of_find_device_by_node(node);
|
||||
if (!pdev)
|
||||
return -ENODEV;
|
||||
|
||||
parent_domain = irq_find_host(parent);
|
||||
if (!parent_domain) {
|
||||
dev_err(&pdev->dev, "cannot find parent domain\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->base = devm_of_iomap(&pdev->dev, pdev->dev.of_node, 0, NULL);
|
||||
if (IS_ERR(priv->base))
|
||||
return PTR_ERR(priv->base);
|
||||
|
||||
ret = rzg2l_irqc_parse_interrupts(priv, node);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "cannot parse interrupts: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
resetn = devm_reset_control_get_exclusive(&pdev->dev, NULL);
|
||||
if (IS_ERR(resetn))
|
||||
return PTR_ERR(resetn);
|
||||
|
||||
ret = reset_control_deassert(resetn);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to deassert resetn pin, %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
ret = pm_runtime_resume_and_get(&pdev->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "pm_runtime_resume_and_get failed: %d\n", ret);
|
||||
goto pm_disable;
|
||||
}
|
||||
|
||||
raw_spin_lock_init(&priv->lock);
|
||||
|
||||
irq_domain = irq_domain_add_hierarchy(parent_domain, 0, IRQC_NUM_IRQ,
|
||||
node, &rzg2l_irqc_domain_ops,
|
||||
priv);
|
||||
if (!irq_domain) {
|
||||
dev_err(&pdev->dev, "failed to add irq domain\n");
|
||||
ret = -ENOMEM;
|
||||
goto pm_put;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
pm_put:
|
||||
pm_runtime_put(&pdev->dev);
|
||||
pm_disable:
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
reset_control_assert(resetn);
|
||||
return ret;
|
||||
}
|
||||
|
||||
IRQCHIP_PLATFORM_DRIVER_BEGIN(rzg2l_irqc)
|
||||
IRQCHIP_MATCH("renesas,rzg2l-irqc", rzg2l_irqc_init)
|
||||
IRQCHIP_PLATFORM_DRIVER_END(rzg2l_irqc)
|
||||
MODULE_AUTHOR("Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>");
|
||||
MODULE_DESCRIPTION("Renesas RZ/G2L IRQC Driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -60,10 +60,13 @@
|
||||
#define PLIC_DISABLE_THRESHOLD 0x7
|
||||
#define PLIC_ENABLE_THRESHOLD 0
|
||||
|
||||
#define PLIC_QUIRK_EDGE_INTERRUPT 0
|
||||
|
||||
struct plic_priv {
|
||||
struct cpumask lmask;
|
||||
struct irq_domain *irqdomain;
|
||||
void __iomem *regs;
|
||||
unsigned long plic_quirks;
|
||||
};
|
||||
|
||||
struct plic_handler {
|
||||
@ -81,6 +84,8 @@ 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 int plic_irq_set_type(struct irq_data *d, unsigned int type);
|
||||
|
||||
static void __plic_toggle(void __iomem *enable_base, int hwirq, int enable)
|
||||
{
|
||||
u32 __iomem *reg = enable_base + (hwirq / 32) * sizeof(u32);
|
||||
@ -103,37 +108,43 @@ static inline void plic_irq_toggle(const struct cpumask *mask,
|
||||
struct irq_data *d, int enable)
|
||||
{
|
||||
int cpu;
|
||||
struct plic_priv *priv = irq_data_get_irq_chip_data(d);
|
||||
|
||||
writel(enable, priv->regs + PRIORITY_BASE + d->hwirq * PRIORITY_PER_ID);
|
||||
for_each_cpu(cpu, mask) {
|
||||
struct plic_handler *handler = per_cpu_ptr(&plic_handlers, cpu);
|
||||
|
||||
if (handler->present &&
|
||||
cpumask_test_cpu(cpu, &handler->priv->lmask))
|
||||
plic_toggle(handler, d->hwirq, enable);
|
||||
plic_toggle(handler, d->hwirq, enable);
|
||||
}
|
||||
}
|
||||
|
||||
static void plic_irq_enable(struct irq_data *d)
|
||||
{
|
||||
plic_irq_toggle(irq_data_get_effective_affinity_mask(d), d, 1);
|
||||
}
|
||||
|
||||
static void plic_irq_disable(struct irq_data *d)
|
||||
{
|
||||
plic_irq_toggle(irq_data_get_effective_affinity_mask(d), d, 0);
|
||||
}
|
||||
|
||||
static void plic_irq_unmask(struct irq_data *d)
|
||||
{
|
||||
struct cpumask amask;
|
||||
unsigned int cpu;
|
||||
struct plic_priv *priv = irq_data_get_irq_chip_data(d);
|
||||
|
||||
cpumask_and(&amask, &priv->lmask, cpu_online_mask);
|
||||
cpu = cpumask_any_and(irq_data_get_affinity_mask(d),
|
||||
&amask);
|
||||
if (WARN_ON_ONCE(cpu >= nr_cpu_ids))
|
||||
return;
|
||||
plic_irq_toggle(cpumask_of(cpu), d, 1);
|
||||
writel(1, priv->regs + PRIORITY_BASE + d->hwirq * PRIORITY_PER_ID);
|
||||
}
|
||||
|
||||
static void plic_irq_mask(struct irq_data *d)
|
||||
{
|
||||
struct plic_priv *priv = irq_data_get_irq_chip_data(d);
|
||||
|
||||
plic_irq_toggle(&priv->lmask, d, 0);
|
||||
writel(0, priv->regs + PRIORITY_BASE + d->hwirq * PRIORITY_PER_ID);
|
||||
}
|
||||
|
||||
static void plic_irq_eoi(struct irq_data *d)
|
||||
{
|
||||
struct plic_handler *handler = this_cpu_ptr(&plic_handlers);
|
||||
|
||||
writel(d->hwirq, handler->hart_base + CONTEXT_CLAIM);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
@ -154,38 +165,68 @@ static int plic_set_affinity(struct irq_data *d,
|
||||
if (cpu >= nr_cpu_ids)
|
||||
return -EINVAL;
|
||||
|
||||
plic_irq_toggle(&priv->lmask, d, 0);
|
||||
plic_irq_toggle(cpumask_of(cpu), d, !irqd_irq_masked(d));
|
||||
plic_irq_disable(d);
|
||||
|
||||
irq_data_update_effective_affinity(d, cpumask_of(cpu));
|
||||
|
||||
if (!irqd_irq_disabled(d))
|
||||
plic_irq_enable(d);
|
||||
|
||||
return IRQ_SET_MASK_OK_DONE;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void plic_irq_eoi(struct irq_data *d)
|
||||
{
|
||||
struct plic_handler *handler = this_cpu_ptr(&plic_handlers);
|
||||
|
||||
if (irqd_irq_masked(d)) {
|
||||
plic_irq_unmask(d);
|
||||
writel(d->hwirq, handler->hart_base + CONTEXT_CLAIM);
|
||||
plic_irq_mask(d);
|
||||
} else {
|
||||
writel(d->hwirq, handler->hart_base + CONTEXT_CLAIM);
|
||||
}
|
||||
}
|
||||
static struct irq_chip plic_edge_chip = {
|
||||
.name = "SiFive PLIC",
|
||||
.irq_enable = plic_irq_enable,
|
||||
.irq_disable = plic_irq_disable,
|
||||
.irq_ack = plic_irq_eoi,
|
||||
.irq_mask = plic_irq_mask,
|
||||
.irq_unmask = plic_irq_unmask,
|
||||
#ifdef CONFIG_SMP
|
||||
.irq_set_affinity = plic_set_affinity,
|
||||
#endif
|
||||
.irq_set_type = plic_irq_set_type,
|
||||
.flags = IRQCHIP_AFFINITY_PRE_STARTUP,
|
||||
};
|
||||
|
||||
static struct irq_chip plic_chip = {
|
||||
.name = "SiFive PLIC",
|
||||
.irq_enable = plic_irq_enable,
|
||||
.irq_disable = plic_irq_disable,
|
||||
.irq_mask = plic_irq_mask,
|
||||
.irq_unmask = plic_irq_unmask,
|
||||
.irq_eoi = plic_irq_eoi,
|
||||
#ifdef CONFIG_SMP
|
||||
.irq_set_affinity = plic_set_affinity,
|
||||
#endif
|
||||
.irq_set_type = plic_irq_set_type,
|
||||
.flags = IRQCHIP_AFFINITY_PRE_STARTUP,
|
||||
};
|
||||
|
||||
static int plic_irq_set_type(struct irq_data *d, unsigned int type)
|
||||
{
|
||||
struct plic_priv *priv = irq_data_get_irq_chip_data(d);
|
||||
|
||||
if (!test_bit(PLIC_QUIRK_EDGE_INTERRUPT, &priv->plic_quirks))
|
||||
return IRQ_SET_MASK_OK_NOCOPY;
|
||||
|
||||
switch (type) {
|
||||
case IRQ_TYPE_EDGE_RISING:
|
||||
irq_set_chip_handler_name_locked(d, &plic_edge_chip,
|
||||
handle_edge_irq, NULL);
|
||||
break;
|
||||
case IRQ_TYPE_LEVEL_HIGH:
|
||||
irq_set_chip_handler_name_locked(d, &plic_chip,
|
||||
handle_fasteoi_irq, NULL);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return IRQ_SET_MASK_OK;
|
||||
}
|
||||
|
||||
static int plic_irqdomain_map(struct irq_domain *d, unsigned int irq,
|
||||
irq_hw_number_t hwirq)
|
||||
{
|
||||
@ -198,6 +239,19 @@ static int plic_irqdomain_map(struct irq_domain *d, unsigned int irq,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int plic_irq_domain_translate(struct irq_domain *d,
|
||||
struct irq_fwspec *fwspec,
|
||||
unsigned long *hwirq,
|
||||
unsigned int *type)
|
||||
{
|
||||
struct plic_priv *priv = d->host_data;
|
||||
|
||||
if (test_bit(PLIC_QUIRK_EDGE_INTERRUPT, &priv->plic_quirks))
|
||||
return irq_domain_translate_twocell(d, fwspec, hwirq, type);
|
||||
|
||||
return irq_domain_translate_onecell(d, fwspec, hwirq, type);
|
||||
}
|
||||
|
||||
static int plic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
|
||||
unsigned int nr_irqs, void *arg)
|
||||
{
|
||||
@ -206,7 +260,7 @@ static int plic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
|
||||
unsigned int type;
|
||||
struct irq_fwspec *fwspec = arg;
|
||||
|
||||
ret = irq_domain_translate_onecell(domain, fwspec, &hwirq, &type);
|
||||
ret = plic_irq_domain_translate(domain, fwspec, &hwirq, &type);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -220,7 +274,7 @@ static int plic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
|
||||
}
|
||||
|
||||
static const struct irq_domain_ops plic_irqdomain_ops = {
|
||||
.translate = irq_domain_translate_onecell,
|
||||
.translate = plic_irq_domain_translate,
|
||||
.alloc = plic_irq_domain_alloc,
|
||||
.free = irq_domain_free_irqs_top,
|
||||
};
|
||||
@ -281,8 +335,9 @@ static int plic_starting_cpu(unsigned int cpu)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init plic_init(struct device_node *node,
|
||||
struct device_node *parent)
|
||||
static int __init __plic_init(struct device_node *node,
|
||||
struct device_node *parent,
|
||||
unsigned long plic_quirks)
|
||||
{
|
||||
int error = 0, nr_contexts, nr_handlers = 0, i;
|
||||
u32 nr_irqs;
|
||||
@ -293,6 +348,8 @@ static int __init plic_init(struct device_node *node,
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->plic_quirks = plic_quirks;
|
||||
|
||||
priv->regs = of_iomap(node, 0);
|
||||
if (WARN_ON(!priv->regs)) {
|
||||
error = -EIO;
|
||||
@ -382,8 +439,11 @@ static int __init plic_init(struct device_node *node,
|
||||
i * CONTEXT_ENABLE_SIZE;
|
||||
handler->priv = priv;
|
||||
done:
|
||||
for (hwirq = 1; hwirq <= nr_irqs; hwirq++)
|
||||
for (hwirq = 1; hwirq <= nr_irqs; hwirq++) {
|
||||
plic_toggle(handler, hwirq, 0);
|
||||
writel(1, priv->regs + PRIORITY_BASE +
|
||||
hwirq * PRIORITY_PER_ID);
|
||||
}
|
||||
nr_handlers++;
|
||||
}
|
||||
|
||||
@ -410,6 +470,20 @@ out_free_priv:
|
||||
return error;
|
||||
}
|
||||
|
||||
static int __init plic_init(struct device_node *node,
|
||||
struct device_node *parent)
|
||||
{
|
||||
return __plic_init(node, parent, 0);
|
||||
}
|
||||
|
||||
IRQCHIP_DECLARE(sifive_plic, "sifive,plic-1.0.0", plic_init);
|
||||
IRQCHIP_DECLARE(riscv_plic0, "riscv,plic0", plic_init); /* for legacy systems */
|
||||
IRQCHIP_DECLARE(thead_c900_plic, "thead,c900-plic", plic_init); /* for firmware driver */
|
||||
|
||||
static int __init plic_edge_init(struct device_node *node,
|
||||
struct device_node *parent)
|
||||
{
|
||||
return __plic_init(node, parent, BIT(PLIC_QUIRK_EDGE_INTERRUPT));
|
||||
}
|
||||
|
||||
IRQCHIP_DECLARE(andestech_nceplic100, "andestech,nceplic100", plic_edge_init);
|
||||
IRQCHIP_DECLARE(thead_c900_plic, "thead,c900-plic", plic_edge_init);
|
||||
|
@ -34,21 +34,15 @@ struct stm32_exti_bank {
|
||||
u32 swier_ofst;
|
||||
u32 rpr_ofst;
|
||||
u32 fpr_ofst;
|
||||
u32 trg_ofst;
|
||||
};
|
||||
|
||||
#define UNDEF_REG ~0
|
||||
|
||||
struct stm32_desc_irq {
|
||||
u32 exti;
|
||||
u32 irq_parent;
|
||||
struct irq_chip *chip;
|
||||
};
|
||||
|
||||
struct stm32_exti_drv_data {
|
||||
const struct stm32_exti_bank **exti_banks;
|
||||
const struct stm32_desc_irq *desc_irqs;
|
||||
const u8 *desc_irqs;
|
||||
u32 bank_nr;
|
||||
u32 irq_nr;
|
||||
};
|
||||
|
||||
struct stm32_exti_chip_data {
|
||||
@ -78,6 +72,7 @@ static const struct stm32_exti_bank stm32f4xx_exti_b1 = {
|
||||
.swier_ofst = 0x10,
|
||||
.rpr_ofst = 0x14,
|
||||
.fpr_ofst = UNDEF_REG,
|
||||
.trg_ofst = UNDEF_REG,
|
||||
};
|
||||
|
||||
static const struct stm32_exti_bank *stm32f4xx_exti_banks[] = {
|
||||
@ -97,6 +92,7 @@ static const struct stm32_exti_bank stm32h7xx_exti_b1 = {
|
||||
.swier_ofst = 0x08,
|
||||
.rpr_ofst = 0x88,
|
||||
.fpr_ofst = UNDEF_REG,
|
||||
.trg_ofst = UNDEF_REG,
|
||||
};
|
||||
|
||||
static const struct stm32_exti_bank stm32h7xx_exti_b2 = {
|
||||
@ -107,6 +103,7 @@ static const struct stm32_exti_bank stm32h7xx_exti_b2 = {
|
||||
.swier_ofst = 0x28,
|
||||
.rpr_ofst = 0x98,
|
||||
.fpr_ofst = UNDEF_REG,
|
||||
.trg_ofst = UNDEF_REG,
|
||||
};
|
||||
|
||||
static const struct stm32_exti_bank stm32h7xx_exti_b3 = {
|
||||
@ -117,6 +114,7 @@ static const struct stm32_exti_bank stm32h7xx_exti_b3 = {
|
||||
.swier_ofst = 0x48,
|
||||
.rpr_ofst = 0xA8,
|
||||
.fpr_ofst = UNDEF_REG,
|
||||
.trg_ofst = UNDEF_REG,
|
||||
};
|
||||
|
||||
static const struct stm32_exti_bank *stm32h7xx_exti_banks[] = {
|
||||
@ -132,32 +130,35 @@ static const struct stm32_exti_drv_data stm32h7xx_drv_data = {
|
||||
|
||||
static const struct stm32_exti_bank stm32mp1_exti_b1 = {
|
||||
.imr_ofst = 0x80,
|
||||
.emr_ofst = 0x84,
|
||||
.emr_ofst = UNDEF_REG,
|
||||
.rtsr_ofst = 0x00,
|
||||
.ftsr_ofst = 0x04,
|
||||
.swier_ofst = 0x08,
|
||||
.rpr_ofst = 0x0C,
|
||||
.fpr_ofst = 0x10,
|
||||
.trg_ofst = 0x3EC,
|
||||
};
|
||||
|
||||
static const struct stm32_exti_bank stm32mp1_exti_b2 = {
|
||||
.imr_ofst = 0x90,
|
||||
.emr_ofst = 0x94,
|
||||
.emr_ofst = UNDEF_REG,
|
||||
.rtsr_ofst = 0x20,
|
||||
.ftsr_ofst = 0x24,
|
||||
.swier_ofst = 0x28,
|
||||
.rpr_ofst = 0x2C,
|
||||
.fpr_ofst = 0x30,
|
||||
.trg_ofst = 0x3E8,
|
||||
};
|
||||
|
||||
static const struct stm32_exti_bank stm32mp1_exti_b3 = {
|
||||
.imr_ofst = 0xA0,
|
||||
.emr_ofst = 0xA4,
|
||||
.emr_ofst = UNDEF_REG,
|
||||
.rtsr_ofst = 0x40,
|
||||
.ftsr_ofst = 0x44,
|
||||
.swier_ofst = 0x48,
|
||||
.rpr_ofst = 0x4C,
|
||||
.fpr_ofst = 0x50,
|
||||
.trg_ofst = 0x3E4,
|
||||
};
|
||||
|
||||
static const struct stm32_exti_bank *stm32mp1_exti_banks[] = {
|
||||
@ -169,126 +170,114 @@ static const struct stm32_exti_bank *stm32mp1_exti_banks[] = {
|
||||
static struct irq_chip stm32_exti_h_chip;
|
||||
static struct irq_chip stm32_exti_h_chip_direct;
|
||||
|
||||
static const struct stm32_desc_irq stm32mp1_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 = 23, .chip = &stm32_exti_h_chip },
|
||||
{ .exti = 6, .irq_parent = 64, .chip = &stm32_exti_h_chip },
|
||||
{ .exti = 7, .irq_parent = 65, .chip = &stm32_exti_h_chip },
|
||||
{ .exti = 8, .irq_parent = 66, .chip = &stm32_exti_h_chip },
|
||||
{ .exti = 9, .irq_parent = 67, .chip = &stm32_exti_h_chip },
|
||||
{ .exti = 10, .irq_parent = 40, .chip = &stm32_exti_h_chip },
|
||||
{ .exti = 11, .irq_parent = 42, .chip = &stm32_exti_h_chip },
|
||||
{ .exti = 12, .irq_parent = 76, .chip = &stm32_exti_h_chip },
|
||||
{ .exti = 13, .irq_parent = 77, .chip = &stm32_exti_h_chip },
|
||||
{ .exti = 14, .irq_parent = 121, .chip = &stm32_exti_h_chip },
|
||||
{ .exti = 15, .irq_parent = 127, .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 = 31, .chip = &stm32_exti_h_chip_direct },
|
||||
{ .exti = 22, .irq_parent = 33, .chip = &stm32_exti_h_chip_direct },
|
||||
{ .exti = 23, .irq_parent = 72, .chip = &stm32_exti_h_chip_direct },
|
||||
{ .exti = 24, .irq_parent = 95, .chip = &stm32_exti_h_chip_direct },
|
||||
{ .exti = 25, .irq_parent = 107, .chip = &stm32_exti_h_chip_direct },
|
||||
{ .exti = 26, .irq_parent = 37, .chip = &stm32_exti_h_chip_direct },
|
||||
{ .exti = 27, .irq_parent = 38, .chip = &stm32_exti_h_chip_direct },
|
||||
{ .exti = 28, .irq_parent = 39, .chip = &stm32_exti_h_chip_direct },
|
||||
{ .exti = 29, .irq_parent = 71, .chip = &stm32_exti_h_chip_direct },
|
||||
{ .exti = 30, .irq_parent = 52, .chip = &stm32_exti_h_chip_direct },
|
||||
{ .exti = 31, .irq_parent = 53, .chip = &stm32_exti_h_chip_direct },
|
||||
{ .exti = 32, .irq_parent = 82, .chip = &stm32_exti_h_chip_direct },
|
||||
{ .exti = 33, .irq_parent = 83, .chip = &stm32_exti_h_chip_direct },
|
||||
{ .exti = 47, .irq_parent = 93, .chip = &stm32_exti_h_chip_direct },
|
||||
{ .exti = 48, .irq_parent = 138, .chip = &stm32_exti_h_chip_direct },
|
||||
{ .exti = 50, .irq_parent = 139, .chip = &stm32_exti_h_chip_direct },
|
||||
{ .exti = 52, .irq_parent = 140, .chip = &stm32_exti_h_chip_direct },
|
||||
{ .exti = 53, .irq_parent = 141, .chip = &stm32_exti_h_chip_direct },
|
||||
{ .exti = 54, .irq_parent = 135, .chip = &stm32_exti_h_chip_direct },
|
||||
{ .exti = 61, .irq_parent = 100, .chip = &stm32_exti_h_chip_direct },
|
||||
{ .exti = 65, .irq_parent = 144, .chip = &stm32_exti_h_chip },
|
||||
{ .exti = 68, .irq_parent = 143, .chip = &stm32_exti_h_chip },
|
||||
{ .exti = 70, .irq_parent = 62, .chip = &stm32_exti_h_chip_direct },
|
||||
{ .exti = 73, .irq_parent = 129, .chip = &stm32_exti_h_chip },
|
||||
#define EXTI_INVALID_IRQ U8_MAX
|
||||
#define STM32MP1_DESC_IRQ_SIZE (ARRAY_SIZE(stm32mp1_exti_banks) * IRQS_PER_BANK)
|
||||
|
||||
static const u8 stm32mp1_desc_irq[] = {
|
||||
/* default value */
|
||||
[0 ... (STM32MP1_DESC_IRQ_SIZE - 1)] = EXTI_INVALID_IRQ,
|
||||
|
||||
[0] = 6,
|
||||
[1] = 7,
|
||||
[2] = 8,
|
||||
[3] = 9,
|
||||
[4] = 10,
|
||||
[5] = 23,
|
||||
[6] = 64,
|
||||
[7] = 65,
|
||||
[8] = 66,
|
||||
[9] = 67,
|
||||
[10] = 40,
|
||||
[11] = 42,
|
||||
[12] = 76,
|
||||
[13] = 77,
|
||||
[14] = 121,
|
||||
[15] = 127,
|
||||
[16] = 1,
|
||||
[19] = 3,
|
||||
[21] = 31,
|
||||
[22] = 33,
|
||||
[23] = 72,
|
||||
[24] = 95,
|
||||
[25] = 107,
|
||||
[26] = 37,
|
||||
[27] = 38,
|
||||
[28] = 39,
|
||||
[29] = 71,
|
||||
[30] = 52,
|
||||
[31] = 53,
|
||||
[32] = 82,
|
||||
[33] = 83,
|
||||
[47] = 93,
|
||||
[48] = 138,
|
||||
[50] = 139,
|
||||
[52] = 140,
|
||||
[53] = 141,
|
||||
[54] = 135,
|
||||
[61] = 100,
|
||||
[65] = 144,
|
||||
[68] = 143,
|
||||
[70] = 62,
|
||||
[73] = 129,
|
||||
};
|
||||
|
||||
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 u8 stm32mp13_desc_irq[] = {
|
||||
/* default value */
|
||||
[0 ... (STM32MP1_DESC_IRQ_SIZE - 1)] = EXTI_INVALID_IRQ,
|
||||
|
||||
[0] = 6,
|
||||
[1] = 7,
|
||||
[2] = 8,
|
||||
[3] = 9,
|
||||
[4] = 10,
|
||||
[5] = 24,
|
||||
[6] = 65,
|
||||
[7] = 66,
|
||||
[8] = 67,
|
||||
[9] = 68,
|
||||
[10] = 41,
|
||||
[11] = 43,
|
||||
[12] = 77,
|
||||
[13] = 78,
|
||||
[14] = 106,
|
||||
[15] = 109,
|
||||
[16] = 1,
|
||||
[19] = 3,
|
||||
[21] = 32,
|
||||
[22] = 34,
|
||||
[23] = 73,
|
||||
[24] = 93,
|
||||
[25] = 114,
|
||||
[26] = 38,
|
||||
[27] = 39,
|
||||
[28] = 40,
|
||||
[29] = 72,
|
||||
[30] = 53,
|
||||
[31] = 54,
|
||||
[32] = 83,
|
||||
[33] = 84,
|
||||
[44] = 96,
|
||||
[47] = 92,
|
||||
[48] = 116,
|
||||
[50] = 117,
|
||||
[52] = 118,
|
||||
[53] = 119,
|
||||
[68] = 63,
|
||||
[70] = 98,
|
||||
};
|
||||
|
||||
static const struct stm32_exti_drv_data stm32mp1_drv_data = {
|
||||
.exti_banks = stm32mp1_exti_banks,
|
||||
.bank_nr = ARRAY_SIZE(stm32mp1_exti_banks),
|
||||
.desc_irqs = stm32mp1_desc_irq,
|
||||
.irq_nr = ARRAY_SIZE(stm32mp1_desc_irq),
|
||||
};
|
||||
|
||||
static const struct stm32_exti_drv_data stm32mp13_drv_data = {
|
||||
.exti_banks = stm32mp1_exti_banks,
|
||||
.bank_nr = ARRAY_SIZE(stm32mp1_exti_banks),
|
||||
.desc_irqs = stm32mp13_desc_irq,
|
||||
.irq_nr = ARRAY_SIZE(stm32mp13_desc_irq),
|
||||
};
|
||||
|
||||
static const struct
|
||||
stm32_desc_irq *stm32_exti_get_desc(const struct stm32_exti_drv_data *drv_data,
|
||||
irq_hw_number_t hwirq)
|
||||
{
|
||||
const struct stm32_desc_irq *desc = NULL;
|
||||
int i;
|
||||
|
||||
if (!drv_data->desc_irqs)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < drv_data->irq_nr; i++) {
|
||||
desc = &drv_data->desc_irqs[i];
|
||||
if (desc->exti == hwirq)
|
||||
break;
|
||||
}
|
||||
|
||||
return desc;
|
||||
}
|
||||
|
||||
static unsigned long stm32_exti_pending(struct irq_chip_generic *gc)
|
||||
{
|
||||
struct stm32_exti_chip_data *chip_data = gc->private;
|
||||
@ -614,7 +603,7 @@ static int stm32_exti_h_set_affinity(struct irq_data *d,
|
||||
if (d->parent_data->chip)
|
||||
return irq_chip_set_affinity_parent(d, dest, force);
|
||||
|
||||
return -EINVAL;
|
||||
return IRQ_SET_MASK_OK_DONE;
|
||||
}
|
||||
|
||||
static int __maybe_unused stm32_exti_h_suspend(void)
|
||||
@ -691,8 +680,8 @@ static struct irq_chip stm32_exti_h_chip_direct = {
|
||||
.name = "stm32-exti-h-direct",
|
||||
.irq_eoi = irq_chip_eoi_parent,
|
||||
.irq_ack = irq_chip_ack_parent,
|
||||
.irq_mask = irq_chip_mask_parent,
|
||||
.irq_unmask = irq_chip_unmask_parent,
|
||||
.irq_mask = stm32_exti_h_mask,
|
||||
.irq_unmask = stm32_exti_h_unmask,
|
||||
.irq_retrigger = irq_chip_retrigger_hierarchy,
|
||||
.irq_set_type = irq_chip_set_type_parent,
|
||||
.irq_set_wake = stm32_exti_h_set_wake,
|
||||
@ -706,28 +695,36 @@ static int stm32_exti_h_domain_alloc(struct irq_domain *dm,
|
||||
{
|
||||
struct stm32_exti_host_data *host_data = dm->host_data;
|
||||
struct stm32_exti_chip_data *chip_data;
|
||||
const struct stm32_desc_irq *desc;
|
||||
u8 desc_irq;
|
||||
struct irq_fwspec *fwspec = data;
|
||||
struct irq_fwspec p_fwspec;
|
||||
irq_hw_number_t hwirq;
|
||||
int bank;
|
||||
u32 event_trg;
|
||||
struct irq_chip *chip;
|
||||
|
||||
hwirq = fwspec->param[0];
|
||||
if (hwirq >= host_data->drv_data->bank_nr * IRQS_PER_BANK)
|
||||
return -EINVAL;
|
||||
|
||||
bank = hwirq / IRQS_PER_BANK;
|
||||
chip_data = &host_data->chips_data[bank];
|
||||
|
||||
event_trg = readl_relaxed(host_data->base + chip_data->reg_bank->trg_ofst);
|
||||
chip = (event_trg & BIT(hwirq % IRQS_PER_BANK)) ?
|
||||
&stm32_exti_h_chip : &stm32_exti_h_chip_direct;
|
||||
|
||||
desc = stm32_exti_get_desc(host_data->drv_data, hwirq);
|
||||
if (!desc)
|
||||
irq_domain_set_hwirq_and_chip(dm, virq, hwirq, chip, chip_data);
|
||||
|
||||
if (!host_data->drv_data || !host_data->drv_data->desc_irqs)
|
||||
return -EINVAL;
|
||||
|
||||
irq_domain_set_hwirq_and_chip(dm, virq, hwirq, desc->chip,
|
||||
chip_data);
|
||||
if (desc->irq_parent) {
|
||||
desc_irq = host_data->drv_data->desc_irqs[hwirq];
|
||||
if (desc_irq != EXTI_INVALID_IRQ) {
|
||||
p_fwspec.fwnode = dm->parent->fwnode;
|
||||
p_fwspec.param_count = 3;
|
||||
p_fwspec.param[0] = GIC_SPI;
|
||||
p_fwspec.param[1] = desc->irq_parent;
|
||||
p_fwspec.param[1] = desc_irq;
|
||||
p_fwspec.param[2] = IRQ_TYPE_LEVEL_HIGH;
|
||||
|
||||
return irq_domain_alloc_irqs_parent(dm, virq, 1, &p_fwspec);
|
||||
@ -792,7 +789,8 @@ stm32_exti_chip_data *stm32_exti_chip_init(struct stm32_exti_host_data *h_data,
|
||||
* clear registers to avoid residue
|
||||
*/
|
||||
writel_relaxed(0, base + stm32_bank->imr_ofst);
|
||||
writel_relaxed(0, base + stm32_bank->emr_ofst);
|
||||
if (stm32_bank->emr_ofst != UNDEF_REG)
|
||||
writel_relaxed(0, base + stm32_bank->emr_ofst);
|
||||
|
||||
pr_info("%pOF: bank%d\n", node, bank_idx);
|
||||
|
||||
|
@ -677,7 +677,7 @@ static int iosapic_set_affinity_irq(struct irq_data *d,
|
||||
if (dest_cpu < 0)
|
||||
return -1;
|
||||
|
||||
cpumask_copy(irq_data_get_affinity_mask(d), cpumask_of(dest_cpu));
|
||||
irq_data_update_affinity(d, cpumask_of(dest_cpu));
|
||||
vi->txn_addr = txn_affinity_addr(d->irq, dest_cpu);
|
||||
|
||||
spin_lock_irqsave(&iosapic_lock, flags);
|
||||
|
@ -642,7 +642,7 @@ static void hv_arch_irq_unmask(struct irq_data *data)
|
||||
struct hv_retarget_device_interrupt *params;
|
||||
struct tran_int_desc *int_desc;
|
||||
struct hv_pcibus_device *hbus;
|
||||
struct cpumask *dest;
|
||||
const struct cpumask *dest;
|
||||
cpumask_var_t tmp;
|
||||
struct pci_bus *pbus;
|
||||
struct pci_dev *pdev;
|
||||
@ -1613,7 +1613,7 @@ out:
|
||||
}
|
||||
|
||||
static u32 hv_compose_msi_req_v1(
|
||||
struct pci_create_interrupt *int_pkt, struct cpumask *affinity,
|
||||
struct pci_create_interrupt *int_pkt, const struct cpumask *affinity,
|
||||
u32 slot, u8 vector, u8 vector_count)
|
||||
{
|
||||
int_pkt->message_type.type = PCI_CREATE_INTERRUPT_MESSAGE;
|
||||
@ -1635,13 +1635,13 @@ static u32 hv_compose_msi_req_v1(
|
||||
* Create MSI w/ dummy vCPU set targeting just one vCPU, overwritten
|
||||
* by subsequent retarget in hv_irq_unmask().
|
||||
*/
|
||||
static int hv_compose_msi_req_get_cpu(struct cpumask *affinity)
|
||||
static int hv_compose_msi_req_get_cpu(const struct cpumask *affinity)
|
||||
{
|
||||
return cpumask_first_and(affinity, cpu_online_mask);
|
||||
}
|
||||
|
||||
static u32 hv_compose_msi_req_v2(
|
||||
struct pci_create_interrupt2 *int_pkt, struct cpumask *affinity,
|
||||
struct pci_create_interrupt2 *int_pkt, const struct cpumask *affinity,
|
||||
u32 slot, u8 vector, u8 vector_count)
|
||||
{
|
||||
int cpu;
|
||||
@ -1660,7 +1660,7 @@ static u32 hv_compose_msi_req_v2(
|
||||
}
|
||||
|
||||
static u32 hv_compose_msi_req_v3(
|
||||
struct pci_create_interrupt3 *int_pkt, struct cpumask *affinity,
|
||||
struct pci_create_interrupt3 *int_pkt, const struct cpumask *affinity,
|
||||
u32 slot, u32 vector, u8 vector_count)
|
||||
{
|
||||
int cpu;
|
||||
@ -1697,7 +1697,7 @@ static void hv_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
|
||||
struct hv_pci_dev *hpdev;
|
||||
struct pci_bus *pbus;
|
||||
struct pci_dev *pdev;
|
||||
struct cpumask *dest;
|
||||
const struct cpumask *dest;
|
||||
struct compose_comp_ctxt comp;
|
||||
struct tran_int_desc *int_desc;
|
||||
struct msi_desc *msi_desc;
|
||||
|
@ -1810,6 +1810,7 @@ static void ocelot_irq_mask(struct irq_data *data)
|
||||
|
||||
regmap_update_bits(info->map, REG(OCELOT_GPIO_INTR_ENA, info, gpio),
|
||||
BIT(gpio % 32), 0);
|
||||
gpiochip_disable_irq(chip, gpio);
|
||||
}
|
||||
|
||||
static void ocelot_irq_unmask(struct irq_data *data)
|
||||
@ -1818,6 +1819,7 @@ static void ocelot_irq_unmask(struct irq_data *data)
|
||||
struct ocelot_pinctrl *info = gpiochip_get_data(chip);
|
||||
unsigned int gpio = irqd_to_hwirq(data);
|
||||
|
||||
gpiochip_enable_irq(chip, gpio);
|
||||
regmap_update_bits(info->map, REG(OCELOT_GPIO_INTR_ENA, info, gpio),
|
||||
BIT(gpio % 32), BIT(gpio % 32));
|
||||
}
|
||||
@ -1839,8 +1841,10 @@ static struct irq_chip ocelot_eoi_irqchip = {
|
||||
.irq_mask = ocelot_irq_mask,
|
||||
.irq_eoi = ocelot_irq_ack,
|
||||
.irq_unmask = ocelot_irq_unmask,
|
||||
.flags = IRQCHIP_EOI_THREADED | IRQCHIP_EOI_IF_HANDLED,
|
||||
.flags = IRQCHIP_EOI_THREADED | IRQCHIP_EOI_IF_HANDLED |
|
||||
IRQCHIP_IMMUTABLE,
|
||||
.irq_set_type = ocelot_irq_set_type,
|
||||
GPIOCHIP_IRQ_RESOURCE_HELPERS
|
||||
};
|
||||
|
||||
static struct irq_chip ocelot_irqchip = {
|
||||
@ -1849,6 +1853,8 @@ static struct irq_chip ocelot_irqchip = {
|
||||
.irq_ack = ocelot_irq_ack,
|
||||
.irq_unmask = ocelot_irq_unmask,
|
||||
.irq_set_type = ocelot_irq_set_type,
|
||||
.flags = IRQCHIP_IMMUTABLE,
|
||||
GPIOCHIP_IRQ_RESOURCE_HELPERS
|
||||
};
|
||||
|
||||
static int ocelot_irq_set_type(struct irq_data *data, unsigned int type)
|
||||
@ -1912,7 +1918,7 @@ static int ocelot_gpiochip_register(struct platform_device *pdev,
|
||||
irq = platform_get_irq_optional(pdev, 0);
|
||||
if (irq > 0) {
|
||||
girq = &gc->irq;
|
||||
girq->chip = &ocelot_irqchip;
|
||||
gpio_irq_chip_set_chip(girq, &ocelot_irqchip);
|
||||
girq->parent_handler = ocelot_irq_handler;
|
||||
girq->num_parents = 1;
|
||||
girq->parents = devm_kcalloc(&pdev->dev, 1,
|
||||
|
@ -966,16 +966,13 @@ static int pmic_gpio_child_to_parent_hwirq(struct gpio_chip *chip,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *pmic_gpio_populate_parent_fwspec(struct gpio_chip *chip,
|
||||
unsigned int parent_hwirq,
|
||||
unsigned int parent_type)
|
||||
static int pmic_gpio_populate_parent_fwspec(struct gpio_chip *chip,
|
||||
union gpio_irq_fwspec *gfwspec,
|
||||
unsigned int parent_hwirq,
|
||||
unsigned int parent_type)
|
||||
{
|
||||
struct pmic_gpio_state *state = gpiochip_get_data(chip);
|
||||
struct irq_fwspec *fwspec;
|
||||
|
||||
fwspec = kzalloc(sizeof(*fwspec), GFP_KERNEL);
|
||||
if (!fwspec)
|
||||
return NULL;
|
||||
struct irq_fwspec *fwspec = &gfwspec->fwspec;
|
||||
|
||||
fwspec->fwnode = chip->irq.parent_domain->fwnode;
|
||||
|
||||
@ -985,7 +982,7 @@ static void *pmic_gpio_populate_parent_fwspec(struct gpio_chip *chip,
|
||||
/* param[2] must be left as 0 */
|
||||
fwspec->param[3] = parent_type;
|
||||
|
||||
return fwspec;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pmic_gpio_probe(struct platform_device *pdev)
|
||||
|
@ -9,8 +9,10 @@
|
||||
#include <linux/clk.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/pinctrl/pinconf-generic.h>
|
||||
#include <linux/pinctrl/pinconf.h>
|
||||
#include <linux/pinctrl/pinctrl.h>
|
||||
@ -89,6 +91,7 @@
|
||||
#define PIN(n) (0x0800 + 0x10 + (n))
|
||||
#define IOLH(n) (0x1000 + (n) * 8)
|
||||
#define IEN(n) (0x1800 + (n) * 8)
|
||||
#define ISEL(n) (0x2c80 + (n) * 8)
|
||||
#define PWPR (0x3014)
|
||||
#define SD_CH(n) (0x3000 + (n) * 4)
|
||||
#define QSPI (0x3008)
|
||||
@ -112,6 +115,10 @@
|
||||
#define RZG2L_PIN_ID_TO_PORT_OFFSET(id) (RZG2L_PIN_ID_TO_PORT(id) + 0x10)
|
||||
#define RZG2L_PIN_ID_TO_PIN(id) ((id) % RZG2L_PINS_PER_PORT)
|
||||
|
||||
#define RZG2L_TINT_MAX_INTERRUPT 32
|
||||
#define RZG2L_TINT_IRQ_START_INDEX 9
|
||||
#define RZG2L_PACK_HWIRQ(t, i) (((t) << 16) | (i))
|
||||
|
||||
struct rzg2l_dedicated_configs {
|
||||
const char *name;
|
||||
u32 config;
|
||||
@ -137,6 +144,9 @@ struct rzg2l_pinctrl {
|
||||
|
||||
struct gpio_chip gpio_chip;
|
||||
struct pinctrl_gpio_range gpio_range;
|
||||
DECLARE_BITMAP(tint_slot, RZG2L_TINT_MAX_INTERRUPT);
|
||||
spinlock_t bitmap_lock;
|
||||
unsigned int hwirq[RZG2L_TINT_MAX_INTERRUPT];
|
||||
|
||||
spinlock_t lock;
|
||||
};
|
||||
@ -883,8 +893,14 @@ static int rzg2l_gpio_get(struct gpio_chip *chip, unsigned int offset)
|
||||
|
||||
static void rzg2l_gpio_free(struct gpio_chip *chip, unsigned int offset)
|
||||
{
|
||||
unsigned int virq;
|
||||
|
||||
pinctrl_gpio_free(chip->base + offset);
|
||||
|
||||
virq = irq_find_mapping(chip->irq.domain, offset);
|
||||
if (virq)
|
||||
irq_dispose_mapping(virq);
|
||||
|
||||
/*
|
||||
* Set the GPIO as an input to ensure that the next GPIO request won't
|
||||
* drive the GPIO pin as an output.
|
||||
@ -1104,14 +1120,221 @@ static struct {
|
||||
}
|
||||
};
|
||||
|
||||
static int rzg2l_gpio_get_gpioint(unsigned int virq)
|
||||
{
|
||||
unsigned int gpioint;
|
||||
unsigned int i;
|
||||
u32 port, bit;
|
||||
|
||||
port = virq / 8;
|
||||
bit = virq % 8;
|
||||
|
||||
if (port >= ARRAY_SIZE(rzg2l_gpio_configs) ||
|
||||
bit >= RZG2L_GPIO_PORT_GET_PINCNT(rzg2l_gpio_configs[port]))
|
||||
return -EINVAL;
|
||||
|
||||
gpioint = bit;
|
||||
for (i = 0; i < port; i++)
|
||||
gpioint += RZG2L_GPIO_PORT_GET_PINCNT(rzg2l_gpio_configs[i]);
|
||||
|
||||
return gpioint;
|
||||
}
|
||||
|
||||
static void rzg2l_gpio_irq_disable(struct irq_data *d)
|
||||
{
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct rzg2l_pinctrl *pctrl = container_of(gc, struct rzg2l_pinctrl, gpio_chip);
|
||||
unsigned int hwirq = irqd_to_hwirq(d);
|
||||
unsigned long flags;
|
||||
void __iomem *addr;
|
||||
u32 port;
|
||||
u8 bit;
|
||||
|
||||
port = RZG2L_PIN_ID_TO_PORT(hwirq);
|
||||
bit = RZG2L_PIN_ID_TO_PIN(hwirq);
|
||||
|
||||
addr = pctrl->base + ISEL(port);
|
||||
if (bit >= 4) {
|
||||
bit -= 4;
|
||||
addr += 4;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&pctrl->lock, flags);
|
||||
writel(readl(addr) & ~BIT(bit * 8), addr);
|
||||
spin_unlock_irqrestore(&pctrl->lock, flags);
|
||||
|
||||
gpiochip_disable_irq(gc, hwirq);
|
||||
irq_chip_disable_parent(d);
|
||||
}
|
||||
|
||||
static void rzg2l_gpio_irq_enable(struct irq_data *d)
|
||||
{
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct rzg2l_pinctrl *pctrl = container_of(gc, struct rzg2l_pinctrl, gpio_chip);
|
||||
unsigned int hwirq = irqd_to_hwirq(d);
|
||||
unsigned long flags;
|
||||
void __iomem *addr;
|
||||
u32 port;
|
||||
u8 bit;
|
||||
|
||||
gpiochip_enable_irq(gc, hwirq);
|
||||
|
||||
port = RZG2L_PIN_ID_TO_PORT(hwirq);
|
||||
bit = RZG2L_PIN_ID_TO_PIN(hwirq);
|
||||
|
||||
addr = pctrl->base + ISEL(port);
|
||||
if (bit >= 4) {
|
||||
bit -= 4;
|
||||
addr += 4;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&pctrl->lock, flags);
|
||||
writel(readl(addr) | BIT(bit * 8), addr);
|
||||
spin_unlock_irqrestore(&pctrl->lock, flags);
|
||||
|
||||
irq_chip_enable_parent(d);
|
||||
}
|
||||
|
||||
static int rzg2l_gpio_irq_set_type(struct irq_data *d, unsigned int type)
|
||||
{
|
||||
return irq_chip_set_type_parent(d, type);
|
||||
}
|
||||
|
||||
static void rzg2l_gpio_irqc_eoi(struct irq_data *d)
|
||||
{
|
||||
irq_chip_eoi_parent(d);
|
||||
}
|
||||
|
||||
static void rzg2l_gpio_irq_print_chip(struct irq_data *data, struct seq_file *p)
|
||||
{
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(data);
|
||||
|
||||
seq_printf(p, dev_name(gc->parent));
|
||||
}
|
||||
|
||||
static const struct irq_chip rzg2l_gpio_irqchip = {
|
||||
.name = "rzg2l-gpio",
|
||||
.irq_disable = rzg2l_gpio_irq_disable,
|
||||
.irq_enable = rzg2l_gpio_irq_enable,
|
||||
.irq_mask = irq_chip_mask_parent,
|
||||
.irq_unmask = irq_chip_unmask_parent,
|
||||
.irq_set_type = rzg2l_gpio_irq_set_type,
|
||||
.irq_eoi = rzg2l_gpio_irqc_eoi,
|
||||
.irq_print_chip = rzg2l_gpio_irq_print_chip,
|
||||
.flags = IRQCHIP_IMMUTABLE,
|
||||
GPIOCHIP_IRQ_RESOURCE_HELPERS,
|
||||
};
|
||||
|
||||
static int rzg2l_gpio_child_to_parent_hwirq(struct gpio_chip *gc,
|
||||
unsigned int child,
|
||||
unsigned int child_type,
|
||||
unsigned int *parent,
|
||||
unsigned int *parent_type)
|
||||
{
|
||||
struct rzg2l_pinctrl *pctrl = gpiochip_get_data(gc);
|
||||
unsigned long flags;
|
||||
int gpioint, irq;
|
||||
|
||||
gpioint = rzg2l_gpio_get_gpioint(child);
|
||||
if (gpioint < 0)
|
||||
return gpioint;
|
||||
|
||||
spin_lock_irqsave(&pctrl->bitmap_lock, flags);
|
||||
irq = bitmap_find_free_region(pctrl->tint_slot, RZG2L_TINT_MAX_INTERRUPT, get_order(1));
|
||||
spin_unlock_irqrestore(&pctrl->bitmap_lock, flags);
|
||||
if (irq < 0)
|
||||
return -ENOSPC;
|
||||
pctrl->hwirq[irq] = child;
|
||||
irq += RZG2L_TINT_IRQ_START_INDEX;
|
||||
|
||||
/* All these interrupts are level high in the CPU */
|
||||
*parent_type = IRQ_TYPE_LEVEL_HIGH;
|
||||
*parent = RZG2L_PACK_HWIRQ(gpioint, irq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rzg2l_gpio_populate_parent_fwspec(struct gpio_chip *chip,
|
||||
union gpio_irq_fwspec *gfwspec,
|
||||
unsigned int parent_hwirq,
|
||||
unsigned int parent_type)
|
||||
{
|
||||
struct irq_fwspec *fwspec = &gfwspec->fwspec;
|
||||
|
||||
fwspec->fwnode = chip->irq.parent_domain->fwnode;
|
||||
fwspec->param_count = 2;
|
||||
fwspec->param[0] = parent_hwirq;
|
||||
fwspec->param[1] = parent_type;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rzg2l_gpio_irq_domain_free(struct irq_domain *domain, unsigned int virq,
|
||||
unsigned int nr_irqs)
|
||||
{
|
||||
struct irq_data *d;
|
||||
|
||||
d = irq_domain_get_irq_data(domain, virq);
|
||||
if (d) {
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct rzg2l_pinctrl *pctrl = container_of(gc, struct rzg2l_pinctrl, gpio_chip);
|
||||
irq_hw_number_t hwirq = irqd_to_hwirq(d);
|
||||
unsigned long flags;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < RZG2L_TINT_MAX_INTERRUPT; i++) {
|
||||
if (pctrl->hwirq[i] == hwirq) {
|
||||
spin_lock_irqsave(&pctrl->bitmap_lock, flags);
|
||||
bitmap_release_region(pctrl->tint_slot, i, get_order(1));
|
||||
spin_unlock_irqrestore(&pctrl->bitmap_lock, flags);
|
||||
pctrl->hwirq[i] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
irq_domain_free_irqs_common(domain, virq, nr_irqs);
|
||||
}
|
||||
|
||||
static void rzg2l_init_irq_valid_mask(struct gpio_chip *gc,
|
||||
unsigned long *valid_mask,
|
||||
unsigned int ngpios)
|
||||
{
|
||||
struct rzg2l_pinctrl *pctrl = gpiochip_get_data(gc);
|
||||
struct gpio_chip *chip = &pctrl->gpio_chip;
|
||||
unsigned int offset;
|
||||
|
||||
/* Forbid unused lines to be mapped as IRQs */
|
||||
for (offset = 0; offset < chip->ngpio; offset++) {
|
||||
u32 port, bit;
|
||||
|
||||
port = offset / 8;
|
||||
bit = offset % 8;
|
||||
|
||||
if (port >= ARRAY_SIZE(rzg2l_gpio_configs) ||
|
||||
bit >= RZG2L_GPIO_PORT_GET_PINCNT(rzg2l_gpio_configs[port]))
|
||||
clear_bit(offset, valid_mask);
|
||||
}
|
||||
}
|
||||
|
||||
static int rzg2l_gpio_register(struct rzg2l_pinctrl *pctrl)
|
||||
{
|
||||
struct device_node *np = pctrl->dev->of_node;
|
||||
struct gpio_chip *chip = &pctrl->gpio_chip;
|
||||
const char *name = dev_name(pctrl->dev);
|
||||
struct irq_domain *parent_domain;
|
||||
struct of_phandle_args of_args;
|
||||
struct device_node *parent_np;
|
||||
struct gpio_irq_chip *girq;
|
||||
int ret;
|
||||
|
||||
parent_np = of_irq_find_parent(np);
|
||||
if (!parent_np)
|
||||
return -ENXIO;
|
||||
|
||||
parent_domain = irq_find_host(parent_np);
|
||||
of_node_put(parent_np);
|
||||
if (!parent_domain)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
ret = of_parse_phandle_with_fixed_args(np, "gpio-ranges", 3, 0, &of_args);
|
||||
if (ret) {
|
||||
dev_err(pctrl->dev, "Unable to parse gpio-ranges\n");
|
||||
@ -1138,6 +1361,15 @@ static int rzg2l_gpio_register(struct rzg2l_pinctrl *pctrl)
|
||||
chip->base = -1;
|
||||
chip->ngpio = of_args.args[2];
|
||||
|
||||
girq = &chip->irq;
|
||||
gpio_irq_chip_set_chip(girq, &rzg2l_gpio_irqchip);
|
||||
girq->fwnode = of_node_to_fwnode(np);
|
||||
girq->parent_domain = parent_domain;
|
||||
girq->child_to_parent_hwirq = rzg2l_gpio_child_to_parent_hwirq;
|
||||
girq->populate_parent_alloc_arg = rzg2l_gpio_populate_parent_fwspec;
|
||||
girq->child_irq_domain_ops.free = rzg2l_gpio_irq_domain_free;
|
||||
girq->init_valid_mask = rzg2l_init_irq_valid_mask;
|
||||
|
||||
pctrl->gpio_range.id = 0;
|
||||
pctrl->gpio_range.pin_base = 0;
|
||||
pctrl->gpio_range.base = 0;
|
||||
@ -1253,6 +1485,7 @@ static int rzg2l_pinctrl_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
spin_lock_init(&pctrl->lock);
|
||||
spin_lock_init(&pctrl->bitmap_lock);
|
||||
|
||||
platform_set_drvdata(pdev, pctrl);
|
||||
|
||||
|
@ -72,7 +72,7 @@ static int intc_set_affinity(struct irq_data *data,
|
||||
if (!cpumask_intersects(cpumask, cpu_online_mask))
|
||||
return -1;
|
||||
|
||||
cpumask_copy(irq_data_get_affinity_mask(data), cpumask);
|
||||
irq_data_update_affinity(data, cpumask);
|
||||
|
||||
return IRQ_SET_MASK_OK_NOCOPY;
|
||||
}
|
||||
|
@ -528,9 +528,10 @@ static void bind_evtchn_to_cpu(evtchn_port_t evtchn, unsigned int cpu,
|
||||
BUG_ON(irq == -1);
|
||||
|
||||
if (IS_ENABLED(CONFIG_SMP) && force_affinity) {
|
||||
cpumask_copy(irq_get_affinity_mask(irq), cpumask_of(cpu));
|
||||
cpumask_copy(irq_get_effective_affinity_mask(irq),
|
||||
cpumask_of(cpu));
|
||||
struct irq_data *data = irq_get_irq_data(irq);
|
||||
|
||||
irq_data_update_affinity(data, cpumask_of(cpu));
|
||||
irq_data_update_effective_affinity(data, cpumask_of(cpu));
|
||||
}
|
||||
|
||||
xen_evtchn_port_bind_to_cpu(evtchn, cpu, info->cpu);
|
||||
|
@ -105,6 +105,7 @@ enum acpi_irq_model_id {
|
||||
ACPI_IRQ_MODEL_IOSAPIC,
|
||||
ACPI_IRQ_MODEL_PLATFORM,
|
||||
ACPI_IRQ_MODEL_GIC,
|
||||
ACPI_IRQ_MODEL_LPIC,
|
||||
ACPI_IRQ_MODEL_COUNT
|
||||
};
|
||||
|
||||
@ -356,7 +357,8 @@ int acpi_gsi_to_irq (u32 gsi, unsigned int *irq);
|
||||
int acpi_isa_irq_to_gsi (unsigned isa_irq, u32 *gsi);
|
||||
|
||||
void acpi_set_irq_model(enum acpi_irq_model_id model,
|
||||
struct fwnode_handle *fwnode);
|
||||
struct fwnode_handle *(*)(u32));
|
||||
void acpi_set_gsi_to_irq_fallback(u32 (*)(u32));
|
||||
|
||||
struct irq_domain *acpi_irq_create_hierarchy(unsigned int flags,
|
||||
unsigned int size,
|
||||
|
@ -150,6 +150,7 @@ enum cpuhp_state {
|
||||
CPUHP_AP_IRQ_BCM2836_STARTING,
|
||||
CPUHP_AP_IRQ_MIPS_GIC_STARTING,
|
||||
CPUHP_AP_IRQ_RISCV_STARTING,
|
||||
CPUHP_AP_IRQ_LOONGARCH_STARTING,
|
||||
CPUHP_AP_IRQ_SIFIVE_PLIC_STARTING,
|
||||
CPUHP_AP_ARM_MVEBU_COHERENCY,
|
||||
CPUHP_AP_MICROCODE_LOADER,
|
||||
|
@ -12,6 +12,8 @@
|
||||
#include <linux/property.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <asm/msi.h>
|
||||
|
||||
struct gpio_desc;
|
||||
struct of_phandle_args;
|
||||
struct device_node;
|
||||
@ -23,6 +25,13 @@ enum gpio_lookup_flags;
|
||||
|
||||
struct gpio_chip;
|
||||
|
||||
union gpio_irq_fwspec {
|
||||
struct irq_fwspec fwspec;
|
||||
#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN
|
||||
msi_alloc_info_t msiinfo;
|
||||
#endif
|
||||
};
|
||||
|
||||
#define GPIO_LINE_DIRECTION_IN 1
|
||||
#define GPIO_LINE_DIRECTION_OUT 0
|
||||
|
||||
@ -103,9 +112,10 @@ struct gpio_irq_chip {
|
||||
* variant named &gpiochip_populate_parent_fwspec_fourcell is also
|
||||
* available.
|
||||
*/
|
||||
void *(*populate_parent_alloc_arg)(struct gpio_chip *gc,
|
||||
unsigned int parent_hwirq,
|
||||
unsigned int parent_type);
|
||||
int (*populate_parent_alloc_arg)(struct gpio_chip *gc,
|
||||
union gpio_irq_fwspec *fwspec,
|
||||
unsigned int parent_hwirq,
|
||||
unsigned int parent_type);
|
||||
|
||||
/**
|
||||
* @child_offset_to_irq:
|
||||
@ -649,28 +659,14 @@ struct bgpio_pdata {
|
||||
|
||||
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
|
||||
|
||||
void *gpiochip_populate_parent_fwspec_twocell(struct gpio_chip *gc,
|
||||
int gpiochip_populate_parent_fwspec_twocell(struct gpio_chip *gc,
|
||||
union gpio_irq_fwspec *gfwspec,
|
||||
unsigned int parent_hwirq,
|
||||
unsigned int parent_type);
|
||||
int gpiochip_populate_parent_fwspec_fourcell(struct gpio_chip *gc,
|
||||
union gpio_irq_fwspec *gfwspec,
|
||||
unsigned int parent_hwirq,
|
||||
unsigned int parent_type);
|
||||
void *gpiochip_populate_parent_fwspec_fourcell(struct gpio_chip *gc,
|
||||
unsigned int parent_hwirq,
|
||||
unsigned int parent_type);
|
||||
|
||||
#else
|
||||
|
||||
static inline void *gpiochip_populate_parent_fwspec_twocell(struct gpio_chip *gc,
|
||||
unsigned int parent_hwirq,
|
||||
unsigned int parent_type)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void *gpiochip_populate_parent_fwspec_fourcell(struct gpio_chip *gc,
|
||||
unsigned int parent_hwirq,
|
||||
unsigned int parent_type)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_IRQ_DOMAIN_HIERARCHY */
|
||||
|
||||
|
@ -151,7 +151,9 @@ struct irq_common_data {
|
||||
#endif
|
||||
void *handler_data;
|
||||
struct msi_desc *msi_desc;
|
||||
#ifdef CONFIG_SMP
|
||||
cpumask_var_t affinity;
|
||||
#endif
|
||||
#ifdef CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK
|
||||
cpumask_var_t effective_affinity;
|
||||
#endif
|
||||
@ -879,21 +881,34 @@ static inline int irq_data_get_node(struct irq_data *d)
|
||||
return irq_common_data_get_node(d->common);
|
||||
}
|
||||
|
||||
static inline struct cpumask *irq_get_affinity_mask(int irq)
|
||||
static inline
|
||||
const struct cpumask *irq_data_get_affinity_mask(struct irq_data *d)
|
||||
{
|
||||
#ifdef CONFIG_SMP
|
||||
return d->common->affinity;
|
||||
#else
|
||||
return cpumask_of(0);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void irq_data_update_affinity(struct irq_data *d,
|
||||
const struct cpumask *m)
|
||||
{
|
||||
#ifdef CONFIG_SMP
|
||||
cpumask_copy(d->common->affinity, m);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline const struct cpumask *irq_get_affinity_mask(int irq)
|
||||
{
|
||||
struct irq_data *d = irq_get_irq_data(irq);
|
||||
|
||||
return d ? d->common->affinity : NULL;
|
||||
}
|
||||
|
||||
static inline struct cpumask *irq_data_get_affinity_mask(struct irq_data *d)
|
||||
{
|
||||
return d->common->affinity;
|
||||
return d ? irq_data_get_affinity_mask(d) : NULL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK
|
||||
static inline
|
||||
struct cpumask *irq_data_get_effective_affinity_mask(struct irq_data *d)
|
||||
const struct cpumask *irq_data_get_effective_affinity_mask(struct irq_data *d)
|
||||
{
|
||||
return d->common->effective_affinity;
|
||||
}
|
||||
@ -908,13 +923,14 @@ static inline void irq_data_update_effective_affinity(struct irq_data *d,
|
||||
{
|
||||
}
|
||||
static inline
|
||||
struct cpumask *irq_data_get_effective_affinity_mask(struct irq_data *d)
|
||||
const struct cpumask *irq_data_get_effective_affinity_mask(struct irq_data *d)
|
||||
{
|
||||
return d->common->affinity;
|
||||
return irq_data_get_affinity_mask(d);
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline struct cpumask *irq_get_effective_affinity_mask(unsigned int irq)
|
||||
static inline
|
||||
const struct cpumask *irq_get_effective_affinity_mask(unsigned int irq)
|
||||
{
|
||||
struct irq_data *d = irq_get_irq_data(irq);
|
||||
|
||||
@ -1121,6 +1137,7 @@ int irq_gc_set_wake(struct irq_data *d, unsigned int on);
|
||||
/* Setup functions for irq_chip_generic */
|
||||
int irq_map_generic_chip(struct irq_domain *d, unsigned int virq,
|
||||
irq_hw_number_t hw_irq);
|
||||
void irq_unmap_generic_chip(struct irq_domain *d, unsigned int virq);
|
||||
struct irq_chip_generic *
|
||||
irq_alloc_generic_chip(const char *name, int nr_ct, unsigned int irq_base,
|
||||
void __iomem *reg_base, irq_flow_handler_t handler);
|
||||
|
@ -4,4 +4,7 @@
|
||||
|
||||
extern struct irq_chip icu_irq_chip;
|
||||
|
||||
extern void icu_init_irq(void);
|
||||
extern void mmp2_init_icu(void);
|
||||
|
||||
#endif /* __IRQCHIP_MMP_H */
|
||||
|
@ -209,14 +209,15 @@ static inline void irq_set_handler_locked(struct irq_data *data,
|
||||
* Must be called with irq_desc locked and valid parameters.
|
||||
*/
|
||||
static inline void
|
||||
irq_set_chip_handler_name_locked(struct irq_data *data, struct irq_chip *chip,
|
||||
irq_set_chip_handler_name_locked(struct irq_data *data,
|
||||
const struct irq_chip *chip,
|
||||
irq_flow_handler_t handler, const char *name)
|
||||
{
|
||||
struct irq_desc *desc = irq_data_to_desc(data);
|
||||
|
||||
desc->handle_irq = handler;
|
||||
desc->name = name;
|
||||
data->chip = chip;
|
||||
data->chip = (struct irq_chip *)chip;
|
||||
}
|
||||
|
||||
bool irq_check_status_bit(unsigned int irq, unsigned int bitmask);
|
||||
|
@ -24,6 +24,7 @@ config GENERIC_IRQ_SHOW_LEVEL
|
||||
|
||||
# Supports effective affinity mask
|
||||
config GENERIC_IRQ_EFFECTIVE_AFF_MASK
|
||||
depends on SMP
|
||||
bool
|
||||
|
||||
# Support for delayed migration from interrupt context
|
||||
@ -82,6 +83,7 @@ config IRQ_FASTEOI_HIERARCHY_HANDLERS
|
||||
# Generic IRQ IPI support
|
||||
config GENERIC_IRQ_IPI
|
||||
bool
|
||||
depends on SMP
|
||||
select IRQ_DOMAIN_HIERARCHY
|
||||
|
||||
# Generic MSI interrupt support
|
||||
|
@ -188,7 +188,8 @@ enum {
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
static int
|
||||
__irq_startup_managed(struct irq_desc *desc, struct cpumask *aff, bool force)
|
||||
__irq_startup_managed(struct irq_desc *desc, const struct cpumask *aff,
|
||||
bool force)
|
||||
{
|
||||
struct irq_data *d = irq_desc_get_irq_data(desc);
|
||||
|
||||
@ -224,7 +225,8 @@ __irq_startup_managed(struct irq_desc *desc, struct cpumask *aff, bool force)
|
||||
}
|
||||
#else
|
||||
static __always_inline int
|
||||
__irq_startup_managed(struct irq_desc *desc, struct cpumask *aff, bool force)
|
||||
__irq_startup_managed(struct irq_desc *desc, const struct cpumask *aff,
|
||||
bool force)
|
||||
{
|
||||
return IRQ_STARTUP_NORMAL;
|
||||
}
|
||||
@ -252,7 +254,7 @@ static int __irq_startup(struct irq_desc *desc)
|
||||
int irq_startup(struct irq_desc *desc, bool resend, bool force)
|
||||
{
|
||||
struct irq_data *d = irq_desc_get_irq_data(desc);
|
||||
struct cpumask *aff = irq_data_get_affinity_mask(d);
|
||||
const struct cpumask *aff = irq_data_get_affinity_mask(d);
|
||||
int ret = 0;
|
||||
|
||||
desc->depth = 0;
|
||||
@ -1516,7 +1518,8 @@ int irq_chip_request_resources_parent(struct irq_data *data)
|
||||
if (data->chip->irq_request_resources)
|
||||
return data->chip->irq_request_resources(data);
|
||||
|
||||
return -ENOSYS;
|
||||
/* no error on missing optional irq_chip::irq_request_resources */
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(irq_chip_request_resources_parent);
|
||||
|
||||
|
@ -30,7 +30,7 @@ static void irq_debug_show_bits(struct seq_file *m, int ind, unsigned int state,
|
||||
static void irq_debug_show_masks(struct seq_file *m, struct irq_desc *desc)
|
||||
{
|
||||
struct irq_data *data = irq_desc_get_irq_data(desc);
|
||||
struct cpumask *msk;
|
||||
const struct cpumask *msk;
|
||||
|
||||
msk = irq_data_get_affinity_mask(data);
|
||||
seq_printf(m, "affinity: %*pbl\n", cpumask_pr_args(msk));
|
||||
|
@ -431,7 +431,7 @@ int irq_map_generic_chip(struct irq_domain *d, unsigned int virq,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void irq_unmap_generic_chip(struct irq_domain *d, unsigned int virq)
|
||||
void irq_unmap_generic_chip(struct irq_domain *d, unsigned int virq)
|
||||
{
|
||||
struct irq_data *data = irq_domain_get_irq_data(d, virq);
|
||||
struct irq_domain_chip_generic *dgc = d->gc;
|
||||
|
@ -115,11 +115,11 @@ free_descs:
|
||||
int irq_destroy_ipi(unsigned int irq, const struct cpumask *dest)
|
||||
{
|
||||
struct irq_data *data = irq_get_irq_data(irq);
|
||||
struct cpumask *ipimask = data ? irq_data_get_affinity_mask(data) : NULL;
|
||||
const struct cpumask *ipimask;
|
||||
struct irq_domain *domain;
|
||||
unsigned int nr_irqs;
|
||||
|
||||
if (!irq || !data || !ipimask)
|
||||
if (!irq || !data)
|
||||
return -EINVAL;
|
||||
|
||||
domain = data->domain;
|
||||
@ -131,7 +131,8 @@ int irq_destroy_ipi(unsigned int irq, const struct cpumask *dest)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (WARN_ON(!cpumask_subset(dest, ipimask)))
|
||||
ipimask = irq_data_get_affinity_mask(data);
|
||||
if (!ipimask || WARN_ON(!cpumask_subset(dest, ipimask)))
|
||||
/*
|
||||
* Must be destroying a subset of CPUs to which this IPI
|
||||
* was set up to target
|
||||
@ -162,12 +163,13 @@ int irq_destroy_ipi(unsigned int irq, const struct cpumask *dest)
|
||||
irq_hw_number_t ipi_get_hwirq(unsigned int irq, unsigned int cpu)
|
||||
{
|
||||
struct irq_data *data = irq_get_irq_data(irq);
|
||||
struct cpumask *ipimask = data ? irq_data_get_affinity_mask(data) : NULL;
|
||||
const struct cpumask *ipimask;
|
||||
|
||||
if (!data || !ipimask || cpu >= nr_cpu_ids)
|
||||
if (!data || cpu >= nr_cpu_ids)
|
||||
return INVALID_HWIRQ;
|
||||
|
||||
if (!cpumask_test_cpu(cpu, ipimask))
|
||||
ipimask = irq_data_get_affinity_mask(data);
|
||||
if (!ipimask || !cpumask_test_cpu(cpu, ipimask))
|
||||
return INVALID_HWIRQ;
|
||||
|
||||
/*
|
||||
@ -186,7 +188,7 @@ EXPORT_SYMBOL_GPL(ipi_get_hwirq);
|
||||
static int ipi_send_verify(struct irq_chip *chip, struct irq_data *data,
|
||||
const struct cpumask *dest, unsigned int cpu)
|
||||
{
|
||||
struct cpumask *ipimask = irq_data_get_affinity_mask(data);
|
||||
const struct cpumask *ipimask = irq_data_get_affinity_mask(data);
|
||||
|
||||
if (!chip || !ipimask)
|
||||
return -EINVAL;
|
||||
|
@ -251,7 +251,7 @@ static ssize_t actions_show(struct kobject *kobj,
|
||||
char *p = "";
|
||||
|
||||
raw_spin_lock_irq(&desc->lock);
|
||||
for (action = desc->action; action != NULL; action = action->next) {
|
||||
for_each_action_of_desc(desc, action) {
|
||||
ret += scnprintf(buf + ret, PAGE_SIZE - ret, "%s%s",
|
||||
p, action->name);
|
||||
p = ",";
|
||||
|
@ -147,7 +147,8 @@ struct irq_domain *__irq_domain_add(struct fwnode_handle *fwnode, unsigned int s
|
||||
static atomic_t unknown_domains;
|
||||
|
||||
if (WARN_ON((size && direct_max) ||
|
||||
(!IS_ENABLED(CONFIG_IRQ_DOMAIN_NOMAP) && direct_max)))
|
||||
(!IS_ENABLED(CONFIG_IRQ_DOMAIN_NOMAP) && direct_max) ||
|
||||
(direct_max && (direct_max != hwirq_max))))
|
||||
return NULL;
|
||||
|
||||
domain = kzalloc_node(struct_size(domain, revmap, size),
|
||||
@ -219,7 +220,6 @@ struct irq_domain *__irq_domain_add(struct fwnode_handle *fwnode, unsigned int s
|
||||
domain->hwirq_max = hwirq_max;
|
||||
|
||||
if (direct_max) {
|
||||
size = direct_max;
|
||||
domain->flags |= IRQ_DOMAIN_FLAG_NO_MAP;
|
||||
}
|
||||
|
||||
@ -650,9 +650,9 @@ unsigned int irq_create_direct_mapping(struct irq_domain *domain)
|
||||
pr_debug("create_direct virq allocation failed\n");
|
||||
return 0;
|
||||
}
|
||||
if (virq >= domain->revmap_size) {
|
||||
pr_err("ERROR: no free irqs available below %i maximum\n",
|
||||
domain->revmap_size);
|
||||
if (virq >= domain->hwirq_max) {
|
||||
pr_err("ERROR: no free irqs available below %lu maximum\n",
|
||||
domain->hwirq_max);
|
||||
irq_free_desc(virq);
|
||||
return 0;
|
||||
}
|
||||
@ -906,10 +906,12 @@ struct irq_desc *__irq_resolve_mapping(struct irq_domain *domain,
|
||||
return desc;
|
||||
|
||||
if (irq_domain_is_nomap(domain)) {
|
||||
if (hwirq < domain->revmap_size) {
|
||||
if (hwirq < domain->hwirq_max) {
|
||||
data = irq_domain_get_irq_data(domain, hwirq);
|
||||
if (data && data->hwirq == hwirq)
|
||||
desc = irq_data_to_desc(data);
|
||||
if (irq && desc)
|
||||
*irq = hwirq;
|
||||
}
|
||||
|
||||
return desc;
|
||||
|
@ -205,16 +205,8 @@ static void irq_validate_effective_affinity(struct irq_data *data)
|
||||
pr_warn_once("irq_chip %s did not update eff. affinity mask of irq %u\n",
|
||||
chip->name, data->irq);
|
||||
}
|
||||
|
||||
static inline void irq_init_effective_affinity(struct irq_data *data,
|
||||
const struct cpumask *mask)
|
||||
{
|
||||
cpumask_copy(irq_data_get_effective_affinity_mask(data), mask);
|
||||
}
|
||||
#else
|
||||
static inline void irq_validate_effective_affinity(struct irq_data *data) { }
|
||||
static inline void irq_init_effective_affinity(struct irq_data *data,
|
||||
const struct cpumask *mask) { }
|
||||
#endif
|
||||
|
||||
int irq_do_set_affinity(struct irq_data *data, const struct cpumask *mask,
|
||||
@ -347,7 +339,7 @@ static bool irq_set_affinity_deactivated(struct irq_data *data,
|
||||
return false;
|
||||
|
||||
cpumask_copy(desc->irq_common_data.affinity, mask);
|
||||
irq_init_effective_affinity(data, mask);
|
||||
irq_data_update_effective_affinity(data, mask);
|
||||
irqd_set(data, IRQD_AFFINITY_SET);
|
||||
return true;
|
||||
}
|
||||
|
@ -147,7 +147,6 @@ void suspend_device_irqs(void)
|
||||
synchronize_irq(irq);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(suspend_device_irqs);
|
||||
|
||||
static void resume_irq(struct irq_desc *desc)
|
||||
{
|
||||
@ -259,4 +258,3 @@ void resume_device_irqs(void)
|
||||
{
|
||||
resume_irqs(false);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(resume_device_irqs);
|
||||
|
Loading…
Reference in New Issue
Block a user