| Haojian Zhuang | 2f7e8fa | 2009-12-04 09:41:28 -0500 | [diff] [blame] | 1 | /* | 
 | 2 |  *  linux/arch/arm/mach-mmp/irq-mmp2.c | 
 | 3 |  * | 
 | 4 |  *  Generic IRQ handling, GPIO IRQ demultiplexing, etc. | 
 | 5 |  * | 
 | 6 |  *  Author:	Haojian Zhuang <haojian.zhuang@marvell.com> | 
 | 7 |  *  Copyright:	Marvell International Ltd. | 
 | 8 |  * | 
 | 9 |  *  This program is free software; you can redistribute it and/or modify | 
 | 10 |  *  it under the terms of the GNU General Public License version 2 as | 
 | 11 |  *  published by the Free Software Foundation. | 
 | 12 |  */ | 
 | 13 |  | 
 | 14 | #include <linux/init.h> | 
 | 15 | #include <linux/irq.h> | 
 | 16 | #include <linux/io.h> | 
 | 17 |  | 
 | 18 | #include <mach/regs-icu.h> | 
| Eric Miao | 2728701 | 2010-07-15 22:22:33 +0800 | [diff] [blame] | 19 | #include <mach/mmp2.h> | 
| Haojian Zhuang | 2f7e8fa | 2009-12-04 09:41:28 -0500 | [diff] [blame] | 20 |  | 
 | 21 | #include "common.h" | 
 | 22 |  | 
| Lennert Buytenhek | a157f26 | 2010-11-29 10:36:07 +0100 | [diff] [blame] | 23 | static void icu_mask_irq(struct irq_data *d) | 
| Haojian Zhuang | 2f7e8fa | 2009-12-04 09:41:28 -0500 | [diff] [blame] | 24 | { | 
| Lennert Buytenhek | a157f26 | 2010-11-29 10:36:07 +0100 | [diff] [blame] | 25 | 	uint32_t r = __raw_readl(ICU_INT_CONF(d->irq)); | 
| Haojian Zhuang | 2f7e8fa | 2009-12-04 09:41:28 -0500 | [diff] [blame] | 26 |  | 
 | 27 | 	r &= ~ICU_INT_ROUTE_PJ4_IRQ; | 
| Lennert Buytenhek | a157f26 | 2010-11-29 10:36:07 +0100 | [diff] [blame] | 28 | 	__raw_writel(r, ICU_INT_CONF(d->irq)); | 
| Haojian Zhuang | 2f7e8fa | 2009-12-04 09:41:28 -0500 | [diff] [blame] | 29 | } | 
 | 30 |  | 
| Lennert Buytenhek | a157f26 | 2010-11-29 10:36:07 +0100 | [diff] [blame] | 31 | static void icu_unmask_irq(struct irq_data *d) | 
| Haojian Zhuang | 2f7e8fa | 2009-12-04 09:41:28 -0500 | [diff] [blame] | 32 | { | 
| Lennert Buytenhek | a157f26 | 2010-11-29 10:36:07 +0100 | [diff] [blame] | 33 | 	uint32_t r = __raw_readl(ICU_INT_CONF(d->irq)); | 
| Haojian Zhuang | 2f7e8fa | 2009-12-04 09:41:28 -0500 | [diff] [blame] | 34 |  | 
 | 35 | 	r |= ICU_INT_ROUTE_PJ4_IRQ; | 
| Lennert Buytenhek | a157f26 | 2010-11-29 10:36:07 +0100 | [diff] [blame] | 36 | 	__raw_writel(r, ICU_INT_CONF(d->irq)); | 
| Haojian Zhuang | 2f7e8fa | 2009-12-04 09:41:28 -0500 | [diff] [blame] | 37 | } | 
 | 38 |  | 
 | 39 | static struct irq_chip icu_irq_chip = { | 
 | 40 | 	.name		= "icu_irq", | 
| Lennert Buytenhek | a157f26 | 2010-11-29 10:36:07 +0100 | [diff] [blame] | 41 | 	.irq_mask	= icu_mask_irq, | 
 | 42 | 	.irq_mask_ack	= icu_mask_irq, | 
 | 43 | 	.irq_unmask	= icu_unmask_irq, | 
| Haojian Zhuang | 2f7e8fa | 2009-12-04 09:41:28 -0500 | [diff] [blame] | 44 | }; | 
 | 45 |  | 
| Lennert Buytenhek | a157f26 | 2010-11-29 10:36:07 +0100 | [diff] [blame] | 46 | static void pmic_irq_ack(struct irq_data *d) | 
| Haojian Zhuang | df0c382 | 2010-02-03 10:01:18 -0500 | [diff] [blame] | 47 | { | 
| Lennert Buytenhek | a157f26 | 2010-11-29 10:36:07 +0100 | [diff] [blame] | 48 | 	if (d->irq == IRQ_MMP2_PMIC) | 
| Haojian Zhuang | df0c382 | 2010-02-03 10:01:18 -0500 | [diff] [blame] | 49 | 		mmp2_clear_pmic_int(); | 
 | 50 | } | 
 | 51 |  | 
| Haojian Zhuang | 2f7e8fa | 2009-12-04 09:41:28 -0500 | [diff] [blame] | 52 | #define SECOND_IRQ_MASK(_name_, irq_base, prefix)			\ | 
| Lennert Buytenhek | a157f26 | 2010-11-29 10:36:07 +0100 | [diff] [blame] | 53 | static void _name_##_mask_irq(struct irq_data *d)			\ | 
| Haojian Zhuang | 2f7e8fa | 2009-12-04 09:41:28 -0500 | [diff] [blame] | 54 | {									\ | 
 | 55 | 	uint32_t r;							\ | 
| Lennert Buytenhek | a157f26 | 2010-11-29 10:36:07 +0100 | [diff] [blame] | 56 | 	r = __raw_readl(prefix##_MASK) | (1 << (d->irq - irq_base));	\ | 
| Haojian Zhuang | 2f7e8fa | 2009-12-04 09:41:28 -0500 | [diff] [blame] | 57 | 	__raw_writel(r, prefix##_MASK);					\ | 
 | 58 | } | 
 | 59 |  | 
 | 60 | #define SECOND_IRQ_UNMASK(_name_, irq_base, prefix)			\ | 
| Lennert Buytenhek | a157f26 | 2010-11-29 10:36:07 +0100 | [diff] [blame] | 61 | static void _name_##_unmask_irq(struct irq_data *d)			\ | 
| Haojian Zhuang | 2f7e8fa | 2009-12-04 09:41:28 -0500 | [diff] [blame] | 62 | {									\ | 
 | 63 | 	uint32_t r;							\ | 
| Lennert Buytenhek | a157f26 | 2010-11-29 10:36:07 +0100 | [diff] [blame] | 64 | 	r = __raw_readl(prefix##_MASK) & ~(1 << (d->irq - irq_base));	\ | 
| Haojian Zhuang | 2f7e8fa | 2009-12-04 09:41:28 -0500 | [diff] [blame] | 65 | 	__raw_writel(r, prefix##_MASK);					\ | 
 | 66 | } | 
 | 67 |  | 
 | 68 | #define SECOND_IRQ_DEMUX(_name_, irq_base, prefix)			\ | 
 | 69 | static void _name_##_irq_demux(unsigned int irq, struct irq_desc *desc)	\ | 
 | 70 | {									\ | 
 | 71 | 	unsigned long status, mask, n;					\ | 
 | 72 | 	mask = __raw_readl(prefix##_MASK);				\ | 
 | 73 | 	while (1) {							\ | 
 | 74 | 		status = __raw_readl(prefix##_STATUS) & ~mask;		\ | 
 | 75 | 		if (status == 0)					\ | 
 | 76 | 			break;						\ | 
 | 77 | 		n = find_first_bit(&status, BITS_PER_LONG);		\ | 
 | 78 | 		while (n < BITS_PER_LONG) {				\ | 
 | 79 | 			generic_handle_irq(irq_base + n);		\ | 
 | 80 | 			n = find_next_bit(&status, BITS_PER_LONG, n+1);	\ | 
 | 81 | 		}							\ | 
 | 82 | 	}								\ | 
 | 83 | } | 
 | 84 |  | 
 | 85 | #define SECOND_IRQ_CHIP(_name_, irq_base, prefix)			\ | 
 | 86 | SECOND_IRQ_MASK(_name_, irq_base, prefix)				\ | 
 | 87 | SECOND_IRQ_UNMASK(_name_, irq_base, prefix)				\ | 
 | 88 | SECOND_IRQ_DEMUX(_name_, irq_base, prefix)				\ | 
 | 89 | static struct irq_chip _name_##_irq_chip = {				\ | 
 | 90 | 	.name		= #_name_,					\ | 
| Lennert Buytenhek | a157f26 | 2010-11-29 10:36:07 +0100 | [diff] [blame] | 91 | 	.irq_mask	= _name_##_mask_irq,				\ | 
 | 92 | 	.irq_unmask	= _name_##_unmask_irq,				\ | 
| Haojian Zhuang | 2f7e8fa | 2009-12-04 09:41:28 -0500 | [diff] [blame] | 93 | } | 
 | 94 |  | 
 | 95 | SECOND_IRQ_CHIP(pmic, IRQ_MMP2_PMIC_BASE, MMP2_ICU_INT4); | 
 | 96 | SECOND_IRQ_CHIP(rtc,  IRQ_MMP2_RTC_BASE,  MMP2_ICU_INT5); | 
 | 97 | SECOND_IRQ_CHIP(twsi, IRQ_MMP2_TWSI_BASE, MMP2_ICU_INT17); | 
 | 98 | SECOND_IRQ_CHIP(misc, IRQ_MMP2_MISC_BASE, MMP2_ICU_INT35); | 
 | 99 | SECOND_IRQ_CHIP(ssp,  IRQ_MMP2_SSP_BASE,  MMP2_ICU_INT51); | 
 | 100 |  | 
 | 101 | static void init_mux_irq(struct irq_chip *chip, int start, int num) | 
 | 102 | { | 
 | 103 | 	int irq; | 
 | 104 |  | 
 | 105 | 	for (irq = start; num > 0; irq++, num--) { | 
| Lennert Buytenhek | a157f26 | 2010-11-29 10:36:07 +0100 | [diff] [blame] | 106 | 		struct irq_data *d = irq_get_irq_data(irq); | 
 | 107 |  | 
| Eric Miao | 2029e56 | 2010-02-02 23:39:35 -0800 | [diff] [blame] | 108 | 		/* mask and clear the IRQ */ | 
| Lennert Buytenhek | a157f26 | 2010-11-29 10:36:07 +0100 | [diff] [blame] | 109 | 		chip->irq_mask(d); | 
 | 110 | 		if (chip->irq_ack) | 
 | 111 | 			chip->irq_ack(d); | 
| Eric Miao | 2029e56 | 2010-02-02 23:39:35 -0800 | [diff] [blame] | 112 |  | 
| Haojian Zhuang | 2f7e8fa | 2009-12-04 09:41:28 -0500 | [diff] [blame] | 113 | 		set_irq_chip(irq, chip); | 
 | 114 | 		set_irq_flags(irq, IRQF_VALID); | 
 | 115 | 		set_irq_handler(irq, handle_level_irq); | 
 | 116 | 	} | 
 | 117 | } | 
 | 118 |  | 
| Haojian Zhuang | 16144bf | 2010-01-25 06:03:54 -0500 | [diff] [blame] | 119 | void __init mmp2_init_icu(void) | 
| Haojian Zhuang | 2f7e8fa | 2009-12-04 09:41:28 -0500 | [diff] [blame] | 120 | { | 
 | 121 | 	int irq; | 
 | 122 |  | 
 | 123 | 	for (irq = 0; irq < IRQ_MMP2_MUX_BASE; irq++) { | 
| Lennert Buytenhek | a157f26 | 2010-11-29 10:36:07 +0100 | [diff] [blame] | 124 | 		icu_mask_irq(irq_get_irq_data(irq)); | 
| Haojian Zhuang | 2f7e8fa | 2009-12-04 09:41:28 -0500 | [diff] [blame] | 125 | 		set_irq_chip(irq, &icu_irq_chip); | 
 | 126 | 		set_irq_flags(irq, IRQF_VALID); | 
 | 127 |  | 
 | 128 | 		switch (irq) { | 
 | 129 | 		case IRQ_MMP2_PMIC_MUX: | 
 | 130 | 		case IRQ_MMP2_RTC_MUX: | 
 | 131 | 		case IRQ_MMP2_TWSI_MUX: | 
 | 132 | 		case IRQ_MMP2_MISC_MUX: | 
 | 133 | 		case IRQ_MMP2_SSP_MUX: | 
 | 134 | 			break; | 
 | 135 | 		default: | 
 | 136 | 			set_irq_handler(irq, handle_level_irq); | 
 | 137 | 			break; | 
 | 138 | 		} | 
 | 139 | 	} | 
 | 140 |  | 
| Haojian Zhuang | df0c382 | 2010-02-03 10:01:18 -0500 | [diff] [blame] | 141 | 	/* NOTE: IRQ_MMP2_PMIC requires the PMIC MFPR register | 
 | 142 | 	 * to be written to clear the interrupt | 
 | 143 | 	 */ | 
| Lennert Buytenhek | a157f26 | 2010-11-29 10:36:07 +0100 | [diff] [blame] | 144 | 	pmic_irq_chip.irq_ack = pmic_irq_ack; | 
| Haojian Zhuang | df0c382 | 2010-02-03 10:01:18 -0500 | [diff] [blame] | 145 |  | 
| Haojian Zhuang | 2f7e8fa | 2009-12-04 09:41:28 -0500 | [diff] [blame] | 146 | 	init_mux_irq(&pmic_irq_chip, IRQ_MMP2_PMIC_BASE, 2); | 
 | 147 | 	init_mux_irq(&rtc_irq_chip, IRQ_MMP2_RTC_BASE, 2); | 
 | 148 | 	init_mux_irq(&twsi_irq_chip, IRQ_MMP2_TWSI_BASE, 5); | 
 | 149 | 	init_mux_irq(&misc_irq_chip, IRQ_MMP2_MISC_BASE, 15); | 
 | 150 | 	init_mux_irq(&ssp_irq_chip, IRQ_MMP2_SSP_BASE, 2); | 
 | 151 |  | 
 | 152 | 	set_irq_chained_handler(IRQ_MMP2_PMIC_MUX, pmic_irq_demux); | 
 | 153 | 	set_irq_chained_handler(IRQ_MMP2_RTC_MUX, rtc_irq_demux); | 
 | 154 | 	set_irq_chained_handler(IRQ_MMP2_TWSI_MUX, twsi_irq_demux); | 
 | 155 | 	set_irq_chained_handler(IRQ_MMP2_MISC_MUX, misc_irq_demux); | 
 | 156 | 	set_irq_chained_handler(IRQ_MMP2_SSP_MUX, ssp_irq_demux); | 
 | 157 | } |