Skip to content

Commit b2d3e33

Browse files
chenhuacaiMarc Zyngier
authored andcommitted
irqchip: Add LoongArch CPU interrupt controller support
LoongArch CPUINTC stands for CSR.ECFG/CSR.ESTAT and related interrupt controller that described in Section 7.4 of "LoongArch Reference Manual, Vol 1". For more information please refer Documentation/loongarch/irq- chip-model.rst. LoongArch CPUINTC has 13 interrupt sources: SWI0~1, HWI0~7, IPI, TI (Timer) and PCOV (PMC). IRQ mappings of HWI0~7 are configurable (can be created from DT/ACPI), but IPI, TI (Timer) and PCOV (PMC) are hardcoded bits, so we expose the fwnode_handle to map them, and get mapped irq by irq_create_mapping when using them. Co-developed-by: Jianmin Lv <lvjianmin@loongson.cn> Signed-off-by: Jianmin Lv <lvjianmin@loongson.cn> Signed-off-by: Huacai Chen <chenhuacai@loongson.cn> Signed-off-by: Marc Zyngier <maz@kernel.org> Link: https://lore.kernel.org/r/1658314292-35346-13-git-send-email-lvjianmin@loongson.cn
1 parent dd281e1 commit b2d3e33

File tree

6 files changed

+149
-10
lines changed

6 files changed

+149
-10
lines changed

arch/loongarch/include/asm/irq.h

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,6 @@ static inline bool on_irq_stack(int cpu, unsigned long sp)
3535
return (low <= sp && sp <= high);
3636
}
3737

38-
int get_ipi_irq(void);
39-
int get_pmc_irq(void);
40-
int get_timer_irq(void);
4138
void spurious_interrupt(void);
4239

4340
#define NR_IRQS_LEGACY 16
@@ -94,8 +91,6 @@ struct acpi_madt_bio_pic;
9491
struct acpi_madt_msi_pic;
9592
struct acpi_madt_lpc_pic;
9693

97-
struct irq_domain *loongarch_cpu_irq_init(void);
98-
9994
int liointc_acpi_init(struct irq_domain *parent,
10095
struct acpi_madt_lio_pic *acpi_liointc);
10196
int eiointc_acpi_init(struct irq_domain *parent,
@@ -128,7 +123,7 @@ extern struct acpi_madt_lpc_pic *acpi_pchlpc;
128123
extern struct acpi_madt_msi_pic *acpi_pchmsi[MAX_IO_PICS];
129124
extern struct acpi_madt_bio_pic *acpi_pchpic[MAX_IO_PICS];
130125

131-
extern struct irq_domain *cpu_domain;
126+
extern struct fwnode_handle *cpuintc_handle;
132127
extern struct fwnode_handle *liointc_handle;
133128
extern struct fwnode_handle *pch_lpc_handle;
134129
extern struct fwnode_handle *pch_pic_handle[MAX_IO_PICS];

arch/loongarch/kernel/irq.c

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,6 @@ DEFINE_PER_CPU(unsigned long, irq_stack);
2525
DEFINE_PER_CPU_SHARED_ALIGNED(irq_cpustat_t, irq_stat);
2626
EXPORT_PER_CPU_SYMBOL(irq_stat);
2727

28-
struct irq_domain *cpu_domain;
29-
3028
struct acpi_vector_group pch_group[MAX_IO_PICS];
3129
struct acpi_vector_group msi_group[MAX_IO_PICS];
3230
/*
@@ -89,6 +87,16 @@ static void __init init_vec_parent_group(void)
8987
acpi_table_parse(ACPI_SIG_MCFG, early_pci_mcfg_parse);
9088
}
9189

90+
static int __init get_ipi_irq(void)
91+
{
92+
struct irq_domain *d = irq_find_matching_fwnode(cpuintc_handle, DOMAIN_BUS_ANY);
93+
94+
if (d)
95+
return irq_create_mapping(d, EXCCODE_IPI - EXCCODE_INT_START);
96+
97+
return -EINVAL;
98+
}
99+
92100
void __init init_IRQ(void)
93101
{
94102
int i;
@@ -105,7 +113,9 @@ void __init init_IRQ(void)
105113
init_vec_parent_group();
106114
irqchip_init();
107115
#ifdef CONFIG_SMP
108-
ipi_irq = EXCCODE_IPI - EXCCODE_INT_START;
116+
ipi_irq = get_ipi_irq();
117+
if (ipi_irq < 0)
118+
panic("IPI IRQ mapping failed\n");
109119
irq_set_percpu_devid(ipi_irq);
110120
r = request_percpu_irq(ipi_irq, loongson3_ipi_interrupt, "IPI", &ipi_dummy_dev);
111121
if (r < 0)

arch/loongarch/kernel/time.c

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,16 @@ void sync_counter(void)
123123
csr_write64(-init_timeval, LOONGARCH_CSR_CNTC);
124124
}
125125

126+
static int get_timer_irq(void)
127+
{
128+
struct irq_domain *d = irq_find_matching_fwnode(cpuintc_handle, DOMAIN_BUS_ANY);
129+
130+
if (d)
131+
return irq_create_mapping(d, EXCCODE_TIMER - EXCCODE_INT_START);
132+
133+
return -EINVAL;
134+
}
135+
126136
int constant_clockevent_init(void)
127137
{
128138
unsigned int irq;
@@ -132,7 +142,9 @@ int constant_clockevent_init(void)
132142
struct clock_event_device *cd;
133143
static int timer_irq_installed = 0;
134144

135-
irq = EXCCODE_TIMER - EXCCODE_INT_START;
145+
irq = get_timer_irq();
146+
if (irq < 0)
147+
pr_err("Failed to map irq %d (timer)\n", irq);
136148

137149
cd = &per_cpu(constant_clockevent_device, cpu);
138150

drivers/irqchip/Kconfig

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -546,6 +546,16 @@ config EXYNOS_IRQ_COMBINER
546546
Say yes here to add support for the IRQ combiner devices embedded
547547
in Samsung Exynos chips.
548548

549+
config IRQ_LOONGARCH_CPU
550+
bool
551+
select GENERIC_IRQ_CHIP
552+
select IRQ_DOMAIN
553+
select GENERIC_IRQ_EFFECTIVE_AFF_MASK
554+
help
555+
Support for the LoongArch CPU Interrupt Controller. For details of
556+
irq chip hierarchy on LoongArch platforms please read the document
557+
Documentation/loongarch/irq-chip-model.rst.
558+
549559
config LOONGSON_LIOINTC
550560
bool "Loongson Local I/O Interrupt Controller"
551561
depends on MACH_LOONGSON64

drivers/irqchip/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ obj-$(CONFIG_LS1X_IRQ) += irq-ls1x.o
103103
obj-$(CONFIG_TI_SCI_INTR_IRQCHIP) += irq-ti-sci-intr.o
104104
obj-$(CONFIG_TI_SCI_INTA_IRQCHIP) += irq-ti-sci-inta.o
105105
obj-$(CONFIG_TI_PRUSS_INTC) += irq-pruss-intc.o
106+
obj-$(CONFIG_IRQ_LOONGARCH_CPU) += irq-loongarch-cpu.o
106107
obj-$(CONFIG_LOONGSON_LIOINTC) += irq-loongson-liointc.o
107108
obj-$(CONFIG_LOONGSON_EIOINTC) += irq-loongson-eiointc.o
108109
obj-$(CONFIG_LOONGSON_HTPIC) += irq-loongson-htpic.o

drivers/irqchip/irq-loongarch-cpu.c

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
4+
*/
5+
6+
#include <linux/init.h>
7+
#include <linux/kernel.h>
8+
#include <linux/interrupt.h>
9+
#include <linux/irq.h>
10+
#include <linux/irqchip.h>
11+
#include <linux/irqdomain.h>
12+
13+
#include <asm/loongarch.h>
14+
#include <asm/setup.h>
15+
16+
static struct irq_domain *irq_domain;
17+
struct fwnode_handle *cpuintc_handle;
18+
19+
static void mask_loongarch_irq(struct irq_data *d)
20+
{
21+
clear_csr_ecfg(ECFGF(d->hwirq));
22+
}
23+
24+
static void unmask_loongarch_irq(struct irq_data *d)
25+
{
26+
set_csr_ecfg(ECFGF(d->hwirq));
27+
}
28+
29+
static struct irq_chip cpu_irq_controller = {
30+
.name = "CPUINTC",
31+
.irq_mask = mask_loongarch_irq,
32+
.irq_unmask = unmask_loongarch_irq,
33+
};
34+
35+
static void handle_cpu_irq(struct pt_regs *regs)
36+
{
37+
int hwirq;
38+
unsigned int estat = read_csr_estat() & CSR_ESTAT_IS;
39+
40+
while ((hwirq = ffs(estat))) {
41+
estat &= ~BIT(hwirq - 1);
42+
generic_handle_domain_irq(irq_domain, hwirq - 1);
43+
}
44+
}
45+
46+
static int loongarch_cpu_intc_map(struct irq_domain *d, unsigned int irq,
47+
irq_hw_number_t hwirq)
48+
{
49+
irq_set_noprobe(irq);
50+
irq_set_chip_and_handler(irq, &cpu_irq_controller, handle_percpu_irq);
51+
52+
return 0;
53+
}
54+
55+
static const struct irq_domain_ops loongarch_cpu_intc_irq_domain_ops = {
56+
.map = loongarch_cpu_intc_map,
57+
.xlate = irq_domain_xlate_onecell,
58+
};
59+
60+
static int __init
61+
liointc_parse_madt(union acpi_subtable_headers *header,
62+
const unsigned long end)
63+
{
64+
struct acpi_madt_lio_pic *liointc_entry = (struct acpi_madt_lio_pic *)header;
65+
66+
return liointc_acpi_init(irq_domain, liointc_entry);
67+
}
68+
69+
static int __init
70+
eiointc_parse_madt(union acpi_subtable_headers *header,
71+
const unsigned long end)
72+
{
73+
struct acpi_madt_eio_pic *eiointc_entry = (struct acpi_madt_eio_pic *)header;
74+
75+
return eiointc_acpi_init(irq_domain, eiointc_entry);
76+
}
77+
78+
static int __init acpi_cascade_irqdomain_init(void)
79+
{
80+
acpi_table_parse_madt(ACPI_MADT_TYPE_LIO_PIC,
81+
liointc_parse_madt, 0);
82+
acpi_table_parse_madt(ACPI_MADT_TYPE_EIO_PIC,
83+
eiointc_parse_madt, 0);
84+
return 0;
85+
}
86+
87+
static int __init cpuintc_acpi_init(union acpi_subtable_headers *header,
88+
const unsigned long end)
89+
{
90+
if (irq_domain)
91+
return 0;
92+
93+
/* Mask interrupts. */
94+
clear_csr_ecfg(ECFG0_IM);
95+
clear_csr_estat(ESTATF_IP);
96+
97+
cpuintc_handle = irq_domain_alloc_fwnode(NULL);
98+
irq_domain = irq_domain_create_linear(cpuintc_handle, EXCCODE_INT_NUM,
99+
&loongarch_cpu_intc_irq_domain_ops, NULL);
100+
101+
if (!irq_domain)
102+
panic("Failed to add irqdomain for LoongArch CPU");
103+
104+
set_handle_irq(&handle_cpu_irq);
105+
acpi_cascade_irqdomain_init();
106+
107+
return 0;
108+
}
109+
110+
IRQCHIP_ACPI_DECLARE(cpuintc_v1, ACPI_MADT_TYPE_CORE_PIC,
111+
NULL, ACPI_MADT_CORE_PIC_VERSION_V1, cpuintc_acpi_init);

0 commit comments

Comments
 (0)