summaryrefslogtreecommitdiffstats
path: root/arch/ppc/kernel/xics.c
blob: a9772821bf96bb2b4777966423a1c7b2c22dea49 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
/*
 * arch/ppc/kernel/xics.c
 *
 * Copyright 2000 IBM Corporation.
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License
 *  as published by the Free Software Foundation; either version
 *  2 of the License, or (at your option) any later version.
 */
#include <linux/config.h>
#include <linux/types.h>
#include <linux/threads.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <asm/prom.h>
#include <asm/io.h>
#include "i8259.h"
#include "xics.h"

void xics_enable_irq(u_int irq);
void xics_disable_irq(u_int irq);
void xics_mask_and_ack_irq(u_int irq);
void xics_end_irq(u_int irq);

struct hw_interrupt_type xics_pic = {
	" XICS     ",
	NULL,
	NULL,
	xics_enable_irq,
	xics_disable_irq,
	xics_mask_and_ack_irq,
	xics_end_irq
};

struct hw_interrupt_type xics_8259_pic = {
	" XICS/8259",
	NULL,
	NULL,
	NULL,
	NULL,
	xics_mask_and_ack_irq,
	NULL
};

#define XICS_IPI		2
#define XICS_IRQ_8259_CASCADE	0x2c
#define XICS_IRQ_OFFSET		16
#define XICS_IRQ_SPURIOUS	0

#define DEFAULT_SERVER		0
#define	DEFAULT_PRIORITY	0

struct xics_ipl {
	union {
		u32	word;
		u8	bytes[4];
	} xirr_poll;
	union {
		u32 word;
		u8	bytes[4];
	} xirr;
	u32	dummy;
	union {
		u32	word;
		u8	bytes[4];
	} qirr;
};

struct xics_info {
	volatile struct xics_ipl *	per_cpu[NR_CPUS];
};

struct xics_info	xics_info;

#define xirr_info(n_cpu)	(xics_info.per_cpu[n_cpu]->xirr.word)
#define cppr_info(n_cpu)	(xics_info.per_cpu[n_cpu]->xirr.bytes[0])
#define poll_info(n_cpu)	(xics_info.per_cpu[n_cpu]->xirr_poll.word)
#define qirr_info(n_cpu)	(xics_info.per_cpu[n_cpu]->qirr.bytes[0])

void
xics_enable_irq(
	u_int	irq
	)
{
	int	status;
	int	call_status;

	irq -= XICS_IRQ_OFFSET;
	if (irq == XICS_IPI)
		return;
	call_status = call_rtas("ibm,set-xive", 3, 1, (ulong*)&status,
				irq, DEFAULT_SERVER, DEFAULT_PRIORITY);
	if( call_status != 0 ) {
		printk("xics_enable_irq: irq=%x: call_rtas failed; retn=%x, status=%x\n",
		       irq, call_status, status);
		return;
	}
}

void
xics_disable_irq(
	u_int	irq
	)
{
	int	status;
	int	call_status;

	irq -= XICS_IRQ_OFFSET;
	call_status = call_rtas("ibm,int-off", 1, 1, (ulong*)&status, irq);
	if( call_status != 0 ) {
		printk("xics_disable_irq: irq=%x: call_rtas failed, retn=%x\n",
		       irq, call_status);
		return;
	}
}

void
xics_end_irq(
	u_int	irq
	)
{
	int cpu = smp_processor_id();

	cppr_info(cpu) = 0; /* actually the value overwritten by ack */
	xirr_info(cpu) = (0xff<<24) | (irq-XICS_IRQ_OFFSET);
}

void
xics_mask_and_ack_irq(
	u_int	irq
	)
{
	int cpu = smp_processor_id();

	if( irq < XICS_IRQ_OFFSET ) {
		i8259_pic.ack(irq);
		xirr_info(cpu) = (0xff<<24) | XICS_IRQ_8259_CASCADE;
	}
	else {
		cppr_info(cpu) = 0xff;
	}
}

int
xics_get_irq(struct pt_regs *regs)
{
	u_int	cpu = smp_processor_id();
	u_int	vec;
	int irq;
  
	vec = xirr_info(cpu);
	/*  (vec >> 24) == old priority */
	vec &= 0x00ffffff;
	/* for sanity, this had better be < NR_IRQS - 16 */
	if( vec == XICS_IRQ_8259_CASCADE )
		irq = i8259_irq(cpu);
	else if( vec == XICS_IRQ_SPURIOUS )
		irq = -1;
	else
		irq = vec + XICS_IRQ_OFFSET;
	return irq;
}

#ifdef CONFIG_SMP
void xics_ipi_action(int irq, void *dev_id, struct pt_regs *regs)
{
	qirr_info(smp_processor_id()) = 0xff;
	smp_message_recv(MSG_RESCHEDULE);
}

void xics_cause_IPI(int cpu)
{
	qirr_info(cpu) = 0;
}

void xics_setup_cpu(void)
{
	int cpu = smp_processor_id();

	cppr_info(cpu) = 0xff;
}
#endif /* CONFIG_SMP */

void
xics_init_IRQ( void )
{
	int i;
	extern unsigned long smp_chrp_cpu_nr;

#ifdef CONFIG_SMP
	for (i = 0; i < smp_chrp_cpu_nr; ++i)
		xics_info.per_cpu[i] =
			ioremap(0xfe000000 + smp_hw_index[i] * 0x1000, 0x20);
#else
	xics_info.per_cpu[0] = ioremap(0xfe000000, 0x20);
#endif /* CONFIG_SMP */
	xics_8259_pic.enable = i8259_pic.enable;
	xics_8259_pic.disable = i8259_pic.disable;
	for (i = 0; i < 16; ++i)
		irq_desc[i].handler = &xics_8259_pic;
	for (; i < NR_IRQS; ++i)
		irq_desc[i].handler = &xics_pic;

	cppr_info(0) = 0xff;
	if (request_irq(XICS_IRQ_8259_CASCADE + XICS_IRQ_OFFSET, no_action,
			0, "8259 cascade", 0))
		printk(KERN_ERR "xics_init_IRQ: couldn't get 8259 cascade\n");
	i8259_init();

#ifdef CONFIG_SMP
	request_irq(XICS_IPI + XICS_IRQ_OFFSET, xics_ipi_action, 0, "IPI", 0);
#endif
}