news5000.c revision 1.19 1 /* $NetBSD: news5000.c,v 1.19 2011/03/09 13:21:36 tsutsui Exp $ */
2
3 /*-
4 * Copyright (C) 1999 SHIMIZU Ryo. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: news5000.c,v 1.19 2011/03/09 13:21:36 tsutsui Exp $");
31
32 #define __INTR_PRIVATE
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/kernel.h>
36 #include <sys/timetc.h>
37 #include <sys/cpu.h>
38 #include <sys/intr.h>
39
40 #include <machine/adrsmap.h>
41
42 #include <newsmips/apbus/apbusvar.h>
43 #include <newsmips/newsmips/machid.h>
44
45 static void news5000_level1_intr(void);
46 static void news5000_level0_intr(void);
47
48 static void news5000_enable_intr(void);
49 static void news5000_disable_intr(void);
50 static void news5000_enable_timer(void);
51 static void news5000_readidrom(uint8_t *);
52 static void news5000_tc_init(void);
53 static uint32_t news5000_getfreerun(struct timecounter *);
54
55 /*
56 * This is a mask of bits to clear in the SR when we go to a
57 * given interrupt priority level.
58 */
59 static const struct ipl_sr_map news5000_ipl_sr_map = {
60 .sr_bits = {
61 [IPL_NONE] = 0,
62 [IPL_SOFTCLOCK] = MIPS_SOFT_INT_MASK_0,
63 [IPL_SOFTNET] = MIPS_SOFT_INT_MASK,
64 [IPL_VM] = MIPS_SOFT_INT_MASK
65 | MIPS_INT_MASK_0
66 | MIPS_INT_MASK_1,
67 [IPL_SCHED] = MIPS_SOFT_INT_MASK
68 | MIPS_INT_MASK_0
69 | MIPS_INT_MASK_1
70 | MIPS_INT_MASK_2,
71 [IPL_DDB] = MIPS_INT_MASK,
72 [IPL_HIGH] = MIPS_INT_MASK,
73 },
74 };
75
76 /*
77 * Handle news5000 interrupts.
78 */
79 void
80 news5000_intr(int ppl, vaddr_t pc, uint32_t status)
81 {
82 uint32_t ipending;
83 int ipl;
84
85 while (ppl < (ipl = splintr(&ipending))) {
86
87 if (ipending & MIPS_INT_MASK_2) {
88 #ifdef DEBUG
89 static int l2cnt = 0;
90 #endif
91 uint32_t int2stat;
92 struct clockframe cf;
93
94 int2stat = *(volatile uint32_t *)NEWS5000_INTST2;
95
96 #ifdef DEBUG
97 l2cnt++;
98 if (l2cnt == 50) {
99 *(volatile uint32_t *)NEWS5000_LED_SEC = 1;
100 }
101 if (l2cnt == 100) {
102 *(volatile uint32_t *)NEWS5000_LED_SEC = 0;
103 l2cnt = 0;
104 }
105 #endif
106
107 if (int2stat & NEWS5000_INT2_TIMER0) {
108 *(volatile uint32_t *)NEWS5000_TIMER0 = 1;
109
110 cf.pc = pc;
111 cf.sr = status;
112
113 hardclock(&cf);
114 intrcnt[HARDCLOCK_INTR]++;
115 }
116
117 apbus_wbflush();
118 }
119
120 if (ipending & MIPS_INT_MASK_5) {
121 uint32_t int5stat;
122
123 int5stat = *(volatile u_int *)NEWS5000_INTST5;
124 printf("level5 interrupt (%08x)\n", int5stat);
125
126 apbus_wbflush();
127 }
128
129 if (ipending & MIPS_INT_MASK_4) {
130 uint32_t int4stat;
131
132 int4stat = *(volatile uint32_t *)NEWS5000_INTST4;
133 printf("level4 interrupt (%08x)\n", int4stat);
134 if (int4stat & NEWS5000_INT4_APBUS) {
135 uint32_t stat;
136
137 stat = *(volatile uint32_t *)NEWS5000_APBUS_INTST;
138 printf("APbus error 0x%04x\n", stat & 0xffff);
139 if (stat & NEWS5000_APBUS_INT_DMAADDR) {
140 printf("DMA Address Error: "
141 "slot=%x, addr=0x%08x\n",
142 *(volatile uint32_t *)NEWS5000_APBUS_DER_S,
143 *(volatile uint32_t *)NEWS5000_APBUS_DER_A);
144 }
145 if (stat & NEWS5000_APBUS_INT_RDTIMEO)
146 printf("IO Read Timeout: addr=0x%08x\n",
147 *(volatile uint32_t *)NEWS5000_APBUS_BER_A);
148 if (stat & NEWS5000_APBUS_INT_WRTIMEO)
149 printf("IO Write Timeout: addr=0x%08x\n",
150 *(volatile uint32_t *)NEWS5000_APBUS_BER_A);
151 *(volatile uint32_t *)0xb4c00014 = stat;
152 }
153
154 apbus_wbflush();
155 }
156
157 if (ipending & MIPS_INT_MASK_3) {
158 uint32_t int3stat;
159
160 int3stat = *(volatile uint32_t *)NEWS5000_INTST3;
161 printf("level3 interrupt (%08x)\n", int3stat);
162
163 apbus_wbflush();
164 }
165
166 if (ipending & MIPS_INT_MASK_1) {
167 news5000_level1_intr();
168 apbus_wbflush();
169 }
170
171 if (ipending & MIPS_INT_MASK_0) {
172 news5000_level0_intr();
173 apbus_wbflush();
174 }
175 }
176 }
177
178
179 static void
180 news5000_level1_intr(void)
181 {
182 uint32_t int1stat;
183
184 int1stat = *(volatile uint32_t *)NEWS5000_INTST1;
185
186 if (int1stat) {
187 if (apbus_intr_dispatch(1, int1stat) == 0)
188 printf("level1_intr: no handler (mask 0x%04x)\n",
189 int1stat);
190 } else
191 printf("level1 stray interrupt?\n");
192 }
193
194 static void
195 news5000_level0_intr(void)
196 {
197 uint32_t int0stat;
198
199 int0stat = *(volatile uint32_t *)NEWS5000_INTST0;
200
201 if (int0stat) {
202 if (apbus_intr_dispatch(0, int0stat) == 0)
203 printf("level0_intr: no handler (mask 0x%04x)\n",
204 int0stat);
205 } else
206 printf("level0 stray interrupt?\n");
207 }
208
209 static void
210 news5000_enable_intr(void)
211 {
212
213 /* INT0 and INT1 has been enabled at attach */
214 /* INT2 -- It's not a time to enable timer yet. */
215 /* INT3 -- not used for NWS-5000 */
216
217 *(volatile uint32_t *)NEWS5000_INTEN4 = NEWS5000_INT4_APBUS;
218 *(volatile uint32_t *)NEWS5000_APBUS_INTMSK = 0xffff;
219
220 /* INT5 -- currently ignored */
221 *(volatile uint32_t *)NEWS5000_INTEN5 = 0;
222 }
223
224 static void
225 news5000_disable_intr(void)
226 {
227
228 *(volatile uint32_t *)NEWS5000_INTEN0 = 0;
229 *(volatile uint32_t *)NEWS5000_INTEN1 = 0;
230 *(volatile uint32_t *)NEWS5000_INTEN2 = 0;
231 *(volatile uint32_t *)NEWS5000_INTEN3 = 0;
232 *(volatile uint32_t *)NEWS5000_INTEN4 = 0;
233 *(volatile uint32_t *)NEWS5000_INTEN5 = 0;
234 }
235
236 static void
237 news5000_enable_timer(void)
238 {
239
240 news5000_tc_init();
241
242 /* enable timer interrpt */
243 *(volatile uint32_t *)NEWS5000_INTEN2 = NEWS5000_INT2_TIMER0;
244 }
245
246 static uint32_t
247 news5000_getfreerun(struct timecounter *tc)
248 {
249 return *(volatile uint32_t *)NEWS5000_FREERUN;
250 }
251
252 static void
253 news5000_tc_init(void)
254 {
255 static struct timecounter tc = {
256 .tc_get_timecount = news5000_getfreerun,
257 .tc_frequency = 1000000,
258 .tc_counter_mask = ~0,
259 .tc_name = "news5000_freerun",
260 .tc_quality = 100,
261 };
262
263 tc_init(&tc);
264 }
265
266
267 static void
268 news5000_readidrom(uint8_t *rom)
269 {
270 uint32_t *p = (void *)NEWS5000_IDROM;
271 int i;
272
273 for (i = 0; i < sizeof(struct idrom); i++, p += 2)
274 *rom++ = ((*p & 0x0f) << 4) + (*(p + 1) & 0x0f);
275 }
276
277 extern struct idrom idrom;
278
279 void
280 news5000_init(void)
281 {
282
283 ipl_sr_map = news5000_ipl_sr_map;
284
285 enable_intr = news5000_enable_intr;
286 disable_intr = news5000_disable_intr;
287 enable_timer = news5000_enable_timer;
288
289 news5000_readidrom((uint8_t *)&idrom);
290 hostid = idrom.id_serial;
291
292 /* XXX reset uPD72067 FDC to avoid spurious interrupts */
293 #define NEWS5000_FDC_FDOUT 0xbed20000
294 #define FDO_FRST 0x04
295 *(volatile uint8_t *)NEWS5000_FDC_FDOUT = FDO_FRST;
296 }
297