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