Linux Audio

Check our new training course

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