186d7f5d3SJohn Marino /*-
286d7f5d3SJohn Marino * Copyright (c) 1998, 1999 Nicolas Souchu
386d7f5d3SJohn Marino * All rights reserved.
486d7f5d3SJohn Marino *
586d7f5d3SJohn Marino * Redistribution and use in source and binary forms, with or without
686d7f5d3SJohn Marino * modification, are permitted provided that the following conditions
786d7f5d3SJohn Marino * are met:
886d7f5d3SJohn Marino * 1. Redistributions of source code must retain the above copyright
986d7f5d3SJohn Marino * notice, this list of conditions and the following disclaimer.
1086d7f5d3SJohn Marino * 2. Redistributions in binary form must reproduce the above copyright
1186d7f5d3SJohn Marino * notice, this list of conditions and the following disclaimer in the
1286d7f5d3SJohn Marino * documentation and/or other materials provided with the distribution.
1386d7f5d3SJohn Marino *
1486d7f5d3SJohn Marino * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1586d7f5d3SJohn Marino * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1686d7f5d3SJohn Marino * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1786d7f5d3SJohn Marino * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1886d7f5d3SJohn Marino * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1986d7f5d3SJohn Marino * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2086d7f5d3SJohn Marino * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2186d7f5d3SJohn Marino * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2286d7f5d3SJohn Marino * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2386d7f5d3SJohn Marino * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2486d7f5d3SJohn Marino * SUCH DAMAGE.
2586d7f5d3SJohn Marino *
2686d7f5d3SJohn Marino * $FreeBSD: src/sys/dev/ppbus/ppb_msq.c,v 1.9.2.1 2000/05/24 00:20:57 n_hibma Exp $
2786d7f5d3SJohn Marino * $DragonFly: src/sys/bus/ppbus/ppb_msq.c,v 1.9 2006/12/22 23:12:17 swildner Exp $
2886d7f5d3SJohn Marino *
2986d7f5d3SJohn Marino */
3086d7f5d3SJohn Marino #include <machine/stdarg.h>
3186d7f5d3SJohn Marino
3286d7f5d3SJohn Marino #include <sys/param.h>
3386d7f5d3SJohn Marino #include <sys/systm.h>
3486d7f5d3SJohn Marino #include <sys/bus.h>
3586d7f5d3SJohn Marino
3686d7f5d3SJohn Marino #include "ppbconf.h"
3786d7f5d3SJohn Marino #include "ppb_msq.h"
3886d7f5d3SJohn Marino
3986d7f5d3SJohn Marino #include "ppbus_if.h"
4086d7f5d3SJohn Marino
4186d7f5d3SJohn Marino /* msq index (see PPB_MAX_XFER)
4286d7f5d3SJohn Marino * These are device modes
4386d7f5d3SJohn Marino */
4486d7f5d3SJohn Marino #define COMPAT_MSQ 0x0
4586d7f5d3SJohn Marino #define NIBBLE_MSQ 0x1
4686d7f5d3SJohn Marino #define PS2_MSQ 0x2
4786d7f5d3SJohn Marino #define EPP17_MSQ 0x3
4886d7f5d3SJohn Marino #define EPP19_MSQ 0x4
4986d7f5d3SJohn Marino #define ECP_MSQ 0x5
5086d7f5d3SJohn Marino
5186d7f5d3SJohn Marino /*
5286d7f5d3SJohn Marino * Device mode to submsq conversion
5386d7f5d3SJohn Marino */
5486d7f5d3SJohn Marino static struct ppb_xfer *
mode2xfer(device_t bus,struct ppb_device * ppbdev,int opcode)5586d7f5d3SJohn Marino mode2xfer(device_t bus, struct ppb_device *ppbdev, int opcode)
5686d7f5d3SJohn Marino {
5786d7f5d3SJohn Marino int index, epp;
5886d7f5d3SJohn Marino struct ppb_xfer *table;
5986d7f5d3SJohn Marino
6086d7f5d3SJohn Marino switch (opcode) {
6186d7f5d3SJohn Marino case MS_OP_GET:
6286d7f5d3SJohn Marino table = ppbdev->get_xfer;
6386d7f5d3SJohn Marino break;
6486d7f5d3SJohn Marino
6586d7f5d3SJohn Marino case MS_OP_PUT:
6686d7f5d3SJohn Marino table = ppbdev->put_xfer;
6786d7f5d3SJohn Marino break;
6886d7f5d3SJohn Marino
6986d7f5d3SJohn Marino default:
7086d7f5d3SJohn Marino panic("%s: unknown opcode (%d)", __func__, opcode);
7186d7f5d3SJohn Marino }
7286d7f5d3SJohn Marino
7386d7f5d3SJohn Marino /* retrieve the device operating mode */
7486d7f5d3SJohn Marino switch (ppb_get_mode(bus)) {
7586d7f5d3SJohn Marino case PPB_COMPATIBLE:
7686d7f5d3SJohn Marino index = COMPAT_MSQ;
7786d7f5d3SJohn Marino break;
7886d7f5d3SJohn Marino case PPB_NIBBLE:
7986d7f5d3SJohn Marino index = NIBBLE_MSQ;
8086d7f5d3SJohn Marino break;
8186d7f5d3SJohn Marino case PPB_PS2:
8286d7f5d3SJohn Marino index = PS2_MSQ;
8386d7f5d3SJohn Marino break;
8486d7f5d3SJohn Marino case PPB_EPP:
8586d7f5d3SJohn Marino switch ((epp = ppb_get_epp_protocol(bus))) {
8686d7f5d3SJohn Marino case EPP_1_7:
8786d7f5d3SJohn Marino index = EPP17_MSQ;
8886d7f5d3SJohn Marino break;
8986d7f5d3SJohn Marino case EPP_1_9:
9086d7f5d3SJohn Marino index = EPP19_MSQ;
9186d7f5d3SJohn Marino break;
9286d7f5d3SJohn Marino default:
9386d7f5d3SJohn Marino panic("%s: unknown EPP protocol (0x%x)!", __func__,
9486d7f5d3SJohn Marino epp);
9586d7f5d3SJohn Marino }
9686d7f5d3SJohn Marino break;
9786d7f5d3SJohn Marino case PPB_ECP:
9886d7f5d3SJohn Marino index = ECP_MSQ;
9986d7f5d3SJohn Marino break;
10086d7f5d3SJohn Marino default:
10186d7f5d3SJohn Marino panic("%s: unknown mode (%d)", __func__, ppbdev->mode);
10286d7f5d3SJohn Marino }
10386d7f5d3SJohn Marino
10486d7f5d3SJohn Marino return (&table[index]);
10586d7f5d3SJohn Marino }
10686d7f5d3SJohn Marino
10786d7f5d3SJohn Marino /*
10886d7f5d3SJohn Marino * ppb_MS_init()
10986d7f5d3SJohn Marino *
11086d7f5d3SJohn Marino * Initialize device dependent submicrosequence of the current mode
11186d7f5d3SJohn Marino *
11286d7f5d3SJohn Marino */
11386d7f5d3SJohn Marino int
ppb_MS_init(device_t bus,device_t dev,struct ppb_microseq * loop,int opcode)11486d7f5d3SJohn Marino ppb_MS_init(device_t bus, device_t dev, struct ppb_microseq *loop, int opcode)
11586d7f5d3SJohn Marino {
11686d7f5d3SJohn Marino struct ppb_device *ppbdev = (struct ppb_device *)device_get_ivars(dev);
11786d7f5d3SJohn Marino struct ppb_xfer *xfer = mode2xfer(bus, ppbdev, opcode);
11886d7f5d3SJohn Marino
11986d7f5d3SJohn Marino xfer->loop = loop;
12086d7f5d3SJohn Marino
12186d7f5d3SJohn Marino return (0);
12286d7f5d3SJohn Marino }
12386d7f5d3SJohn Marino
12486d7f5d3SJohn Marino /*
12586d7f5d3SJohn Marino * ppb_MS_exec()
12686d7f5d3SJohn Marino *
12786d7f5d3SJohn Marino * Execute any microsequence opcode - expensive
12886d7f5d3SJohn Marino *
12986d7f5d3SJohn Marino */
13086d7f5d3SJohn Marino int
ppb_MS_exec(device_t bus,device_t dev,int opcode,union ppb_insarg param1,union ppb_insarg param2,union ppb_insarg param3,int * ret)13186d7f5d3SJohn Marino ppb_MS_exec(device_t bus, device_t dev, int opcode, union ppb_insarg param1,
13286d7f5d3SJohn Marino union ppb_insarg param2, union ppb_insarg param3, int *ret)
13386d7f5d3SJohn Marino {
13486d7f5d3SJohn Marino struct ppb_microseq msq[] = {
13586d7f5d3SJohn Marino { MS_UNKNOWN, { { MS_UNKNOWN }, { MS_UNKNOWN }, { MS_UNKNOWN } } },
13686d7f5d3SJohn Marino MS_RET(0)
13786d7f5d3SJohn Marino };
13886d7f5d3SJohn Marino
13986d7f5d3SJohn Marino /* initialize the corresponding microseq */
14086d7f5d3SJohn Marino msq[0].opcode = opcode;
14186d7f5d3SJohn Marino msq[0].arg[0] = param1;
14286d7f5d3SJohn Marino msq[0].arg[1] = param2;
14386d7f5d3SJohn Marino msq[0].arg[2] = param3;
14486d7f5d3SJohn Marino
14586d7f5d3SJohn Marino /* execute the microseq */
14686d7f5d3SJohn Marino return (ppb_MS_microseq(bus, dev, msq, ret));
14786d7f5d3SJohn Marino }
14886d7f5d3SJohn Marino
14986d7f5d3SJohn Marino /*
15086d7f5d3SJohn Marino * ppb_MS_loop()
15186d7f5d3SJohn Marino *
15286d7f5d3SJohn Marino * Execute a microseq loop
15386d7f5d3SJohn Marino *
15486d7f5d3SJohn Marino */
15586d7f5d3SJohn Marino int
ppb_MS_loop(device_t bus,device_t dev,struct ppb_microseq * prolog,struct ppb_microseq * body,struct ppb_microseq * epilog,int iter,int * ret)15686d7f5d3SJohn Marino ppb_MS_loop(device_t bus, device_t dev, struct ppb_microseq *prolog,
15786d7f5d3SJohn Marino struct ppb_microseq *body, struct ppb_microseq *epilog,
15886d7f5d3SJohn Marino int iter, int *ret)
15986d7f5d3SJohn Marino {
16086d7f5d3SJohn Marino struct ppb_microseq loop_microseq[] = {
16186d7f5d3SJohn Marino MS_CALL(0), /* execute prolog */
16286d7f5d3SJohn Marino
16386d7f5d3SJohn Marino MS_SET(MS_UNKNOWN), /* set size of transfer */
16486d7f5d3SJohn Marino /* loop: */
16586d7f5d3SJohn Marino MS_CALL(0), /* execute body */
16686d7f5d3SJohn Marino MS_DBRA(-1 /* loop: */),
16786d7f5d3SJohn Marino
16886d7f5d3SJohn Marino MS_CALL(0), /* execute epilog */
16986d7f5d3SJohn Marino MS_RET(0)
17086d7f5d3SJohn Marino };
17186d7f5d3SJohn Marino
17286d7f5d3SJohn Marino /* initialize the structure */
17386d7f5d3SJohn Marino loop_microseq[0].arg[0].p = (void *)prolog;
17486d7f5d3SJohn Marino loop_microseq[1].arg[0].i = iter;
17586d7f5d3SJohn Marino loop_microseq[2].arg[0].p = (void *)body;
17686d7f5d3SJohn Marino loop_microseq[4].arg[0].p = (void *)epilog;
17786d7f5d3SJohn Marino
17886d7f5d3SJohn Marino /* execute the loop */
17986d7f5d3SJohn Marino return (ppb_MS_microseq(bus, dev, loop_microseq, ret));
18086d7f5d3SJohn Marino }
18186d7f5d3SJohn Marino
18286d7f5d3SJohn Marino /*
18386d7f5d3SJohn Marino * ppb_MS_init_msq()
18486d7f5d3SJohn Marino *
18586d7f5d3SJohn Marino * Initialize a microsequence - see macros in ppb_msq.h
18686d7f5d3SJohn Marino *
18786d7f5d3SJohn Marino */
18886d7f5d3SJohn Marino int
ppb_MS_init_msq(struct ppb_microseq * msq,int nbparam,...)18986d7f5d3SJohn Marino ppb_MS_init_msq(struct ppb_microseq *msq, int nbparam, ...)
19086d7f5d3SJohn Marino {
19186d7f5d3SJohn Marino int i;
19286d7f5d3SJohn Marino int param, ins, arg, type;
19386d7f5d3SJohn Marino __va_list p_list;
19486d7f5d3SJohn Marino
19586d7f5d3SJohn Marino __va_start(p_list, nbparam);
19686d7f5d3SJohn Marino
19786d7f5d3SJohn Marino for (i=0; i<nbparam; i++) {
19886d7f5d3SJohn Marino /* retrieve the parameter descriptor */
19986d7f5d3SJohn Marino param = __va_arg(p_list, int);
20086d7f5d3SJohn Marino
20186d7f5d3SJohn Marino ins = MS_INS(param);
20286d7f5d3SJohn Marino arg = MS_ARG(param);
20386d7f5d3SJohn Marino type = MS_TYP(param);
20486d7f5d3SJohn Marino
20586d7f5d3SJohn Marino /* check the instruction position */
20686d7f5d3SJohn Marino if (arg >= PPB_MS_MAXARGS)
20786d7f5d3SJohn Marino panic("%s: parameter out of range (0x%x)!",
20886d7f5d3SJohn Marino __func__, param);
20986d7f5d3SJohn Marino
21086d7f5d3SJohn Marino #if 0
21186d7f5d3SJohn Marino kprintf("%s: param = %d, ins = %d, arg = %d, type = %d\n",
21286d7f5d3SJohn Marino __func__, param, ins, arg, type);
21386d7f5d3SJohn Marino #endif
21486d7f5d3SJohn Marino
21586d7f5d3SJohn Marino /* properly cast the parameter */
21686d7f5d3SJohn Marino switch (type) {
21786d7f5d3SJohn Marino case MS_TYP_INT:
21886d7f5d3SJohn Marino msq[ins].arg[arg].i = __va_arg(p_list, int);
21986d7f5d3SJohn Marino break;
22086d7f5d3SJohn Marino
22186d7f5d3SJohn Marino case MS_TYP_CHA:
22286d7f5d3SJohn Marino msq[ins].arg[arg].i = (int)__va_arg(p_list, int);
22386d7f5d3SJohn Marino break;
22486d7f5d3SJohn Marino
22586d7f5d3SJohn Marino case MS_TYP_PTR:
22686d7f5d3SJohn Marino msq[ins].arg[arg].p = __va_arg(p_list, void *);
22786d7f5d3SJohn Marino break;
22886d7f5d3SJohn Marino
22986d7f5d3SJohn Marino case MS_TYP_FUN:
23086d7f5d3SJohn Marino msq[ins].arg[arg].f = __va_arg(p_list, void *);
23186d7f5d3SJohn Marino break;
23286d7f5d3SJohn Marino
23386d7f5d3SJohn Marino default:
23486d7f5d3SJohn Marino panic("%s: unknown parameter (0x%x)!", __func__,
23586d7f5d3SJohn Marino param);
23686d7f5d3SJohn Marino }
23786d7f5d3SJohn Marino }
23886d7f5d3SJohn Marino
23986d7f5d3SJohn Marino return (0);
24086d7f5d3SJohn Marino }
24186d7f5d3SJohn Marino
24286d7f5d3SJohn Marino /*
24386d7f5d3SJohn Marino * ppb_MS_microseq()
24486d7f5d3SJohn Marino *
24586d7f5d3SJohn Marino * Interprete a microsequence. Some microinstructions are executed at adapter
24686d7f5d3SJohn Marino * level to avoid function call overhead between ppbus and the adapter
24786d7f5d3SJohn Marino */
24886d7f5d3SJohn Marino int
ppb_MS_microseq(device_t bus,device_t dev,struct ppb_microseq * msq,int * ret)24986d7f5d3SJohn Marino ppb_MS_microseq(device_t bus, device_t dev, struct ppb_microseq *msq, int *ret)
25086d7f5d3SJohn Marino {
25186d7f5d3SJohn Marino struct ppb_data *ppb = (struct ppb_data *)device_get_softc(bus);
25286d7f5d3SJohn Marino struct ppb_device *ppbdev = (struct ppb_device *)device_get_ivars(dev);
25386d7f5d3SJohn Marino
25486d7f5d3SJohn Marino struct ppb_microseq *mi; /* current microinstruction */
25586d7f5d3SJohn Marino int error;
25686d7f5d3SJohn Marino
25786d7f5d3SJohn Marino struct ppb_xfer *xfer;
25886d7f5d3SJohn Marino
25986d7f5d3SJohn Marino /* microsequence executed to initialize the transfer */
26086d7f5d3SJohn Marino struct ppb_microseq initxfer[] = {
26186d7f5d3SJohn Marino MS_PTR(MS_UNKNOWN), /* set ptr to buffer */
26286d7f5d3SJohn Marino MS_SET(MS_UNKNOWN), /* set transfer size */
26386d7f5d3SJohn Marino MS_RET(0)
26486d7f5d3SJohn Marino };
26586d7f5d3SJohn Marino
26686d7f5d3SJohn Marino if (ppb->ppb_owner != dev)
26786d7f5d3SJohn Marino return (EACCES);
26886d7f5d3SJohn Marino
26986d7f5d3SJohn Marino #define INCR_PC (mi ++)
27086d7f5d3SJohn Marino
27186d7f5d3SJohn Marino mi = msq;
27286d7f5d3SJohn Marino for (;;) {
27386d7f5d3SJohn Marino switch (mi->opcode) {
27486d7f5d3SJohn Marino case MS_OP_PUT:
27586d7f5d3SJohn Marino case MS_OP_GET:
27686d7f5d3SJohn Marino
27786d7f5d3SJohn Marino /* attempt to choose the best mode for the device */
27886d7f5d3SJohn Marino xfer = mode2xfer(bus, ppbdev, mi->opcode);
27986d7f5d3SJohn Marino
28086d7f5d3SJohn Marino /* figure out if we should use ieee1284 code */
28186d7f5d3SJohn Marino if (!xfer->loop) {
28286d7f5d3SJohn Marino if (mi->opcode == MS_OP_PUT) {
28386d7f5d3SJohn Marino if ((error = PPBUS_WRITE(
28486d7f5d3SJohn Marino device_get_parent(bus),
28586d7f5d3SJohn Marino (char *)mi->arg[0].p,
28686d7f5d3SJohn Marino mi->arg[1].i, 0)))
28786d7f5d3SJohn Marino goto error;
28886d7f5d3SJohn Marino
28986d7f5d3SJohn Marino INCR_PC;
29086d7f5d3SJohn Marino continue;
29186d7f5d3SJohn Marino } else {
29286d7f5d3SJohn Marino panic("%s: IEEE1284 read not supported", __func__);
29386d7f5d3SJohn Marino }
29486d7f5d3SJohn Marino }
29586d7f5d3SJohn Marino
29686d7f5d3SJohn Marino /* XXX should use ppb_MS_init_msq() */
29786d7f5d3SJohn Marino initxfer[0].arg[0].p = mi->arg[0].p;
29886d7f5d3SJohn Marino initxfer[1].arg[0].i = mi->arg[1].i;
29986d7f5d3SJohn Marino
30086d7f5d3SJohn Marino /* initialize transfer */
30186d7f5d3SJohn Marino ppb_MS_microseq(bus, dev, initxfer, &error);
30286d7f5d3SJohn Marino
30386d7f5d3SJohn Marino if (error)
30486d7f5d3SJohn Marino goto error;
30586d7f5d3SJohn Marino
30686d7f5d3SJohn Marino /* the xfer microsequence should not contain any
30786d7f5d3SJohn Marino * MS_OP_PUT or MS_OP_GET!
30886d7f5d3SJohn Marino */
30986d7f5d3SJohn Marino ppb_MS_microseq(bus, dev, xfer->loop, &error);
31086d7f5d3SJohn Marino
31186d7f5d3SJohn Marino if (error)
31286d7f5d3SJohn Marino goto error;
31386d7f5d3SJohn Marino
31486d7f5d3SJohn Marino INCR_PC;
31586d7f5d3SJohn Marino break;
31686d7f5d3SJohn Marino
31786d7f5d3SJohn Marino case MS_OP_RET:
31886d7f5d3SJohn Marino if (ret)
31986d7f5d3SJohn Marino *ret = mi->arg[0].i; /* return code */
32086d7f5d3SJohn Marino return (0);
32186d7f5d3SJohn Marino break;
32286d7f5d3SJohn Marino
32386d7f5d3SJohn Marino default:
32486d7f5d3SJohn Marino /* executing microinstructions at ppc level is
32586d7f5d3SJohn Marino * faster. This is the default if the microinstr
32686d7f5d3SJohn Marino * is unknown here
32786d7f5d3SJohn Marino */
32886d7f5d3SJohn Marino if ((error = PPBUS_EXEC_MICROSEQ(
32986d7f5d3SJohn Marino device_get_parent(bus), &mi)))
33086d7f5d3SJohn Marino goto error;
33186d7f5d3SJohn Marino break;
33286d7f5d3SJohn Marino }
33386d7f5d3SJohn Marino }
33486d7f5d3SJohn Marino error:
33586d7f5d3SJohn Marino return (error);
33686d7f5d3SJohn Marino }
33786d7f5d3SJohn Marino
338