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 51static IOP mac68k_iops[2]; 52 53static void iopism_hand(void *); 54static void load_msg_to_iop(IOPHW *, struct iop_msg *); 55static void iop_message_sent(IOP *, int); 56static void receive_iop_message(IOP *, int); 57static void default_listener(IOP *, struct iop_msg *); 58 59static inline int iop_alive(IOPHW *); 60static inline int iop_read1(IOPHW *, u_long); 61static inline void iop_write1(IOPHW *, u_long, u_char); 62static inline void _iop_upload(IOPHW *, u_char *, u_long, u_long); 63static inline void _iop_download(IOPHW *, u_char *, u_long, u_long); 64 65static inline int 66iop_read1(IOPHW *ioph, u_long iopbase) 67{ 68 IOP_LOADADDR(ioph, iopbase); 69 return ioph->data; 70} 71 72static inline void 73iop_write1(IOPHW *ioph, u_long iopbase, u_char data) 74{ 75 IOP_LOADADDR(ioph, iopbase); 76 ioph->data = data; 77} 78 79static inline int 80iop_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 89static void 90default_listener(IOP *iop, struct iop_msg *msg) 91{ 92 printf("unsolicited message on channel %d.\n", msg->channel); 93} 94 95void 96iop_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 201static inline void 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 210void 211iop_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 222static inline void 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 231void 232iop_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 243static void 244iopism_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 256printf("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 294static void 295load_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 308static void 309iop_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 331static void 332receive_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 367int 368iop_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; 382printf("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; 395printf("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); 397printf("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++) { 401printf(" cs: 0x%x V1-%x- ", (unsigned) iop->iop->control_status, (unsigned)via_reg(VIA1, vIFR)); 402delay(1000); 403}} 404 splx(s); 405 406 return 0; 407} 408 409int 410iop_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 438int 439iop_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