xref: /openbmc/linux/drivers/irqchip/irq-renesas-irqc.c (revision fbc83b7f59dd8ed1154286b6de00b6d03c24a3c4)
1*fbc83b7fSMagnus Damm /*
2*fbc83b7fSMagnus Damm  * Renesas IRQC Driver
3*fbc83b7fSMagnus Damm  *
4*fbc83b7fSMagnus Damm  *  Copyright (C) 2013 Magnus Damm
5*fbc83b7fSMagnus Damm  *
6*fbc83b7fSMagnus Damm  * This program is free software; you can redistribute it and/or modify
7*fbc83b7fSMagnus Damm  * it under the terms of the GNU General Public License as published by
8*fbc83b7fSMagnus Damm  * the Free Software Foundation; either version 2 of the License
9*fbc83b7fSMagnus Damm  *
10*fbc83b7fSMagnus Damm  * This program is distributed in the hope that it will be useful,
11*fbc83b7fSMagnus Damm  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12*fbc83b7fSMagnus Damm  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13*fbc83b7fSMagnus Damm  * GNU General Public License for more details.
14*fbc83b7fSMagnus Damm  *
15*fbc83b7fSMagnus Damm  * You should have received a copy of the GNU General Public License
16*fbc83b7fSMagnus Damm  * along with this program; if not, write to the Free Software
17*fbc83b7fSMagnus Damm  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18*fbc83b7fSMagnus Damm  */
19*fbc83b7fSMagnus Damm 
20*fbc83b7fSMagnus Damm #include <linux/init.h>
21*fbc83b7fSMagnus Damm #include <linux/platform_device.h>
22*fbc83b7fSMagnus Damm #include <linux/spinlock.h>
23*fbc83b7fSMagnus Damm #include <linux/interrupt.h>
24*fbc83b7fSMagnus Damm #include <linux/ioport.h>
25*fbc83b7fSMagnus Damm #include <linux/io.h>
26*fbc83b7fSMagnus Damm #include <linux/irq.h>
27*fbc83b7fSMagnus Damm #include <linux/irqdomain.h>
28*fbc83b7fSMagnus Damm #include <linux/err.h>
29*fbc83b7fSMagnus Damm #include <linux/slab.h>
30*fbc83b7fSMagnus Damm #include <linux/module.h>
31*fbc83b7fSMagnus Damm #include <linux/platform_data/irq-renesas-irqc.h>
32*fbc83b7fSMagnus Damm 
33*fbc83b7fSMagnus Damm #define IRQC_IRQ_MAX 32 /* maximum 32 interrupts per driver instance */
34*fbc83b7fSMagnus Damm 
35*fbc83b7fSMagnus Damm #define IRQC_REQ_STS 0x00
36*fbc83b7fSMagnus Damm #define IRQC_EN_STS 0x04
37*fbc83b7fSMagnus Damm #define IRQC_EN_SET 0x08
38*fbc83b7fSMagnus Damm #define IRQC_INT_CPU_BASE(n) (0x000 + ((n) * 0x10))
39*fbc83b7fSMagnus Damm #define DETECT_STATUS 0x100
40*fbc83b7fSMagnus Damm #define IRQC_CONFIG(n) (0x180 + ((n) * 0x04))
41*fbc83b7fSMagnus Damm 
42*fbc83b7fSMagnus Damm struct irqc_irq {
43*fbc83b7fSMagnus Damm 	int hw_irq;
44*fbc83b7fSMagnus Damm 	int requested_irq;
45*fbc83b7fSMagnus Damm 	int domain_irq;
46*fbc83b7fSMagnus Damm 	struct irqc_priv *p;
47*fbc83b7fSMagnus Damm };
48*fbc83b7fSMagnus Damm 
49*fbc83b7fSMagnus Damm struct irqc_priv {
50*fbc83b7fSMagnus Damm 	void __iomem *iomem;
51*fbc83b7fSMagnus Damm 	void __iomem *cpu_int_base;
52*fbc83b7fSMagnus Damm 	struct irqc_irq irq[IRQC_IRQ_MAX];
53*fbc83b7fSMagnus Damm 	struct renesas_irqc_config config;
54*fbc83b7fSMagnus Damm 	unsigned int number_of_irqs;
55*fbc83b7fSMagnus Damm 	struct platform_device *pdev;
56*fbc83b7fSMagnus Damm 	struct irq_chip irq_chip;
57*fbc83b7fSMagnus Damm 	struct irq_domain *irq_domain;
58*fbc83b7fSMagnus Damm };
59*fbc83b7fSMagnus Damm 
60*fbc83b7fSMagnus Damm static void irqc_dbg(struct irqc_irq *i, char *str)
61*fbc83b7fSMagnus Damm {
62*fbc83b7fSMagnus Damm 	dev_dbg(&i->p->pdev->dev, "%s (%d:%d:%d)\n",
63*fbc83b7fSMagnus Damm 		str, i->requested_irq, i->hw_irq, i->domain_irq);
64*fbc83b7fSMagnus Damm }
65*fbc83b7fSMagnus Damm 
66*fbc83b7fSMagnus Damm static void irqc_irq_enable(struct irq_data *d)
67*fbc83b7fSMagnus Damm {
68*fbc83b7fSMagnus Damm 	struct irqc_priv *p = irq_data_get_irq_chip_data(d);
69*fbc83b7fSMagnus Damm 	int hw_irq = irqd_to_hwirq(d);
70*fbc83b7fSMagnus Damm 
71*fbc83b7fSMagnus Damm 	irqc_dbg(&p->irq[hw_irq], "enable");
72*fbc83b7fSMagnus Damm 	iowrite32(BIT(hw_irq), p->cpu_int_base + IRQC_EN_SET);
73*fbc83b7fSMagnus Damm }
74*fbc83b7fSMagnus Damm 
75*fbc83b7fSMagnus Damm static void irqc_irq_disable(struct irq_data *d)
76*fbc83b7fSMagnus Damm {
77*fbc83b7fSMagnus Damm 	struct irqc_priv *p = irq_data_get_irq_chip_data(d);
78*fbc83b7fSMagnus Damm 	int hw_irq = irqd_to_hwirq(d);
79*fbc83b7fSMagnus Damm 
80*fbc83b7fSMagnus Damm 	irqc_dbg(&p->irq[hw_irq], "disable");
81*fbc83b7fSMagnus Damm 	iowrite32(BIT(hw_irq), p->cpu_int_base + IRQC_EN_STS);
82*fbc83b7fSMagnus Damm }
83*fbc83b7fSMagnus Damm 
84*fbc83b7fSMagnus Damm #define INTC_IRQ_SENSE_VALID 0x10
85*fbc83b7fSMagnus Damm #define INTC_IRQ_SENSE(x) (x + INTC_IRQ_SENSE_VALID)
86*fbc83b7fSMagnus Damm 
87*fbc83b7fSMagnus Damm static unsigned char irqc_sense[IRQ_TYPE_SENSE_MASK + 1] = {
88*fbc83b7fSMagnus Damm 	[IRQ_TYPE_LEVEL_LOW] = INTC_IRQ_SENSE(0x01),
89*fbc83b7fSMagnus Damm 	[IRQ_TYPE_LEVEL_HIGH] = INTC_IRQ_SENSE(0x02),
90*fbc83b7fSMagnus Damm 	[IRQ_TYPE_EDGE_FALLING] = INTC_IRQ_SENSE(0x04), /* Synchronous */
91*fbc83b7fSMagnus Damm 	[IRQ_TYPE_EDGE_RISING] = INTC_IRQ_SENSE(0x08), /* Synchronous */
92*fbc83b7fSMagnus Damm 	[IRQ_TYPE_EDGE_BOTH] = INTC_IRQ_SENSE(0x0c),  /* Synchronous */
93*fbc83b7fSMagnus Damm };
94*fbc83b7fSMagnus Damm 
95*fbc83b7fSMagnus Damm static int irqc_irq_set_type(struct irq_data *d, unsigned int type)
96*fbc83b7fSMagnus Damm {
97*fbc83b7fSMagnus Damm 	struct irqc_priv *p = irq_data_get_irq_chip_data(d);
98*fbc83b7fSMagnus Damm 	int hw_irq = irqd_to_hwirq(d);
99*fbc83b7fSMagnus Damm 	unsigned char value = irqc_sense[type & IRQ_TYPE_SENSE_MASK];
100*fbc83b7fSMagnus Damm 	unsigned long tmp;
101*fbc83b7fSMagnus Damm 
102*fbc83b7fSMagnus Damm 	irqc_dbg(&p->irq[hw_irq], "sense");
103*fbc83b7fSMagnus Damm 
104*fbc83b7fSMagnus Damm 	if (!(value & INTC_IRQ_SENSE_VALID))
105*fbc83b7fSMagnus Damm 		return -EINVAL;
106*fbc83b7fSMagnus Damm 
107*fbc83b7fSMagnus Damm 	tmp = ioread32(p->iomem + IRQC_CONFIG(hw_irq));
108*fbc83b7fSMagnus Damm 	tmp &= ~0x3f;
109*fbc83b7fSMagnus Damm 	tmp |= value ^ INTC_IRQ_SENSE_VALID;
110*fbc83b7fSMagnus Damm 	iowrite32(tmp, p->iomem + IRQC_CONFIG(hw_irq));
111*fbc83b7fSMagnus Damm 	return 0;
112*fbc83b7fSMagnus Damm }
113*fbc83b7fSMagnus Damm 
114*fbc83b7fSMagnus Damm static irqreturn_t irqc_irq_handler(int irq, void *dev_id)
115*fbc83b7fSMagnus Damm {
116*fbc83b7fSMagnus Damm 	struct irqc_irq *i = dev_id;
117*fbc83b7fSMagnus Damm 	struct irqc_priv *p = i->p;
118*fbc83b7fSMagnus Damm 	unsigned long bit = BIT(i->hw_irq);
119*fbc83b7fSMagnus Damm 
120*fbc83b7fSMagnus Damm 	irqc_dbg(i, "demux1");
121*fbc83b7fSMagnus Damm 
122*fbc83b7fSMagnus Damm 	if (ioread32(p->iomem + DETECT_STATUS) & bit) {
123*fbc83b7fSMagnus Damm 		iowrite32(bit, p->iomem + DETECT_STATUS);
124*fbc83b7fSMagnus Damm 		irqc_dbg(i, "demux2");
125*fbc83b7fSMagnus Damm 		generic_handle_irq(i->domain_irq);
126*fbc83b7fSMagnus Damm 		return IRQ_HANDLED;
127*fbc83b7fSMagnus Damm 	}
128*fbc83b7fSMagnus Damm 	return IRQ_NONE;
129*fbc83b7fSMagnus Damm }
130*fbc83b7fSMagnus Damm 
131*fbc83b7fSMagnus Damm static int irqc_irq_domain_map(struct irq_domain *h, unsigned int virq,
132*fbc83b7fSMagnus Damm 			       irq_hw_number_t hw)
133*fbc83b7fSMagnus Damm {
134*fbc83b7fSMagnus Damm 	struct irqc_priv *p = h->host_data;
135*fbc83b7fSMagnus Damm 
136*fbc83b7fSMagnus Damm 	p->irq[hw].domain_irq = virq;
137*fbc83b7fSMagnus Damm 	p->irq[hw].hw_irq = hw;
138*fbc83b7fSMagnus Damm 
139*fbc83b7fSMagnus Damm 	irqc_dbg(&p->irq[hw], "map");
140*fbc83b7fSMagnus Damm 	irq_set_chip_data(virq, h->host_data);
141*fbc83b7fSMagnus Damm 	irq_set_chip_and_handler(virq, &p->irq_chip, handle_level_irq);
142*fbc83b7fSMagnus Damm 	set_irq_flags(virq, IRQF_VALID); /* kill me now */
143*fbc83b7fSMagnus Damm 	return 0;
144*fbc83b7fSMagnus Damm }
145*fbc83b7fSMagnus Damm 
146*fbc83b7fSMagnus Damm static struct irq_domain_ops irqc_irq_domain_ops = {
147*fbc83b7fSMagnus Damm 	.map	= irqc_irq_domain_map,
148*fbc83b7fSMagnus Damm };
149*fbc83b7fSMagnus Damm 
150*fbc83b7fSMagnus Damm static int irqc_probe(struct platform_device *pdev)
151*fbc83b7fSMagnus Damm {
152*fbc83b7fSMagnus Damm 	struct renesas_irqc_config *pdata = pdev->dev.platform_data;
153*fbc83b7fSMagnus Damm 	struct irqc_priv *p;
154*fbc83b7fSMagnus Damm 	struct resource *io;
155*fbc83b7fSMagnus Damm 	struct resource *irq;
156*fbc83b7fSMagnus Damm 	struct irq_chip *irq_chip;
157*fbc83b7fSMagnus Damm 	const char *name = dev_name(&pdev->dev);
158*fbc83b7fSMagnus Damm 	int ret;
159*fbc83b7fSMagnus Damm 	int k;
160*fbc83b7fSMagnus Damm 
161*fbc83b7fSMagnus Damm 	p = kzalloc(sizeof(*p), GFP_KERNEL);
162*fbc83b7fSMagnus Damm 	if (!p) {
163*fbc83b7fSMagnus Damm 		dev_err(&pdev->dev, "failed to allocate driver data\n");
164*fbc83b7fSMagnus Damm 		ret = -ENOMEM;
165*fbc83b7fSMagnus Damm 		goto err0;
166*fbc83b7fSMagnus Damm 	}
167*fbc83b7fSMagnus Damm 
168*fbc83b7fSMagnus Damm 	/* deal with driver instance configuration */
169*fbc83b7fSMagnus Damm 	if (pdata)
170*fbc83b7fSMagnus Damm 		memcpy(&p->config, pdata, sizeof(*pdata));
171*fbc83b7fSMagnus Damm 
172*fbc83b7fSMagnus Damm 	p->pdev = pdev;
173*fbc83b7fSMagnus Damm 	platform_set_drvdata(pdev, p);
174*fbc83b7fSMagnus Damm 
175*fbc83b7fSMagnus Damm 	/* get hold of manadatory IOMEM */
176*fbc83b7fSMagnus Damm 	io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
177*fbc83b7fSMagnus Damm 	if (!io) {
178*fbc83b7fSMagnus Damm 		dev_err(&pdev->dev, "not enough IOMEM resources\n");
179*fbc83b7fSMagnus Damm 		ret = -EINVAL;
180*fbc83b7fSMagnus Damm 		goto err1;
181*fbc83b7fSMagnus Damm 	}
182*fbc83b7fSMagnus Damm 
183*fbc83b7fSMagnus Damm 	/* allow any number of IRQs between 1 and IRQC_IRQ_MAX */
184*fbc83b7fSMagnus Damm 	for (k = 0; k < IRQC_IRQ_MAX; k++) {
185*fbc83b7fSMagnus Damm 		irq = platform_get_resource(pdev, IORESOURCE_IRQ, k);
186*fbc83b7fSMagnus Damm 		if (!irq)
187*fbc83b7fSMagnus Damm 			break;
188*fbc83b7fSMagnus Damm 
189*fbc83b7fSMagnus Damm 		p->irq[k].p = p;
190*fbc83b7fSMagnus Damm 		p->irq[k].requested_irq = irq->start;
191*fbc83b7fSMagnus Damm 	}
192*fbc83b7fSMagnus Damm 
193*fbc83b7fSMagnus Damm 	p->number_of_irqs = k;
194*fbc83b7fSMagnus Damm 	if (p->number_of_irqs < 1) {
195*fbc83b7fSMagnus Damm 		dev_err(&pdev->dev, "not enough IRQ resources\n");
196*fbc83b7fSMagnus Damm 		ret = -EINVAL;
197*fbc83b7fSMagnus Damm 		goto err1;
198*fbc83b7fSMagnus Damm 	}
199*fbc83b7fSMagnus Damm 
200*fbc83b7fSMagnus Damm 	/* ioremap IOMEM and setup read/write callbacks */
201*fbc83b7fSMagnus Damm 	p->iomem = ioremap_nocache(io->start, resource_size(io));
202*fbc83b7fSMagnus Damm 	if (!p->iomem) {
203*fbc83b7fSMagnus Damm 		dev_err(&pdev->dev, "failed to remap IOMEM\n");
204*fbc83b7fSMagnus Damm 		ret = -ENXIO;
205*fbc83b7fSMagnus Damm 		goto err2;
206*fbc83b7fSMagnus Damm 	}
207*fbc83b7fSMagnus Damm 
208*fbc83b7fSMagnus Damm 	p->cpu_int_base = p->iomem + IRQC_INT_CPU_BASE(0); /* SYS-SPI */
209*fbc83b7fSMagnus Damm 
210*fbc83b7fSMagnus Damm 	irq_chip = &p->irq_chip;
211*fbc83b7fSMagnus Damm 	irq_chip->name = name;
212*fbc83b7fSMagnus Damm 	irq_chip->irq_mask = irqc_irq_disable;
213*fbc83b7fSMagnus Damm 	irq_chip->irq_unmask = irqc_irq_enable;
214*fbc83b7fSMagnus Damm 	irq_chip->irq_enable = irqc_irq_enable;
215*fbc83b7fSMagnus Damm 	irq_chip->irq_disable = irqc_irq_disable;
216*fbc83b7fSMagnus Damm 	irq_chip->irq_set_type = irqc_irq_set_type;
217*fbc83b7fSMagnus Damm 	irq_chip->flags	= IRQCHIP_SKIP_SET_WAKE;
218*fbc83b7fSMagnus Damm 
219*fbc83b7fSMagnus Damm 	p->irq_domain = irq_domain_add_simple(pdev->dev.of_node,
220*fbc83b7fSMagnus Damm 					      p->number_of_irqs,
221*fbc83b7fSMagnus Damm 					      p->config.irq_base,
222*fbc83b7fSMagnus Damm 					      &irqc_irq_domain_ops, p);
223*fbc83b7fSMagnus Damm 	if (!p->irq_domain) {
224*fbc83b7fSMagnus Damm 		ret = -ENXIO;
225*fbc83b7fSMagnus Damm 		dev_err(&pdev->dev, "cannot initialize irq domain\n");
226*fbc83b7fSMagnus Damm 		goto err2;
227*fbc83b7fSMagnus Damm 	}
228*fbc83b7fSMagnus Damm 
229*fbc83b7fSMagnus Damm 	/* request interrupts one by one */
230*fbc83b7fSMagnus Damm 	for (k = 0; k < p->number_of_irqs; k++) {
231*fbc83b7fSMagnus Damm 		if (request_irq(p->irq[k].requested_irq, irqc_irq_handler,
232*fbc83b7fSMagnus Damm 				0, name, &p->irq[k])) {
233*fbc83b7fSMagnus Damm 			dev_err(&pdev->dev, "failed to request IRQ\n");
234*fbc83b7fSMagnus Damm 			ret = -ENOENT;
235*fbc83b7fSMagnus Damm 			goto err3;
236*fbc83b7fSMagnus Damm 		}
237*fbc83b7fSMagnus Damm 	}
238*fbc83b7fSMagnus Damm 
239*fbc83b7fSMagnus Damm 	dev_info(&pdev->dev, "driving %d irqs\n", p->number_of_irqs);
240*fbc83b7fSMagnus Damm 
241*fbc83b7fSMagnus Damm 	/* warn in case of mismatch if irq base is specified */
242*fbc83b7fSMagnus Damm 	if (p->config.irq_base) {
243*fbc83b7fSMagnus Damm 		if (p->config.irq_base != p->irq[0].domain_irq)
244*fbc83b7fSMagnus Damm 			dev_warn(&pdev->dev, "irq base mismatch (%d/%d)\n",
245*fbc83b7fSMagnus Damm 				 p->config.irq_base, p->irq[0].domain_irq);
246*fbc83b7fSMagnus Damm 	}
247*fbc83b7fSMagnus Damm 
248*fbc83b7fSMagnus Damm 	return 0;
249*fbc83b7fSMagnus Damm err3:
250*fbc83b7fSMagnus Damm 	for (; k >= 0; k--)
251*fbc83b7fSMagnus Damm 		free_irq(p->irq[k - 1].requested_irq, &p->irq[k - 1]);
252*fbc83b7fSMagnus Damm 
253*fbc83b7fSMagnus Damm 	irq_domain_remove(p->irq_domain);
254*fbc83b7fSMagnus Damm err2:
255*fbc83b7fSMagnus Damm 	iounmap(p->iomem);
256*fbc83b7fSMagnus Damm err1:
257*fbc83b7fSMagnus Damm 	kfree(p);
258*fbc83b7fSMagnus Damm err0:
259*fbc83b7fSMagnus Damm 	return ret;
260*fbc83b7fSMagnus Damm }
261*fbc83b7fSMagnus Damm 
262*fbc83b7fSMagnus Damm static int irqc_remove(struct platform_device *pdev)
263*fbc83b7fSMagnus Damm {
264*fbc83b7fSMagnus Damm 	struct irqc_priv *p = platform_get_drvdata(pdev);
265*fbc83b7fSMagnus Damm 	int k;
266*fbc83b7fSMagnus Damm 
267*fbc83b7fSMagnus Damm 	for (k = 0; k < p->number_of_irqs; k++)
268*fbc83b7fSMagnus Damm 		free_irq(p->irq[k].requested_irq, &p->irq[k]);
269*fbc83b7fSMagnus Damm 
270*fbc83b7fSMagnus Damm 	irq_domain_remove(p->irq_domain);
271*fbc83b7fSMagnus Damm 	iounmap(p->iomem);
272*fbc83b7fSMagnus Damm 	kfree(p);
273*fbc83b7fSMagnus Damm 	return 0;
274*fbc83b7fSMagnus Damm }
275*fbc83b7fSMagnus Damm 
276*fbc83b7fSMagnus Damm static struct platform_driver irqc_device_driver = {
277*fbc83b7fSMagnus Damm 	.probe		= irqc_probe,
278*fbc83b7fSMagnus Damm 	.remove		= irqc_remove,
279*fbc83b7fSMagnus Damm 	.driver		= {
280*fbc83b7fSMagnus Damm 		.name	= "renesas_irqc",
281*fbc83b7fSMagnus Damm 	}
282*fbc83b7fSMagnus Damm };
283*fbc83b7fSMagnus Damm 
284*fbc83b7fSMagnus Damm static int __init irqc_init(void)
285*fbc83b7fSMagnus Damm {
286*fbc83b7fSMagnus Damm 	return platform_driver_register(&irqc_device_driver);
287*fbc83b7fSMagnus Damm }
288*fbc83b7fSMagnus Damm postcore_initcall(irqc_init);
289*fbc83b7fSMagnus Damm 
290*fbc83b7fSMagnus Damm static void __exit irqc_exit(void)
291*fbc83b7fSMagnus Damm {
292*fbc83b7fSMagnus Damm 	platform_driver_unregister(&irqc_device_driver);
293*fbc83b7fSMagnus Damm }
294*fbc83b7fSMagnus Damm module_exit(irqc_exit);
295*fbc83b7fSMagnus Damm 
296*fbc83b7fSMagnus Damm MODULE_AUTHOR("Magnus Damm");
297*fbc83b7fSMagnus Damm MODULE_DESCRIPTION("Renesas IRQC Driver");
298*fbc83b7fSMagnus Damm MODULE_LICENSE("GPL v2");
299