1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * linux/arch/arm/common/vic.c
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (C) 1999 - 2003 ARM Limited
6*4882a593Smuzhiyun * Copyright (C) 2000 Deep Blue Solutions Ltd
7*4882a593Smuzhiyun */
8*4882a593Smuzhiyun
9*4882a593Smuzhiyun #include <linux/export.h>
10*4882a593Smuzhiyun #include <linux/init.h>
11*4882a593Smuzhiyun #include <linux/list.h>
12*4882a593Smuzhiyun #include <linux/io.h>
13*4882a593Smuzhiyun #include <linux/irq.h>
14*4882a593Smuzhiyun #include <linux/irqchip.h>
15*4882a593Smuzhiyun #include <linux/irqchip/chained_irq.h>
16*4882a593Smuzhiyun #include <linux/irqdomain.h>
17*4882a593Smuzhiyun #include <linux/of.h>
18*4882a593Smuzhiyun #include <linux/of_address.h>
19*4882a593Smuzhiyun #include <linux/of_irq.h>
20*4882a593Smuzhiyun #include <linux/syscore_ops.h>
21*4882a593Smuzhiyun #include <linux/device.h>
22*4882a593Smuzhiyun #include <linux/amba/bus.h>
23*4882a593Smuzhiyun #include <linux/irqchip/arm-vic.h>
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun #include <asm/exception.h>
26*4882a593Smuzhiyun #include <asm/irq.h>
27*4882a593Smuzhiyun
28*4882a593Smuzhiyun #define VIC_IRQ_STATUS 0x00
29*4882a593Smuzhiyun #define VIC_FIQ_STATUS 0x04
30*4882a593Smuzhiyun #define VIC_RAW_STATUS 0x08
31*4882a593Smuzhiyun #define VIC_INT_SELECT 0x0c /* 1 = FIQ, 0 = IRQ */
32*4882a593Smuzhiyun #define VIC_INT_ENABLE 0x10 /* 1 = enable, 0 = disable */
33*4882a593Smuzhiyun #define VIC_INT_ENABLE_CLEAR 0x14
34*4882a593Smuzhiyun #define VIC_INT_SOFT 0x18
35*4882a593Smuzhiyun #define VIC_INT_SOFT_CLEAR 0x1c
36*4882a593Smuzhiyun #define VIC_PROTECT 0x20
37*4882a593Smuzhiyun #define VIC_PL190_VECT_ADDR 0x30 /* PL190 only */
38*4882a593Smuzhiyun #define VIC_PL190_DEF_VECT_ADDR 0x34 /* PL190 only */
39*4882a593Smuzhiyun
40*4882a593Smuzhiyun #define VIC_VECT_ADDR0 0x100 /* 0 to 15 (0..31 PL192) */
41*4882a593Smuzhiyun #define VIC_VECT_CNTL0 0x200 /* 0 to 15 (0..31 PL192) */
42*4882a593Smuzhiyun #define VIC_ITCR 0x300 /* VIC test control register */
43*4882a593Smuzhiyun
44*4882a593Smuzhiyun #define VIC_VECT_CNTL_ENABLE (1 << 5)
45*4882a593Smuzhiyun
46*4882a593Smuzhiyun #define VIC_PL192_VECT_ADDR 0xF00
47*4882a593Smuzhiyun
48*4882a593Smuzhiyun /**
49*4882a593Smuzhiyun * struct vic_device - VIC PM device
50*4882a593Smuzhiyun * @parent_irq: The parent IRQ number of the VIC if cascaded, or 0.
51*4882a593Smuzhiyun * @irq: The IRQ number for the base of the VIC.
52*4882a593Smuzhiyun * @base: The register base for the VIC.
53*4882a593Smuzhiyun * @valid_sources: A bitmask of valid interrupts
54*4882a593Smuzhiyun * @resume_sources: A bitmask of interrupts for resume.
55*4882a593Smuzhiyun * @resume_irqs: The IRQs enabled for resume.
56*4882a593Smuzhiyun * @int_select: Save for VIC_INT_SELECT.
57*4882a593Smuzhiyun * @int_enable: Save for VIC_INT_ENABLE.
58*4882a593Smuzhiyun * @soft_int: Save for VIC_INT_SOFT.
59*4882a593Smuzhiyun * @protect: Save for VIC_PROTECT.
60*4882a593Smuzhiyun * @domain: The IRQ domain for the VIC.
61*4882a593Smuzhiyun */
62*4882a593Smuzhiyun struct vic_device {
63*4882a593Smuzhiyun void __iomem *base;
64*4882a593Smuzhiyun int irq;
65*4882a593Smuzhiyun u32 valid_sources;
66*4882a593Smuzhiyun u32 resume_sources;
67*4882a593Smuzhiyun u32 resume_irqs;
68*4882a593Smuzhiyun u32 int_select;
69*4882a593Smuzhiyun u32 int_enable;
70*4882a593Smuzhiyun u32 soft_int;
71*4882a593Smuzhiyun u32 protect;
72*4882a593Smuzhiyun struct irq_domain *domain;
73*4882a593Smuzhiyun };
74*4882a593Smuzhiyun
75*4882a593Smuzhiyun /* we cannot allocate memory when VICs are initially registered */
76*4882a593Smuzhiyun static struct vic_device vic_devices[CONFIG_ARM_VIC_NR];
77*4882a593Smuzhiyun
78*4882a593Smuzhiyun static int vic_id;
79*4882a593Smuzhiyun
80*4882a593Smuzhiyun static void vic_handle_irq(struct pt_regs *regs);
81*4882a593Smuzhiyun
82*4882a593Smuzhiyun /**
83*4882a593Smuzhiyun * vic_init2 - common initialisation code
84*4882a593Smuzhiyun * @base: Base of the VIC.
85*4882a593Smuzhiyun *
86*4882a593Smuzhiyun * Common initialisation code for registration
87*4882a593Smuzhiyun * and resume.
88*4882a593Smuzhiyun */
vic_init2(void __iomem * base)89*4882a593Smuzhiyun static void vic_init2(void __iomem *base)
90*4882a593Smuzhiyun {
91*4882a593Smuzhiyun int i;
92*4882a593Smuzhiyun
93*4882a593Smuzhiyun for (i = 0; i < 16; i++) {
94*4882a593Smuzhiyun void __iomem *reg = base + VIC_VECT_CNTL0 + (i * 4);
95*4882a593Smuzhiyun writel(VIC_VECT_CNTL_ENABLE | i, reg);
96*4882a593Smuzhiyun }
97*4882a593Smuzhiyun
98*4882a593Smuzhiyun writel(32, base + VIC_PL190_DEF_VECT_ADDR);
99*4882a593Smuzhiyun }
100*4882a593Smuzhiyun
101*4882a593Smuzhiyun #ifdef CONFIG_PM
resume_one_vic(struct vic_device * vic)102*4882a593Smuzhiyun static void resume_one_vic(struct vic_device *vic)
103*4882a593Smuzhiyun {
104*4882a593Smuzhiyun void __iomem *base = vic->base;
105*4882a593Smuzhiyun
106*4882a593Smuzhiyun printk(KERN_DEBUG "%s: resuming vic at %p\n", __func__, base);
107*4882a593Smuzhiyun
108*4882a593Smuzhiyun /* re-initialise static settings */
109*4882a593Smuzhiyun vic_init2(base);
110*4882a593Smuzhiyun
111*4882a593Smuzhiyun writel(vic->int_select, base + VIC_INT_SELECT);
112*4882a593Smuzhiyun writel(vic->protect, base + VIC_PROTECT);
113*4882a593Smuzhiyun
114*4882a593Smuzhiyun /* set the enabled ints and then clear the non-enabled */
115*4882a593Smuzhiyun writel(vic->int_enable, base + VIC_INT_ENABLE);
116*4882a593Smuzhiyun writel(~vic->int_enable, base + VIC_INT_ENABLE_CLEAR);
117*4882a593Smuzhiyun
118*4882a593Smuzhiyun /* and the same for the soft-int register */
119*4882a593Smuzhiyun
120*4882a593Smuzhiyun writel(vic->soft_int, base + VIC_INT_SOFT);
121*4882a593Smuzhiyun writel(~vic->soft_int, base + VIC_INT_SOFT_CLEAR);
122*4882a593Smuzhiyun }
123*4882a593Smuzhiyun
vic_resume(void)124*4882a593Smuzhiyun static void vic_resume(void)
125*4882a593Smuzhiyun {
126*4882a593Smuzhiyun int id;
127*4882a593Smuzhiyun
128*4882a593Smuzhiyun for (id = vic_id - 1; id >= 0; id--)
129*4882a593Smuzhiyun resume_one_vic(vic_devices + id);
130*4882a593Smuzhiyun }
131*4882a593Smuzhiyun
suspend_one_vic(struct vic_device * vic)132*4882a593Smuzhiyun static void suspend_one_vic(struct vic_device *vic)
133*4882a593Smuzhiyun {
134*4882a593Smuzhiyun void __iomem *base = vic->base;
135*4882a593Smuzhiyun
136*4882a593Smuzhiyun printk(KERN_DEBUG "%s: suspending vic at %p\n", __func__, base);
137*4882a593Smuzhiyun
138*4882a593Smuzhiyun vic->int_select = readl(base + VIC_INT_SELECT);
139*4882a593Smuzhiyun vic->int_enable = readl(base + VIC_INT_ENABLE);
140*4882a593Smuzhiyun vic->soft_int = readl(base + VIC_INT_SOFT);
141*4882a593Smuzhiyun vic->protect = readl(base + VIC_PROTECT);
142*4882a593Smuzhiyun
143*4882a593Smuzhiyun /* set the interrupts (if any) that are used for
144*4882a593Smuzhiyun * resuming the system */
145*4882a593Smuzhiyun
146*4882a593Smuzhiyun writel(vic->resume_irqs, base + VIC_INT_ENABLE);
147*4882a593Smuzhiyun writel(~vic->resume_irqs, base + VIC_INT_ENABLE_CLEAR);
148*4882a593Smuzhiyun }
149*4882a593Smuzhiyun
vic_suspend(void)150*4882a593Smuzhiyun static int vic_suspend(void)
151*4882a593Smuzhiyun {
152*4882a593Smuzhiyun int id;
153*4882a593Smuzhiyun
154*4882a593Smuzhiyun for (id = 0; id < vic_id; id++)
155*4882a593Smuzhiyun suspend_one_vic(vic_devices + id);
156*4882a593Smuzhiyun
157*4882a593Smuzhiyun return 0;
158*4882a593Smuzhiyun }
159*4882a593Smuzhiyun
160*4882a593Smuzhiyun static struct syscore_ops vic_syscore_ops = {
161*4882a593Smuzhiyun .suspend = vic_suspend,
162*4882a593Smuzhiyun .resume = vic_resume,
163*4882a593Smuzhiyun };
164*4882a593Smuzhiyun
165*4882a593Smuzhiyun /**
166*4882a593Smuzhiyun * vic_pm_init - initicall to register VIC pm
167*4882a593Smuzhiyun *
168*4882a593Smuzhiyun * This is called via late_initcall() to register
169*4882a593Smuzhiyun * the resources for the VICs due to the early
170*4882a593Smuzhiyun * nature of the VIC's registration.
171*4882a593Smuzhiyun */
vic_pm_init(void)172*4882a593Smuzhiyun static int __init vic_pm_init(void)
173*4882a593Smuzhiyun {
174*4882a593Smuzhiyun if (vic_id > 0)
175*4882a593Smuzhiyun register_syscore_ops(&vic_syscore_ops);
176*4882a593Smuzhiyun
177*4882a593Smuzhiyun return 0;
178*4882a593Smuzhiyun }
179*4882a593Smuzhiyun late_initcall(vic_pm_init);
180*4882a593Smuzhiyun #endif /* CONFIG_PM */
181*4882a593Smuzhiyun
182*4882a593Smuzhiyun static struct irq_chip vic_chip;
183*4882a593Smuzhiyun
vic_irqdomain_map(struct irq_domain * d,unsigned int irq,irq_hw_number_t hwirq)184*4882a593Smuzhiyun static int vic_irqdomain_map(struct irq_domain *d, unsigned int irq,
185*4882a593Smuzhiyun irq_hw_number_t hwirq)
186*4882a593Smuzhiyun {
187*4882a593Smuzhiyun struct vic_device *v = d->host_data;
188*4882a593Smuzhiyun
189*4882a593Smuzhiyun /* Skip invalid IRQs, only register handlers for the real ones */
190*4882a593Smuzhiyun if (!(v->valid_sources & (1 << hwirq)))
191*4882a593Smuzhiyun return -EPERM;
192*4882a593Smuzhiyun irq_set_chip_and_handler(irq, &vic_chip, handle_level_irq);
193*4882a593Smuzhiyun irq_set_chip_data(irq, v->base);
194*4882a593Smuzhiyun irq_set_probe(irq);
195*4882a593Smuzhiyun return 0;
196*4882a593Smuzhiyun }
197*4882a593Smuzhiyun
198*4882a593Smuzhiyun /*
199*4882a593Smuzhiyun * Handle each interrupt in a single VIC. Returns non-zero if we've
200*4882a593Smuzhiyun * handled at least one interrupt. This reads the status register
201*4882a593Smuzhiyun * before handling each interrupt, which is necessary given that
202*4882a593Smuzhiyun * handle_IRQ may briefly re-enable interrupts for soft IRQ handling.
203*4882a593Smuzhiyun */
handle_one_vic(struct vic_device * vic,struct pt_regs * regs)204*4882a593Smuzhiyun static int handle_one_vic(struct vic_device *vic, struct pt_regs *regs)
205*4882a593Smuzhiyun {
206*4882a593Smuzhiyun u32 stat, irq;
207*4882a593Smuzhiyun int handled = 0;
208*4882a593Smuzhiyun
209*4882a593Smuzhiyun while ((stat = readl_relaxed(vic->base + VIC_IRQ_STATUS))) {
210*4882a593Smuzhiyun irq = ffs(stat) - 1;
211*4882a593Smuzhiyun handle_domain_irq(vic->domain, irq, regs);
212*4882a593Smuzhiyun handled = 1;
213*4882a593Smuzhiyun }
214*4882a593Smuzhiyun
215*4882a593Smuzhiyun return handled;
216*4882a593Smuzhiyun }
217*4882a593Smuzhiyun
vic_handle_irq_cascaded(struct irq_desc * desc)218*4882a593Smuzhiyun static void vic_handle_irq_cascaded(struct irq_desc *desc)
219*4882a593Smuzhiyun {
220*4882a593Smuzhiyun u32 stat, hwirq;
221*4882a593Smuzhiyun struct irq_chip *host_chip = irq_desc_get_chip(desc);
222*4882a593Smuzhiyun struct vic_device *vic = irq_desc_get_handler_data(desc);
223*4882a593Smuzhiyun
224*4882a593Smuzhiyun chained_irq_enter(host_chip, desc);
225*4882a593Smuzhiyun
226*4882a593Smuzhiyun while ((stat = readl_relaxed(vic->base + VIC_IRQ_STATUS))) {
227*4882a593Smuzhiyun hwirq = ffs(stat) - 1;
228*4882a593Smuzhiyun generic_handle_irq(irq_find_mapping(vic->domain, hwirq));
229*4882a593Smuzhiyun }
230*4882a593Smuzhiyun
231*4882a593Smuzhiyun chained_irq_exit(host_chip, desc);
232*4882a593Smuzhiyun }
233*4882a593Smuzhiyun
234*4882a593Smuzhiyun /*
235*4882a593Smuzhiyun * Keep iterating over all registered VIC's until there are no pending
236*4882a593Smuzhiyun * interrupts.
237*4882a593Smuzhiyun */
vic_handle_irq(struct pt_regs * regs)238*4882a593Smuzhiyun static void __exception_irq_entry vic_handle_irq(struct pt_regs *regs)
239*4882a593Smuzhiyun {
240*4882a593Smuzhiyun int i, handled;
241*4882a593Smuzhiyun
242*4882a593Smuzhiyun do {
243*4882a593Smuzhiyun for (i = 0, handled = 0; i < vic_id; ++i)
244*4882a593Smuzhiyun handled |= handle_one_vic(&vic_devices[i], regs);
245*4882a593Smuzhiyun } while (handled);
246*4882a593Smuzhiyun }
247*4882a593Smuzhiyun
248*4882a593Smuzhiyun static const struct irq_domain_ops vic_irqdomain_ops = {
249*4882a593Smuzhiyun .map = vic_irqdomain_map,
250*4882a593Smuzhiyun .xlate = irq_domain_xlate_onetwocell,
251*4882a593Smuzhiyun };
252*4882a593Smuzhiyun
253*4882a593Smuzhiyun /**
254*4882a593Smuzhiyun * vic_register() - Register a VIC.
255*4882a593Smuzhiyun * @base: The base address of the VIC.
256*4882a593Smuzhiyun * @parent_irq: The parent IRQ if cascaded, else 0.
257*4882a593Smuzhiyun * @irq: The base IRQ for the VIC.
258*4882a593Smuzhiyun * @valid_sources: bitmask of valid interrupts
259*4882a593Smuzhiyun * @resume_sources: bitmask of interrupts allowed for resume sources.
260*4882a593Smuzhiyun * @node: The device tree node associated with the VIC.
261*4882a593Smuzhiyun *
262*4882a593Smuzhiyun * Register the VIC with the system device tree so that it can be notified
263*4882a593Smuzhiyun * of suspend and resume requests and ensure that the correct actions are
264*4882a593Smuzhiyun * taken to re-instate the settings on resume.
265*4882a593Smuzhiyun *
266*4882a593Smuzhiyun * This also configures the IRQ domain for the VIC.
267*4882a593Smuzhiyun */
vic_register(void __iomem * base,unsigned int parent_irq,unsigned int irq,u32 valid_sources,u32 resume_sources,struct device_node * node)268*4882a593Smuzhiyun static void __init vic_register(void __iomem *base, unsigned int parent_irq,
269*4882a593Smuzhiyun unsigned int irq,
270*4882a593Smuzhiyun u32 valid_sources, u32 resume_sources,
271*4882a593Smuzhiyun struct device_node *node)
272*4882a593Smuzhiyun {
273*4882a593Smuzhiyun struct vic_device *v;
274*4882a593Smuzhiyun int i;
275*4882a593Smuzhiyun
276*4882a593Smuzhiyun if (vic_id >= ARRAY_SIZE(vic_devices)) {
277*4882a593Smuzhiyun printk(KERN_ERR "%s: too few VICs, increase CONFIG_ARM_VIC_NR\n", __func__);
278*4882a593Smuzhiyun return;
279*4882a593Smuzhiyun }
280*4882a593Smuzhiyun
281*4882a593Smuzhiyun v = &vic_devices[vic_id];
282*4882a593Smuzhiyun v->base = base;
283*4882a593Smuzhiyun v->valid_sources = valid_sources;
284*4882a593Smuzhiyun v->resume_sources = resume_sources;
285*4882a593Smuzhiyun set_handle_irq(vic_handle_irq);
286*4882a593Smuzhiyun vic_id++;
287*4882a593Smuzhiyun
288*4882a593Smuzhiyun if (parent_irq) {
289*4882a593Smuzhiyun irq_set_chained_handler_and_data(parent_irq,
290*4882a593Smuzhiyun vic_handle_irq_cascaded, v);
291*4882a593Smuzhiyun }
292*4882a593Smuzhiyun
293*4882a593Smuzhiyun v->domain = irq_domain_add_simple(node, fls(valid_sources), irq,
294*4882a593Smuzhiyun &vic_irqdomain_ops, v);
295*4882a593Smuzhiyun /* create an IRQ mapping for each valid IRQ */
296*4882a593Smuzhiyun for (i = 0; i < fls(valid_sources); i++)
297*4882a593Smuzhiyun if (valid_sources & (1 << i))
298*4882a593Smuzhiyun irq_create_mapping(v->domain, i);
299*4882a593Smuzhiyun /* If no base IRQ was passed, figure out our allocated base */
300*4882a593Smuzhiyun if (irq)
301*4882a593Smuzhiyun v->irq = irq;
302*4882a593Smuzhiyun else
303*4882a593Smuzhiyun v->irq = irq_find_mapping(v->domain, 0);
304*4882a593Smuzhiyun }
305*4882a593Smuzhiyun
vic_ack_irq(struct irq_data * d)306*4882a593Smuzhiyun static void vic_ack_irq(struct irq_data *d)
307*4882a593Smuzhiyun {
308*4882a593Smuzhiyun void __iomem *base = irq_data_get_irq_chip_data(d);
309*4882a593Smuzhiyun unsigned int irq = d->hwirq;
310*4882a593Smuzhiyun writel(1 << irq, base + VIC_INT_ENABLE_CLEAR);
311*4882a593Smuzhiyun /* moreover, clear the soft-triggered, in case it was the reason */
312*4882a593Smuzhiyun writel(1 << irq, base + VIC_INT_SOFT_CLEAR);
313*4882a593Smuzhiyun }
314*4882a593Smuzhiyun
vic_mask_irq(struct irq_data * d)315*4882a593Smuzhiyun static void vic_mask_irq(struct irq_data *d)
316*4882a593Smuzhiyun {
317*4882a593Smuzhiyun void __iomem *base = irq_data_get_irq_chip_data(d);
318*4882a593Smuzhiyun unsigned int irq = d->hwirq;
319*4882a593Smuzhiyun writel(1 << irq, base + VIC_INT_ENABLE_CLEAR);
320*4882a593Smuzhiyun }
321*4882a593Smuzhiyun
vic_unmask_irq(struct irq_data * d)322*4882a593Smuzhiyun static void vic_unmask_irq(struct irq_data *d)
323*4882a593Smuzhiyun {
324*4882a593Smuzhiyun void __iomem *base = irq_data_get_irq_chip_data(d);
325*4882a593Smuzhiyun unsigned int irq = d->hwirq;
326*4882a593Smuzhiyun writel(1 << irq, base + VIC_INT_ENABLE);
327*4882a593Smuzhiyun }
328*4882a593Smuzhiyun
329*4882a593Smuzhiyun #if defined(CONFIG_PM)
vic_from_irq(unsigned int irq)330*4882a593Smuzhiyun static struct vic_device *vic_from_irq(unsigned int irq)
331*4882a593Smuzhiyun {
332*4882a593Smuzhiyun struct vic_device *v = vic_devices;
333*4882a593Smuzhiyun unsigned int base_irq = irq & ~31;
334*4882a593Smuzhiyun int id;
335*4882a593Smuzhiyun
336*4882a593Smuzhiyun for (id = 0; id < vic_id; id++, v++) {
337*4882a593Smuzhiyun if (v->irq == base_irq)
338*4882a593Smuzhiyun return v;
339*4882a593Smuzhiyun }
340*4882a593Smuzhiyun
341*4882a593Smuzhiyun return NULL;
342*4882a593Smuzhiyun }
343*4882a593Smuzhiyun
vic_set_wake(struct irq_data * d,unsigned int on)344*4882a593Smuzhiyun static int vic_set_wake(struct irq_data *d, unsigned int on)
345*4882a593Smuzhiyun {
346*4882a593Smuzhiyun struct vic_device *v = vic_from_irq(d->irq);
347*4882a593Smuzhiyun unsigned int off = d->hwirq;
348*4882a593Smuzhiyun u32 bit = 1 << off;
349*4882a593Smuzhiyun
350*4882a593Smuzhiyun if (!v)
351*4882a593Smuzhiyun return -EINVAL;
352*4882a593Smuzhiyun
353*4882a593Smuzhiyun if (!(bit & v->resume_sources))
354*4882a593Smuzhiyun return -EINVAL;
355*4882a593Smuzhiyun
356*4882a593Smuzhiyun if (on)
357*4882a593Smuzhiyun v->resume_irqs |= bit;
358*4882a593Smuzhiyun else
359*4882a593Smuzhiyun v->resume_irqs &= ~bit;
360*4882a593Smuzhiyun
361*4882a593Smuzhiyun return 0;
362*4882a593Smuzhiyun }
363*4882a593Smuzhiyun #else
364*4882a593Smuzhiyun #define vic_set_wake NULL
365*4882a593Smuzhiyun #endif /* CONFIG_PM */
366*4882a593Smuzhiyun
367*4882a593Smuzhiyun static struct irq_chip vic_chip = {
368*4882a593Smuzhiyun .name = "VIC",
369*4882a593Smuzhiyun .irq_ack = vic_ack_irq,
370*4882a593Smuzhiyun .irq_mask = vic_mask_irq,
371*4882a593Smuzhiyun .irq_unmask = vic_unmask_irq,
372*4882a593Smuzhiyun .irq_set_wake = vic_set_wake,
373*4882a593Smuzhiyun };
374*4882a593Smuzhiyun
vic_disable(void __iomem * base)375*4882a593Smuzhiyun static void __init vic_disable(void __iomem *base)
376*4882a593Smuzhiyun {
377*4882a593Smuzhiyun writel(0, base + VIC_INT_SELECT);
378*4882a593Smuzhiyun writel(0, base + VIC_INT_ENABLE);
379*4882a593Smuzhiyun writel(~0, base + VIC_INT_ENABLE_CLEAR);
380*4882a593Smuzhiyun writel(0, base + VIC_ITCR);
381*4882a593Smuzhiyun writel(~0, base + VIC_INT_SOFT_CLEAR);
382*4882a593Smuzhiyun }
383*4882a593Smuzhiyun
vic_clear_interrupts(void __iomem * base)384*4882a593Smuzhiyun static void __init vic_clear_interrupts(void __iomem *base)
385*4882a593Smuzhiyun {
386*4882a593Smuzhiyun unsigned int i;
387*4882a593Smuzhiyun
388*4882a593Smuzhiyun writel(0, base + VIC_PL190_VECT_ADDR);
389*4882a593Smuzhiyun for (i = 0; i < 19; i++) {
390*4882a593Smuzhiyun unsigned int value;
391*4882a593Smuzhiyun
392*4882a593Smuzhiyun value = readl(base + VIC_PL190_VECT_ADDR);
393*4882a593Smuzhiyun writel(value, base + VIC_PL190_VECT_ADDR);
394*4882a593Smuzhiyun }
395*4882a593Smuzhiyun }
396*4882a593Smuzhiyun
397*4882a593Smuzhiyun /*
398*4882a593Smuzhiyun * The PL190 cell from ARM has been modified by ST to handle 64 interrupts.
399*4882a593Smuzhiyun * The original cell has 32 interrupts, while the modified one has 64,
400*4882a593Smuzhiyun * replocating two blocks 0x00..0x1f in 0x20..0x3f. In that case
401*4882a593Smuzhiyun * the probe function is called twice, with base set to offset 000
402*4882a593Smuzhiyun * and 020 within the page. We call this "second block".
403*4882a593Smuzhiyun */
vic_init_st(void __iomem * base,unsigned int irq_start,u32 vic_sources,struct device_node * node)404*4882a593Smuzhiyun static void __init vic_init_st(void __iomem *base, unsigned int irq_start,
405*4882a593Smuzhiyun u32 vic_sources, struct device_node *node)
406*4882a593Smuzhiyun {
407*4882a593Smuzhiyun unsigned int i;
408*4882a593Smuzhiyun int vic_2nd_block = ((unsigned long)base & ~PAGE_MASK) != 0;
409*4882a593Smuzhiyun
410*4882a593Smuzhiyun /* Disable all interrupts initially. */
411*4882a593Smuzhiyun vic_disable(base);
412*4882a593Smuzhiyun
413*4882a593Smuzhiyun /*
414*4882a593Smuzhiyun * Make sure we clear all existing interrupts. The vector registers
415*4882a593Smuzhiyun * in this cell are after the second block of general registers,
416*4882a593Smuzhiyun * so we can address them using standard offsets, but only from
417*4882a593Smuzhiyun * the second base address, which is 0x20 in the page
418*4882a593Smuzhiyun */
419*4882a593Smuzhiyun if (vic_2nd_block) {
420*4882a593Smuzhiyun vic_clear_interrupts(base);
421*4882a593Smuzhiyun
422*4882a593Smuzhiyun /* ST has 16 vectors as well, but we don't enable them by now */
423*4882a593Smuzhiyun for (i = 0; i < 16; i++) {
424*4882a593Smuzhiyun void __iomem *reg = base + VIC_VECT_CNTL0 + (i * 4);
425*4882a593Smuzhiyun writel(0, reg);
426*4882a593Smuzhiyun }
427*4882a593Smuzhiyun
428*4882a593Smuzhiyun writel(32, base + VIC_PL190_DEF_VECT_ADDR);
429*4882a593Smuzhiyun }
430*4882a593Smuzhiyun
431*4882a593Smuzhiyun vic_register(base, 0, irq_start, vic_sources, 0, node);
432*4882a593Smuzhiyun }
433*4882a593Smuzhiyun
__vic_init(void __iomem * base,int parent_irq,int irq_start,u32 vic_sources,u32 resume_sources,struct device_node * node)434*4882a593Smuzhiyun static void __init __vic_init(void __iomem *base, int parent_irq, int irq_start,
435*4882a593Smuzhiyun u32 vic_sources, u32 resume_sources,
436*4882a593Smuzhiyun struct device_node *node)
437*4882a593Smuzhiyun {
438*4882a593Smuzhiyun unsigned int i;
439*4882a593Smuzhiyun u32 cellid = 0;
440*4882a593Smuzhiyun enum amba_vendor vendor;
441*4882a593Smuzhiyun
442*4882a593Smuzhiyun /* Identify which VIC cell this one is, by reading the ID */
443*4882a593Smuzhiyun for (i = 0; i < 4; i++) {
444*4882a593Smuzhiyun void __iomem *addr;
445*4882a593Smuzhiyun addr = (void __iomem *)((u32)base & PAGE_MASK) + 0xfe0 + (i * 4);
446*4882a593Smuzhiyun cellid |= (readl(addr) & 0xff) << (8 * i);
447*4882a593Smuzhiyun }
448*4882a593Smuzhiyun vendor = (cellid >> 12) & 0xff;
449*4882a593Smuzhiyun printk(KERN_INFO "VIC @%p: id 0x%08x, vendor 0x%02x\n",
450*4882a593Smuzhiyun base, cellid, vendor);
451*4882a593Smuzhiyun
452*4882a593Smuzhiyun switch(vendor) {
453*4882a593Smuzhiyun case AMBA_VENDOR_ST:
454*4882a593Smuzhiyun vic_init_st(base, irq_start, vic_sources, node);
455*4882a593Smuzhiyun return;
456*4882a593Smuzhiyun default:
457*4882a593Smuzhiyun printk(KERN_WARNING "VIC: unknown vendor, continuing anyways\n");
458*4882a593Smuzhiyun fallthrough;
459*4882a593Smuzhiyun case AMBA_VENDOR_ARM:
460*4882a593Smuzhiyun break;
461*4882a593Smuzhiyun }
462*4882a593Smuzhiyun
463*4882a593Smuzhiyun /* Disable all interrupts initially. */
464*4882a593Smuzhiyun vic_disable(base);
465*4882a593Smuzhiyun
466*4882a593Smuzhiyun /* Make sure we clear all existing interrupts */
467*4882a593Smuzhiyun vic_clear_interrupts(base);
468*4882a593Smuzhiyun
469*4882a593Smuzhiyun vic_init2(base);
470*4882a593Smuzhiyun
471*4882a593Smuzhiyun vic_register(base, parent_irq, irq_start, vic_sources, resume_sources, node);
472*4882a593Smuzhiyun }
473*4882a593Smuzhiyun
474*4882a593Smuzhiyun /**
475*4882a593Smuzhiyun * vic_init() - initialise a vectored interrupt controller
476*4882a593Smuzhiyun * @base: iomem base address
477*4882a593Smuzhiyun * @irq_start: starting interrupt number, must be muliple of 32
478*4882a593Smuzhiyun * @vic_sources: bitmask of interrupt sources to allow
479*4882a593Smuzhiyun * @resume_sources: bitmask of interrupt sources to allow for resume
480*4882a593Smuzhiyun */
vic_init(void __iomem * base,unsigned int irq_start,u32 vic_sources,u32 resume_sources)481*4882a593Smuzhiyun void __init vic_init(void __iomem *base, unsigned int irq_start,
482*4882a593Smuzhiyun u32 vic_sources, u32 resume_sources)
483*4882a593Smuzhiyun {
484*4882a593Smuzhiyun __vic_init(base, 0, irq_start, vic_sources, resume_sources, NULL);
485*4882a593Smuzhiyun }
486*4882a593Smuzhiyun
487*4882a593Smuzhiyun #ifdef CONFIG_OF
vic_of_init(struct device_node * node,struct device_node * parent)488*4882a593Smuzhiyun static int __init vic_of_init(struct device_node *node,
489*4882a593Smuzhiyun struct device_node *parent)
490*4882a593Smuzhiyun {
491*4882a593Smuzhiyun void __iomem *regs;
492*4882a593Smuzhiyun u32 interrupt_mask = ~0;
493*4882a593Smuzhiyun u32 wakeup_mask = ~0;
494*4882a593Smuzhiyun int parent_irq;
495*4882a593Smuzhiyun
496*4882a593Smuzhiyun regs = of_iomap(node, 0);
497*4882a593Smuzhiyun if (WARN_ON(!regs))
498*4882a593Smuzhiyun return -EIO;
499*4882a593Smuzhiyun
500*4882a593Smuzhiyun of_property_read_u32(node, "valid-mask", &interrupt_mask);
501*4882a593Smuzhiyun of_property_read_u32(node, "valid-wakeup-mask", &wakeup_mask);
502*4882a593Smuzhiyun parent_irq = of_irq_get(node, 0);
503*4882a593Smuzhiyun if (parent_irq < 0)
504*4882a593Smuzhiyun parent_irq = 0;
505*4882a593Smuzhiyun
506*4882a593Smuzhiyun /*
507*4882a593Smuzhiyun * Passing 0 as first IRQ makes the simple domain allocate descriptors
508*4882a593Smuzhiyun */
509*4882a593Smuzhiyun __vic_init(regs, parent_irq, 0, interrupt_mask, wakeup_mask, node);
510*4882a593Smuzhiyun
511*4882a593Smuzhiyun return 0;
512*4882a593Smuzhiyun }
513*4882a593Smuzhiyun IRQCHIP_DECLARE(arm_pl190_vic, "arm,pl190-vic", vic_of_init);
514*4882a593Smuzhiyun IRQCHIP_DECLARE(arm_pl192_vic, "arm,pl192-vic", vic_of_init);
515*4882a593Smuzhiyun IRQCHIP_DECLARE(arm_versatile_vic, "arm,versatile-vic", vic_of_init);
516*4882a593Smuzhiyun #endif /* CONFIG OF */
517