aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGuo Ren <ren_guo@c-sky.com>2018-09-16 03:57:14 -0400
committerGuo Ren <ren_guo@c-sky.com>2018-10-25 12:54:29 -0400
commitd8a5f5f79122b42b8afdce29f03683c1b8cdb60e (patch)
tree417ea1d6d4bf864f5f4814679b3bb3eb6b106b4d
parent243b40a469d1c5d7824df8235ccddbde8c45f03e (diff)
irqchip: add C-SKY SMP interrupt controller
The driver is for C-SKY SMP interrupt controller. It support 16 soft-irqs, 16 private-irqs, and 992 max external-irqs, a total of 1024 interrupts. C-SKY CPU 807/810/860 SMP/non-SMP could use it. Signed-off-by: Guo Ren <ren_guo@c-sky.com> Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>
-rw-r--r--drivers/irqchip/Kconfig9
-rw-r--r--drivers/irqchip/Makefile1
-rw-r--r--drivers/irqchip/irq-csky-mpintc.c198
3 files changed, 208 insertions, 0 deletions
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 383e7b70221d..8103f6f99dff 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -371,6 +371,15 @@ config QCOM_PDC
371 Power Domain Controller driver to manage and configure wakeup 371 Power Domain Controller driver to manage and configure wakeup
372 IRQs for Qualcomm Technologies Inc (QTI) mobile chips. 372 IRQs for Qualcomm Technologies Inc (QTI) mobile chips.
373 373
374config CSKY_MPINTC
375 bool "C-SKY Multi Processor Interrupt Controller"
376 depends on CSKY
377 help
378 Say yes here to enable C-SKY SMP interrupt controller driver used
379 for C-SKY SMP system.
380 In fact it's not mmio map in hw and it use ld/st to visit the
381 controller's register inside CPU.
382
374endmenu 383endmenu
375 384
376config SIFIVE_PLIC 385config SIFIVE_PLIC
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index fbd1ec8070ef..6b739ea27f85 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -87,4 +87,5 @@ obj-$(CONFIG_MESON_IRQ_GPIO) += irq-meson-gpio.o
87obj-$(CONFIG_GOLDFISH_PIC) += irq-goldfish-pic.o 87obj-$(CONFIG_GOLDFISH_PIC) += irq-goldfish-pic.o
88obj-$(CONFIG_NDS32) += irq-ativic32.o 88obj-$(CONFIG_NDS32) += irq-ativic32.o
89obj-$(CONFIG_QCOM_PDC) += qcom-pdc.o 89obj-$(CONFIG_QCOM_PDC) += qcom-pdc.o
90obj-$(CONFIG_CSKY_MPINTC) += irq-csky-mpintc.o
90obj-$(CONFIG_SIFIVE_PLIC) += irq-sifive-plic.o 91obj-$(CONFIG_SIFIVE_PLIC) += irq-sifive-plic.o
diff --git a/drivers/irqchip/irq-csky-mpintc.c b/drivers/irqchip/irq-csky-mpintc.c
new file mode 100644
index 000000000000..c67c961ab6cc
--- /dev/null
+++ b/drivers/irqchip/irq-csky-mpintc.c
@@ -0,0 +1,198 @@
1// SPDX-License-Identifier: GPL-2.0
2// Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd.
3
4#include <linux/kernel.h>
5#include <linux/init.h>
6#include <linux/of.h>
7#include <linux/of_address.h>
8#include <linux/module.h>
9#include <linux/irqdomain.h>
10#include <linux/irqchip.h>
11#include <linux/irq.h>
12#include <linux/interrupt.h>
13#include <linux/smp.h>
14#include <linux/io.h>
15#include <asm/irq.h>
16#include <asm/traps.h>
17#include <asm/reg_ops.h>
18
19static struct irq_domain *root_domain;
20static void __iomem *INTCG_base;
21static void __iomem *INTCL_base;
22
23#define IPI_IRQ 15
24#define INTC_IRQS 256
25#define COMM_IRQ_BASE 32
26
27#define INTCG_SIZE 0x8000
28#define INTCL_SIZE 0x1000
29
30#define INTCG_ICTLR 0x0
31#define INTCG_CICFGR 0x100
32#define INTCG_CIDSTR 0x1000
33
34#define INTCL_PICTLR 0x0
35#define INTCL_SIGR 0x60
36#define INTCL_HPPIR 0x68
37#define INTCL_RDYIR 0x6c
38#define INTCL_SENR 0xa0
39#define INTCL_CENR 0xa4
40#define INTCL_CACR 0xb4
41
42static DEFINE_PER_CPU(void __iomem *, intcl_reg);
43
44static void csky_mpintc_handler(struct pt_regs *regs)
45{
46 void __iomem *reg_base = this_cpu_read(intcl_reg);
47
48 do {
49 handle_domain_irq(root_domain,
50 readl_relaxed(reg_base + INTCL_RDYIR),
51 regs);
52 } while (readl_relaxed(reg_base + INTCL_HPPIR) & BIT(31));
53}
54
55static void csky_mpintc_enable(struct irq_data *d)
56{
57 void __iomem *reg_base = this_cpu_read(intcl_reg);
58
59 writel_relaxed(d->hwirq, reg_base + INTCL_SENR);
60}
61
62static void csky_mpintc_disable(struct irq_data *d)
63{
64 void __iomem *reg_base = this_cpu_read(intcl_reg);
65
66 writel_relaxed(d->hwirq, reg_base + INTCL_CENR);
67}
68
69static void csky_mpintc_eoi(struct irq_data *d)
70{
71 void __iomem *reg_base = this_cpu_read(intcl_reg);
72
73 writel_relaxed(d->hwirq, reg_base + INTCL_CACR);
74}
75
76#ifdef CONFIG_SMP
77static int csky_irq_set_affinity(struct irq_data *d,
78 const struct cpumask *mask_val,
79 bool force)
80{
81 unsigned int cpu;
82 unsigned int offset = 4 * (d->hwirq - COMM_IRQ_BASE);
83
84 if (!force)
85 cpu = cpumask_any_and(mask_val, cpu_online_mask);
86 else
87 cpu = cpumask_first(mask_val);
88
89 if (cpu >= nr_cpu_ids)
90 return -EINVAL;
91
92 /* Enable interrupt destination */
93 cpu |= BIT(31);
94
95 writel_relaxed(cpu, INTCG_base + INTCG_CIDSTR + offset);
96
97 irq_data_update_effective_affinity(d, cpumask_of(cpu));
98
99 return IRQ_SET_MASK_OK_DONE;
100}
101#endif
102
103static struct irq_chip csky_irq_chip = {
104 .name = "C-SKY SMP Intc",
105 .irq_eoi = csky_mpintc_eoi,
106 .irq_enable = csky_mpintc_enable,
107 .irq_disable = csky_mpintc_disable,
108#ifdef CONFIG_SMP
109 .irq_set_affinity = csky_irq_set_affinity,
110#endif
111};
112
113static int csky_irqdomain_map(struct irq_domain *d, unsigned int irq,
114 irq_hw_number_t hwirq)
115{
116 if (hwirq < COMM_IRQ_BASE) {
117 irq_set_percpu_devid(irq);
118 irq_set_chip_and_handler(irq, &csky_irq_chip,
119 handle_percpu_irq);
120 } else {
121 irq_set_chip_and_handler(irq, &csky_irq_chip,
122 handle_fasteoi_irq);
123 }
124
125 return 0;
126}
127
128static const struct irq_domain_ops csky_irqdomain_ops = {
129 .map = csky_irqdomain_map,
130 .xlate = irq_domain_xlate_onecell,
131};
132
133#ifdef CONFIG_SMP
134static void csky_mpintc_send_ipi(const struct cpumask *mask)
135{
136 void __iomem *reg_base = this_cpu_read(intcl_reg);
137
138 /*
139 * INTCL_SIGR[3:0] INTID
140 * INTCL_SIGR[8:15] CPUMASK
141 */
142 writel_relaxed((*cpumask_bits(mask)) << 8 | IPI_IRQ,
143 reg_base + INTCL_SIGR);
144}
145#endif
146
147/* C-SKY multi processor interrupt controller */
148static int __init
149csky_mpintc_init(struct device_node *node, struct device_node *parent)
150{
151 int ret;
152 unsigned int cpu, nr_irq;
153#ifdef CONFIG_SMP
154 unsigned int ipi_irq;
155#endif
156
157 if (parent)
158 return 0;
159
160 ret = of_property_read_u32(node, "csky,num-irqs", &nr_irq);
161 if (ret < 0)
162 nr_irq = INTC_IRQS;
163
164 if (INTCG_base == NULL) {
165 INTCG_base = ioremap(mfcr("cr<31, 14>"),
166 INTCL_SIZE*nr_cpu_ids + INTCG_SIZE);
167 if (INTCG_base == NULL)
168 return -EIO;
169
170 INTCL_base = INTCG_base + INTCG_SIZE;
171
172 writel_relaxed(BIT(0), INTCG_base + INTCG_ICTLR);
173 }
174
175 root_domain = irq_domain_add_linear(node, nr_irq, &csky_irqdomain_ops,
176 NULL);
177 if (!root_domain)
178 return -ENXIO;
179
180 /* for every cpu */
181 for_each_present_cpu(cpu) {
182 per_cpu(intcl_reg, cpu) = INTCL_base + (INTCL_SIZE * cpu);
183 writel_relaxed(BIT(0), per_cpu(intcl_reg, cpu) + INTCL_PICTLR);
184 }
185
186 set_handle_irq(&csky_mpintc_handler);
187
188#ifdef CONFIG_SMP
189 ipi_irq = irq_create_mapping(root_domain, IPI_IRQ);
190 if (!ipi_irq)
191 return -EIO;
192
193 set_send_ipi(&csky_mpintc_send_ipi, ipi_irq);
194#endif
195
196 return 0;
197}
198IRQCHIP_DECLARE(csky_mpintc, "csky,mpintc", csky_mpintc_init);