1/*	$NetBSD: iop.c,v 1.10 2005/12/24 23:24:00 perry 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.10 2005/12/24 23:24:00 perry 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
52static IOP	mac68k_iops[2];
53
54static void	iopism_hand(void *);
55static void	load_msg_to_iop(IOPHW *, struct iop_msg *);
56static void	iop_message_sent(IOP *, int);
57static void	receive_iop_message(IOP *, int);
58static void	default_listener(IOP *, struct iop_msg *);
59
60static inline int iop_alive(IOPHW *);
61static inline int iop_read1(IOPHW *, u_long);
62static inline void iop_write1(IOPHW *, u_long, u_char);
63static inline void _iop_upload(IOPHW *, u_char *, u_long, u_long);
64static inline void _iop_download(IOPHW *, u_char *, u_long, u_long);
65
66static inline int
67iop_read1(IOPHW *ioph, u_long iopbase)
68{
69	IOP_LOADADDR(ioph, iopbase);
70	return ioph->data;
71}
72
73static inline void
74iop_write1(IOPHW *ioph, u_long iopbase, u_char data)
75{
76	IOP_LOADADDR(ioph, iopbase);
77	ioph->data = data;
78}
79
80static inline int
81iop_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
90static void
91default_listener(IOP *iop, struct iop_msg *msg)
92{
93	printf("unsolicited message on channel %d.\n", msg->channel);
94}
95
96void
97iop_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
202static 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
211void
212iop_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
223static 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
232void
233iop_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
244static void
245iopism_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
257printf("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
295static void
296load_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
309static void
310iop_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
332static void
333receive_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
368int
369iop_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;
383printf("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;
396printf("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);
398printf("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++) {
402printf(" cs: 0x%x V1-%x- ", (unsigned) iop->iop->control_status, (unsigned)via_reg(VIA1, vIFR));
403delay(1000);
404}}
405	splx(s);
406
407	return 0;
408}
409
410int
411iop_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
439int
440iop_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