1ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
2ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean *
3ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean * This program is free software; you can redistribute it and/or modify
4ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean * it under the terms of the GNU General Public License version 2 and
5ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean * only version 2 as published by the Free Software Foundation.
6ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean *
7ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean * This program is distributed in the hope that it will be useful,
8ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean * but WITHOUT ANY WARRANTY; without even the implied warranty of
9ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean * GNU General Public License for more details.
11ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean *
12ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean * You should have received a copy of the GNU General Public License
13ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean * along with this program; if not, write to the Free Software
14ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean * 02110-1301, USA.
16ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean *
17ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean */
18ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean
19ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean#include <linux/io.h>
20ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean#include <linux/irq.h>
21ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean#include <linux/interrupt.h>
22ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean#include <asm/irq.h>
23ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean
24ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Beanstatic unsigned int int_enable;
25ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Beanstatic unsigned int wake_enable;
26ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean
27ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Beanstatic struct sirc_regs_t sirc_regs = {
28ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	.int_enable       = SPSS_SIRC_INT_ENABLE,
29ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	.int_enable_clear = SPSS_SIRC_INT_ENABLE_CLEAR,
30ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	.int_enable_set   = SPSS_SIRC_INT_ENABLE_SET,
31ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	.int_type         = SPSS_SIRC_INT_TYPE,
32ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	.int_polarity     = SPSS_SIRC_INT_POLARITY,
33ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	.int_clear        = SPSS_SIRC_INT_CLEAR,
34ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean};
35ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean
36ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Beanstatic struct sirc_cascade_regs sirc_reg_table[] = {
37ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	{
38ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean		.int_status  = SPSS_SIRC_IRQ_STATUS,
39ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean		.cascade_irq = INT_SIRC_0,
40ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	}
41ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean};
42ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean
43ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean/* Mask off the given interrupt. Keep the int_enable mask in sync with
44ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean   the enable reg, so it can be restored after power collapse. */
450f86ee082caa043d5e2990d42a1d5034d2a5caf6Lennert Buytenhekstatic void sirc_irq_mask(struct irq_data *d)
46ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean{
47ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	unsigned int mask;
48ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean
490f86ee082caa043d5e2990d42a1d5034d2a5caf6Lennert Buytenhek	mask = 1 << (d->irq - FIRST_SIRC_IRQ);
50ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	writel(mask, sirc_regs.int_enable_clear);
51ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	int_enable &= ~mask;
52ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	return;
53ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean}
54ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean
55ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean/* Unmask the given interrupt. Keep the int_enable mask in sync with
56ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean   the enable reg, so it can be restored after power collapse. */
570f86ee082caa043d5e2990d42a1d5034d2a5caf6Lennert Buytenhekstatic void sirc_irq_unmask(struct irq_data *d)
58ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean{
59ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	unsigned int mask;
60ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean
610f86ee082caa043d5e2990d42a1d5034d2a5caf6Lennert Buytenhek	mask = 1 << (d->irq - FIRST_SIRC_IRQ);
62ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	writel(mask, sirc_regs.int_enable_set);
63ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	int_enable |= mask;
64ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	return;
65ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean}
66ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean
670f86ee082caa043d5e2990d42a1d5034d2a5caf6Lennert Buytenhekstatic void sirc_irq_ack(struct irq_data *d)
68ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean{
69ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	unsigned int mask;
70ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean
710f86ee082caa043d5e2990d42a1d5034d2a5caf6Lennert Buytenhek	mask = 1 << (d->irq - FIRST_SIRC_IRQ);
72ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	writel(mask, sirc_regs.int_clear);
73ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	return;
74ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean}
75ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean
760f86ee082caa043d5e2990d42a1d5034d2a5caf6Lennert Buytenhekstatic int sirc_irq_set_wake(struct irq_data *d, unsigned int on)
77ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean{
78ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	unsigned int mask;
79ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean
80ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	/* Used to set the interrupt enable mask during power collapse. */
810f86ee082caa043d5e2990d42a1d5034d2a5caf6Lennert Buytenhek	mask = 1 << (d->irq - FIRST_SIRC_IRQ);
82ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	if (on)
83ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean		wake_enable |= mask;
84ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	else
85ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean		wake_enable &= ~mask;
86ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean
87ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	return 0;
88ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean}
89ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean
900f86ee082caa043d5e2990d42a1d5034d2a5caf6Lennert Buytenhekstatic int sirc_irq_set_type(struct irq_data *d, unsigned int flow_type)
91ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean{
92ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	unsigned int mask;
93ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	unsigned int val;
94ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean
950f86ee082caa043d5e2990d42a1d5034d2a5caf6Lennert Buytenhek	mask = 1 << (d->irq - FIRST_SIRC_IRQ);
96ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	val = readl(sirc_regs.int_polarity);
97ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean
98ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	if (flow_type & (IRQF_TRIGGER_LOW | IRQF_TRIGGER_FALLING))
99ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean		val |= mask;
100ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	else
101ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean		val &= ~mask;
102ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean
103ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	writel(val, sirc_regs.int_polarity);
104ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean
105ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	val = readl(sirc_regs.int_type);
106ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) {
107ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean		val |= mask;
10870c4fa2265dde6570fe5bd300fe56986190d7c8cThomas Gleixner		__irq_set_handler_locked(d->irq, handle_edge_irq);
109ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	} else {
110ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean		val &= ~mask;
11170c4fa2265dde6570fe5bd300fe56986190d7c8cThomas Gleixner		__irq_set_handler_locked(d->irq, handle_level_irq);
112ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	}
113ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean
114ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	writel(val, sirc_regs.int_type);
115ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean
116ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	return 0;
117ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean}
118ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean
119ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean/* Finds the pending interrupt on the passed cascade irq and redrives it */
120ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Beanstatic void sirc_irq_handler(unsigned int irq, struct irq_desc *desc)
121ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean{
122ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	unsigned int reg = 0;
123ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	unsigned int sirq;
124ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	unsigned int status;
125ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean
126ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	while ((reg < ARRAY_SIZE(sirc_reg_table)) &&
127ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean		(sirc_reg_table[reg].cascade_irq != irq))
128ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean		reg++;
129ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean
130ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	status = readl(sirc_reg_table[reg].int_status);
131ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	status &= SIRC_MASK;
132ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	if (status == 0)
133ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean		return;
134ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean
135ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	for (sirq = 0;
136ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	     (sirq < NR_SIRC_IRQS) && ((status & (1U << sirq)) == 0);
137ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	     sirq++)
138ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean		;
139ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	generic_handle_irq(sirq+FIRST_SIRC_IRQ);
140ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean
1410f86ee082caa043d5e2990d42a1d5034d2a5caf6Lennert Buytenhek	desc->irq_data.chip->irq_ack(&desc->irq_data);
142ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean}
143ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean
144ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Beanstatic struct irq_chip sirc_irq_chip = {
1450f86ee082caa043d5e2990d42a1d5034d2a5caf6Lennert Buytenhek	.name          = "sirc",
1460f86ee082caa043d5e2990d42a1d5034d2a5caf6Lennert Buytenhek	.irq_ack       = sirc_irq_ack,
1470f86ee082caa043d5e2990d42a1d5034d2a5caf6Lennert Buytenhek	.irq_mask      = sirc_irq_mask,
1480f86ee082caa043d5e2990d42a1d5034d2a5caf6Lennert Buytenhek	.irq_unmask    = sirc_irq_unmask,
1490f86ee082caa043d5e2990d42a1d5034d2a5caf6Lennert Buytenhek	.irq_set_wake  = sirc_irq_set_wake,
1500f86ee082caa043d5e2990d42a1d5034d2a5caf6Lennert Buytenhek	.irq_set_type  = sirc_irq_set_type,
151ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean};
152ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean
153ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Beanvoid __init msm_init_sirc(void)
154ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean{
155ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	int i;
156ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean
157ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	int_enable = 0;
158ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	wake_enable = 0;
159ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean
160ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	for (i = FIRST_SIRC_IRQ; i < LAST_SIRC_IRQ; i++) {
161f38c02f3b338651e145aac2889ba976baf6b28b3Thomas Gleixner		irq_set_chip_and_handler(i, &sirc_irq_chip, handle_edge_irq);
162ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean		set_irq_flags(i, IRQF_VALID);
163ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	}
164ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean
165ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	for (i = 0; i < ARRAY_SIZE(sirc_reg_table); i++) {
1666845664a6a7d443f03883db59d10749d38d98b8eThomas Gleixner		irq_set_chained_handler(sirc_reg_table[i].cascade_irq,
167ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean					sirc_irq_handler);
1686845664a6a7d443f03883db59d10749d38d98b8eThomas Gleixner		irq_set_irq_wake(sirc_reg_table[i].cascade_irq, 1);
169ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	}
170ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean	return;
171ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean}
172ec4d79255c684a74ade2f2394b9f9a669cee0036Gregory Bean
173