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