news5000.c revision 1.17 1 /* $NetBSD: news5000.c,v 1.17 2007/12/03 15:34:05 ad 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.17 2007/12/03 15:34:05 ad Exp $");
31
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/kernel.h>
35 #include <sys/timetc.h>
36 #include <sys/cpu.h>
37
38 #include <machine/adrsmap.h>
39 #include <machine/intr.h>
40
41 #include <newsmips/apbus/apbusvar.h>
42 #include <newsmips/newsmips/machid.h>
43
44 static void news5000_level1_intr(void);
45 static void news5000_level0_intr(void);
46
47 static void news5000_enable_intr(void);
48 static void news5000_disable_intr(void);
49 static void news5000_enable_timer(void);
50 static void news5000_readidrom(uint8_t *);
51 static void news5000_tc_init(void);
52 static uint32_t news5000_getfreerun(struct timecounter *);
53
54 /*
55 * Handle news5000 interrupts.
56 */
57 void
58 news5000_intr(uint32_t status, uint32_t cause, uint32_t pc, uint32_t ipending)
59 {
60 struct cpu_info *ci;
61
62 ci = curcpu();
63 ci->ci_idepth++;
64
65 if (ipending & MIPS_INT_MASK_2) {
66 #ifdef DEBUG
67 static int l2cnt = 0;
68 #endif
69 uint32_t int2stat;
70 struct clockframe cf;
71
72 int2stat = *(volatile uint32_t *)NEWS5000_INTST2;
73
74 #ifdef DEBUG
75 l2cnt++;
76 if (l2cnt == 50) {
77 *(volatile uint32_t *)NEWS5000_LED_SEC = 1;
78 }
79 if (l2cnt == 100) {
80 *(volatile uint32_t *)NEWS5000_LED_SEC = 0;
81 l2cnt = 0;
82 }
83 #endif
84
85 if (int2stat & NEWS5000_INT2_TIMER0) {
86 *(volatile uint32_t *)NEWS5000_TIMER0 = 1;
87
88 cf.pc = pc;
89 cf.sr = status;
90
91 hardclock(&cf);
92 intrcnt[HARDCLOCK_INTR]++;
93 }
94
95 apbus_wbflush();
96 cause &= ~MIPS_INT_MASK_2;
97 }
98 /* If clock interrupts were enabled, re-enable them ASAP. */
99 _splset(MIPS_SR_INT_IE | (status & MIPS_INT_MASK_2));
100
101 if (ipending & MIPS_INT_MASK_5) {
102 uint32_t int5stat;
103
104 int5stat = *(volatile u_int *)NEWS5000_INTST5;
105 printf("level5 interrupt (%08x)\n", int5stat);
106
107 apbus_wbflush();
108 cause &= ~MIPS_INT_MASK_5;
109 }
110
111 if (ipending & MIPS_INT_MASK_4) {
112 uint32_t int4stat;
113
114 int4stat = *(volatile uint32_t *)NEWS5000_INTST4;
115 printf("level4 interrupt (%08x)\n", int4stat);
116 if (int4stat & NEWS5000_INT4_APBUS) {
117 uint32_t stat;
118
119 stat = *(volatile uint32_t *)NEWS5000_APBUS_INTST;
120 printf("APbus error 0x%04x\n", stat & 0xffff);
121 if (stat & NEWS5000_APBUS_INT_DMAADDR) {
122 printf("DMA Address Error: "
123 "slot=%x, addr=0x%08x\n",
124 *(volatile uint32_t *)NEWS5000_APBUS_DER_S,
125 *(volatile uint32_t *)NEWS5000_APBUS_DER_A);
126 }
127 if (stat & NEWS5000_APBUS_INT_RDTIMEO)
128 printf("IO Read Timeout: addr=0x%08x\n",
129 *(volatile uint32_t *)NEWS5000_APBUS_BER_A);
130 if (stat & NEWS5000_APBUS_INT_WRTIMEO)
131 printf("IO Write Timeout: addr=0x%08x\n",
132 *(volatile uint32_t *)NEWS5000_APBUS_BER_A);
133 *(volatile uint32_t *)0xb4c00014 = stat;
134 }
135
136 apbus_wbflush();
137 cause &= ~MIPS_INT_MASK_4;
138 }
139
140 if (ipending & MIPS_INT_MASK_3) {
141 uint32_t int3stat;
142
143 int3stat = *(volatile uint32_t *)NEWS5000_INTST3;
144 printf("level3 interrupt (%08x)\n", int3stat);
145
146 apbus_wbflush();
147 cause &= ~MIPS_INT_MASK_3;
148 }
149
150 if (ipending & MIPS_INT_MASK_1) {
151 news5000_level1_intr();
152 apbus_wbflush();
153 cause &= ~MIPS_INT_MASK_1;
154 }
155
156 if (ipending & MIPS_INT_MASK_0) {
157 news5000_level0_intr();
158 apbus_wbflush();
159 cause &= ~MIPS_INT_MASK_0;
160 }
161
162 ci->ci_idepth--;
163 _splset((status & ~cause & MIPS_HARD_INT_MASK) | MIPS_SR_INT_IE);
164 }
165
166
167 static void
168 news5000_level1_intr(void)
169 {
170 uint32_t int1stat;
171
172 int1stat = *(volatile uint32_t *)NEWS5000_INTST1;
173
174 if (int1stat) {
175 if (apbus_intr_dispatch(1, int1stat) == 0)
176 printf("level1_intr: no handler (mask 0x%04x)\n",
177 int1stat);
178 } else
179 printf("level1 stray interrupt?\n");
180 }
181
182 static void
183 news5000_level0_intr(void)
184 {
185 uint32_t int0stat;
186
187 int0stat = *(volatile uint32_t *)NEWS5000_INTST0;
188
189 if (int0stat) {
190 if (apbus_intr_dispatch(0, int0stat) == 0)
191 printf("level0_intr: no handler (mask 0x%04x)\n",
192 int0stat);
193 } else
194 printf("level0 stray interrupt?\n");
195 }
196
197 static void
198 news5000_enable_intr(void)
199 {
200
201 /* INT0 and INT1 has been enabled at attach */
202 /* INT2 -- It's not a time to enable timer yet. */
203 /* INT3 -- not used for NWS-5000 */
204
205 *(volatile uint32_t *)NEWS5000_INTEN4 = NEWS5000_INT4_APBUS;
206 *(volatile uint32_t *)NEWS5000_APBUS_INTMSK = 0xffff;
207
208 /* INT5 -- currently ignored */
209 *(volatile uint32_t *)NEWS5000_INTEN5 = 0;
210 }
211
212 static void
213 news5000_disable_intr(void)
214 {
215
216 *(volatile uint32_t *)NEWS5000_INTEN0 = 0;
217 *(volatile uint32_t *)NEWS5000_INTEN1 = 0;
218 *(volatile uint32_t *)NEWS5000_INTEN2 = 0;
219 *(volatile uint32_t *)NEWS5000_INTEN3 = 0;
220 *(volatile uint32_t *)NEWS5000_INTEN4 = 0;
221 *(volatile uint32_t *)NEWS5000_INTEN5 = 0;
222 }
223
224 static void
225 news5000_enable_timer(void)
226 {
227
228 news5000_tc_init();
229
230 /* enable timer interrpt */
231 *(volatile uint32_t *)NEWS5000_INTEN2 = NEWS5000_INT2_TIMER0;
232 }
233
234 static uint32_t
235 news5000_getfreerun(struct timecounter *tc)
236 {
237 return *(volatile uint32_t *)NEWS5000_FREERUN;
238 }
239
240 static void
241 news5000_tc_init(void)
242 {
243 static struct timecounter tc = {
244 .tc_get_timecount = news5000_getfreerun,
245 .tc_frequency = 1000000,
246 .tc_counter_mask = ~0,
247 .tc_name = "news5000_freerun",
248 .tc_quality = 100,
249 };
250
251 tc_init(&tc);
252 }
253
254
255 static void
256 news5000_readidrom(uint8_t *rom)
257 {
258 uint32_t *p = (void *)NEWS5000_IDROM;
259 int i;
260
261 for (i = 0; i < sizeof(struct idrom); i++, p += 2)
262 *rom++ = ((*p & 0x0f) << 4) + (*(p + 1) & 0x0f);
263 }
264
265 extern struct idrom idrom;
266
267 void
268 news5000_init(void)
269 {
270
271 enable_intr = news5000_enable_intr;
272 disable_intr = news5000_disable_intr;
273 enable_timer = news5000_enable_timer;
274
275 news5000_readidrom((uint8_t *)&idrom);
276 hostid = idrom.id_serial;
277
278 /* XXX reset uPD72067 FDC to avoid spurious interrupts */
279 #define NEWS5000_FDC_FDOUT 0xbed20000
280 #define FDO_FRST 0x04
281 *(volatile uint8_t *)NEWS5000_FDC_FDOUT = FDO_FRST;
282 }
283