ppbus_msq.c revision 1.7
1/* $NetBSD: ppbus_msq.c,v 1.7 2005/12/11 12:23:28 christos Exp $ */
2
3/*-
4 * Copyright (c) 1998, 1999 Nicolas Souchu
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 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 * FreeBSD: src/sys/dev/ppbus/ppb_msq.c,v 1.9.2.1 2000/05/24 00:20:57 n_hibma Exp
29 *
30 */
31
32#include <sys/cdefs.h>
33__KERNEL_RCSID(0, "$NetBSD: ppbus_msq.c,v 1.7 2005/12/11 12:23:28 christos Exp $");
34
35#include <machine/stdarg.h>
36
37#include <sys/param.h>
38#include <sys/systm.h>
39
40#include <dev/ppbus/ppbus_conf.h>
41#include <dev/ppbus/ppbus_base.h>
42#include <dev/ppbus/ppbus_device.h>
43#include <dev/ppbus/ppbus_msq.h>
44#include <dev/ppbus/ppbus_var.h>
45
46/*
47#include "ppbus_if.h"
48*/
49
50/*
51 * msq index (see PPBUS_MAX_XFER)
52 * These are device modes
53 */
54#define COMPAT_MSQ	0x0
55#define NIBBLE_MSQ	0x1
56#define PS2_MSQ		0x2
57#define EPP17_MSQ	0x3
58#define EPP19_MSQ	0x4
59#define ECP_MSQ		0x5
60
61/* Function prototypes */
62static struct ppbus_xfer * mode2xfer(struct ppbus_softc *,
63	struct ppbus_device_softc *, int);
64
65/*
66 * Device mode to submsq conversion
67 */
68static struct ppbus_xfer *
69mode2xfer(struct ppbus_softc * bus, struct ppbus_device_softc * ppbdev,
70	int opcode)
71{
72	int index;
73	unsigned int epp;
74	struct ppbus_xfer * table;
75
76	switch (opcode) {
77	case MS_OP_GET:
78		table = ppbdev->get_xfer;
79		break;
80
81	case MS_OP_PUT:
82		table = ppbdev->put_xfer;
83		break;
84
85	default:
86		panic("%s: unknown opcode (%d)", __func__, opcode);
87	}
88
89	/* retrieve the device operating mode */
90	switch (bus->sc_mode) {
91	case PPBUS_COMPATIBLE:
92		index = COMPAT_MSQ;
93		break;
94	case PPBUS_NIBBLE:
95		index = NIBBLE_MSQ;
96		break;
97	case PPBUS_PS2:
98		index = PS2_MSQ;
99		break;
100	case PPBUS_EPP:
101		ppbus_read_ivar(&(bus->sc_dev), PPBUS_IVAR_EPP_PROTO, &epp);
102		switch (epp) {
103		case PPBUS_EPP_1_7:
104			index = EPP17_MSQ;
105			break;
106		case PPBUS_EPP_1_9:
107			index = EPP19_MSQ;
108			break;
109		default:
110			panic("%s: unknown EPP protocol [%u]!", __func__, epp);
111		}
112		break;
113	case PPBUS_ECP:
114		index = ECP_MSQ;
115		break;
116	default:
117		panic("%s: unknown mode (%d)", __func__, ppbdev->mode);
118	}
119
120	return (&table[index]);
121}
122
123/*
124 * ppbus_MS_init()
125 *
126 * Initialize device dependent submicrosequence of the current mode
127 *
128 */
129int
130ppbus_MS_init(struct device * dev, struct device * ppbdev,
131	struct ppbus_microseq * loop, int opcode)
132{
133	struct ppbus_softc * bus = (struct ppbus_softc *) dev;
134	struct ppbus_xfer *xfer = mode2xfer(bus, (struct ppbus_device_softc *)
135		ppbdev, opcode);
136
137	xfer->loop = loop;
138
139	return 0;
140}
141
142/*
143 * ppbus_MS_exec()
144 *
145 * Execute any microsequence opcode - expensive
146 *
147 */
148int
149ppbus_MS_exec(struct device * ppb, struct device * dev,
150	int opcode, union ppbus_insarg param1, union ppbus_insarg param2,
151	union ppbus_insarg param3, int * ret)
152{
153	struct ppbus_microseq msq[] = {
154		{ MS_UNKNOWN, { { MS_UNKNOWN }, { MS_UNKNOWN },
155			{ MS_UNKNOWN } } },
156		MS_RET(0)
157	};
158
159	/* initialize the corresponding microseq */
160	msq[0].opcode = opcode;
161	msq[0].arg[0] = param1;
162	msq[0].arg[1] = param2;
163	msq[0].arg[2] = param3;
164
165	/* execute the microseq */
166	return (ppbus_MS_microseq(ppb, dev, msq, ret));
167}
168
169/*
170 * ppbus_MS_loop()
171 *
172 * Execute a microseq loop
173 *
174 */
175int
176ppbus_MS_loop(struct device * ppb, struct device * dev,
177	struct ppbus_microseq * prolog, struct ppbus_microseq * body,
178	struct ppbus_microseq * epilog, int iter, int * ret)
179{
180	struct ppbus_microseq loop_microseq[] = {
181		MS_CALL(0),			/* execute prolog */
182		MS_SET(MS_UNKNOWN),		/* set size of transfer */
183
184		/* loop: */
185		MS_CALL(0),			/* execute body */
186		MS_DBRA(-1 /* loop: */),
187
188		MS_CALL(0),			/* execute epilog */
189		MS_RET(0)
190	};
191
192	/* initialize the structure */
193	loop_microseq[0].arg[0].p = (void *)prolog;
194	loop_microseq[1].arg[0].i = iter;
195	loop_microseq[2].arg[0].p = (void *)body;
196	loop_microseq[4].arg[0].p = (void *)epilog;
197
198	/* execute the loop */
199	return (ppbus_MS_microseq(ppb, dev, loop_microseq, ret));
200}
201
202/*
203 * ppbus_MS_init_msq()
204 *
205 * Initialize a microsequence - see macros in ppbus_msq.h
206 * KNF does not work here, since using '...' requires you use the
207 * standard C way of function definotion.
208 *
209 */
210int
211ppbus_MS_init_msq(struct ppbus_microseq * msq, int nbparam, ...)
212{
213	int i;
214	int param, ins, arg, type;
215	va_list p_list;
216
217	va_start(p_list, nbparam);
218
219	for(i = 0; i < nbparam; i++) {
220		/* retrieve the parameter descriptor */
221		param = va_arg(p_list, int);
222
223		ins  = MS_INS(param);
224		arg  = MS_ARG(param);
225		type = MS_TYP(param);
226
227		/* check the instruction position */
228		if (arg >= PPBUS_MS_MAXARGS)
229			panic("%s: parameter out of range (0x%x)!", __func__,
230				param);
231
232#if 0
233		printf("%s: param = %d, ins = %d, arg = %d, type = %d\n",
234			__func__, param, ins, arg, type);
235
236#endif
237
238		/* properly cast the parameter */
239		switch (type) {
240		case MS_TYP_INT:
241			msq[ins].arg[arg].i = va_arg(p_list, int);
242			break;
243
244		case MS_TYP_CHA:
245			/* XXX was:
246			msq[ins].arg[arg].i = (int)va_arg(p_list, char);
247			  which gives warning with gcc 3.3
248			*/
249			msq[ins].arg[arg].i = (int)va_arg(p_list, int);
250			break;
251
252		case MS_TYP_PTR:
253			msq[ins].arg[arg].p = va_arg(p_list, void *);
254			break;
255
256		case MS_TYP_FUN:
257			msq[ins].arg[arg].f = va_arg(p_list, void *);
258			break;
259
260		default:
261			panic("%s: unknown parameter (0x%x)!", __func__, param);
262		}
263	}
264
265	return (0);
266}
267
268/*
269 * ppbus_MS_microseq()
270 *
271 * Interprete a microsequence. Some microinstructions are executed at adapter
272 * level to avoid function call overhead between ppbus and the adapter
273 */
274int
275ppbus_MS_microseq(struct device * dev, struct device * busdev,
276	struct ppbus_microseq * msq, int * ret)
277{
278	struct ppbus_device_softc * ppbdev = (struct ppbus_device_softc *)
279		busdev;
280	struct ppbus_softc * bus = (struct ppbus_softc *) dev;
281	struct ppbus_microseq * mi;		/* current microinstruction */
282	size_t cnt;
283	int error;
284
285	struct ppbus_xfer * xfer;
286
287	/* microsequence executed to initialize the transfer */
288	struct ppbus_microseq initxfer[] = {
289		MS_PTR(MS_UNKNOWN), 	/* set ptr to buffer */
290		MS_SET(MS_UNKNOWN),	/* set transfer size */
291		MS_RET(0)
292	};
293
294	if(bus->ppbus_owner != busdev) {
295		return (EACCES);
296	}
297
298#define INCR_PC (mi ++)
299
300	mi = msq;
301again:
302	for (;;) {
303		switch (mi->opcode) {
304		case MS_OP_PUT:
305		case MS_OP_GET:
306
307			/* attempt to choose the best mode for the device */
308			xfer = mode2xfer(bus, ppbdev, mi->opcode);
309
310			/* figure out if we should use ieee1284 code */
311			if (!xfer->loop) {
312				if (mi->opcode == MS_OP_PUT) {
313					if ((error = ppbus_write(
314						&(bus->sc_dev),
315						(char *)mi->arg[0].p,
316						mi->arg[1].i, 0, &cnt))) {
317						goto error;
318					}
319
320					INCR_PC;
321					goto again;
322				}
323				else {
324					panic("%s: IEEE1284 read not supported",
325						__func__);
326				}
327			}
328
329			/* XXX should use ppbus_MS_init_msq() */
330			initxfer[0].arg[0].p = mi->arg[0].p;
331			initxfer[1].arg[0].i = mi->arg[1].i;
332
333			/* initialize transfer */
334			ppbus_MS_microseq(dev, busdev, initxfer, &error);
335
336			if (error)
337				goto error;
338
339			/* the xfer microsequence should not contain any
340			 * MS_OP_PUT or MS_OP_GET!
341			 */
342			ppbus_MS_microseq(dev, busdev, xfer->loop, &error);
343
344			if (error)
345				goto error;
346
347			INCR_PC;
348			break;
349
350                case MS_OP_RET:
351			if (ret)
352				*ret = mi->arg[0].i;	/* return code */
353			return (0);
354                        break;
355
356		default:
357			/* executing microinstructions at ppc level is
358			 * faster. This is the default if the microinstr
359			 * is unknown here
360			 */
361			if((error =
362				bus->ppbus_exec_microseq(
363				(struct device *)bus, &mi))) {
364
365				goto error;
366			}
367			break;
368		}
369	}
370error:
371	return (error);
372}
373
374