xref: /OK3568_Linux_fs/kernel/drivers/irqchip/irq-clps711x.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  *  CLPS711X IRQ driver
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  *  Copyright (C) 2013 Alexander Shiyan <shc_work@mail.ru>
6*4882a593Smuzhiyun  */
7*4882a593Smuzhiyun 
8*4882a593Smuzhiyun #include <linux/io.h>
9*4882a593Smuzhiyun #include <linux/irq.h>
10*4882a593Smuzhiyun #include <linux/irqchip.h>
11*4882a593Smuzhiyun #include <linux/irqdomain.h>
12*4882a593Smuzhiyun #include <linux/of_address.h>
13*4882a593Smuzhiyun #include <linux/of_irq.h>
14*4882a593Smuzhiyun #include <linux/slab.h>
15*4882a593Smuzhiyun 
16*4882a593Smuzhiyun #include <asm/exception.h>
17*4882a593Smuzhiyun #include <asm/mach/irq.h>
18*4882a593Smuzhiyun 
19*4882a593Smuzhiyun #define CLPS711X_INTSR1	(0x0240)
20*4882a593Smuzhiyun #define CLPS711X_INTMR1	(0x0280)
21*4882a593Smuzhiyun #define CLPS711X_BLEOI	(0x0600)
22*4882a593Smuzhiyun #define CLPS711X_MCEOI	(0x0640)
23*4882a593Smuzhiyun #define CLPS711X_TEOI	(0x0680)
24*4882a593Smuzhiyun #define CLPS711X_TC1EOI	(0x06c0)
25*4882a593Smuzhiyun #define CLPS711X_TC2EOI	(0x0700)
26*4882a593Smuzhiyun #define CLPS711X_RTCEOI	(0x0740)
27*4882a593Smuzhiyun #define CLPS711X_UMSEOI	(0x0780)
28*4882a593Smuzhiyun #define CLPS711X_COEOI	(0x07c0)
29*4882a593Smuzhiyun #define CLPS711X_INTSR2	(0x1240)
30*4882a593Smuzhiyun #define CLPS711X_INTMR2	(0x1280)
31*4882a593Smuzhiyun #define CLPS711X_SRXEOF	(0x1600)
32*4882a593Smuzhiyun #define CLPS711X_KBDEOI	(0x1700)
33*4882a593Smuzhiyun #define CLPS711X_INTSR3	(0x2240)
34*4882a593Smuzhiyun #define CLPS711X_INTMR3	(0x2280)
35*4882a593Smuzhiyun 
36*4882a593Smuzhiyun static const struct {
37*4882a593Smuzhiyun #define CLPS711X_FLAG_EN	(1 << 0)
38*4882a593Smuzhiyun #define CLPS711X_FLAG_FIQ	(1 << 1)
39*4882a593Smuzhiyun 	unsigned int	flags;
40*4882a593Smuzhiyun 	phys_addr_t	eoi;
41*4882a593Smuzhiyun } clps711x_irqs[] = {
42*4882a593Smuzhiyun 	[1]	= { CLPS711X_FLAG_FIQ, CLPS711X_BLEOI, },
43*4882a593Smuzhiyun 	[3]	= { CLPS711X_FLAG_FIQ, CLPS711X_MCEOI, },
44*4882a593Smuzhiyun 	[4]	= { CLPS711X_FLAG_EN, CLPS711X_COEOI, },
45*4882a593Smuzhiyun 	[5]	= { CLPS711X_FLAG_EN, },
46*4882a593Smuzhiyun 	[6]	= { CLPS711X_FLAG_EN, },
47*4882a593Smuzhiyun 	[7]	= { CLPS711X_FLAG_EN, },
48*4882a593Smuzhiyun 	[8]	= { CLPS711X_FLAG_EN, CLPS711X_TC1EOI, },
49*4882a593Smuzhiyun 	[9]	= { CLPS711X_FLAG_EN, CLPS711X_TC2EOI, },
50*4882a593Smuzhiyun 	[10]	= { CLPS711X_FLAG_EN, CLPS711X_RTCEOI, },
51*4882a593Smuzhiyun 	[11]	= { CLPS711X_FLAG_EN, CLPS711X_TEOI, },
52*4882a593Smuzhiyun 	[12]	= { CLPS711X_FLAG_EN, },
53*4882a593Smuzhiyun 	[13]	= { CLPS711X_FLAG_EN, },
54*4882a593Smuzhiyun 	[14]	= { CLPS711X_FLAG_EN, CLPS711X_UMSEOI, },
55*4882a593Smuzhiyun 	[15]	= { CLPS711X_FLAG_EN, CLPS711X_SRXEOF, },
56*4882a593Smuzhiyun 	[16]	= { CLPS711X_FLAG_EN, CLPS711X_KBDEOI, },
57*4882a593Smuzhiyun 	[17]	= { CLPS711X_FLAG_EN, },
58*4882a593Smuzhiyun 	[18]	= { CLPS711X_FLAG_EN, },
59*4882a593Smuzhiyun 	[28]	= { CLPS711X_FLAG_EN, },
60*4882a593Smuzhiyun 	[29]	= { CLPS711X_FLAG_EN, },
61*4882a593Smuzhiyun 	[32]	= { CLPS711X_FLAG_FIQ, },
62*4882a593Smuzhiyun };
63*4882a593Smuzhiyun 
64*4882a593Smuzhiyun static struct {
65*4882a593Smuzhiyun 	void __iomem		*base;
66*4882a593Smuzhiyun 	void __iomem		*intmr[3];
67*4882a593Smuzhiyun 	void __iomem		*intsr[3];
68*4882a593Smuzhiyun 	struct irq_domain	*domain;
69*4882a593Smuzhiyun 	struct irq_domain_ops	ops;
70*4882a593Smuzhiyun } *clps711x_intc;
71*4882a593Smuzhiyun 
clps711x_irqh(struct pt_regs * regs)72*4882a593Smuzhiyun static asmlinkage void __exception_irq_entry clps711x_irqh(struct pt_regs *regs)
73*4882a593Smuzhiyun {
74*4882a593Smuzhiyun 	u32 irqstat;
75*4882a593Smuzhiyun 
76*4882a593Smuzhiyun 	do {
77*4882a593Smuzhiyun 		irqstat = readw_relaxed(clps711x_intc->intmr[0]) &
78*4882a593Smuzhiyun 			  readw_relaxed(clps711x_intc->intsr[0]);
79*4882a593Smuzhiyun 		if (irqstat)
80*4882a593Smuzhiyun 			handle_domain_irq(clps711x_intc->domain,
81*4882a593Smuzhiyun 					  fls(irqstat) - 1, regs);
82*4882a593Smuzhiyun 
83*4882a593Smuzhiyun 		irqstat = readw_relaxed(clps711x_intc->intmr[1]) &
84*4882a593Smuzhiyun 			  readw_relaxed(clps711x_intc->intsr[1]);
85*4882a593Smuzhiyun 		if (irqstat)
86*4882a593Smuzhiyun 			handle_domain_irq(clps711x_intc->domain,
87*4882a593Smuzhiyun 					  fls(irqstat) - 1 + 16, regs);
88*4882a593Smuzhiyun 	} while (irqstat);
89*4882a593Smuzhiyun }
90*4882a593Smuzhiyun 
clps711x_intc_eoi(struct irq_data * d)91*4882a593Smuzhiyun static void clps711x_intc_eoi(struct irq_data *d)
92*4882a593Smuzhiyun {
93*4882a593Smuzhiyun 	irq_hw_number_t hwirq = irqd_to_hwirq(d);
94*4882a593Smuzhiyun 
95*4882a593Smuzhiyun 	writel_relaxed(0, clps711x_intc->base + clps711x_irqs[hwirq].eoi);
96*4882a593Smuzhiyun }
97*4882a593Smuzhiyun 
clps711x_intc_mask(struct irq_data * d)98*4882a593Smuzhiyun static void clps711x_intc_mask(struct irq_data *d)
99*4882a593Smuzhiyun {
100*4882a593Smuzhiyun 	irq_hw_number_t hwirq = irqd_to_hwirq(d);
101*4882a593Smuzhiyun 	void __iomem *intmr = clps711x_intc->intmr[hwirq / 16];
102*4882a593Smuzhiyun 	u32 tmp;
103*4882a593Smuzhiyun 
104*4882a593Smuzhiyun 	tmp = readl_relaxed(intmr);
105*4882a593Smuzhiyun 	tmp &= ~(1 << (hwirq % 16));
106*4882a593Smuzhiyun 	writel_relaxed(tmp, intmr);
107*4882a593Smuzhiyun }
108*4882a593Smuzhiyun 
clps711x_intc_unmask(struct irq_data * d)109*4882a593Smuzhiyun static void clps711x_intc_unmask(struct irq_data *d)
110*4882a593Smuzhiyun {
111*4882a593Smuzhiyun 	irq_hw_number_t hwirq = irqd_to_hwirq(d);
112*4882a593Smuzhiyun 	void __iomem *intmr = clps711x_intc->intmr[hwirq / 16];
113*4882a593Smuzhiyun 	u32 tmp;
114*4882a593Smuzhiyun 
115*4882a593Smuzhiyun 	tmp = readl_relaxed(intmr);
116*4882a593Smuzhiyun 	tmp |= 1 << (hwirq % 16);
117*4882a593Smuzhiyun 	writel_relaxed(tmp, intmr);
118*4882a593Smuzhiyun }
119*4882a593Smuzhiyun 
120*4882a593Smuzhiyun static struct irq_chip clps711x_intc_chip = {
121*4882a593Smuzhiyun 	.name		= "clps711x-intc",
122*4882a593Smuzhiyun 	.irq_eoi	= clps711x_intc_eoi,
123*4882a593Smuzhiyun 	.irq_mask	= clps711x_intc_mask,
124*4882a593Smuzhiyun 	.irq_unmask	= clps711x_intc_unmask,
125*4882a593Smuzhiyun };
126*4882a593Smuzhiyun 
clps711x_intc_irq_map(struct irq_domain * h,unsigned int virq,irq_hw_number_t hw)127*4882a593Smuzhiyun static int __init clps711x_intc_irq_map(struct irq_domain *h, unsigned int virq,
128*4882a593Smuzhiyun 					irq_hw_number_t hw)
129*4882a593Smuzhiyun {
130*4882a593Smuzhiyun 	irq_flow_handler_t handler = handle_level_irq;
131*4882a593Smuzhiyun 	unsigned int flags = 0;
132*4882a593Smuzhiyun 
133*4882a593Smuzhiyun 	if (!clps711x_irqs[hw].flags)
134*4882a593Smuzhiyun 		return 0;
135*4882a593Smuzhiyun 
136*4882a593Smuzhiyun 	if (clps711x_irqs[hw].flags & CLPS711X_FLAG_FIQ) {
137*4882a593Smuzhiyun 		handler = handle_bad_irq;
138*4882a593Smuzhiyun 		flags |= IRQ_NOAUTOEN;
139*4882a593Smuzhiyun 	} else if (clps711x_irqs[hw].eoi) {
140*4882a593Smuzhiyun 		handler = handle_fasteoi_irq;
141*4882a593Smuzhiyun 	}
142*4882a593Smuzhiyun 
143*4882a593Smuzhiyun 	/* Clear down pending interrupt */
144*4882a593Smuzhiyun 	if (clps711x_irqs[hw].eoi)
145*4882a593Smuzhiyun 		writel_relaxed(0, clps711x_intc->base + clps711x_irqs[hw].eoi);
146*4882a593Smuzhiyun 
147*4882a593Smuzhiyun 	irq_set_chip_and_handler(virq, &clps711x_intc_chip, handler);
148*4882a593Smuzhiyun 	irq_modify_status(virq, IRQ_NOPROBE, flags);
149*4882a593Smuzhiyun 
150*4882a593Smuzhiyun 	return 0;
151*4882a593Smuzhiyun }
152*4882a593Smuzhiyun 
_clps711x_intc_init(struct device_node * np,phys_addr_t base,resource_size_t size)153*4882a593Smuzhiyun static int __init _clps711x_intc_init(struct device_node *np,
154*4882a593Smuzhiyun 				      phys_addr_t base, resource_size_t size)
155*4882a593Smuzhiyun {
156*4882a593Smuzhiyun 	int err;
157*4882a593Smuzhiyun 
158*4882a593Smuzhiyun 	clps711x_intc = kzalloc(sizeof(*clps711x_intc), GFP_KERNEL);
159*4882a593Smuzhiyun 	if (!clps711x_intc)
160*4882a593Smuzhiyun 		return -ENOMEM;
161*4882a593Smuzhiyun 
162*4882a593Smuzhiyun 	clps711x_intc->base = ioremap(base, size);
163*4882a593Smuzhiyun 	if (!clps711x_intc->base) {
164*4882a593Smuzhiyun 		err = -ENOMEM;
165*4882a593Smuzhiyun 		goto out_kfree;
166*4882a593Smuzhiyun 	}
167*4882a593Smuzhiyun 
168*4882a593Smuzhiyun 	clps711x_intc->intsr[0] = clps711x_intc->base + CLPS711X_INTSR1;
169*4882a593Smuzhiyun 	clps711x_intc->intmr[0] = clps711x_intc->base + CLPS711X_INTMR1;
170*4882a593Smuzhiyun 	clps711x_intc->intsr[1] = clps711x_intc->base + CLPS711X_INTSR2;
171*4882a593Smuzhiyun 	clps711x_intc->intmr[1] = clps711x_intc->base + CLPS711X_INTMR2;
172*4882a593Smuzhiyun 	clps711x_intc->intsr[2] = clps711x_intc->base + CLPS711X_INTSR3;
173*4882a593Smuzhiyun 	clps711x_intc->intmr[2] = clps711x_intc->base + CLPS711X_INTMR3;
174*4882a593Smuzhiyun 
175*4882a593Smuzhiyun 	/* Mask all interrupts */
176*4882a593Smuzhiyun 	writel_relaxed(0, clps711x_intc->intmr[0]);
177*4882a593Smuzhiyun 	writel_relaxed(0, clps711x_intc->intmr[1]);
178*4882a593Smuzhiyun 	writel_relaxed(0, clps711x_intc->intmr[2]);
179*4882a593Smuzhiyun 
180*4882a593Smuzhiyun 	err = irq_alloc_descs(-1, 0, ARRAY_SIZE(clps711x_irqs), numa_node_id());
181*4882a593Smuzhiyun 	if (err < 0)
182*4882a593Smuzhiyun 		goto out_iounmap;
183*4882a593Smuzhiyun 
184*4882a593Smuzhiyun 	clps711x_intc->ops.map = clps711x_intc_irq_map;
185*4882a593Smuzhiyun 	clps711x_intc->ops.xlate = irq_domain_xlate_onecell;
186*4882a593Smuzhiyun 	clps711x_intc->domain =
187*4882a593Smuzhiyun 		irq_domain_add_legacy(np, ARRAY_SIZE(clps711x_irqs),
188*4882a593Smuzhiyun 				      0, 0, &clps711x_intc->ops, NULL);
189*4882a593Smuzhiyun 	if (!clps711x_intc->domain) {
190*4882a593Smuzhiyun 		err = -ENOMEM;
191*4882a593Smuzhiyun 		goto out_irqfree;
192*4882a593Smuzhiyun 	}
193*4882a593Smuzhiyun 
194*4882a593Smuzhiyun 	irq_set_default_host(clps711x_intc->domain);
195*4882a593Smuzhiyun 	set_handle_irq(clps711x_irqh);
196*4882a593Smuzhiyun 
197*4882a593Smuzhiyun #ifdef CONFIG_FIQ
198*4882a593Smuzhiyun 	init_FIQ(0);
199*4882a593Smuzhiyun #endif
200*4882a593Smuzhiyun 
201*4882a593Smuzhiyun 	return 0;
202*4882a593Smuzhiyun 
203*4882a593Smuzhiyun out_irqfree:
204*4882a593Smuzhiyun 	irq_free_descs(0, ARRAY_SIZE(clps711x_irqs));
205*4882a593Smuzhiyun 
206*4882a593Smuzhiyun out_iounmap:
207*4882a593Smuzhiyun 	iounmap(clps711x_intc->base);
208*4882a593Smuzhiyun 
209*4882a593Smuzhiyun out_kfree:
210*4882a593Smuzhiyun 	kfree(clps711x_intc);
211*4882a593Smuzhiyun 
212*4882a593Smuzhiyun 	return err;
213*4882a593Smuzhiyun }
214*4882a593Smuzhiyun 
clps711x_intc_init(phys_addr_t base,resource_size_t size)215*4882a593Smuzhiyun void __init clps711x_intc_init(phys_addr_t base, resource_size_t size)
216*4882a593Smuzhiyun {
217*4882a593Smuzhiyun 	BUG_ON(_clps711x_intc_init(NULL, base, size));
218*4882a593Smuzhiyun }
219*4882a593Smuzhiyun 
220*4882a593Smuzhiyun #ifdef CONFIG_IRQCHIP
clps711x_intc_init_dt(struct device_node * np,struct device_node * parent)221*4882a593Smuzhiyun static int __init clps711x_intc_init_dt(struct device_node *np,
222*4882a593Smuzhiyun 					struct device_node *parent)
223*4882a593Smuzhiyun {
224*4882a593Smuzhiyun 	struct resource res;
225*4882a593Smuzhiyun 	int err;
226*4882a593Smuzhiyun 
227*4882a593Smuzhiyun 	err = of_address_to_resource(np, 0, &res);
228*4882a593Smuzhiyun 	if (err)
229*4882a593Smuzhiyun 		return err;
230*4882a593Smuzhiyun 
231*4882a593Smuzhiyun 	return _clps711x_intc_init(np, res.start, resource_size(&res));
232*4882a593Smuzhiyun }
233*4882a593Smuzhiyun IRQCHIP_DECLARE(clps711x, "cirrus,ep7209-intc", clps711x_intc_init_dt);
234*4882a593Smuzhiyun #endif
235