xref: /freebsd/sys/dev/ppbus/ppb_msq.c (revision 1e413cf93298b5b97441a21d9a50fdcd0ee9945e)
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  *
27  */
28 
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31 #include <machine/stdarg.h>
32 
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/bus.h>
36 
37 #include <dev/ppbus/ppbconf.h>
38 #include <dev/ppbus/ppb_msq.h>
39 
40 #include "ppbus_if.h"
41 
42 /* msq index (see PPB_MAX_XFER)
43  * These are device modes
44  */
45 #define COMPAT_MSQ	0x0
46 #define NIBBLE_MSQ	0x1
47 #define PS2_MSQ		0x2
48 #define EPP17_MSQ	0x3
49 #define EPP19_MSQ	0x4
50 #define ECP_MSQ		0x5
51 
52 /*
53  * Device mode to submsq conversion
54  */
55 static struct ppb_xfer *
56 mode2xfer(device_t bus, struct ppb_device *ppbdev, int opcode)
57 {
58 	int index, epp;
59 	struct ppb_xfer *table;
60 
61 	switch (opcode) {
62 	case MS_OP_GET:
63 		table = ppbdev->get_xfer;
64 		break;
65 
66 	case MS_OP_PUT:
67 		table = ppbdev->put_xfer;
68 		break;
69 
70 	default:
71 		panic("%s: unknown opcode (%d)", __func__, opcode);
72 	}
73 
74 	/* retrieve the device operating mode */
75 	switch (ppb_get_mode(bus)) {
76 	case PPB_COMPATIBLE:
77 		index = COMPAT_MSQ;
78 		break;
79 	case PPB_NIBBLE:
80 		index = NIBBLE_MSQ;
81 		break;
82 	case PPB_PS2:
83 		index = PS2_MSQ;
84 		break;
85 	case PPB_EPP:
86 		switch ((epp = ppb_get_epp_protocol(bus))) {
87 		case EPP_1_7:
88 			index = EPP17_MSQ;
89 			break;
90 		case EPP_1_9:
91 			index = EPP19_MSQ;
92 			break;
93 		default:
94 			panic("%s: unknown EPP protocol (0x%x)!", __func__,
95 				epp);
96 		}
97 		break;
98 	case PPB_ECP:
99 		index = ECP_MSQ;
100 		break;
101 	default:
102 		panic("%s: unknown mode (%d)", __func__, ppbdev->mode);
103 	}
104 
105 	return (&table[index]);
106 }
107 
108 /*
109  * ppb_MS_init()
110  *
111  * Initialize device dependent submicrosequence of the current mode
112  *
113  */
114 int
115 ppb_MS_init(device_t bus, device_t dev, struct ppb_microseq *loop, int opcode)
116 {
117 	struct ppb_device *ppbdev = (struct ppb_device *)device_get_ivars(dev);
118 	struct ppb_xfer *xfer = mode2xfer(bus, ppbdev, opcode);
119 
120 	xfer->loop = loop;
121 
122 	return (0);
123 }
124 
125 /*
126  * ppb_MS_exec()
127  *
128  * Execute any microsequence opcode - expensive
129  *
130  */
131 int
132 ppb_MS_exec(device_t bus, device_t dev, int opcode, union ppb_insarg param1,
133 		union ppb_insarg param2, union ppb_insarg param3, int *ret)
134 {
135 	struct ppb_microseq msq[] = {
136 		  { MS_UNKNOWN, { { MS_UNKNOWN }, { MS_UNKNOWN }, { MS_UNKNOWN } } },
137 		  MS_RET(0)
138 	};
139 
140 	/* initialize the corresponding microseq */
141 	msq[0].opcode = opcode;
142 	msq[0].arg[0] = param1;
143 	msq[0].arg[1] = param2;
144 	msq[0].arg[2] = param3;
145 
146 	/* execute the microseq */
147 	return (ppb_MS_microseq(bus, dev, msq, ret));
148 }
149 
150 /*
151  * ppb_MS_loop()
152  *
153  * Execute a microseq loop
154  *
155  */
156 int
157 ppb_MS_loop(device_t bus, device_t dev, struct ppb_microseq *prolog,
158 		struct ppb_microseq *body, struct ppb_microseq *epilog,
159 		int iter, int *ret)
160 {
161 	struct ppb_microseq loop_microseq[] = {
162 		  MS_CALL(0),			/* execute prolog */
163 
164 		  MS_SET(MS_UNKNOWN),		/* set size of transfer */
165 	/* loop: */
166 		  MS_CALL(0),			/* execute body */
167 		  MS_DBRA(-1 /* loop: */),
168 
169 		  MS_CALL(0),			/* execute epilog */
170 		  MS_RET(0)
171 	};
172 
173 	/* initialize the structure */
174 	loop_microseq[0].arg[0].p = (void *)prolog;
175 	loop_microseq[1].arg[0].i = iter;
176 	loop_microseq[2].arg[0].p = (void *)body;
177 	loop_microseq[4].arg[0].p = (void *)epilog;
178 
179 	/* execute the loop */
180 	return (ppb_MS_microseq(bus, dev, loop_microseq, ret));
181 }
182 
183 /*
184  * ppb_MS_init_msq()
185  *
186  * Initialize a microsequence - see macros in ppb_msq.h
187  *
188  */
189 int
190 ppb_MS_init_msq(struct ppb_microseq *msq, int nbparam, ...)
191 {
192 	int i;
193 	int param, ins, arg, type;
194 	va_list p_list;
195 
196 	va_start(p_list, nbparam);
197 
198 	for (i=0; i<nbparam; i++) {
199 		/* retrieve the parameter descriptor */
200 		param = va_arg(p_list, int);
201 
202 		ins  = MS_INS(param);
203 		arg  = MS_ARG(param);
204 		type = MS_TYP(param);
205 
206 		/* check the instruction position */
207 		if (arg >= PPB_MS_MAXARGS)
208 			panic("%s: parameter out of range (0x%x)!",
209 				__func__, param);
210 
211 #if 0
212 		printf("%s: param = %d, ins = %d, arg = %d, type = %d\n",
213 			__func__, param, ins, arg, type);
214 #endif
215 
216 		/* properly cast the parameter */
217 		switch (type) {
218 		case MS_TYP_INT:
219 			msq[ins].arg[arg].i = va_arg(p_list, int);
220 			break;
221 
222 		case MS_TYP_CHA:
223 			msq[ins].arg[arg].i = (int)va_arg(p_list, int);
224 			break;
225 
226 		case MS_TYP_PTR:
227 			msq[ins].arg[arg].p = va_arg(p_list, void *);
228 			break;
229 
230 		case MS_TYP_FUN:
231 			msq[ins].arg[arg].f = va_arg(p_list, void *);
232 			break;
233 
234 		default:
235 			panic("%s: unknown parameter (0x%x)!", __func__,
236 				param);
237 		}
238 	}
239 
240 	return (0);
241 }
242 
243 /*
244  * ppb_MS_microseq()
245  *
246  * Interprete a microsequence. Some microinstructions are executed at adapter
247  * level to avoid function call overhead between ppbus and the adapter
248  */
249 int
250 ppb_MS_microseq(device_t bus, device_t dev, struct ppb_microseq *msq, int *ret)
251 {
252 	struct ppb_data *ppb = (struct ppb_data *)device_get_softc(bus);
253 	struct ppb_device *ppbdev = (struct ppb_device *)device_get_ivars(dev);
254 
255 	struct ppb_microseq *mi;		/* current microinstruction */
256 	int error;
257 
258 	struct ppb_xfer *xfer;
259 
260 	/* microsequence executed to initialize the transfer */
261 	struct ppb_microseq initxfer[] = {
262 		MS_PTR(MS_UNKNOWN), 	/* set ptr to buffer */
263 		MS_SET(MS_UNKNOWN),	/* set transfer size */
264 		MS_RET(0)
265 	};
266 
267 	if (ppb->ppb_owner != dev)
268 		return (EACCES);
269 
270 #define INCR_PC (mi ++)
271 
272 	mi = msq;
273 	for (;;) {
274 		switch (mi->opcode) {
275 		case MS_OP_PUT:
276 		case MS_OP_GET:
277 
278 			/* attempt to choose the best mode for the device */
279 			xfer = mode2xfer(bus, ppbdev, mi->opcode);
280 
281 			/* figure out if we should use ieee1284 code */
282 			if (!xfer->loop) {
283 				if (mi->opcode == MS_OP_PUT) {
284 					if ((error = PPBUS_WRITE(
285 						device_get_parent(bus),
286 						(char *)mi->arg[0].p,
287 						mi->arg[1].i, 0)))
288 							goto error;
289 
290 					INCR_PC;
291 					goto next;
292 				} else
293 					panic("%s: IEEE1284 read not supported", __func__);
294 			}
295 
296 			/* XXX should use ppb_MS_init_msq() */
297 			initxfer[0].arg[0].p = mi->arg[0].p;
298 			initxfer[1].arg[0].i = mi->arg[1].i;
299 
300 			/* initialize transfer */
301 			ppb_MS_microseq(bus, dev, initxfer, &error);
302 
303 			if (error)
304 				goto error;
305 
306 			/* the xfer microsequence should not contain any
307 			 * MS_OP_PUT or MS_OP_GET!
308 			 */
309 			ppb_MS_microseq(bus, dev, xfer->loop, &error);
310 
311 			if (error)
312 				goto error;
313 
314 			INCR_PC;
315 			break;
316 
317                 case MS_OP_RET:
318 			if (ret)
319 				*ret = mi->arg[0].i;	/* return code */
320 			return (0);
321                         break;
322 
323 		default:
324 			/* executing microinstructions at ppc level is
325 			 * faster. This is the default if the microinstr
326 			 * is unknown here
327 			 */
328 			if ((error = PPBUS_EXEC_MICROSEQ(
329 						device_get_parent(bus), &mi)))
330 				goto error;
331 			break;
332 		}
333 	next:
334 		continue;
335 	}
336 error:
337 	return (error);
338 }
339 
340