xref: /dragonfly/sys/bus/ppbus/ppb_msq.c (revision 984263bc)
1 /*-
2  * Copyright (c) 1998, 1999 Nicolas Souchu
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD: src/sys/dev/ppbus/ppb_msq.c,v 1.9.2.1 2000/05/24 00:20:57 n_hibma Exp $
27  *
28  */
29 #include <machine/stdarg.h>
30 
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/bus.h>
34 
35 #include <dev/ppbus/ppbconf.h>
36 #include <dev/ppbus/ppb_msq.h>
37 
38 #include "ppbus_if.h"
39 
40 /* msq index (see PPB_MAX_XFER)
41  * These are device modes
42  */
43 #define COMPAT_MSQ	0x0
44 #define NIBBLE_MSQ	0x1
45 #define PS2_MSQ		0x2
46 #define EPP17_MSQ	0x3
47 #define EPP19_MSQ	0x4
48 #define ECP_MSQ		0x5
49 
50 /*
51  * Device mode to submsq conversion
52  */
53 static struct ppb_xfer *
54 mode2xfer(device_t bus, struct ppb_device *ppbdev, int opcode)
55 {
56 	int index, epp;
57 	struct ppb_xfer *table;
58 
59 	switch (opcode) {
60 	case MS_OP_GET:
61 		table = ppbdev->get_xfer;
62 		break;
63 
64 	case MS_OP_PUT:
65 		table = ppbdev->put_xfer;
66 		break;
67 
68 	default:
69 		panic("%s: unknown opcode (%d)", __FUNCTION__, opcode);
70 	}
71 
72 	/* retrieve the device operating mode */
73 	switch (ppb_get_mode(bus)) {
74 	case PPB_COMPATIBLE:
75 		index = COMPAT_MSQ;
76 		break;
77 	case PPB_NIBBLE:
78 		index = NIBBLE_MSQ;
79 		break;
80 	case PPB_PS2:
81 		index = PS2_MSQ;
82 		break;
83 	case PPB_EPP:
84 		switch ((epp = ppb_get_epp_protocol(bus))) {
85 		case EPP_1_7:
86 			index = EPP17_MSQ;
87 			break;
88 		case EPP_1_9:
89 			index = EPP19_MSQ;
90 			break;
91 		default:
92 			panic("%s: unknown EPP protocol (0x%x)!", __FUNCTION__,
93 				epp);
94 		}
95 		break;
96 	case PPB_ECP:
97 		index = ECP_MSQ;
98 		break;
99 	default:
100 		panic("%s: unknown mode (%d)", __FUNCTION__, ppbdev->mode);
101 	}
102 
103 	return (&table[index]);
104 }
105 
106 /*
107  * ppb_MS_init()
108  *
109  * Initialize device dependent submicrosequence of the current mode
110  *
111  */
112 int
113 ppb_MS_init(device_t bus, device_t dev, struct ppb_microseq *loop, int opcode)
114 {
115 	struct ppb_device *ppbdev = (struct ppb_device *)device_get_ivars(dev);
116 	struct ppb_xfer *xfer = mode2xfer(bus, ppbdev, opcode);
117 
118 	xfer->loop = loop;
119 
120 	return (0);
121 }
122 
123 /*
124  * ppb_MS_exec()
125  *
126  * Execute any microsequence opcode - expensive
127  *
128  */
129 int
130 ppb_MS_exec(device_t bus, device_t dev, int opcode, union ppb_insarg param1,
131 		union ppb_insarg param2, union ppb_insarg param3, int *ret)
132 {
133 	struct ppb_microseq msq[] = {
134 		  { MS_UNKNOWN, { { MS_UNKNOWN }, { MS_UNKNOWN }, { MS_UNKNOWN } } },
135 		  MS_RET(0)
136 	};
137 
138 	/* initialize the corresponding microseq */
139 	msq[0].opcode = opcode;
140 	msq[0].arg[0] = param1;
141 	msq[0].arg[1] = param2;
142 	msq[0].arg[2] = param3;
143 
144 	/* execute the microseq */
145 	return (ppb_MS_microseq(bus, dev, msq, ret));
146 }
147 
148 /*
149  * ppb_MS_loop()
150  *
151  * Execute a microseq loop
152  *
153  */
154 int
155 ppb_MS_loop(device_t bus, device_t dev, struct ppb_microseq *prolog,
156 		struct ppb_microseq *body, struct ppb_microseq *epilog,
157 		int iter, int *ret)
158 {
159 	struct ppb_microseq loop_microseq[] = {
160 		  MS_CALL(0),			/* execute prolog */
161 
162 		  MS_SET(MS_UNKNOWN),		/* set size of transfer */
163 	/* loop: */
164 		  MS_CALL(0),			/* execute body */
165 		  MS_DBRA(-1 /* loop: */),
166 
167 		  MS_CALL(0),			/* execute epilog */
168 		  MS_RET(0)
169 	};
170 
171 	/* initialize the structure */
172 	loop_microseq[0].arg[0].p = (void *)prolog;
173 	loop_microseq[1].arg[0].i = iter;
174 	loop_microseq[2].arg[0].p = (void *)body;
175 	loop_microseq[4].arg[0].p = (void *)epilog;
176 
177 	/* execute the loop */
178 	return (ppb_MS_microseq(bus, dev, loop_microseq, ret));
179 }
180 
181 /*
182  * ppb_MS_init_msq()
183  *
184  * Initialize a microsequence - see macros in ppb_msq.h
185  *
186  */
187 int
188 ppb_MS_init_msq(struct ppb_microseq *msq, int nbparam, ...)
189 {
190 	int i;
191 	int param, ins, arg, type;
192 	va_list p_list;
193 
194 	va_start(p_list, nbparam);
195 
196 	for (i=0; i<nbparam; i++) {
197 		/* retrieve the parameter descriptor */
198 		param = va_arg(p_list, int);
199 
200 		ins  = MS_INS(param);
201 		arg  = MS_ARG(param);
202 		type = MS_TYP(param);
203 
204 		/* check the instruction position */
205 		if (arg >= PPB_MS_MAXARGS)
206 			panic("%s: parameter out of range (0x%x)!",
207 				__FUNCTION__, param);
208 
209 #if 0
210 		printf("%s: param = %d, ins = %d, arg = %d, type = %d\n",
211 			__FUNCTION__, param, ins, arg, type);
212 #endif
213 
214 		/* properly cast the parameter */
215 		switch (type) {
216 		case MS_TYP_INT:
217 			msq[ins].arg[arg].i = va_arg(p_list, int);
218 			break;
219 
220 		case MS_TYP_CHA:
221 			msq[ins].arg[arg].i = (int)va_arg(p_list, char);
222 			break;
223 
224 		case MS_TYP_PTR:
225 			msq[ins].arg[arg].p = va_arg(p_list, void *);
226 			break;
227 
228 		case MS_TYP_FUN:
229 			msq[ins].arg[arg].f = va_arg(p_list, void *);
230 			break;
231 
232 		default:
233 			panic("%s: unknown parameter (0x%x)!", __FUNCTION__,
234 				param);
235 		}
236 	}
237 
238 	return (0);
239 }
240 
241 /*
242  * ppb_MS_microseq()
243  *
244  * Interprete a microsequence. Some microinstructions are executed at adapter
245  * level to avoid function call overhead between ppbus and the adapter
246  */
247 int
248 ppb_MS_microseq(device_t bus, device_t dev, struct ppb_microseq *msq, int *ret)
249 {
250 	struct ppb_data *ppb = (struct ppb_data *)device_get_softc(bus);
251 	struct ppb_device *ppbdev = (struct ppb_device *)device_get_ivars(dev);
252 
253 	struct ppb_microseq *mi;		/* current microinstruction */
254 	int error;
255 
256 	struct ppb_xfer *xfer;
257 
258 	/* microsequence executed to initialize the transfer */
259 	struct ppb_microseq initxfer[] = {
260 		MS_PTR(MS_UNKNOWN), 	/* set ptr to buffer */
261 		MS_SET(MS_UNKNOWN),	/* set transfer size */
262 		MS_RET(0)
263 	};
264 
265 	if (ppb->ppb_owner != dev)
266 		return (EACCES);
267 
268 #define INCR_PC (mi ++)
269 
270 	mi = msq;
271 	for (;;) {
272 		switch (mi->opcode) {
273 		case MS_OP_PUT:
274 		case MS_OP_GET:
275 
276 			/* attempt to choose the best mode for the device */
277 			xfer = mode2xfer(bus, ppbdev, mi->opcode);
278 
279 			/* figure out if we should use ieee1284 code */
280 			if (!xfer->loop) {
281 				if (mi->opcode == MS_OP_PUT) {
282 					if ((error = PPBUS_WRITE(
283 						device_get_parent(bus),
284 						(char *)mi->arg[0].p,
285 						mi->arg[1].i, 0)))
286 							goto error;
287 
288 					INCR_PC;
289 					goto next;
290 				} else
291 					panic("%s: IEEE1284 read not supported", __FUNCTION__);
292 			}
293 
294 			/* XXX should use ppb_MS_init_msq() */
295 			initxfer[0].arg[0].p = mi->arg[0].p;
296 			initxfer[1].arg[0].i = mi->arg[1].i;
297 
298 			/* initialize transfer */
299 			ppb_MS_microseq(bus, dev, initxfer, &error);
300 
301 			if (error)
302 				goto error;
303 
304 			/* the xfer microsequence should not contain any
305 			 * MS_OP_PUT or MS_OP_GET!
306 			 */
307 			ppb_MS_microseq(bus, dev, xfer->loop, &error);
308 
309 			if (error)
310 				goto error;
311 
312 			INCR_PC;
313 			break;
314 
315                 case MS_OP_RET:
316 			if (ret)
317 				*ret = mi->arg[0].i;	/* return code */
318 			return (0);
319                         break;
320 
321 		default:
322 			/* executing microinstructions at ppc level is
323 			 * faster. This is the default if the microinstr
324 			 * is unknown here
325 			 */
326 			if ((error = PPBUS_EXEC_MICROSEQ(
327 						device_get_parent(bus), &mi)))
328 				goto error;
329 			break;
330 		}
331 	next:
332 	}
333 error:
334 	return (error);
335 }
336 
337