1 /* $NetBSD: ppbus_msq.c,v 1.9 2008/04/18 14:56:40 cegger 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.9 2008/04/18 14:56:40 cegger 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 */ 62 static struct ppbus_xfer * mode2xfer(struct ppbus_softc *, 63 struct ppbus_device_softc *, int); 64 65 /* 66 * Device mode to submsq conversion 67 */ 68 static struct ppbus_xfer * 69 mode2xfer(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 */ 129 int 130 ppbus_MS_init(device_t dev, device_t ppbdev, 131 struct ppbus_microseq * loop, int opcode) 132 { 133 struct ppbus_softc * bus = device_private(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 */ 148 int 149 ppbus_MS_exec(device_t ppb, device_t 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 */ 175 int 176 ppbus_MS_loop(device_t ppb, device_t 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 */ 210 int 211 ppbus_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 */ 274 int 275 ppbus_MS_microseq(device_t dev, device_t busdev, 276 struct ppbus_microseq * msq, int * ret) 277 { 278 struct ppbus_device_softc * ppbdev = device_private(busdev); 279 struct ppbus_softc * bus = device_private(dev); 280 struct ppbus_microseq * mi; /* current microinstruction */ 281 size_t cnt; 282 int error; 283 284 struct ppbus_xfer * xfer; 285 286 /* microsequence executed to initialize the transfer */ 287 struct ppbus_microseq initxfer[] = { 288 MS_PTR(MS_UNKNOWN), /* set ptr to buffer */ 289 MS_SET(MS_UNKNOWN), /* set transfer size */ 290 MS_RET(0) 291 }; 292 293 if(bus->ppbus_owner != busdev) { 294 return (EACCES); 295 } 296 297 #define INCR_PC (mi ++) 298 299 mi = msq; 300 again: 301 for (;;) { 302 switch (mi->opcode) { 303 case MS_OP_PUT: 304 case MS_OP_GET: 305 306 /* attempt to choose the best mode for the device */ 307 xfer = mode2xfer(bus, ppbdev, mi->opcode); 308 309 /* figure out if we should use ieee1284 code */ 310 if (!xfer->loop) { 311 if (mi->opcode == MS_OP_PUT) { 312 if ((error = ppbus_write( 313 bus->sc_dev, 314 (char *)mi->arg[0].p, 315 mi->arg[1].i, 0, &cnt))) { 316 goto error; 317 } 318 319 INCR_PC; 320 goto again; 321 } 322 else { 323 panic("%s: IEEE1284 read not supported", 324 __func__); 325 } 326 } 327 328 /* XXX should use ppbus_MS_init_msq() */ 329 initxfer[0].arg[0].p = mi->arg[0].p; 330 initxfer[1].arg[0].i = mi->arg[1].i; 331 332 /* initialize transfer */ 333 ppbus_MS_microseq(dev, busdev, initxfer, &error); 334 335 if (error) 336 goto error; 337 338 /* the xfer microsequence should not contain any 339 * MS_OP_PUT or MS_OP_GET! 340 */ 341 ppbus_MS_microseq(dev, busdev, xfer->loop, &error); 342 343 if (error) 344 goto error; 345 346 INCR_PC; 347 break; 348 349 case MS_OP_RET: 350 if (ret) 351 *ret = mi->arg[0].i; /* return code */ 352 return (0); 353 break; 354 355 default: 356 /* executing microinstructions at ppc level is 357 * faster. This is the default if the microinstr 358 * is unknown here 359 */ 360 if((error = 361 bus->ppbus_exec_microseq( 362 bus->sc_dev, &mi))) { 363 364 goto error; 365 } 366 break; 367 } 368 } 369 error: 370 return (error); 371 } 372 373