1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2020 Loongson Technologies, Inc.
4 */
5
6#include <linux/init.h>
7#include <linux/interrupt.h>
8#include <linux/kernel.h>
9#include <linux/irq.h>
10#include <linux/irqchip.h>
11#include <linux/irqdomain.h>
12
13#include <asm/setup.h>
14#include <asm/loongarchregs.h>
15
16static struct irq_domain *irq_domain;
17
18static void mask_loongarch_irq(struct irq_data *d)
19{
20	clear_csr_ecfg(ECFGF(d->hwirq));
21}
22
23static void unmask_loongarch_irq(struct irq_data *d)
24{
25	set_csr_ecfg(ECFGF(d->hwirq));
26}
27
28static struct irq_chip cpu_irq_controller = {
29	.name		= "LoongArch",
30	.irq_mask	= mask_loongarch_irq,
31	.irq_unmask	= unmask_loongarch_irq,
32};
33
34static void handle_cpu_irq(struct pt_regs *regs)
35{
36	int hwirq;
37	unsigned int estat = read_csr_estat() & CSR_ESTAT_IS;
38
39	while ((hwirq = ffs(estat))) {
40		estat &= ~BIT(hwirq - 1);
41		handle_domain_irq(irq_domain, hwirq - 1, regs);
42	}
43}
44
45int get_ipi_irq(void)
46{
47	return irq_create_mapping(irq_domain, INT_IPI);
48}
49
50int get_pmc_irq(void)
51{
52	return irq_create_mapping(irq_domain, INT_PCOV);
53}
54
55int get_timer_irq(void)
56{
57	return irq_create_mapping(irq_domain, INT_TI);
58}
59
60static int loongarch_cpu_intc_map(struct irq_domain *d, unsigned int irq,
61			     irq_hw_number_t hwirq)
62{
63	irq_set_noprobe(irq);
64	irq_set_chip_and_handler(irq, &cpu_irq_controller, handle_percpu_irq);
65
66	return 0;
67}
68
69static const struct irq_domain_ops loongarch_cpu_intc_irq_domain_ops = {
70	.map = loongarch_cpu_intc_map,
71	.xlate = irq_domain_xlate_onecell,
72};
73
74struct irq_domain * __init loongarch_cpu_irq_init(void)
75{
76	struct fwnode_handle *domain_handle;
77
78	/* Mask interrupts. */
79	clear_csr_ecfg(ECFG0_IM);
80	clear_csr_estat(ESTATF_IP);
81
82	domain_handle = irq_domain_alloc_fwnode(NULL);
83	irq_domain = irq_domain_create_linear(domain_handle, EXCCODE_INT_NUM,
84					&loongarch_cpu_intc_irq_domain_ops, NULL);
85
86	if (!irq_domain)
87		panic("Failed to add irqdomain for LoongArch CPU");
88
89	set_handle_irq(&handle_cpu_irq);
90
91	return irq_domain;
92}
93