1/*
2 * Copyright (c) 2011 Zhang, Keguang <keguang.zhang@gmail.com>
3 *
4 * This program is free software; you can redistribute	it and/or modify it
5 * under  the terms of	the GNU General	 Public License as published by the
6 * Free Software Foundation;  either version 2 of the  License, or (at your
7 * option) any later version.
8 */
9
10#include <linux/interrupt.h>
11#include <linux/irq.h>
12#include <asm/irq_cpu.h>
13
14#include <loongson1.h>
15#include <irq.h>
16
17#define LS1X_INTC_REG(n, x) \
18		((void __iomem *)KSEG1ADDR(LS1X_INTC_BASE + (n * 0x18) + (x)))
19
20#define LS1X_INTC_INTISR(n)		LS1X_INTC_REG(n, 0x0)
21#define LS1X_INTC_INTIEN(n)		LS1X_INTC_REG(n, 0x4)
22#define LS1X_INTC_INTSET(n)		LS1X_INTC_REG(n, 0x8)
23#define LS1X_INTC_INTCLR(n)		LS1X_INTC_REG(n, 0xc)
24#define LS1X_INTC_INTPOL(n)		LS1X_INTC_REG(n, 0x10)
25#define LS1X_INTC_INTEDGE(n)		LS1X_INTC_REG(n, 0x14)
26
27static void ls1x_irq_ack(struct irq_data *d)
28{
29	unsigned int bit = (d->irq - LS1X_IRQ_BASE) & 0x1f;
30	unsigned int n = (d->irq - LS1X_IRQ_BASE) >> 5;
31
32	__raw_writel(__raw_readl(LS1X_INTC_INTCLR(n))
33			| (1 << bit), LS1X_INTC_INTCLR(n));
34}
35
36static void ls1x_irq_mask(struct irq_data *d)
37{
38	unsigned int bit = (d->irq - LS1X_IRQ_BASE) & 0x1f;
39	unsigned int n = (d->irq - LS1X_IRQ_BASE) >> 5;
40
41	__raw_writel(__raw_readl(LS1X_INTC_INTIEN(n))
42			& ~(1 << bit), LS1X_INTC_INTIEN(n));
43}
44
45static void ls1x_irq_mask_ack(struct irq_data *d)
46{
47	unsigned int bit = (d->irq - LS1X_IRQ_BASE) & 0x1f;
48	unsigned int n = (d->irq - LS1X_IRQ_BASE) >> 5;
49
50	__raw_writel(__raw_readl(LS1X_INTC_INTIEN(n))
51			& ~(1 << bit), LS1X_INTC_INTIEN(n));
52	__raw_writel(__raw_readl(LS1X_INTC_INTCLR(n))
53			| (1 << bit), LS1X_INTC_INTCLR(n));
54}
55
56static void ls1x_irq_unmask(struct irq_data *d)
57{
58	unsigned int bit = (d->irq - LS1X_IRQ_BASE) & 0x1f;
59	unsigned int n = (d->irq - LS1X_IRQ_BASE) >> 5;
60
61	__raw_writel(__raw_readl(LS1X_INTC_INTIEN(n))
62			| (1 << bit), LS1X_INTC_INTIEN(n));
63}
64
65static struct irq_chip ls1x_irq_chip = {
66	.name		= "LS1X-INTC",
67	.irq_ack	= ls1x_irq_ack,
68	.irq_mask	= ls1x_irq_mask,
69	.irq_mask_ack	= ls1x_irq_mask_ack,
70	.irq_unmask	= ls1x_irq_unmask,
71};
72
73static void ls1x_irq_dispatch(int n)
74{
75	u32 int_status, irq;
76
77	/* Get pending sources, masked by current enables */
78	int_status = __raw_readl(LS1X_INTC_INTISR(n)) &
79			__raw_readl(LS1X_INTC_INTIEN(n));
80
81	if (int_status) {
82		irq = LS1X_IRQ(n, __ffs(int_status));
83		do_IRQ(irq);
84	}
85}
86
87asmlinkage void plat_irq_dispatch(void)
88{
89	unsigned int pending;
90
91	pending = read_c0_cause() & read_c0_status() & ST0_IM;
92
93	if (pending & CAUSEF_IP7)
94		do_IRQ(TIMER_IRQ);
95	else if (pending & CAUSEF_IP2)
96		ls1x_irq_dispatch(0); /* INT0 */
97	else if (pending & CAUSEF_IP3)
98		ls1x_irq_dispatch(1); /* INT1 */
99	else if (pending & CAUSEF_IP4)
100		ls1x_irq_dispatch(2); /* INT2 */
101	else if (pending & CAUSEF_IP5)
102		ls1x_irq_dispatch(3); /* INT3 */
103	else if (pending & CAUSEF_IP6)
104		ls1x_irq_dispatch(4); /* INT4 */
105	else
106		spurious_interrupt();
107
108}
109
110struct irqaction cascade_irqaction = {
111	.handler = no_action,
112	.name = "cascade",
113	.flags = IRQF_NO_THREAD,
114};
115
116static void __init ls1x_irq_init(int base)
117{
118	int n;
119
120	/* Disable interrupts and clear pending,
121	 * setup all IRQs as high level triggered
122	 */
123	for (n = 0; n < 4; n++) {
124		__raw_writel(0x0, LS1X_INTC_INTIEN(n));
125		__raw_writel(0xffffffff, LS1X_INTC_INTCLR(n));
126		__raw_writel(0xffffffff, LS1X_INTC_INTPOL(n));
127		/* set DMA0, DMA1 and DMA2 to edge trigger */
128		__raw_writel(n ? 0x0 : 0xe000, LS1X_INTC_INTEDGE(n));
129	}
130
131
132	for (n = base; n < LS1X_IRQS; n++) {
133		irq_set_chip_and_handler(n, &ls1x_irq_chip,
134					 handle_level_irq);
135	}
136
137	setup_irq(INT0_IRQ, &cascade_irqaction);
138	setup_irq(INT1_IRQ, &cascade_irqaction);
139	setup_irq(INT2_IRQ, &cascade_irqaction);
140	setup_irq(INT3_IRQ, &cascade_irqaction);
141}
142
143void __init arch_init_irq(void)
144{
145	mips_cpu_irq_init();
146	ls1x_irq_init(LS1X_IRQ_BASE);
147}
148