summaryrefslogtreecommitdiffstats
path: root/bsps/powerpc/haleakala/irq/irq.c
blob: c9607a032df5b8581459d74a99ff81752cce29b1 (plain) (blame)
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
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
/*
 *
 *
 *  The license and distribution terms for this file may be
 *  found in the file LICENSE in this distribution or at
 *  http://www.rtems.org/license/LICENSE.
 *
 *  Middleware support for PPC405 by M.Hamel ADInstruments Ltd 2008
 */

#include <rtems.h>
#include <bsp.h>
#include <bsp/irq.h>
#include <bsp/irq_supp.h>
#include <bsp/vectors.h>
#include <libcpu/powerpc-utility.h>


/* PPC405EX UIC numbers */
#define UIC_DCR_BASE 0xc0
#define UIC0_SR      (UIC_DCR_BASE+0x0)  /* UIC status                       */
#define UIC0_SRS     (UIC_DCR_BASE+0x1)  /* UIC status set                   */
#define UIC0_ER      (UIC_DCR_BASE+0x2)  /* UIC enable                       */
#define UIC0_CR      (UIC_DCR_BASE+0x3)  /* UIC critical                     */
#define UIC0_PR      (UIC_DCR_BASE+0x4)  /* UIC polarity                     */
#define UIC0_TR      (UIC_DCR_BASE+0x5)  /* UIC triggering                   */
#define UIC0_MSR     (UIC_DCR_BASE+0x6)  /* UIC masked status                */
#define UIC0_VR      (UIC_DCR_BASE+0x7)  /* UIC vector                       */
#define UIC0_VCR     (UIC_DCR_BASE+0x8)  /* UIC vector configuration         */

#define UIC1_SR      (UIC_DCR_BASE+0x10)  /* UIC status                       */
#define UIC1_SRS     (UIC_DCR_BASE+0x11)  /* UIC status set                   */
#define UIC1_ER      (UIC_DCR_BASE+0x12)  /* UIC enable                       */
#define UIC1_CR      (UIC_DCR_BASE+0x13)  /* UIC critical                     */
#define UIC1_PR      (UIC_DCR_BASE+0x14)  /* UIC polarity                     */
#define UIC1_TR      (UIC_DCR_BASE+0x15)  /* UIC triggering                   */
#define UIC1_MSR     (UIC_DCR_BASE+0x16)  /* UIC masked status                */
#define UIC1_VR      (UIC_DCR_BASE+0x17)  /* UIC vector                       */
#define UIC1_VCR     (UIC_DCR_BASE+0x18)  /* UIC vector configuration         */

#define UIC2_SR      (UIC_DCR_BASE+0x20)  /* UIC status                       */
#define UIC2_SRS     (UIC_DCR_BASE+0x21)  /* UIC status set                   */
#define UIC2_ER      (UIC_DCR_BASE+0x22)  /* UIC enable                       */
#define UIC2_CR      (UIC_DCR_BASE+0x23)  /* UIC critical                     */
#define UIC2_PR      (UIC_DCR_BASE+0x24)  /* UIC polarity                     */
#define UIC2_TR      (UIC_DCR_BASE+0x25)  /* UIC triggering                   */
#define UIC2_MSR     (UIC_DCR_BASE+0x26)  /* UIC masked status                */
#define UIC2_VR      (UIC_DCR_BASE+0x27)  /* UIC vector                       */
#define UIC2_VCR     (UIC_DCR_BASE+0x28)  /* UIC vector configuration         */

enum { kUICWords = 3 };

static rtems_irq_connect_data* rtems_hdl_tblP;
static rtems_irq_connect_data  dflt_entry;

static uint32_t	gEnabledInts[kUICWords];		/* 1-bits mean enabled */
static uint32_t	gIntInhibited[kUICWords];		/* 1-bits disable, overriding gEnabledInts because the interrupt
												   is being processed in C_dispatch_irq_handler */

static inline int IsUICIRQ(const rtems_irq_number irqLine)
{
  return (((int) irqLine <= BSP_UIC_IRQ_MAX_OFFSET) &&
	  ((int) irqLine >= BSP_UIC_IRQ_LOWEST_OFFSET)
	 );
}

static void WriteIState(void)
/* Write the gEnabledInts state masked by gIntInhibited to the hardware */
{
	PPC_SET_DEVICE_CONTROL_REGISTER(UIC0_ER,
					gEnabledInts[0] & ~gIntInhibited[0]);
	PPC_SET_DEVICE_CONTROL_REGISTER(UIC1_ER,
					gEnabledInts[1] & ~gIntInhibited[1]);
	PPC_SET_DEVICE_CONTROL_REGISTER(UIC2_ER,
					gEnabledInts[2] & ~gIntInhibited[2]);
}

void
BSP_enable_irq_at_pic(const rtems_irq_number irq)
/* Enable an interrupt; this can be called from inside C_dispatch_irq_handler */
{
	if (IsUICIRQ(irq)) {
		/* Set relevant bit in the state, write state to the UIC */
		gEnabledInts[irq>>5] |= (0x80000000 >> (irq & 0x1F));
		WriteIState();
	}
}

int
BSP_disable_irq_at_pic(const rtems_irq_number irq)
/* Enable an interrupt; this can be called from inside C_dispatch_irq_handler */
{
	if (IsUICIRQ(irq)) {
		uint32_t oldState;
		int		 iword = irq>>5;
		uint32_t mask = (0x80000000 >> (irq & 0x1F));

		oldState = gEnabledInts[iword] & mask;
		gEnabledInts[iword] &= ~mask;
		WriteIState();
		return oldState ? 1 : 0;
	} else
		return -1;
}

int
BSP_setup_the_pic(rtems_irq_global_settings* config)
{
	int i;

	dflt_entry     = config->defaultEntry;
	rtems_hdl_tblP = config->irqHdlTbl;
	for (i=0; i<kUICWords; i++)
		gIntInhibited[i] = 0;

	/* disable all interrupts */
	PPC_SET_DEVICE_CONTROL_REGISTER (UIC2_ER, 0x00000000);
	/* Set Critical / Non Critical interrupts */
	PPC_SET_DEVICE_CONTROL_REGISTER (UIC2_CR, 0x00000000);
	/* Set Interrupt Polarities */
	PPC_SET_DEVICE_CONTROL_REGISTER (UIC2_PR, 0xf7ffffff);
	/* Set Interrupt Trigger Levels */
	PPC_SET_DEVICE_CONTROL_REGISTER (UIC2_TR, 0x01e1fff8);
	/* Set Vect base=0,INT31 Highest priority */
	PPC_SET_DEVICE_CONTROL_REGISTER (UIC2_VR, 0x00000001);
	/* clear all interrupts */
	PPC_SET_DEVICE_CONTROL_REGISTER (UIC2_SR, 0xffffffff);

	/* disable all interrupts */
	PPC_SET_DEVICE_CONTROL_REGISTER (UIC1_ER, 0x00000000);
	/* Set Critical / Non Critical interrupts */
	PPC_SET_DEVICE_CONTROL_REGISTER (UIC1_CR, 0x00000000);
	/* Set Interrupt Polarities */
	PPC_SET_DEVICE_CONTROL_REGISTER (UIC1_PR, 0xfffac785);
	/* Set Interrupt Trigger Levels */
	PPC_SET_DEVICE_CONTROL_REGISTER (UIC1_TR, 0x001d0040);
	/* Set Vect base=0,INT31 Highest priority */
	PPC_SET_DEVICE_CONTROL_REGISTER (UIC1_VR, 0x00000001);
	/* clear all interrupts */
	PPC_SET_DEVICE_CONTROL_REGISTER (UIC1_SR, 0xffffffff);

	/* Disable all interrupts except cascade UIC0 and UIC1 */
	PPC_SET_DEVICE_CONTROL_REGISTER (UIC0_ER, 0x0000000a);
	/* Set Critical / Non Critical interrupts */
	PPC_SET_DEVICE_CONTROL_REGISTER (UIC0_CR, 0x00000000);
	/* Set Interrupt Polarities */
	PPC_SET_DEVICE_CONTROL_REGISTER (UIC0_PR, 0xffbfefef);
	/* Set Interrupt Trigger Levels */
	PPC_SET_DEVICE_CONTROL_REGISTER (UIC0_TR, 0x00007000);
	/* Set Vect base=0,INT31 Highest priority */
	PPC_SET_DEVICE_CONTROL_REGISTER (UIC0_VR, 0x00000001);
	/* clear all interrupts */
	PPC_SET_DEVICE_CONTROL_REGISTER (UIC0_SR, 0xffffffff);

	return 1;
}


/*
 * High level IRQ handler called from shared_raw_irq_code_entry; decode and
 * dispatch. Note that this routine needs to be re-entrant
 *
 * No support for critical interrupts here yet
 */

int
C_dispatch_irq_handler( BSP_Exception_frame* frame, unsigned int excNum )
{
	if (excNum == ASM_EXT_VECTOR) {
		uint32_t active[kUICWords];

		/* Fetch the masked flags that tell us what external ints are active.
		   Likely to be only one, but we need to handle more than one,
		   OR the flags into gIntInhibited */
		active[0] = PPC_DEVICE_CONTROL_REGISTER(UIC0_MSR);
		active[1] = PPC_DEVICE_CONTROL_REGISTER(UIC1_MSR);
		active[2] = PPC_DEVICE_CONTROL_REGISTER(UIC2_MSR);
		gIntInhibited[0] |= active[0];
		gIntInhibited[1] |= active[1];
		gIntInhibited[2] |= active[2];

		/*  ...and update the hardware so the active interrupts are disabled */
		WriteIState();

		/* Loop, calling bsp_irq_dispatch_list for each active interrupt */
		while ((active[0] | active[1] | active[2]) != 0) {
			uint32_t index = -1;
			uint32_t bit, bmask;

			/* Find an active interrupt, searching 0..2, bit 0..bit 31 (IBM order) */
			do {
				index++;
				asm volatile ("	cntlzw %0, %1":"=r" (bit):"r" (active[index]));
			} while (bit==32);

			/* Call the matching handler */
			bsp_irq_dispatch_list(rtems_hdl_tblP, (index*32)+bit, dflt_entry.hdl);

			/* Write a 1-bit to the appropriate status register to clear it */
			bmask = 0x80000000 >> bit;
			switch (index) {
			case 0:
			  PPC_SET_DEVICE_CONTROL_REGISTER(UIC0_SR, bmask);
			  break;
			case 1:
			  PPC_SET_DEVICE_CONTROL_REGISTER(UIC1_SR, bmask);
			  break;
			case 2:
			  PPC_SET_DEVICE_CONTROL_REGISTER(UIC2_SR, bmask);
			  break;
			}

			/* Clear in the active record and gIntInhibited */
			active[index] &= ~bmask;
			gIntInhibited[index] &= ~bmask;
		};

		/* Update the hardware again so the interrupts we have handled are unmasked */
		WriteIState();
		return 0;

	} else if (excNum == ASM_DEC_VECTOR) {		/* 0x1000 remapped by C_dispatch_dec_handler_bookE  */
		bsp_irq_dispatch_list(rtems_hdl_tblP, BSP_PIT, dflt_entry.hdl);
		return 0;

	} else if (excNum == ASM_BOOKE_FIT_VECTOR) {		/* 0x1010 mapped to 0x13 by ppc_get_vector_addr */
		bsp_irq_dispatch_list(rtems_hdl_tblP, BSP_FIT, dflt_entry.hdl);
		return 0;

	} else if (excNum == ASM_BOOKE_WDOG_VECTOR) {		/* 0x1020 mapped to 0x14 by ppc_get_vector_addr */
		bsp_irq_dispatch_list(rtems_hdl_tblP, BSP_WDOG, dflt_entry.hdl);
		return 0;

	} else
		return -1; /* unhandled interrupt, panic time */
}