blob: 7895d277421e74c225bfddcb76b19a8a7bd72e24 [file] [log] [blame]
Haojian Zhuang2f7e8fa2009-12-04 09:41:28 -05001/*
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
Rob Herring8661fb92012-01-03 16:50:40 -060018#include <mach/irqs.h>
Haojian Zhuang2f7e8fa2009-12-04 09:41:28 -050019#include <mach/regs-icu.h>
Eric Miao27287012010-07-15 22:22:33 +080020#include <mach/mmp2.h>
Haojian Zhuang2f7e8fa2009-12-04 09:41:28 -050021
22#include "common.h"
23
Lennert Buytenheka157f262010-11-29 10:36:07 +010024static void icu_mask_irq(struct irq_data *d)
Haojian Zhuang2f7e8fa2009-12-04 09:41:28 -050025{
Lennert Buytenheka157f262010-11-29 10:36:07 +010026 uint32_t r = __raw_readl(ICU_INT_CONF(d->irq));
Haojian Zhuang2f7e8fa2009-12-04 09:41:28 -050027
28 r &= ~ICU_INT_ROUTE_PJ4_IRQ;
Lennert Buytenheka157f262010-11-29 10:36:07 +010029 __raw_writel(r, ICU_INT_CONF(d->irq));
Haojian Zhuang2f7e8fa2009-12-04 09:41:28 -050030}
31
Lennert Buytenheka157f262010-11-29 10:36:07 +010032static void icu_unmask_irq(struct irq_data *d)
Haojian Zhuang2f7e8fa2009-12-04 09:41:28 -050033{
Lennert Buytenheka157f262010-11-29 10:36:07 +010034 uint32_t r = __raw_readl(ICU_INT_CONF(d->irq));
Haojian Zhuang2f7e8fa2009-12-04 09:41:28 -050035
36 r |= ICU_INT_ROUTE_PJ4_IRQ;
Lennert Buytenheka157f262010-11-29 10:36:07 +010037 __raw_writel(r, ICU_INT_CONF(d->irq));
Haojian Zhuang2f7e8fa2009-12-04 09:41:28 -050038}
39
40static struct irq_chip icu_irq_chip = {
41 .name = "icu_irq",
Lennert Buytenheka157f262010-11-29 10:36:07 +010042 .irq_mask = icu_mask_irq,
43 .irq_mask_ack = icu_mask_irq,
44 .irq_unmask = icu_unmask_irq,
Haojian Zhuang2f7e8fa2009-12-04 09:41:28 -050045};
46
Lennert Buytenheka157f262010-11-29 10:36:07 +010047static void pmic_irq_ack(struct irq_data *d)
Haojian Zhuangdf0c3822010-02-03 10:01:18 -050048{
Lennert Buytenheka157f262010-11-29 10:36:07 +010049 if (d->irq == IRQ_MMP2_PMIC)
Haojian Zhuangdf0c3822010-02-03 10:01:18 -050050 mmp2_clear_pmic_int();
51}
52
Haojian Zhuang2f7e8fa2009-12-04 09:41:28 -050053#define SECOND_IRQ_MASK(_name_, irq_base, prefix) \
Lennert Buytenheka157f262010-11-29 10:36:07 +010054static void _name_##_mask_irq(struct irq_data *d) \
Haojian Zhuang2f7e8fa2009-12-04 09:41:28 -050055{ \
56 uint32_t r; \
Lennert Buytenheka157f262010-11-29 10:36:07 +010057 r = __raw_readl(prefix##_MASK) | (1 << (d->irq - irq_base)); \
Haojian Zhuang2f7e8fa2009-12-04 09:41:28 -050058 __raw_writel(r, prefix##_MASK); \
59}
60
61#define SECOND_IRQ_UNMASK(_name_, irq_base, prefix) \
Lennert Buytenheka157f262010-11-29 10:36:07 +010062static void _name_##_unmask_irq(struct irq_data *d) \
Haojian Zhuang2f7e8fa2009-12-04 09:41:28 -050063{ \
64 uint32_t r; \
Lennert Buytenheka157f262010-11-29 10:36:07 +010065 r = __raw_readl(prefix##_MASK) & ~(1 << (d->irq - irq_base)); \
Haojian Zhuang2f7e8fa2009-12-04 09:41:28 -050066 __raw_writel(r, prefix##_MASK); \
67}
68
69#define SECOND_IRQ_DEMUX(_name_, irq_base, prefix) \
70static void _name_##_irq_demux(unsigned int irq, struct irq_desc *desc) \
71{ \
72 unsigned long status, mask, n; \
73 mask = __raw_readl(prefix##_MASK); \
74 while (1) { \
75 status = __raw_readl(prefix##_STATUS) & ~mask; \
76 if (status == 0) \
77 break; \
78 n = find_first_bit(&status, BITS_PER_LONG); \
79 while (n < BITS_PER_LONG) { \
80 generic_handle_irq(irq_base + n); \
81 n = find_next_bit(&status, BITS_PER_LONG, n+1); \
82 } \
83 } \
84}
85
86#define SECOND_IRQ_CHIP(_name_, irq_base, prefix) \
87SECOND_IRQ_MASK(_name_, irq_base, prefix) \
88SECOND_IRQ_UNMASK(_name_, irq_base, prefix) \
89SECOND_IRQ_DEMUX(_name_, irq_base, prefix) \
90static struct irq_chip _name_##_irq_chip = { \
91 .name = #_name_, \
Lennert Buytenheka157f262010-11-29 10:36:07 +010092 .irq_mask = _name_##_mask_irq, \
93 .irq_unmask = _name_##_unmask_irq, \
Haojian Zhuang2f7e8fa2009-12-04 09:41:28 -050094}
95
96SECOND_IRQ_CHIP(pmic, IRQ_MMP2_PMIC_BASE, MMP2_ICU_INT4);
97SECOND_IRQ_CHIP(rtc, IRQ_MMP2_RTC_BASE, MMP2_ICU_INT5);
98SECOND_IRQ_CHIP(twsi, IRQ_MMP2_TWSI_BASE, MMP2_ICU_INT17);
99SECOND_IRQ_CHIP(misc, IRQ_MMP2_MISC_BASE, MMP2_ICU_INT35);
100SECOND_IRQ_CHIP(ssp, IRQ_MMP2_SSP_BASE, MMP2_ICU_INT51);
101
102static void init_mux_irq(struct irq_chip *chip, int start, int num)
103{
104 int irq;
105
106 for (irq = start; num > 0; irq++, num--) {
Lennert Buytenheka157f262010-11-29 10:36:07 +0100107 struct irq_data *d = irq_get_irq_data(irq);
108
Eric Miao2029e562010-02-02 23:39:35 -0800109 /* mask and clear the IRQ */
Lennert Buytenheka157f262010-11-29 10:36:07 +0100110 chip->irq_mask(d);
111 if (chip->irq_ack)
112 chip->irq_ack(d);
Eric Miao2029e562010-02-02 23:39:35 -0800113
Thomas Gleixner6845664a2011-03-24 13:25:22 +0100114 irq_set_chip(irq, chip);
Haojian Zhuang2f7e8fa2009-12-04 09:41:28 -0500115 set_irq_flags(irq, IRQF_VALID);
Thomas Gleixner6845664a2011-03-24 13:25:22 +0100116 irq_set_handler(irq, handle_level_irq);
Haojian Zhuang2f7e8fa2009-12-04 09:41:28 -0500117 }
118}
119
Haojian Zhuang16144bf2010-01-25 06:03:54 -0500120void __init mmp2_init_icu(void)
Haojian Zhuang2f7e8fa2009-12-04 09:41:28 -0500121{
122 int irq;
123
124 for (irq = 0; irq < IRQ_MMP2_MUX_BASE; irq++) {
Lennert Buytenheka157f262010-11-29 10:36:07 +0100125 icu_mask_irq(irq_get_irq_data(irq));
Thomas Gleixner6845664a2011-03-24 13:25:22 +0100126 irq_set_chip(irq, &icu_irq_chip);
Haojian Zhuang2f7e8fa2009-12-04 09:41:28 -0500127 set_irq_flags(irq, IRQF_VALID);
128
129 switch (irq) {
130 case IRQ_MMP2_PMIC_MUX:
131 case IRQ_MMP2_RTC_MUX:
132 case IRQ_MMP2_TWSI_MUX:
133 case IRQ_MMP2_MISC_MUX:
134 case IRQ_MMP2_SSP_MUX:
135 break;
136 default:
Thomas Gleixner6845664a2011-03-24 13:25:22 +0100137 irq_set_handler(irq, handle_level_irq);
Haojian Zhuang2f7e8fa2009-12-04 09:41:28 -0500138 break;
139 }
140 }
141
Haojian Zhuangdf0c3822010-02-03 10:01:18 -0500142 /* NOTE: IRQ_MMP2_PMIC requires the PMIC MFPR register
143 * to be written to clear the interrupt
144 */
Lennert Buytenheka157f262010-11-29 10:36:07 +0100145 pmic_irq_chip.irq_ack = pmic_irq_ack;
Haojian Zhuangdf0c3822010-02-03 10:01:18 -0500146
Haojian Zhuang2f7e8fa2009-12-04 09:41:28 -0500147 init_mux_irq(&pmic_irq_chip, IRQ_MMP2_PMIC_BASE, 2);
148 init_mux_irq(&rtc_irq_chip, IRQ_MMP2_RTC_BASE, 2);
149 init_mux_irq(&twsi_irq_chip, IRQ_MMP2_TWSI_BASE, 5);
150 init_mux_irq(&misc_irq_chip, IRQ_MMP2_MISC_BASE, 15);
151 init_mux_irq(&ssp_irq_chip, IRQ_MMP2_SSP_BASE, 2);
152
Thomas Gleixner6845664a2011-03-24 13:25:22 +0100153 irq_set_chained_handler(IRQ_MMP2_PMIC_MUX, pmic_irq_demux);
154 irq_set_chained_handler(IRQ_MMP2_RTC_MUX, rtc_irq_demux);
155 irq_set_chained_handler(IRQ_MMP2_TWSI_MUX, twsi_irq_demux);
156 irq_set_chained_handler(IRQ_MMP2_MISC_MUX, misc_irq_demux);
157 irq_set_chained_handler(IRQ_MMP2_SSP_MUX, ssp_irq_demux);
Haojian Zhuang2f7e8fa2009-12-04 09:41:28 -0500158}