diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-04-04 15:19:26 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-04-04 15:19:26 -0700 |
commit | 5b1f3dc927a2681cb339b05156f828f83bfa1b80 (patch) | |
tree | 7526719ba0a52b35e40ef0df51b39bf6a9c84471 | |
parent | 680014d6d1da84e9c7860831221ec74230721907 (diff) | |
parent | 83fbdf1c0595470d98ee99a6474099aee870640f (diff) |
Merge branch 'irq-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull irq updates from Thomas Gleixner:
"The usual pile of boring changes:
- Consolidate tasklet functions to share code instead of duplicating
it
- The first step for making the low level entry handler management on
multi-platform kernels generic
- A new sysfs file which allows to retrieve the wakeup state of
interrupts.
- Ensure that the interrupt thread follows the effective affinity and
not the programmed affinity to avoid cross core wakeups.
- Two new interrupt controller drivers (Microsemi Ocelot and Qualcomm
PDC)
- Fix the wakeup path clock handling for Reneasas interrupt chips.
- Rework the boot time register reset for ARM GIC-V2/3
- Better suspend/resume support for ARM GIV-V3/ITS
- Add missing locking to the ARM GIC set_type() callback
- Small fixes for the irq simulator code
- SPDX identifiers for the irq core code and removal of boiler plate
- Small cleanups all over the place"
* 'irq-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (37 commits)
openrisc: Set CONFIG_MULTI_IRQ_HANDLER
arm64: Set CONFIG_MULTI_IRQ_HANDLER
genirq: Make GENERIC_IRQ_MULTI_HANDLER depend on !MULTI_IRQ_HANDLER
irqchip/gic: Take lock when updating irq type
irqchip/gic: Update supports_deactivate static key to modern api
irqchip/gic-v3: Ensure GICR_CTLR.EnableLPI=0 is observed before enabling
irqchip: Add a driver for the Microsemi Ocelot controller
dt-bindings: interrupt-controller: Add binding for the Microsemi Ocelot interrupt controller
irqchip/gic-v3: Probe for SCR_EL3 being clear before resetting AP0Rn
irqchip/gic-v3: Don't try to reset AP0Rn
irqchip/gic-v3: Do not check trigger configuration of partitionned LPIs
genirq: Remove license boilerplate/references
genirq: Add missing SPDX identifiers
genirq/matrix: Cleanup SPDX identifier
genirq: Cleanup top of file comments
genirq: Pass desc to __irq_free instead of irq number
irqchip/gic-v3: Loudly complain about the use of IRQ_TYPE_NONE
irqchip/gic: Loudly complain about the use of IRQ_TYPE_NONE
RISC-V: Move to the new GENERIC_IRQ_MULTI_HANDLER handler
genirq: Add CONFIG_GENERIC_IRQ_MULTI_HANDLER
...
46 files changed, 1112 insertions, 277 deletions
diff --git a/Documentation/ABI/testing/sysfs-kernel-irq b/Documentation/ABI/testing/sysfs-kernel-irq index eb074b100986..8910d0c4bcd8 100644 --- a/Documentation/ABI/testing/sysfs-kernel-irq +++ b/Documentation/ABI/testing/sysfs-kernel-irq @@ -51,3 +51,10 @@ Date: September 2016 KernelVersion: 4.9 Contact: Craig Gallek <kraig@google.com> Description: The type of the interrupt. Either the string 'level' or 'edge'. + +What: /sys/kernel/irq/<irq>/wakeup +Date: March 2018 +KernelVersion: 4.17 +Contact: Andy Shevchenko <andriy.shevchenko@linux.intel.com> +Description: The wakeup state of the interrupt. Either the string + 'enabled' or 'disabled'. diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index bf03fe49f1ce..e6d33fa8fed4 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -1739,6 +1739,14 @@ of a GICv2 controller even if the memory range exposed by the device tree is too small. + irqchip.gicv3_nolpi= + [ARM, ARM64] + Force the kernel to ignore the availability of + LPIs (and by consequence ITSs). Intended for system + that use the kernel as a bootloader, and thus want + to let secondary kernels in charge of setting up + LPIs. + irqfixup [HW] When an interrupt is not handled search all handlers for it. Intended to get systems with badly broken diff --git a/Documentation/devicetree/bindings/interrupt-controller/mscc,ocelot-icpu-intr.txt b/Documentation/devicetree/bindings/interrupt-controller/mscc,ocelot-icpu-intr.txt new file mode 100644 index 000000000000..b47a8a02b17b --- /dev/null +++ b/Documentation/devicetree/bindings/interrupt-controller/mscc,ocelot-icpu-intr.txt @@ -0,0 +1,22 @@ +Microsemi Ocelot SoC ICPU Interrupt Controller + +Required properties: + +- compatible : should be "mscc,ocelot-icpu-intr" +- reg : Specifies base physical address and size of the registers. +- interrupt-controller : Identifies the node as an interrupt controller +- #interrupt-cells : Specifies the number of cells needed to encode an + interrupt source. The value shall be 1. +- interrupt-parent : phandle of the CPU interrupt controller. +- interrupts : Specifies the CPU interrupt the controller is connected to. + +Example: + + intc: interrupt-controller@70000070 { + compatible = "mscc,ocelot-icpu-intr"; + reg = <0x70000070 0x70>; + #interrupt-cells = <1>; + interrupt-controller; + interrupt-parent = <&cpuintc>; + interrupts = <2>; + }; diff --git a/Documentation/devicetree/bindings/interrupt-controller/qcom,pdc.txt b/Documentation/devicetree/bindings/interrupt-controller/qcom,pdc.txt new file mode 100644 index 000000000000..0b2c97ddb520 --- /dev/null +++ b/Documentation/devicetree/bindings/interrupt-controller/qcom,pdc.txt @@ -0,0 +1,78 @@ +PDC interrupt controller + +Qualcomm Technologies Inc. SoCs based on the RPM Hardened architecture have a +Power Domain Controller (PDC) that is on always-on domain. In addition to +providing power control for the power domains, the hardware also has an +interrupt controller that can be used to help detect edge low interrupts as +well detect interrupts when the GIC is non-operational. + +GIC is parent interrupt controller at the highest level. Platform interrupt +controller PDC is next in hierarchy, followed by others. Drivers requiring +wakeup capabilities of their device interrupts routed through the PDC, must +specify PDC as their interrupt controller and request the PDC port associated +with the GIC interrupt. See example below. + +Properties: + +- compatible: + Usage: required + Value type: <string> + Definition: Should contain "qcom,<soc>-pdc" + - "qcom,sdm845-pdc": For SDM845 + +- reg: + Usage: required + Value type: <prop-encoded-array> + Definition: Specifies the base physical address for PDC hardware. + +- interrupt-cells: + Usage: required + Value type: <u32> + Definition: Specifies the number of cells needed to encode an interrupt + source. + Must be 2. + The first element of the tuple is the PDC pin for the + interrupt. + The second element is the trigger type. + +- interrupt-parent: + Usage: required + Value type: <phandle> + Definition: Specifies the interrupt parent necessary for hierarchical + domain to operate. + +- interrupt-controller: + Usage: required + Value type: <bool> + Definition: Identifies the node as an interrupt controller. + +- qcom,pdc-ranges: + Usage: required + Value type: <u32 array> + Definition: Specifies the PDC pin offset and the number of PDC ports. + The tuples indicates the valid mapping of valid PDC ports + and their hwirq mapping. + The first element of the tuple is the starting PDC port. + The second element is the GIC hwirq number for the PDC port. + The third element is the number of interrupts in sequence. + +Example: + + pdc: interrupt-controller@b220000 { + compatible = "qcom,sdm845-pdc"; + reg = <0xb220000 0x30000>; + qcom,pdc-ranges = <0 512 94>, <94 641 15>, <115 662 7>; + #interrupt-cells = <2>; + interrupt-parent = <&intc>; + interrupt-controller; + }; + +DT binding of a device that wants to use the GIC SPI 514 as a wakeup +interrupt, must do - + + wake-device { + interrupts-extended = <&pdc 2 IRQ_TYPE_LEVEL_HIGH>; + }; + +In this case interrupt 514 would be mapped to port 2 on the PDC as defined by +the qcom,pdc-ranges property. diff --git a/arch/arm/include/asm/arch_gicv3.h b/arch/arm/include/asm/arch_gicv3.h index 1070044f5c3f..0bd530702118 100644 --- a/arch/arm/include/asm/arch_gicv3.h +++ b/arch/arm/include/asm/arch_gicv3.h @@ -35,6 +35,18 @@ #define ICC_IGRPEN1 __ACCESS_CP15(c12, 0, c12, 7) #define ICC_BPR1 __ACCESS_CP15(c12, 0, c12, 3) +#define __ICC_AP0Rx(x) __ACCESS_CP15(c12, 0, c8, 4 | x) +#define ICC_AP0R0 __ICC_AP0Rx(0) +#define ICC_AP0R1 __ICC_AP0Rx(1) +#define ICC_AP0R2 __ICC_AP0Rx(2) +#define ICC_AP0R3 __ICC_AP0Rx(3) + +#define __ICC_AP1Rx(x) __ACCESS_CP15(c12, 0, c9, x) +#define ICC_AP1R0 __ICC_AP1Rx(0) +#define ICC_AP1R1 __ICC_AP1Rx(1) +#define ICC_AP1R2 __ICC_AP1Rx(2) +#define ICC_AP1R3 __ICC_AP1Rx(3) + #define ICC_HSRE __ACCESS_CP15(c12, 4, c9, 5) #define ICH_VSEIR __ACCESS_CP15(c12, 4, c9, 4) @@ -86,17 +98,17 @@ #define ICH_LRC14 __LRC8(6) #define ICH_LRC15 __LRC8(7) -#define __AP0Rx(x) __ACCESS_CP15(c12, 4, c8, x) -#define ICH_AP0R0 __AP0Rx(0) -#define ICH_AP0R1 __AP0Rx(1) -#define ICH_AP0R2 __AP0Rx(2) -#define ICH_AP0R3 __AP0Rx(3) +#define __ICH_AP0Rx(x) __ACCESS_CP15(c12, 4, c8, x) +#define ICH_AP0R0 __ICH_AP0Rx(0) +#define ICH_AP0R1 __ICH_AP0Rx(1) +#define ICH_AP0R2 __ICH_AP0Rx(2) +#define ICH_AP0R3 __ICH_AP0Rx(3) -#define __AP1Rx(x) __ACCESS_CP15(c12, 4, c9, x) -#define ICH_AP1R0 __AP1Rx(0) -#define ICH_AP1R1 __AP1Rx(1) -#define ICH_AP1R2 __AP1Rx(2) -#define ICH_AP1R3 __AP1Rx(3) +#define __ICH_AP1Rx(x) __ACCESS_CP15(c12, 4, c9, x) +#define ICH_AP1R0 __ICH_AP1Rx(0) +#define ICH_AP1R1 __ICH_AP1Rx(1) +#define ICH_AP1R2 __ICH_AP1Rx(2) +#define ICH_AP1R3 __ICH_AP1Rx(3) /* A32-to-A64 mappings used by VGIC save/restore */ @@ -125,6 +137,16 @@ static inline u64 read_ ## a64(void) \ return val; \ } +CPUIF_MAP(ICC_PMR, ICC_PMR_EL1) +CPUIF_MAP(ICC_AP0R0, ICC_AP0R0_EL1) +CPUIF_MAP(ICC_AP0R1, ICC_AP0R1_EL1) +CPUIF_MAP(ICC_AP0R2, ICC_AP0R2_EL1) +CPUIF_MAP(ICC_AP0R3, ICC_AP0R3_EL1) +CPUIF_MAP(ICC_AP1R0, ICC_AP1R0_EL1) +CPUIF_MAP(ICC_AP1R1, ICC_AP1R1_EL1) +CPUIF_MAP(ICC_AP1R2, ICC_AP1R2_EL1) +CPUIF_MAP(ICC_AP1R3, ICC_AP1R3_EL1) + CPUIF_MAP(ICH_HCR, ICH_HCR_EL2) CPUIF_MAP(ICH_VTR, ICH_VTR_EL2) CPUIF_MAP(ICH_MISR, ICH_MISR_EL2) @@ -185,11 +207,6 @@ static inline u32 gic_read_iar(void) return irqstat; } -static inline void gic_write_pmr(u32 val) -{ - write_sysreg(val, ICC_PMR); -} - static inline void gic_write_ctlr(u32 val) { write_sysreg(val, ICC_CTLR); diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 7381eeb7ef8e..302d0b681676 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -132,6 +132,7 @@ config ARM64 select IRQ_DOMAIN select IRQ_FORCED_THREADING select MODULES_USE_ELF_RELA + select MULTI_IRQ_HANDLER select NO_BOOTMEM select OF select OF_EARLY_FLATTREE @@ -275,6 +276,9 @@ config ARCH_SUPPORTS_UPROBES config ARCH_PROC_KCORE_TEXT def_bool y +config MULTI_IRQ_HANDLER + def_bool y + source "init/Kconfig" source "kernel/Kconfig.freezer" diff --git a/arch/arm64/include/asm/arch_gicv3.h b/arch/arm64/include/asm/arch_gicv3.h index 9becba9ab392..e278f94df0c9 100644 --- a/arch/arm64/include/asm/arch_gicv3.h +++ b/arch/arm64/include/asm/arch_gicv3.h @@ -76,11 +76,6 @@ static inline u64 gic_read_iar_cavium_thunderx(void) return irqstat; } -static inline void gic_write_pmr(u32 val) -{ - write_sysreg_s(val, SYS_ICC_PMR_EL1); -} - static inline void gic_write_ctlr(u32 val) { write_sysreg_s(val, SYS_ICC_CTLR_EL1); diff --git a/arch/openrisc/Kconfig b/arch/openrisc/Kconfig index 339df7324e9c..9ecad05bfc73 100644 --- a/arch/openrisc/Kconfig +++ b/arch/openrisc/Kconfig @@ -27,6 +27,7 @@ config OPENRISC select GENERIC_STRNLEN_USER select GENERIC_SMP_IDLE_THREAD select MODULES_USE_ELF_RELA + select MULTI_IRQ_HANDLER select HAVE_DEBUG_STACKOVERFLOW select OR1K_PIC select CPU_NO_EFFICIENT_FFS if !OPENRISC_HAVE_INST_FF1 @@ -68,6 +69,9 @@ config STACKTRACE_SUPPORT config LOCKDEP_SUPPORT def_bool y +config MULTI_IRQ_HANDLER + def_bool y + source "init/Kconfig" source "kernel/Kconfig.freezer" diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index 04807c7f64cc..148865de1692 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -33,6 +33,7 @@ config RISCV select MODULES_USE_ELF_RELA if MODULES select THREAD_INFO_IN_TASK select RISCV_TIMER + select GENERIC_IRQ_MULTI_HANDLER config MMU def_bool y diff --git a/arch/riscv/include/asm/Kbuild b/arch/riscv/include/asm/Kbuild index 4286a5f83876..1e5fd280fb4d 100644 --- a/arch/riscv/include/asm/Kbuild +++ b/arch/riscv/include/asm/Kbuild @@ -15,6 +15,7 @@ generic-y += fcntl.h generic-y += futex.h generic-y += hardirq.h generic-y += hash.h +generic-y += handle_irq.h generic-y += hw_irq.h generic-y += ioctl.h generic-y += ioctls.h diff --git a/arch/riscv/kernel/entry.S b/arch/riscv/kernel/entry.S index 56fa592cfa34..9aaf6c986771 100644 --- a/arch/riscv/kernel/entry.S +++ b/arch/riscv/kernel/entry.S @@ -167,10 +167,9 @@ ENTRY(handle_exception) bge s4, zero, 1f /* Handle interrupts */ - slli a0, s4, 1 - srli a0, a0, 1 - move a1, sp /* pt_regs */ - tail do_IRQ + move a0, sp /* pt_regs */ + REG_L a1, handle_arch_irq + jr a1 1: /* Exceptions run with interrupts enabled */ csrs sstatus, SR_SIE diff --git a/arch/riscv/kernel/irq.c b/arch/riscv/kernel/irq.c index 328718e8026e..b74cbfbce2d0 100644 --- a/arch/riscv/kernel/irq.c +++ b/arch/riscv/kernel/irq.c @@ -24,16 +24,3 @@ void __init init_IRQ(void) { irqchip_init(); } - -asmlinkage void __irq_entry do_IRQ(unsigned int cause, struct pt_regs *regs) -{ -#ifdef CONFIG_RISCV_INTC - /* - * FIXME: We don't want a direct call to riscv_intc_irq here. The plan - * is to put an IRQ domain here and let the interrupt controller - * register with that, but I poked around the arm64 code a bit and - * there might be a better way to do it (ie, something fully generic). - */ - riscv_intc_irq(cause, regs); -#endif -} diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index d913aec85109..60d5982d8234 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -286,6 +286,11 @@ config IRQ_MXS select IRQ_DOMAIN select STMP_DEVICE +config MSCC_OCELOT_IRQ + bool + select IRQ_DOMAIN + select GENERIC_IRQ_CHIP + config MVEBU_GICP bool @@ -351,4 +356,13 @@ config GOLDFISH_PIC Say yes here to enable Goldfish interrupt controller driver used for Goldfish based virtual platforms. +config QCOM_PDC + bool "QCOM PDC" + depends on ARCH_QCOM + select IRQ_DOMAIN + select IRQ_DOMAIN_HIERARCHY + help + Power Domain Controller driver to manage and configure wakeup + IRQs for Qualcomm Technologies Inc (QTI) mobile chips. + endmenu diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 110de02bb215..280884068959 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -70,6 +70,7 @@ obj-$(CONFIG_ARCH_SA1100) += irq-sa11x0.o obj-$(CONFIG_INGENIC_IRQ) += irq-ingenic.o obj-$(CONFIG_IMX_GPCV2) += irq-imx-gpcv2.o obj-$(CONFIG_PIC32_EVIC) += irq-pic32-evic.o +obj-$(CONFIG_MSCC_OCELOT_IRQ) += irq-mscc-ocelot.o obj-$(CONFIG_MVEBU_GICP) += irq-mvebu-gicp.o obj-$(CONFIG_MVEBU_ICU) += irq-mvebu-icu.o obj-$(CONFIG_MVEBU_ODMI) += irq-mvebu-odmi.o @@ -84,3 +85,4 @@ obj-$(CONFIG_ARCH_SYNQUACER) += irq-sni-exiu.o obj-$(CONFIG_MESON_IRQ_GPIO) += irq-meson-gpio.o obj-$(CONFIG_GOLDFISH_PIC) += irq-goldfish-pic.o obj-$(CONFIG_NDS32) += irq-ativic32.o +obj-$(CONFIG_QCOM_PDC) += qcom-pdc.o diff --git a/drivers/irqchip/irq-gic-common.c b/drivers/irqchip/irq-gic-common.c index 30017df5b54c..01e673c680cd 100644 --- a/drivers/irqchip/irq-gic-common.c +++ b/drivers/irqchip/irq-gic-common.c @@ -21,6 +21,8 @@ #include "irq-gic-common.h" +static DEFINE_RAW_SPINLOCK(irq_controller_lock); + static const struct gic_kvm_info *gic_kvm_info; const struct gic_kvm_info *gic_get_kvm_info(void) @@ -53,11 +55,13 @@ int gic_configure_irq(unsigned int irq, unsigned int type, u32 confoff = (irq / 16) * 4; u32 val, oldval; int ret = 0; + unsigned long flags; /* * Read current configuration register, and insert the config * for "irq", depending on "type". */ + raw_spin_lock_irqsave(&irq_controller_lock, flags); val = oldval = readl_relaxed(base + GIC_DIST_CONFIG + confoff); if (type & IRQ_TYPE_LEVEL_MASK) val &= ~confmask; @@ -65,8 +69,10 @@ int gic_configure_irq(unsigned int irq, unsigned int type, val |= confmask; /* If the current configuration is the same, then we are done */ - if (val == oldval) + if (val == oldval) { + raw_spin_unlock_irqrestore(&irq_controller_lock, flags); return 0; + } /* * Write back the new configuration, and possibly re-enable @@ -84,6 +90,7 @@ int gic_configure_irq(unsigned int irq, unsigned int type, pr_warn("GIC: PPI%d is secure or misconfigured\n", irq - 16); } + raw_spin_unlock_irqrestore(&irq_controller_lock, flags); if (sync_access) sync_access(); diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 2cbb19cddbf8..2982e93d2369 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -33,6 +33,7 @@ #include <linux/of_platform.h> #include <linux/percpu.h> #include <linux/slab.h> +#include <linux/syscore_ops.h> #include <linux/irqchip.h> #include <linux/irqchip/arm-gic-v3.h> @@ -46,6 +47,7 @@ #define ITS_FLAGS_CMDQ_NEEDS_FLUSHING (1ULL << 0) #define ITS_FLAGS_WORKAROUND_CAVIUM_22375 (1ULL << 1) #define ITS_FLAGS_WORKAROUND_CAVIUM_23144 (1ULL << 2) +#define ITS_FLAGS_SAVE_SUSPEND_STATE (1ULL << 3) #define RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING (1 << 0) @@ -101,6 +103,8 @@ struct its_node { struct its_collection *collections; struct fwnode_handle *fwnode_handle; u64 (*get_msi_base)(struct its_device *its_dev); + u64 cbaser_save; + u32 ctlr_save; struct list_head its_device_list; u64 flags; unsigned long list_nr; @@ -1875,16 +1879,6 @@ static void its_cpu_init_lpis(void) gic_data_rdist()->pend_page = pend_page; } - /* Disable LPIs */ - val = readl_relaxed(rbase + GICR_CTLR); - val &= ~GICR_CTLR_ENABLE_LPIS; - writel_relaxed(val, rbase + GICR_CTLR); - - /* - * Make sure any change to the table is observable by the GIC. - */ - dsb(sy); - /* set PROPBASE */ val = (page_to_phys(gic_rdists->prop_page) | GICR_PROPBASER_InnerShareable | @@ -1938,52 +1932,53 @@ static void its_cpu_init_lpis(void) dsb(sy); } -static void its_cpu_init_collection(void) +static void its_cpu_init_collection(struct its_node *its) { - struct its_node *its; - int cpu; - - spin_lock(&its_lock); - cpu = smp_processor_id(); - - list_for_each_entry(its, &its_nodes, entry) { - u64 target; + int cpu = smp_processor_id(); + u64 target; - /* avoid cross node collections and its mapping */ - if (its->flags & ITS_FLAGS_WORKAROUND_CAVIUM_23144) { - struct device_node *cpu_node; + /* avoid cross node collections and its mapping */ + if (its->flags & ITS_FLAGS_WORKAROUND_CAVIUM_23144) { + struct device_node *cpu_node; - cpu_node = of_get_cpu_node(cpu, NULL); - if (its->numa_node != NUMA_NO_NODE && - its->numa_node != of_node_to_nid(cpu_node)) - continue; - } + cpu_node = of_get_cpu_node(cpu, NULL); + if (its->numa_node != NUMA_NO_NODE && + its->numa_node != of_node_to_nid(cpu_node)) + return; + } + /* + * We now have to bind each collection to its target + * redistributor. + */ + if (gic_read_typer(its->base + GITS_TYPER) & GITS_TYPER_PTA) { /* - * We now have to bind each collection to its target + * This ITS wants the physical address of the * redistributor. */ - if (gic_read_typer(its->base + GITS_TYPER) & GITS_TYPER_PTA) { - /* - * This ITS wants the physical address of the - * redistributor. - */ - target = gic_data_rdist()->phys_base; - } else { - /* - * This ITS wants a linear CPU number. - */ - target = gic_read_typer(gic_data_rdist_rd_base() + GICR_TYPER); - target = GICR_TYPER_CPU_NUMBER(target) << 16; - } + target = gic_data_rdist()->phys_base; + } else { + /* This ITS wants a linear CPU number. */ + target = gic_read_typer(gic_data_rdist_rd_base() + GICR_TYPER); + target = GICR_TYPER_CPU_NUMBER(target) << 16; + } - /* Perform collection mapping */ - its->collections[cpu].target_address = target; - its->collections[cpu].col_id = cpu; + /* Perform collection mapping */ + its->collections[cpu].target_address = target; + its->collections[cpu].col_id = cpu; - its_send_mapc(its, &its->collections[cpu], 1); - its_send_invall(its, &its->collections[cpu]); - } + its_send_mapc(its, &its->collections[cpu], 1); + its_send_invall(its, &its->collections[cpu]); +} + +static void its_cpu_init_collections(void) +{ + struct its_node *its; + + spin_lock(&its_lock); + + list_for_each_entry(its, &its_nodes, entry) + its_cpu_init_collection(its); spin_unlock(&its_lock); } @@ -3041,6 +3036,113 @@ static void its_enable_quirks(struct its_node *its) gic_enable_quirks(iidr, its_quirks, its); } +static int its_save_disable(void) +{ + struct its_node *its; + int err = 0; + + spin_lock(&its_lock); + list_for_each_entry(its, &its_nodes, entry) { + void __iomem *base; + + if (!(its->flags & ITS_FLAGS_SAVE_SUSPEND_STATE)) + continue; + + base = its->base; + its->ctlr_save = readl_relaxed(base + GITS_CTLR); + err = its_force_quiescent(base); + if (err) { + pr_err("ITS@%pa: failed to quiesce: %d\n", + &its->phys_base, err); + writel_relaxed(its->ctlr_save, base + GITS_CTLR); + goto err; + } + + its->cbaser_save = gits_read_cbaser(base + GITS_CBASER); + } + +err: + if (err) { + list_for_each_entry_continue_reverse(its, &its_nodes, entry) { + void __iomem *base; + + if (!(its->flags & ITS_FLAGS_SAVE_SUSPEND_STATE)) + continue; + + base = its->base; + writel_relaxed(its->ctlr_save, base + GITS_CTLR); + } + } + spin_unlock(&its_lock); + + return err; +} + +static void its_restore_enable(void) +{ + struct its_node *its; + int ret; + + spin_lock(&its_lock); + list_for_each_entry(its, &its_nodes, entry) { + void __iomem *base; + int i; + + if (!(its->flags & ITS_FLAGS_SAVE_SUSPEND_STATE)) + continue; + + base = its->base; + + /* + * Make sure that the ITS is disabled. If it fails to quiesce, + * don't restore it since writing to CBASER or BASER<n> + * registers is undefined according to the GIC v3 ITS + * Specification. + */ + ret = its_force_quiescent(base); + if (ret) { + pr_err("ITS@%pa: failed to quiesce on resume: %d\n", + &its->phys_base, ret); + continue; + } + + gits_write_cbaser(its->cbaser_save, base + GITS_CBASER); + + /* + * Writing CBASER resets CREADR to 0, so make CWRITER and + * cmd_write line up with it. + */ + its->cmd_write = its->cmd_base; + gits_write_cwriter(0, base + GITS_CWRITER); + + /* Restore GITS_BASER from the value cache. */ + for (i = 0; i < GITS_BASER_NR_REGS; i++) { + struct its_baser *baser = &its->tables[i]; + + if (!(baser->val & GITS_BASER_VALID)) + continue; + + its_write_baser(its, baser, baser->val); + } + writel_relaxed(its->ctlr_save, base + GITS_CTLR); + + /* + * Reinit the collection if it's stored in the ITS. This is + * indicated by the col_id being less than the HCC field. + * CID < HCC as specified in the GIC v3 Documentation. + */ + if (its->collections[smp_processor_id()].col_id < + GITS_TYPER_HCC(gic_read_typer(base + GITS_TYPER))) |