1/*
2 * linux/arch/arm/mach-w90x900/irq.c
3 *
4 * based on linux/arch/arm/plat-s3c24xx/irq.c by Ben Dooks
5 *
6 * Copyright (c) 2008 Nuvoton technology corporation
7 * All rights reserved.
8 *
9 * Wan ZongShun <mcuos.com@gmail.com>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation;version 2 of the License.
14 *
15 */
16
17#include <linux/init.h>
18#include <linux/module.h>
19#include <linux/interrupt.h>
20#include <linux/ioport.h>
21#include <linux/ptrace.h>
22#include <linux/device.h>
23#include <linux/io.h>
24
25#include <asm/irq.h>
26#include <asm/mach/irq.h>
27
28#include <mach/hardware.h>
29#include <mach/regs-irq.h>
30
31#include "nuc9xx.h"
32
33struct group_irq {
34	unsigned long		gpen;
35	unsigned int		enabled;
36	void			(*enable)(struct group_irq *, int enable);
37};
38
39static DEFINE_SPINLOCK(groupirq_lock);
40
41#define DEFINE_GROUP(_name, _ctrlbit, _num)				\
42struct group_irq group_##_name = {					\
43		.enable		= nuc900_group_enable,			\
44		.gpen		= ((1 << _num) - 1) << _ctrlbit,	\
45	}
46
47static void nuc900_group_enable(struct group_irq *gpirq, int enable);
48
49static DEFINE_GROUP(nirq0, 0, 4);
50static DEFINE_GROUP(nirq1, 4, 4);
51static DEFINE_GROUP(usbh, 8, 2);
52static DEFINE_GROUP(ottimer, 16, 3);
53static DEFINE_GROUP(gdma, 20, 2);
54static DEFINE_GROUP(sc, 24, 2);
55static DEFINE_GROUP(i2c, 26, 2);
56static DEFINE_GROUP(ps2, 28, 2);
57
58static int group_irq_enable(struct group_irq *group_irq)
59{
60	unsigned long flags;
61
62	spin_lock_irqsave(&groupirq_lock, flags);
63	if (group_irq->enabled++ == 0)
64		(group_irq->enable)(group_irq, 1);
65	spin_unlock_irqrestore(&groupirq_lock, flags);
66
67	return 0;
68}
69
70static void group_irq_disable(struct group_irq *group_irq)
71{
72	unsigned long flags;
73
74	WARN_ON(group_irq->enabled == 0);
75
76	spin_lock_irqsave(&groupirq_lock, flags);
77	if (--group_irq->enabled == 0)
78		(group_irq->enable)(group_irq, 0);
79	spin_unlock_irqrestore(&groupirq_lock, flags);
80}
81
82static void nuc900_group_enable(struct group_irq *gpirq, int enable)
83{
84	unsigned int groupen = gpirq->gpen;
85	unsigned long regval;
86
87	regval = __raw_readl(REG_AIC_GEN);
88
89	if (enable)
90		regval |= groupen;
91	else
92		regval &= ~groupen;
93
94	__raw_writel(regval, REG_AIC_GEN);
95}
96
97static void nuc900_irq_mask(struct irq_data *d)
98{
99	struct group_irq *group_irq;
100
101	group_irq = NULL;
102
103	__raw_writel(1 << d->irq, REG_AIC_MDCR);
104
105	switch (d->irq) {
106	case IRQ_GROUP0:
107		group_irq = &group_nirq0;
108		break;
109
110	case IRQ_GROUP1:
111		group_irq = &group_nirq1;
112		break;
113
114	case IRQ_USBH:
115		group_irq = &group_usbh;
116		break;
117
118	case IRQ_T_INT_GROUP:
119		group_irq = &group_ottimer;
120		break;
121
122	case IRQ_GDMAGROUP:
123		group_irq = &group_gdma;
124		break;
125
126	case IRQ_SCGROUP:
127		group_irq = &group_sc;
128		break;
129
130	case IRQ_I2CGROUP:
131		group_irq = &group_i2c;
132		break;
133
134	case IRQ_P2SGROUP:
135		group_irq = &group_ps2;
136		break;
137	}
138
139	if (group_irq)
140		group_irq_disable(group_irq);
141}
142
143/*
144 * By the w90p910 spec,any irq,only write 1
145 * to REG_AIC_EOSCR for ACK
146 */
147
148static void nuc900_irq_ack(struct irq_data *d)
149{
150	__raw_writel(0x01, REG_AIC_EOSCR);
151}
152
153static void nuc900_irq_unmask(struct irq_data *d)
154{
155	struct group_irq *group_irq;
156
157	group_irq = NULL;
158
159	__raw_writel(1 << d->irq, REG_AIC_MECR);
160
161	switch (d->irq) {
162	case IRQ_GROUP0:
163		group_irq = &group_nirq0;
164		break;
165
166	case IRQ_GROUP1:
167		group_irq = &group_nirq1;
168		break;
169
170	case IRQ_USBH:
171		group_irq = &group_usbh;
172		break;
173
174	case IRQ_T_INT_GROUP:
175		group_irq = &group_ottimer;
176		break;
177
178	case IRQ_GDMAGROUP:
179		group_irq = &group_gdma;
180		break;
181
182	case IRQ_SCGROUP:
183		group_irq = &group_sc;
184		break;
185
186	case IRQ_I2CGROUP:
187		group_irq = &group_i2c;
188		break;
189
190	case IRQ_P2SGROUP:
191		group_irq = &group_ps2;
192		break;
193	}
194
195	if (group_irq)
196		group_irq_enable(group_irq);
197}
198
199static struct irq_chip nuc900_irq_chip = {
200	.irq_ack	= nuc900_irq_ack,
201	.irq_mask	= nuc900_irq_mask,
202	.irq_unmask	= nuc900_irq_unmask,
203};
204
205void __init nuc900_init_irq(void)
206{
207	int irqno;
208
209	__raw_writel(0xFFFFFFFE, REG_AIC_MDCR);
210
211	for (irqno = IRQ_WDT; irqno <= IRQ_ADC; irqno++) {
212		irq_set_chip_and_handler(irqno, &nuc900_irq_chip,
213					 handle_level_irq);
214		set_irq_flags(irqno, IRQF_VALID);
215	}
216}
217