1 /* $NetBSD: iop.c,v 1.11 2007/03/12 18:18:25 ad Exp $ */
2
3 /*
4 * Copyright (c) 2000 Allen Briggs.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 /*
31 * This code handles VIA, RBV, and OSS functionality.
32 */
33
34 #include <sys/cdefs.h>
35 __KERNEL_RCSID(0, "$NetBSD: iop.c,v 1.11 2007/03/12 18:18:25 ad Exp $");
36
37 #include "opt_mac68k.h"
38
39 #include <sys/param.h>
40 #include <sys/kernel.h>
41 #include <sys/malloc.h>
42 #include <sys/pool.h>
43 #include <sys/queue.h>
44 #include <sys/systm.h>
45
46 #include <machine/cpu.h>
47 #include <machine/frame.h>
48
49 #include <machine/iopreg.h>
50 #include <machine/viareg.h>
51
52 static IOP mac68k_iops[2];
53
54 static void iopism_hand(void *);
55 static void load_msg_to_iop(IOPHW *, struct iop_msg *);
56 static void iop_message_sent(IOP *, int);
57 static void receive_iop_message(IOP *, int);
58 static void default_listener(IOP *, struct iop_msg *);
59
60 static inline int iop_alive(IOPHW *);
61 static inline int iop_read1(IOPHW *, u_long);
62 static inline void iop_write1(IOPHW *, u_long, u_char);
63 static inline void _iop_upload(IOPHW *, u_char *, u_long, u_long);
64 static inline void _iop_download(IOPHW *, u_char *, u_long, u_long);
65
66 static inline int
iop_read1(IOPHW * ioph,u_long iopbase)67 iop_read1(IOPHW *ioph, u_long iopbase)
68 {
69 IOP_LOADADDR(ioph, iopbase);
70 return ioph->data;
71 }
72
73 static inline void
iop_write1(IOPHW * ioph,u_long iopbase,u_char data)74 iop_write1(IOPHW *ioph, u_long iopbase, u_char data)
75 {
76 IOP_LOADADDR(ioph, iopbase);
77 ioph->data = data;
78 }
79
80 static inline int
iop_alive(IOPHW * ioph)81 iop_alive(IOPHW *ioph)
82 {
83 int alive;
84
85 alive = iop_read1(ioph, IOP_ADDR_ALIVE);
86 iop_write1(ioph, IOP_ADDR_ALIVE, 0);
87 return alive;
88 }
89
90 static void
default_listener(IOP * iop,struct iop_msg * msg)91 default_listener(IOP *iop, struct iop_msg *msg)
92 {
93 printf("unsolicited message on channel %d.\n", msg->channel);
94 }
95
96 void
iop_init(int fullinit)97 iop_init(int fullinit)
98 {
99 IOPHW *ioph;
100 IOP *iop;
101 int i, ii;
102
103 switch (current_mac_model->machineid) {
104 default:
105 return;
106 case MACH_MACQ900:
107 case MACH_MACQ950:
108 mac68k_iops[SCC_IOP].iop = (IOPHW *)
109 ((u_char *)IOBase + 0xc000);
110 mac68k_iops[ISM_IOP].iop = (IOPHW *)
111 ((u_char *)IOBase + 0x1e000);
112 break;
113 case MACH_MACIIFX:
114 mac68k_iops[SCC_IOP].iop = (IOPHW *)
115 ((u_char *)IOBase + 0x4000);
116 mac68k_iops[ISM_IOP].iop = (IOPHW *)
117 ((u_char *)IOBase + 0x12000);
118 break;
119 }
120
121 if (!fullinit) {
122 ioph = mac68k_iops[SCC_IOP].iop;
123 ioph->control_status = 0; /* Reset */
124 ioph->control_status = IOP_BYPASS; /* Set to bypass */
125
126 ioph = mac68k_iops[ISM_IOP].iop;
127 ioph->control_status = 0; /* Reset */
128
129 return;
130 }
131
132 for (ii = 0 ; ii < 2 ; ii++) {
133 iop = &mac68k_iops[ii];
134 ioph = iop->iop;
135 for (i = 0; i < IOP_MAXCHAN; i++) {
136 SIMPLEQ_INIT(&iop->sendq[i]);
137 SIMPLEQ_INIT(&iop->recvq[i]);
138 iop->listeners[i] = default_listener;
139 iop->listener_data[i] = NULL;
140 }
141 /* IOP_LOADADDR(ioph, 0x200);
142 for (i = 0x200; i > 0; i--) {
143 ioph->data = 0;
144 }*/
145 }
146
147 switch (current_mac_model->machineid) {
148 default:
149 return;
150 case MACH_MACQ900:
151 case MACH_MACQ950:
152 #ifdef notyet_maybe_not_ever
153 iop = &mac68k_iops[SCC_IOP];
154 intr_establish(iopscc_hand, iop, 4);
155 #endif
156 iop = &mac68k_iops[ISM_IOP];
157 via2_register_irq(0, iopism_hand, iop);
158 via_reg(VIA2, vIER) = 0x81;
159 via_reg(VIA2, vIFR) = 0x01;
160 break;
161 case MACH_MACIIFX:
162 /* oss_register_irq(2, iopism_hand, &ioph); */
163 break;
164 }
165
166 iop = &mac68k_iops[SCC_IOP];
167 ioph = iop->iop;
168 printf("SCC IOP base: 0x%x\n", (unsigned) ioph);
169 pool_init(&iop->pool, sizeof(struct iop_msg), 0, 0, 0, "mac68k_iop1",
170 NULL, IPL_NONE);
171 ioph->control_status = IOP_BYPASS;
172
173 iop = &mac68k_iops[ISM_IOP];
174 ioph = iop->iop;
175 printf("ISM IOP base: 0x%x, alive %x\n", (unsigned) ioph,
176 (unsigned) iop_alive(ioph));
177 pool_init(&iop->pool, sizeof(struct iop_msg), 0, 0, 0, "mac68k_iop2",
178 NULL, IPL_NONE);
179 iop_write1(ioph, IOP_ADDR_ALIVE, 0);
180
181 /*
182 * XXX The problem here seems to be that the IOP wants to go back into
183 * BYPASS mode. The state should be 0x86 after we're done with it
184 * here. It switches to 0x7 almost immediately.
185 * This means one of a couple of things to me--
186 * 1. We're doing something wrong
187 * 2. MacOS is really shutting down the IOP
188 * Most likely, it's the first.
189 */
190 printf("OLD cs0: 0x%x\n", (unsigned) ioph->control_status);
191
192 ioph->control_status = IOP_CS_RUN | IOP_CS_AUTOINC;
193 {unsigned cs, c2;
194 cs = (unsigned) ioph->control_status;
195 printf("OLD cs1: 0x%x\n", cs);
196 cs = 0;
197 do { c2 = iop_read1(ioph, IOP_ADDR_ALIVE); cs++; } while (c2 != 0xff);
198 printf("OLD cs2: 0x%x (i = %d)\n", (unsigned) ioph->control_status, cs);
199 }
200 }
201
202 static inline void
_iop_upload(IOPHW * ioph,u_char * mem,u_long nb,u_long iopbase)203 _iop_upload(IOPHW *ioph, u_char *mem, u_long nb, u_long iopbase)
204 {
205 IOP_LOADADDR(ioph, iopbase);
206 while (nb--) {
207 ioph->data = *mem++;
208 }
209 }
210
211 void
iop_upload(int iopn,u_char * mem,u_long nb,u_long iopbase)212 iop_upload(int iopn, u_char *mem, u_long nb, u_long iopbase)
213 {
214 IOPHW *ioph;
215
216 if (iopn & ~1) return;
217 ioph = mac68k_iops[iopn].iop;
218 if (!ioph) return;
219
220 _iop_upload(ioph, mem, nb, iopbase);
221 }
222
223 static inline void
_iop_download(IOPHW * ioph,u_char * mem,u_long nb,u_long iopbase)224 _iop_download(IOPHW *ioph, u_char *mem, u_long nb, u_long iopbase)
225 {
226 IOP_LOADADDR(ioph, iopbase);
227 while (nb--) {
228 *mem++ = ioph->data;
229 }
230 }
231
232 void
iop_download(int iopn,u_char * mem,u_long nb,u_long iopbase)233 iop_download(int iopn, u_char *mem, u_long nb, u_long iopbase)
234 {
235 IOPHW *ioph;
236
237 if (iopn & ~1) return;
238 ioph = mac68k_iops[iopn].iop;
239 if (!ioph) return;
240
241 _iop_download(ioph, mem, nb, iopbase);
242 }
243
244 static void
iopism_hand(void * arg)245 iopism_hand(void *arg)
246 {
247 IOP *iop;
248 IOPHW *ioph;
249 u_char cs;
250 u_char m, s;
251 int i;
252
253 iop = (IOP *) arg;
254 ioph = iop->iop;
255 cs = ioph->control_status;
256
257 printf("iopism_hand.\n");
258
259 #if DIAGNOSTIC
260 if ((cs & IOP_INTERRUPT) == 0) {
261 printf("IOP_ISM interrupt--no interrupt!? (cs 0x%x)\n",
262 (u_int) cs);
263 }
264 #endif
265
266 /*
267 * Scan send queues for complete messages.
268 */
269 if (cs & IOP_CS_INT0) {
270 ioph->control_status |= IOP_CS_INT0;
271 m = iop_read1(ioph, IOP_ADDR_MAX_SEND_CHAN);
272 for (i = 0; i < m; i++) {
273 s = iop_read1(ioph, IOP_ADDR_SEND_STATE + i);
274 if (s == IOP_MSG_COMPLETE) {
275 iop_message_sent(iop, i);
276 }
277 }
278 }
279
280 /*
281 * Scan receive queue for new messages.
282 */
283 if (cs & IOP_CS_INT1) {
284 ioph->control_status |= IOP_CS_INT1;
285 m = iop_read1(ioph, IOP_ADDR_MAX_RECV_CHAN);
286 for (i = 0; i < m; i++) {
287 s = iop_read1(ioph, IOP_ADDR_RECV_STATE + i);
288 if (s == IOP_MSG_NEW) {
289 receive_iop_message(iop, i);
290 }
291 }
292 }
293 }
294
295 static void
load_msg_to_iop(IOPHW * ioph,struct iop_msg * msg)296 load_msg_to_iop(IOPHW *ioph, struct iop_msg *msg)
297 {
298 int offset;
299
300 msg->status = IOP_MSGSTAT_SENDING;
301 offset = IOP_ADDR_SEND_MSG + msg->channel * IOP_MSGLEN;
302 _iop_upload(ioph, msg->msg, IOP_MSGLEN, offset);
303 iop_write1(ioph, IOP_ADDR_SEND_STATE + msg->channel, IOP_MSG_NEW);
304
305 /* ioph->control_status |= IOP_CS_IRQ; */
306 ioph->control_status = (ioph->control_status & 0xfe) | IOP_CS_IRQ;
307 }
308
309 static void
iop_message_sent(IOP * iop,int chan)310 iop_message_sent(IOP *iop, int chan)
311 {
312 IOPHW *ioph;
313 struct iop_msg *msg;
314
315 ioph = iop->iop;
316
317 msg = SIMPLEQ_FIRST(&iop->sendq[chan]);
318 msg->status = IOP_MSGSTAT_SENT;
319 SIMPLEQ_REMOVE_HEAD(&iop->sendq[chan], iopm);
320
321 msg->handler(iop, msg);
322
323 pool_put(&iop->pool, msg);
324
325 if (!(msg = SIMPLEQ_FIRST(&iop->sendq[chan]))) {
326 iop_write1(ioph, IOP_ADDR_SEND_STATE + chan, IOP_MSG_IDLE);
327 } else {
328 load_msg_to_iop(ioph, msg);
329 }
330 }
331
332 static void
receive_iop_message(IOP * iop,int chan)333 receive_iop_message(IOP *iop, int chan)
334 {
335 IOPHW *ioph;
336 struct iop_msg *msg;
337 int offset;
338
339 ioph = iop->iop;
340
341 msg = SIMPLEQ_FIRST(&iop->recvq[chan]);
342 if (msg) {
343 SIMPLEQ_REMOVE_HEAD(&iop->recvq[chan], iopm);
344 } else {
345 msg = &iop->unsolicited_msg;
346 msg->channel = chan;
347 msg->handler = iop->listeners[chan];
348 msg->user_data = iop->listener_data[chan];
349 }
350
351 offset = IOP_ADDR_RECV_MSG + chan * IOP_MSGLEN;
352 _iop_download(ioph, msg->msg, IOP_MSGLEN, offset);
353 msg->status = IOP_MSGSTAT_RECEIVED;
354
355 msg->handler(iop, msg);
356
357 if (msg != &iop->unsolicited_msg)
358 pool_put(&iop->pool, msg);
359
360 iop_write1(ioph, IOP_ADDR_RECV_STATE + chan, IOP_MSG_COMPLETE);
361 ioph->control_status |= IOP_CS_IRQ;
362
363 if ((msg = SIMPLEQ_FIRST(&iop->recvq[chan])) != NULL) {
364 msg->status = IOP_MSGSTAT_RECEIVING;
365 }
366 }
367
368 int
iop_send_msg(int iopn,int chan,u_char * mesg,int msglen,iop_msg_handler handler,void * user_data)369 iop_send_msg(int iopn, int chan, u_char *mesg, int msglen,
370 iop_msg_handler handler, void *user_data)
371 {
372 struct iop_msg *msg;
373 IOP *iop;
374 int s;
375
376 if (iopn & ~1) return -1;
377 iop = &mac68k_iops[iopn];
378 if (!iop) return -1;
379 if (msglen > IOP_MSGLEN) return -1;
380
381 msg = (struct iop_msg *) pool_get(&iop->pool, PR_WAITOK);
382 if (msg == NULL) return -1;
383 printf("have msg buffer for IOP: %#x\n", (unsigned) iop->iop);
384 msg->channel = chan;
385 if (msglen < IOP_MSGLEN) memset(msg->msg, '\0', IOP_MSGLEN);
386 memcpy(msg->msg, mesg, msglen);
387 msg->handler = handler;
388 msg->user_data = user_data;
389
390 msg->status = IOP_MSGSTAT_QUEUED;
391
392 s = splhigh();
393 SIMPLEQ_INSERT_TAIL(&iop->sendq[chan], msg, iopm);
394 if (msg == SIMPLEQ_FIRST(&iop->sendq[chan])) {
395 msg->status = IOP_MSGSTAT_SENDING;
396 printf("loading msg to iop: cs: 0x%x V1-%x- ", (unsigned) iop->iop->control_status, (unsigned)via_reg(VIA1, vIFR));
397 load_msg_to_iop(iop->iop, msg);
398 printf("msg loaded to iop: cs: 0x%x V1-%x- ", (unsigned) iop->iop->control_status, (unsigned)via_reg(VIA1, vIFR));
399 }
400
401 {int i; for (i=0;i<16;i++) {
402 printf(" cs: 0x%x V1-%x- ", (unsigned) iop->iop->control_status, (unsigned)via_reg(VIA1, vIFR));
403 delay(1000);
404 }}
405 splx(s);
406
407 return 0;
408 }
409
410 int
iop_queue_receipt(int iopn,int chan,iop_msg_handler handler,void * user_data)411 iop_queue_receipt(int iopn, int chan, iop_msg_handler handler, void *user_data)
412 {
413 struct iop_msg *msg;
414 IOP *iop;
415 int s;
416
417 if (iopn & ~1) return -1;
418 iop = &mac68k_iops[iopn];
419 if (!iop) return -1;
420
421 msg = (struct iop_msg *) pool_get(&iop->pool, PR_WAITOK);
422 if (msg == NULL) return -1;
423 msg->channel = chan;
424 msg->handler = handler;
425 msg->user_data = user_data;
426
427 msg->status = IOP_MSGSTAT_QUEUED;
428
429 s = splhigh();
430 SIMPLEQ_INSERT_TAIL(&iop->recvq[chan], msg, iopm);
431 if (msg == SIMPLEQ_FIRST(&iop->recvq[chan])) {
432 msg->status = IOP_MSGSTAT_RECEIVING;
433 }
434 splx(s);
435
436 return 0;
437 }
438
439 int
iop_register_listener(int iopn,int chan,iop_msg_handler handler,void * user_data)440 iop_register_listener(int iopn, int chan, iop_msg_handler handler,
441 void *user_data)
442 {
443 IOP *iop;
444 int s;
445
446 if (iopn & ~1) return -1;
447 iop = &mac68k_iops[iopn];
448 if (!iop) return -1;
449
450 s = splhigh();
451 iop->listeners[chan] = handler;
452 iop->listener_data[chan] = user_data;
453 splx(s);
454
455 return 0;
456 }
457