xref: /netbsd/sys/arch/mac68k/mac68k/iop.c (revision 59d979c5)
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