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 67 iop_read1(IOPHW *ioph, u_long iopbase) 68 { 69 IOP_LOADADDR(ioph, iopbase); 70 return ioph->data; 71 } 72 73 static inline void 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 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 91 default_listener(IOP *iop, struct iop_msg *msg) 92 { 93 printf("unsolicited message on channel %d.\n", msg->channel); 94 } 95 96 void 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 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 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 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 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 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 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 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 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 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 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 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