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