Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.15.
  1/*
  2 *  linux/arch/arm/mach-mmp/irq.c
  3 *
  4 *  Generic IRQ handling, GPIO IRQ demultiplexing, etc.
  5 *  Copyright (C) 2008 - 2012 Marvell Technology Group Ltd.
  6 *
  7 *  Author:	Bin Yang <bin.yang@marvell.com>
  8 *              Haojian Zhuang <haojian.zhuang@gmail.com>
  9 *
 10 *  This program is free software; you can redistribute it and/or modify
 11 *  it under the terms of the GNU General Public License version 2 as
 12 *  published by the Free Software Foundation.
 13 */
 14
 15#include <linux/module.h>
 16#include <linux/init.h>
 17#include <linux/irq.h>
 18#include <linux/irqdomain.h>
 19#include <linux/io.h>
 20#include <linux/ioport.h>
 21#include <linux/of_address.h>
 22#include <linux/of_irq.h>
 23
 24#include <mach/irqs.h>
 25
 26#ifdef CONFIG_CPU_MMP2
 27#include <mach/pm-mmp2.h>
 28#endif
 29#ifdef CONFIG_CPU_PXA910
 30#include <mach/pm-pxa910.h>
 31#endif
 32
 33#include "common.h"
 34
 35#define MAX_ICU_NR		16
 36
 37struct icu_chip_data {
 38	int			nr_irqs;
 39	unsigned int		virq_base;
 40	unsigned int		cascade_irq;
 41	void __iomem		*reg_status;
 42	void __iomem		*reg_mask;
 43	unsigned int		conf_enable;
 44	unsigned int		conf_disable;
 45	unsigned int		conf_mask;
 46	unsigned int		clr_mfp_irq_base;
 47	unsigned int		clr_mfp_hwirq;
 48	struct irq_domain	*domain;
 49};
 50
 51struct mmp_intc_conf {
 52	unsigned int	conf_enable;
 53	unsigned int	conf_disable;
 54	unsigned int	conf_mask;
 55};
 56
 57void __iomem *mmp_icu_base;
 58static struct icu_chip_data icu_data[MAX_ICU_NR];
 59static int max_icu_nr;
 60
 61extern void mmp2_clear_pmic_int(void);
 62
 63static void icu_mask_ack_irq(struct irq_data *d)
 64{
 65	struct irq_domain *domain = d->domain;
 66	struct icu_chip_data *data = (struct icu_chip_data *)domain->host_data;
 67	int hwirq;
 68	u32 r;
 69
 70	hwirq = d->irq - data->virq_base;
 71	if (data == &icu_data[0]) {
 72		r = readl_relaxed(mmp_icu_base + (hwirq << 2));
 73		r &= ~data->conf_mask;
 74		r |= data->conf_disable;
 75		writel_relaxed(r, mmp_icu_base + (hwirq << 2));
 76	} else {
 77#ifdef CONFIG_CPU_MMP2
 78		if ((data->virq_base == data->clr_mfp_irq_base)
 79			&& (hwirq == data->clr_mfp_hwirq))
 80			mmp2_clear_pmic_int();
 81#endif
 82		r = readl_relaxed(data->reg_mask) | (1 << hwirq);
 83		writel_relaxed(r, data->reg_mask);
 84	}
 85}
 86
 87static void icu_mask_irq(struct irq_data *d)
 88{
 89	struct irq_domain *domain = d->domain;
 90	struct icu_chip_data *data = (struct icu_chip_data *)domain->host_data;
 91	int hwirq;
 92	u32 r;
 93
 94	hwirq = d->irq - data->virq_base;
 95	if (data == &icu_data[0]) {
 96		r = readl_relaxed(mmp_icu_base + (hwirq << 2));
 97		r &= ~data->conf_mask;
 98		r |= data->conf_disable;
 99		writel_relaxed(r, mmp_icu_base + (hwirq << 2));
100	} else {
101		r = readl_relaxed(data->reg_mask) | (1 << hwirq);
102		writel_relaxed(r, data->reg_mask);
103	}
104}
105
106static void icu_unmask_irq(struct irq_data *d)
107{
108	struct irq_domain *domain = d->domain;
109	struct icu_chip_data *data = (struct icu_chip_data *)domain->host_data;
110	int hwirq;
111	u32 r;
112
113	hwirq = d->irq - data->virq_base;
114	if (data == &icu_data[0]) {
115		r = readl_relaxed(mmp_icu_base + (hwirq << 2));
116		r &= ~data->conf_mask;
117		r |= data->conf_enable;
118		writel_relaxed(r, mmp_icu_base + (hwirq << 2));
119	} else {
120		r = readl_relaxed(data->reg_mask) & ~(1 << hwirq);
121		writel_relaxed(r, data->reg_mask);
122	}
123}
124
125static struct irq_chip icu_irq_chip = {
126	.name		= "icu_irq",
127	.irq_mask	= icu_mask_irq,
128	.irq_mask_ack	= icu_mask_ack_irq,
129	.irq_unmask	= icu_unmask_irq,
130};
131
132static void icu_mux_irq_demux(unsigned int irq, struct irq_desc *desc)
133{
134	struct irq_domain *domain;
135	struct icu_chip_data *data;
136	int i;
137	unsigned long mask, status, n;
138
139	for (i = 1; i < max_icu_nr; i++) {
140		if (irq == icu_data[i].cascade_irq) {
141			domain = icu_data[i].domain;
142			data = (struct icu_chip_data *)domain->host_data;
143			break;
144		}
145	}
146	if (i >= max_icu_nr) {
147		pr_err("Spurious irq %d in MMP INTC\n", irq);
148		return;
149	}
150
151	mask = readl_relaxed(data->reg_mask);
152	while (1) {
153		status = readl_relaxed(data->reg_status) & ~mask;
154		if (status == 0)
155			break;
156		n = find_first_bit(&status, BITS_PER_LONG);
157		while (n < BITS_PER_LONG) {
158			generic_handle_irq(icu_data[i].virq_base + n);
159			n = find_next_bit(&status, BITS_PER_LONG, n + 1);
160		}
161	}
162}
163
164static int mmp_irq_domain_map(struct irq_domain *d, unsigned int irq,
165			      irq_hw_number_t hw)
166{
167	irq_set_chip_and_handler(irq, &icu_irq_chip, handle_level_irq);
168	set_irq_flags(irq, IRQF_VALID);
169	return 0;
170}
171
172static int mmp_irq_domain_xlate(struct irq_domain *d, struct device_node *node,
173				const u32 *intspec, unsigned int intsize,
174				unsigned long *out_hwirq,
175				unsigned int *out_type)
176{
177	*out_hwirq = intspec[0];
178	return 0;
179}
180
181const struct irq_domain_ops mmp_irq_domain_ops = {
182	.map		= mmp_irq_domain_map,
183	.xlate		= mmp_irq_domain_xlate,
184};
185
186static struct mmp_intc_conf mmp_conf = {
187	.conf_enable	= 0x51,
188	.conf_disable	= 0x0,
189	.conf_mask	= 0x7f,
190};
191
192static struct mmp_intc_conf mmp2_conf = {
193	.conf_enable	= 0x20,
194	.conf_disable	= 0x0,
195	.conf_mask	= 0x7f,
196};
197
198/* MMP (ARMv5) */
199void __init icu_init_irq(void)
200{
201	int irq;
202
203	max_icu_nr = 1;
204	mmp_icu_base = ioremap(0xd4282000, 0x1000);
205	icu_data[0].conf_enable = mmp_conf.conf_enable;
206	icu_data[0].conf_disable = mmp_conf.conf_disable;
207	icu_data[0].conf_mask = mmp_conf.conf_mask;
208	icu_data[0].nr_irqs = 64;
209	icu_data[0].virq_base = 0;
210	icu_data[0].domain = irq_domain_add_legacy(NULL, 64, 0, 0,
211						   &irq_domain_simple_ops,
212						   &icu_data[0]);
213	for (irq = 0; irq < 64; irq++) {
214		icu_mask_irq(irq_get_irq_data(irq));
215		irq_set_chip_and_handler(irq, &icu_irq_chip, handle_level_irq);
216		set_irq_flags(irq, IRQF_VALID);
217	}
218	irq_set_default_host(icu_data[0].domain);
219#ifdef CONFIG_CPU_PXA910
220	icu_irq_chip.irq_set_wake = pxa910_set_wake;
221#endif
222}
223
224/* MMP2 (ARMv7) */
225void __init mmp2_init_icu(void)
226{
227	int irq;
228
229	max_icu_nr = 8;
230	mmp_icu_base = ioremap(0xd4282000, 0x1000);
231	icu_data[0].conf_enable = mmp2_conf.conf_enable;
232	icu_data[0].conf_disable = mmp2_conf.conf_disable;
233	icu_data[0].conf_mask = mmp2_conf.conf_mask;
234	icu_data[0].nr_irqs = 64;
235	icu_data[0].virq_base = 0;
236	icu_data[0].domain = irq_domain_add_legacy(NULL, 64, 0, 0,
237						   &irq_domain_simple_ops,
238						   &icu_data[0]);
239	icu_data[1].reg_status = mmp_icu_base + 0x150;
240	icu_data[1].reg_mask = mmp_icu_base + 0x168;
241	icu_data[1].clr_mfp_irq_base = IRQ_MMP2_PMIC_BASE;
242	icu_data[1].clr_mfp_hwirq = IRQ_MMP2_PMIC - IRQ_MMP2_PMIC_BASE;
243	icu_data[1].nr_irqs = 2;
244	icu_data[1].cascade_irq = 4;
245	icu_data[1].virq_base = IRQ_MMP2_PMIC_BASE;
246	icu_data[1].domain = irq_domain_add_legacy(NULL, icu_data[1].nr_irqs,
247						   icu_data[1].virq_base, 0,
248						   &irq_domain_simple_ops,
249						   &icu_data[1]);
250	icu_data[2].reg_status = mmp_icu_base + 0x154;
251	icu_data[2].reg_mask = mmp_icu_base + 0x16c;
252	icu_data[2].nr_irqs = 2;
253	icu_data[2].cascade_irq = 5;
254	icu_data[2].virq_base = IRQ_MMP2_RTC_BASE;
255	icu_data[2].domain = irq_domain_add_legacy(NULL, icu_data[2].nr_irqs,
256						   icu_data[2].virq_base, 0,
257						   &irq_domain_simple_ops,
258						   &icu_data[2]);
259	icu_data[3].reg_status = mmp_icu_base + 0x180;
260	icu_data[3].reg_mask = mmp_icu_base + 0x17c;
261	icu_data[3].nr_irqs = 3;
262	icu_data[3].cascade_irq = 9;
263	icu_data[3].virq_base = IRQ_MMP2_KEYPAD_BASE;
264	icu_data[3].domain = irq_domain_add_legacy(NULL, icu_data[3].nr_irqs,
265						   icu_data[3].virq_base, 0,
266						   &irq_domain_simple_ops,
267						   &icu_data[3]);
268	icu_data[4].reg_status = mmp_icu_base + 0x158;
269	icu_data[4].reg_mask = mmp_icu_base + 0x170;
270	icu_data[4].nr_irqs = 5;
271	icu_data[4].cascade_irq = 17;
272	icu_data[4].virq_base = IRQ_MMP2_TWSI_BASE;
273	icu_data[4].domain = irq_domain_add_legacy(NULL, icu_data[4].nr_irqs,
274						   icu_data[4].virq_base, 0,
275						   &irq_domain_simple_ops,
276						   &icu_data[4]);
277	icu_data[5].reg_status = mmp_icu_base + 0x15c;
278	icu_data[5].reg_mask = mmp_icu_base + 0x174;
279	icu_data[5].nr_irqs = 15;
280	icu_data[5].cascade_irq = 35;
281	icu_data[5].virq_base = IRQ_MMP2_MISC_BASE;
282	icu_data[5].domain = irq_domain_add_legacy(NULL, icu_data[5].nr_irqs,
283						   icu_data[5].virq_base, 0,
284						   &irq_domain_simple_ops,
285						   &icu_data[5]);
286	icu_data[6].reg_status = mmp_icu_base + 0x160;
287	icu_data[6].reg_mask = mmp_icu_base + 0x178;
288	icu_data[6].nr_irqs = 2;
289	icu_data[6].cascade_irq = 51;
290	icu_data[6].virq_base = IRQ_MMP2_MIPI_HSI1_BASE;
291	icu_data[6].domain = irq_domain_add_legacy(NULL, icu_data[6].nr_irqs,
292						   icu_data[6].virq_base, 0,
293						   &irq_domain_simple_ops,
294						   &icu_data[6]);
295	icu_data[7].reg_status = mmp_icu_base + 0x188;
296	icu_data[7].reg_mask = mmp_icu_base + 0x184;
297	icu_data[7].nr_irqs = 2;
298	icu_data[7].cascade_irq = 55;
299	icu_data[7].virq_base = IRQ_MMP2_MIPI_HSI0_BASE;
300	icu_data[7].domain = irq_domain_add_legacy(NULL, icu_data[7].nr_irqs,
301						   icu_data[7].virq_base, 0,
302						   &irq_domain_simple_ops,
303						   &icu_data[7]);
304	for (irq = 0; irq < IRQ_MMP2_MUX_END; irq++) {
305		icu_mask_irq(irq_get_irq_data(irq));
306		switch (irq) {
307		case IRQ_MMP2_PMIC_MUX:
308		case IRQ_MMP2_RTC_MUX:
309		case IRQ_MMP2_KEYPAD_MUX:
310		case IRQ_MMP2_TWSI_MUX:
311		case IRQ_MMP2_MISC_MUX:
312		case IRQ_MMP2_MIPI_HSI1_MUX:
313		case IRQ_MMP2_MIPI_HSI0_MUX:
314			irq_set_chip(irq, &icu_irq_chip);
315			irq_set_chained_handler(irq, icu_mux_irq_demux);
316			break;
317		default:
318			irq_set_chip_and_handler(irq, &icu_irq_chip,
319						 handle_level_irq);
320			break;
321		}
322		set_irq_flags(irq, IRQF_VALID);
323	}
324	irq_set_default_host(icu_data[0].domain);
325#ifdef CONFIG_CPU_MMP2
326	icu_irq_chip.irq_set_wake = mmp2_set_wake;
327#endif
328}
329
330#ifdef CONFIG_OF
331static const struct of_device_id intc_ids[] __initconst = {
332	{ .compatible = "mrvl,mmp-intc", .data = &mmp_conf },
333	{ .compatible = "mrvl,mmp2-intc", .data = &mmp2_conf },
334	{}
335};
336
337static const struct of_device_id mmp_mux_irq_match[] __initconst = {
338	{ .compatible = "mrvl,mmp2-mux-intc" },
339	{}
340};
341
342int __init mmp2_mux_init(struct device_node *parent)
343{
344	struct device_node *node;
345	const struct of_device_id *of_id;
346	struct resource res;
347	int i, irq_base, ret, irq;
348	u32 nr_irqs, mfp_irq;
349
350	node = parent;
351	max_icu_nr = 1;
352	for (i = 1; i < MAX_ICU_NR; i++) {
353		node = of_find_matching_node(node, mmp_mux_irq_match);
354		if (!node)
355			break;
356		of_id = of_match_node(&mmp_mux_irq_match[0], node);
357		ret = of_property_read_u32(node, "mrvl,intc-nr-irqs",
358					   &nr_irqs);
359		if (ret) {
360			pr_err("Not found mrvl,intc-nr-irqs property\n");
361			ret = -EINVAL;
362			goto err;
363		}
364		ret = of_address_to_resource(node, 0, &res);
365		if (ret < 0) {
366			pr_err("Not found reg property\n");
367			ret = -EINVAL;
368			goto err;
369		}
370		icu_data[i].reg_status = mmp_icu_base + res.start;
371		ret = of_address_to_resource(node, 1, &res);
372		if (ret < 0) {
373			pr_err("Not found reg property\n");
374			ret = -EINVAL;
375			goto err;
376		}
377		icu_data[i].reg_mask = mmp_icu_base + res.start;
378		icu_data[i].cascade_irq = irq_of_parse_and_map(node, 0);
379		if (!icu_data[i].cascade_irq) {
380			ret = -EINVAL;
381			goto err;
382		}
383
384		irq_base = irq_alloc_descs(-1, 0, nr_irqs, 0);
385		if (irq_base < 0) {
386			pr_err("Failed to allocate IRQ numbers for mux intc\n");
387			ret = irq_base;
388			goto err;
389		}
390		if (!of_property_read_u32(node, "mrvl,clr-mfp-irq",
391					  &mfp_irq)) {
392			icu_data[i].clr_mfp_irq_base = irq_base;
393			icu_data[i].clr_mfp_hwirq = mfp_irq;
394		}
395		irq_set_chained_handler(icu_data[i].cascade_irq,
396					icu_mux_irq_demux);
397		icu_data[i].nr_irqs = nr_irqs;
398		icu_data[i].virq_base = irq_base;
399		icu_data[i].domain = irq_domain_add_legacy(node, nr_irqs,
400							   irq_base, 0,
401							   &mmp_irq_domain_ops,
402							   &icu_data[i]);
403		for (irq = irq_base; irq < irq_base + nr_irqs; irq++)
404			icu_mask_irq(irq_get_irq_data(irq));
405	}
406	max_icu_nr = i;
407	return 0;
408err:
409	of_node_put(node);
410	max_icu_nr = i;
411	return ret;
412}
413
414void __init mmp_dt_irq_init(void)
415{
416	struct device_node *node;
417	const struct of_device_id *of_id;
418	struct mmp_intc_conf *conf;
419	int nr_irqs, irq_base, ret, irq;
420
421	node = of_find_matching_node(NULL, intc_ids);
422	if (!node) {
423		pr_err("Failed to find interrupt controller in arch-mmp\n");
424		return;
425	}
426	of_id = of_match_node(intc_ids, node);
427	conf = of_id->data;
428
429	ret = of_property_read_u32(node, "mrvl,intc-nr-irqs", &nr_irqs);
430	if (ret) {
431		pr_err("Not found mrvl,intc-nr-irqs property\n");
432		return;
433	}
434
435	mmp_icu_base = of_iomap(node, 0);
436	if (!mmp_icu_base) {
437		pr_err("Failed to get interrupt controller register\n");
438		return;
439	}
440
441	irq_base = irq_alloc_descs(-1, 0, nr_irqs - NR_IRQS_LEGACY, 0);
442	if (irq_base < 0) {
443		pr_err("Failed to allocate IRQ numbers\n");
444		goto err;
445	} else if (irq_base != NR_IRQS_LEGACY) {
446		pr_err("ICU's irqbase should be started from 0\n");
447		goto err;
448	}
449	icu_data[0].conf_enable = conf->conf_enable;
450	icu_data[0].conf_disable = conf->conf_disable;
451	icu_data[0].conf_mask = conf->conf_mask;
452	icu_data[0].nr_irqs = nr_irqs;
453	icu_data[0].virq_base = 0;
454	icu_data[0].domain = irq_domain_add_legacy(node, nr_irqs, 0, 0,
455						   &mmp_irq_domain_ops,
456						   &icu_data[0]);
457	irq_set_default_host(icu_data[0].domain);
458	for (irq = 0; irq < nr_irqs; irq++)
459		icu_mask_irq(irq_get_irq_data(irq));
460	mmp2_mux_init(node);
461	return;
462err:
463	iounmap(mmp_icu_base);
464}
465#endif