xref: /dragonfly/sys/dev/disk/vpo/vpoio.c (revision 6c29243ff93286508a0cf9101ae06f95c16fdfdf)
1 /*-
2  * Copyright (c) 1998, 1999 Nicolas Souchu
3  * Copyright (c) 2000 Alcove - Nicolas Souchu
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  * $FreeBSD: src/sys/dev/ppbus/vpoio.c,v 1.10.2.3 2001/10/02 05:27:20 nsouch Exp $
28  */
29 
30 #ifdef _KERNEL
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/module.h>
34 #include <sys/bus.h>
35 #include <sys/malloc.h>
36 
37 #include <machine/clock.h>
38 
39 #endif
40 
41 #ifdef    _KERNEL
42 #endif
43 
44 #include "opt_vpo.h"
45 
46 #include <bus/ppbus/ppbio.h>
47 #include <bus/ppbus/ppbconf.h>
48 #include <bus/ppbus/ppb_msq.h>
49 #include "vpoio.h"
50 
51 #include "ppbus_if.h"
52 
53 /*
54  * The driver pools the drive. We may add a timeout queue to avoid
55  * active polling on nACK. I've tried this but it leads to unreliable
56  * transfers
57  */
58 #define VP0_SELTMO            5000      /* select timeout */
59 #define VP0_FAST_SPINTMO      500000    /* wait status timeout */
60 #define VP0_LOW_SPINTMO                 5000000   /* wait status timeout */
61 
62 /*
63  * Actually, VP0 timings are more accurate (about few 16MHZ cycles),
64  * but succeeding in respecting such timings leads to architecture
65  * dependent considerations.
66  */
67 #define VP0_PULSE             1
68 
69 #define VP0_SECTOR_SIZE       512
70 #define VP0_BUFFER_SIZE       0x12000
71 
72 #define n(flags) (~(flags) & (flags))
73 
74 /*
75  * VP0 connections.
76  */
77 #define H_AUTO                n(AUTOFEED)
78 #define H_nAUTO               AUTOFEED
79 #define H_STROBE    n(STROBE)
80 #define H_nSTROBE   STROBE
81 #define H_BSY                 n(nBUSY)
82 #define H_nBSY                nBUSY
83 #define H_SEL                 SELECT
84 #define H_nSEL                n(SELECT)
85 #define H_ERR                 PERROR
86 #define H_nERR                n(PERROR)
87 #define H_ACK                 nACK
88 #define H_nACK                n(nACK)
89 #define H_FLT                 nFAULT
90 #define H_nFLT                n(nFAULT)
91 #define H_SELIN               n(SELECTIN)
92 #define H_nSELIN    SELECTIN
93 #define H_INIT                nINIT
94 #define H_nINIT               n(nINIT)
95 
96 /*
97  * Microcode to execute very fast I/O sequences at the lowest bus level.
98  */
99 
100 #define WAIT_RET    MS_PARAM(4, 2, MS_TYP_PTR)
101 #define WAIT_TMO    MS_PARAM(0, 0, MS_TYP_INT)
102 
103 #define DECLARE_WAIT_MICROSEQUENCE                          \
104 struct ppb_microseq wait_microseq[] = {                     \
105           MS_SET(MS_UNKNOWN),                               \
106           /* loop */                                                  \
107           MS_BRSET(nBUSY, 2 /* ready */),                             \
108           MS_DBRA(-2 /* loop */),                                     \
109           MS_RET(1), /* timed out */                        \
110           /* ready */                                                 \
111           MS_RFETCH(MS_REG_STR, 0xf0, MS_UNKNOWN),          \
112           MS_RET(0) /* no error */                          \
113 }
114 
115 /* call this macro to initialize connect/disconnect microsequences */
116 #define INIT_TRIG_MICROSEQ {                                                    \
117           int i;                                                                          \
118           for (i=1; i <= 7; i+=2) {                                             \
119                     disconnect_microseq[i].arg[2].c = d_pulse;                  \
120                     connect_epp_microseq[i].arg[2].c =                          \
121                     connect_spp_microseq[i].arg[2].c = c_pulse;                 \
122           }                                                                               \
123 }
124 
125 #define trig_d_pulse MS_TRIG(MS_REG_CTR,5,MS_UNKNOWN /* d_pulse */)
126 static char d_pulse[] = {
127            H_AUTO | H_nSELIN | H_INIT | H_STROBE, 0,
128           H_nAUTO | H_nSELIN | H_INIT | H_STROBE, VP0_PULSE,
129            H_AUTO | H_nSELIN | H_INIT | H_STROBE, 0,
130            H_AUTO |  H_SELIN | H_INIT | H_STROBE, VP0_PULSE,
131            H_AUTO | H_nSELIN | H_INIT | H_STROBE, VP0_PULSE
132 };
133 
134 #define trig_c_pulse MS_TRIG(MS_REG_CTR,5,MS_UNKNOWN /* c_pulse */)
135 static char c_pulse[] = {
136            H_AUTO | H_nSELIN | H_INIT | H_STROBE, 0,
137            H_AUTO |  H_SELIN | H_INIT | H_STROBE, 0,
138           H_nAUTO |  H_SELIN | H_INIT | H_STROBE, VP0_PULSE,
139            H_AUTO |  H_SELIN | H_INIT | H_STROBE, 0,
140            H_AUTO | H_nSELIN | H_INIT | H_STROBE, VP0_PULSE
141 };
142 
143 static struct ppb_microseq disconnect_microseq[] = {
144             MS_DASS(0x0), trig_d_pulse, MS_DASS(0x3c), trig_d_pulse,
145             MS_DASS(0x20), trig_d_pulse, MS_DASS(0xf), trig_d_pulse, MS_RET(0)
146 };
147 
148 static struct ppb_microseq connect_epp_microseq[] = {
149             MS_DASS(0x0), trig_c_pulse, MS_DASS(0x3c), trig_c_pulse,
150             MS_DASS(0x20), trig_c_pulse, MS_DASS(0xcf), trig_c_pulse, MS_RET(0)
151 };
152 
153 static struct ppb_microseq connect_spp_microseq[] = {
154             MS_DASS(0x0), trig_c_pulse, MS_DASS(0x3c), trig_c_pulse,
155             MS_DASS(0x20), trig_c_pulse, MS_DASS(0x8f), trig_c_pulse, MS_RET(0)
156 };
157 
158 /*
159  * nibble_inbyte_hook()
160  *
161  * Formats high and low nibble into a character
162  */
163 static int
nibble_inbyte_hook(void * p,char * ptr)164 nibble_inbyte_hook (void *p, char *ptr)
165 {
166           struct vpo_nibble *s = (struct vpo_nibble *)p;
167 
168           /* increment the buffer pointer */
169           *ptr++ = ((s->l >> 4) & 0x0f) + (s->h & 0xf0);
170 
171           return (0);
172 }
173 
174 #define INB_NIBBLE_H MS_PARAM(2, 2, MS_TYP_PTR)
175 #define INB_NIBBLE_L MS_PARAM(4, 2, MS_TYP_PTR)
176 #define INB_NIBBLE_F MS_PARAM(5, 0, MS_TYP_FUN)
177 #define INB_NIBBLE_P MS_PARAM(5, 1, MS_TYP_PTR)
178 
179 /*
180  * This is the sub-microseqence for MS_GET in NIBBLE mode
181  * Retrieve the two nibbles and call the C function to generate the character
182  * and store it in the buffer (see nibble_inbyte_hook())
183  */
184 
185 #define DECLARE_NIBBLE_INBYTE_SUBMICROSEQ                             \
186 struct ppb_microseq nibble_inbyte_submicroseq[] = {                   \
187 /* loop: */                                                                     \
188             MS_CASS( H_AUTO | H_SELIN | H_INIT | H_STROBE), \
189             MS_DELAY(VP0_PULSE),                                                \
190             MS_RFETCH(MS_REG_STR, MS_FETCH_ALL, MS_UNKNOWN /* high nibble */),\
191             MS_CASS(H_nAUTO | H_SELIN | H_INIT | H_STROBE), \
192             MS_RFETCH(MS_REG_STR, MS_FETCH_ALL, MS_UNKNOWN /* low nibble */),\
193             /* do a C call to format the received nibbles */          \
194             MS_C_CALL(MS_UNKNOWN /* C hook */, MS_UNKNOWN /* param */),\
195             MS_DBRA(-7 /* loop */),                                   \
196             MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE), \
197             MS_RET(0)                                                           \
198 }
199 
200 /*
201  * This is the sub-microseqence for MS_GET in PS2 mode
202  */
203 static struct ppb_microseq ps2_inbyte_submicroseq[] = {
204             MS_CASS(PCD | H_AUTO | H_SELIN | H_INIT | H_nSTROBE),
205 
206 /* loop: */
207             MS_RFETCH_P(1, MS_REG_DTR, MS_FETCH_ALL),
208             MS_CASS(PCD | H_nAUTO | H_SELIN | H_INIT | H_nSTROBE),
209             MS_CASS(PCD |  H_AUTO | H_SELIN | H_INIT | H_nSTROBE),
210             MS_DBRA(-4 /* loop */),
211 
212             MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE),
213             MS_RET(0)
214 };
215 
216 /*
217  * This is the sub-microsequence for MS_PUT in both NIBBLE and PS2 modes
218  */
219 static struct ppb_microseq spp_outbyte_submicroseq[] = {
220 
221 /* loop: */
222             MS_RASSERT_P(1, MS_REG_DTR),
223             MS_CASS(H_nAUTO | H_nSELIN | H_INIT | H_STROBE),
224             MS_CASS( H_AUTO | H_nSELIN | H_INIT | H_STROBE),
225             MS_DELAY(VP0_PULSE),
226             MS_DBRA(-5 /* loop */),
227 
228             /* return from the put call */
229             MS_RET(0)
230 };
231 
232 /* EPP 1.7 microsequences, ptr and len set at runtime */
233 static struct ppb_microseq epp17_outstr_body[] = {
234             MS_CASS(H_AUTO | H_SELIN | H_INIT | H_STROBE),
235 
236 /* loop: */
237             MS_RASSERT_P(1, MS_REG_EPP_D),
238             MS_BRSET(TIMEOUT, 3 /* error */),     /* EPP timeout? */
239             MS_DBRA(-3 /* loop */),
240 
241             MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE),
242             MS_RET(0),
243 /* error: */
244             MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE),
245             MS_RET(1)
246 };
247 
248 static struct ppb_microseq epp17_instr_body[] = {
249             MS_CASS(PCD | H_AUTO | H_SELIN | H_INIT | H_STROBE),
250 
251 /* loop: */
252             MS_RFETCH_P(1, MS_REG_EPP_D, MS_FETCH_ALL),
253             MS_BRSET(TIMEOUT, 3 /* error */),     /* EPP timeout? */
254             MS_DBRA(-3 /* loop */),
255 
256             MS_CASS(PCD | H_AUTO | H_nSELIN | H_INIT | H_STROBE),
257             MS_RET(0),
258 /* error: */
259             MS_CASS(PCD | H_AUTO | H_nSELIN | H_INIT | H_STROBE),
260             MS_RET(1)
261 };
262 
263 static struct ppb_microseq in_disk_mode[] = {
264             MS_CASS( H_AUTO | H_nSELIN | H_INIT | H_STROBE),
265             MS_CASS(H_nAUTO | H_nSELIN | H_INIT | H_STROBE),
266 
267             MS_BRCLEAR(H_FLT, 3 /* error */),
268             MS_CASS( H_AUTO | H_nSELIN | H_INIT | H_STROBE),
269             MS_BRSET(H_FLT, 1 /* error */),
270 
271             MS_RET(1),
272 /* error: */
273             MS_RET(0)
274 };
275 
276 static int
vpoio_disconnect(struct vpoio_data * vpo)277 vpoio_disconnect(struct vpoio_data *vpo)
278 {
279           device_t ppbus = device_get_parent(vpo->vpo_dev);
280           int ret;
281 
282           ppb_MS_microseq(ppbus, vpo->vpo_dev, disconnect_microseq, &ret);
283           return (ppb_release_bus(ppbus, vpo->vpo_dev));
284 }
285 
286 /*
287  * how    : PPB_WAIT or PPB_DONTWAIT
288  */
289 static int
vpoio_connect(struct vpoio_data * vpo,int how)290 vpoio_connect(struct vpoio_data *vpo, int how)
291 {
292           device_t ppbus = device_get_parent(vpo->vpo_dev);
293           int error;
294           int ret;
295 
296           if ((error = ppb_request_bus(ppbus, vpo->vpo_dev, how))) {
297 
298 #ifdef VP0_DEBUG
299                     kprintf("%s: can't request bus!\n", __func__);
300 #endif
301                     return error;
302           }
303 
304           if (PPB_IN_EPP_MODE(ppbus))
305                     ppb_MS_microseq(ppbus, vpo->vpo_dev, connect_epp_microseq, &ret);
306           else
307                     ppb_MS_microseq(ppbus, vpo->vpo_dev, connect_spp_microseq, &ret);
308 
309           return (0);
310 }
311 
312 /*
313  * vpoio_reset()
314  *
315  * SCSI reset signal, the drive must be in disk mode
316  */
317 static void
vpoio_reset(struct vpoio_data * vpo)318 vpoio_reset (struct vpoio_data *vpo)
319 {
320           device_t ppbus = device_get_parent(vpo->vpo_dev);
321           int ret;
322 
323           struct ppb_microseq reset_microseq[] = {
324 
325                     #define INITIATOR   MS_PARAM(0, 1, MS_TYP_INT)
326 
327                     MS_DASS(MS_UNKNOWN),
328                     MS_CASS(H_AUTO | H_nSELIN | H_nINIT | H_STROBE),
329                     MS_DELAY(25),
330                     MS_CASS(H_AUTO | H_nSELIN |  H_INIT | H_STROBE),
331                     MS_RET(0)
332           };
333 
334           ppb_MS_init_msq(reset_microseq, 1, INITIATOR, 1 << VP0_INITIATOR);
335           ppb_MS_microseq(ppbus, vpo->vpo_dev, reset_microseq, &ret);
336 
337           return;
338 }
339 
340 /*
341  * vpoio_in_disk_mode()
342  */
343 static int
vpoio_in_disk_mode(struct vpoio_data * vpo)344 vpoio_in_disk_mode(struct vpoio_data *vpo)
345 {
346           device_t ppbus = device_get_parent(vpo->vpo_dev);
347           int ret;
348 
349           ppb_MS_microseq(ppbus, vpo->vpo_dev, in_disk_mode, &ret);
350 
351           return (ret);
352 }
353 
354 /*
355  * vpoio_detect()
356  *
357  * Detect and initialise the VP0 adapter.
358  */
359 static int
vpoio_detect(struct vpoio_data * vpo)360 vpoio_detect(struct vpoio_data *vpo)
361 {
362           device_t ppbus = device_get_parent(vpo->vpo_dev);
363           int error, ret;
364 
365           /* allocate the bus, then apply microsequences */
366           if ((error = ppb_request_bus(ppbus, vpo->vpo_dev, PPB_DONTWAIT)))
367                 return (error);
368 
369           /* Force disconnection */
370           ppb_MS_microseq(ppbus, vpo->vpo_dev, disconnect_microseq, &ret);
371 
372           /* Try to enter EPP mode, then connect to the drive in EPP mode */
373           if (ppb_set_mode(ppbus, PPB_EPP) != -1) {
374                     /* call manually the microseq instead of using the appropriate function
375                      * since we already requested the ppbus */
376                     ppb_MS_microseq(ppbus, vpo->vpo_dev, connect_epp_microseq, &ret);
377           }
378 
379           /* If EPP mode switch failed or ZIP connection in EPP mode failed,
380            * try to connect in NIBBLE mode */
381           if (!vpoio_in_disk_mode(vpo)) {
382 
383                     /* The interface must be at least PS/2 or NIBBLE capable.
384                      * There is no way to know if the ZIP will work with
385                      * PS/2 mode since PS/2 and SPP both use the same connect
386                      * sequence. One must supress PS/2 with boot flags if
387                      * PS/2 mode fails (see ppc(4)).
388                      */
389                     if (ppb_set_mode(ppbus, PPB_PS2) != -1) {
390                               vpo->vpo_mode_found = VP0_MODE_PS2;
391                     } else {
392                               if (ppb_set_mode(ppbus, PPB_NIBBLE) == -1)
393                                         goto error;
394 
395                               vpo->vpo_mode_found = VP0_MODE_NIBBLE;
396                     }
397 
398                     /* Can't know if the interface is capable of PS/2 yet */
399                     ppb_MS_microseq(ppbus, vpo->vpo_dev, connect_spp_microseq, &ret);
400                     if (!vpoio_in_disk_mode(vpo)) {
401                               vpo->vpo_mode_found = VP0_MODE_UNDEFINED;
402                               if (bootverbose)
403                                         kprintf("vpo%d: can't connect to the drive\n",
404                                                   vpo->vpo_unit);
405 
406                               /* disconnect and release the bus */
407                               ppb_MS_microseq(ppbus, vpo->vpo_dev, disconnect_microseq,
408                                                   &ret);
409                               goto error;
410                     }
411           } else {
412                     vpo->vpo_mode_found = VP0_MODE_EPP;
413           }
414 
415           /* send SCSI reset signal */
416           vpoio_reset(vpo);
417 
418           ppb_MS_microseq(ppbus, vpo->vpo_dev, disconnect_microseq, &ret);
419 
420           /* ensure we are disconnected or daisy chained peripheral
421            * may cause serious problem to the disk */
422           if (vpoio_in_disk_mode(vpo)) {
423                     if (bootverbose)
424                               kprintf("vpo%d: can't disconnect from the drive\n",
425                                         vpo->vpo_unit);
426                     goto error;
427           }
428 
429           ppb_release_bus(ppbus, vpo->vpo_dev);
430           return (0);
431 
432 error:
433           ppb_release_bus(ppbus, vpo->vpo_dev);
434           return (VP0_EINITFAILED);
435 }
436 
437 /*
438  * vpoio_outstr()
439  */
440 static int
vpoio_outstr(struct vpoio_data * vpo,char * _buffer,int _size)441 vpoio_outstr(struct vpoio_data *vpo, char *_buffer, int _size)
442 {
443           union ppb_insarg buffer = { .c = _buffer };
444           union ppb_insarg size = { .i = _size };
445           union ppb_insarg unknown = { .i = MS_UNKNOWN };
446           device_t ppbus = device_get_parent(vpo->vpo_dev);
447           int error = 0;
448 
449           ppb_MS_exec(ppbus, vpo->vpo_dev, MS_OP_PUT, buffer, size, unknown,
450               &error);
451 
452           ppb_ecp_sync(ppbus);
453 
454           return (error);
455 }
456 
457 /*
458  * vpoio_instr()
459  */
460 static int
vpoio_instr(struct vpoio_data * vpo,char * _buffer,int _size)461 vpoio_instr(struct vpoio_data *vpo, char *_buffer, int _size)
462 {
463           union ppb_insarg buffer = { .c = _buffer };
464           union ppb_insarg size = { .i = _size };
465           union ppb_insarg unknown = { .i = MS_UNKNOWN };
466           device_t ppbus = device_get_parent(vpo->vpo_dev);
467           int error = 0;
468 
469           ppb_MS_exec(ppbus, vpo->vpo_dev, MS_OP_GET, buffer, size, unknown,
470               &error);
471 
472           ppb_ecp_sync(ppbus);
473 
474           return (error);
475 }
476 
477 static char
vpoio_select(struct vpoio_data * vpo,int initiator,int target)478 vpoio_select(struct vpoio_data *vpo, int initiator, int target)
479 {
480           device_t ppbus = device_get_parent(vpo->vpo_dev);
481           int ret;
482 
483           struct ppb_microseq select_microseq[] = {
484 
485                     /* parameter list
486                      */
487                     #define SELECT_TARGET                   MS_PARAM(0, 1, MS_TYP_INT)
488                     #define SELECT_INITIATOR      MS_PARAM(3, 1, MS_TYP_INT)
489 
490                     /* send the select command to the drive */
491                     MS_DASS(MS_UNKNOWN),
492                     MS_CASS(H_nAUTO | H_nSELIN |  H_INIT | H_STROBE),
493                     MS_CASS( H_AUTO | H_nSELIN |  H_INIT | H_STROBE),
494                     MS_DASS(MS_UNKNOWN),
495                     MS_CASS( H_AUTO | H_nSELIN | H_nINIT | H_STROBE),
496 
497                     /* now, wait until the drive is ready */
498                     MS_SET(VP0_SELTMO),
499 /* loop: */         MS_BRSET(H_ACK, 2 /* ready */),
500                     MS_DBRA(-2 /* loop */),
501 /* error: */        MS_RET(1),
502 /* ready: */        MS_RET(0)
503           };
504 
505           /* initialize the select microsequence */
506           ppb_MS_init_msq(select_microseq, 2,
507                               SELECT_TARGET, 1 << target,
508                               SELECT_INITIATOR, 1 << initiator);
509 
510           ppb_MS_microseq(ppbus, vpo->vpo_dev, select_microseq, &ret);
511 
512           if (ret)
513                     return (VP0_ESELECT_TIMEOUT);
514 
515           return (0);
516 }
517 
518 /*
519  * vpoio_wait()
520  *
521  * H_SELIN must be low.
522  *
523  * XXX should be ported to microseq
524  */
525 static char
vpoio_wait(struct vpoio_data * vpo,int tmo)526 vpoio_wait(struct vpoio_data *vpo, int tmo)
527 {
528           DECLARE_WAIT_MICROSEQUENCE;
529 
530           device_t ppbus = device_get_parent(vpo->vpo_dev);
531           int ret, err;
532 
533 #if 0     /* broken */
534           if (ppb_poll_device(ppbus, 150, nBUSY, nBUSY, PPB_INTR))
535                     return (0);
536 
537           return (ppb_rstr(ppbus) & 0xf0);
538 #endif
539 
540           /*
541            * Return some status information.
542            * Semantics :      0xc0 = ZIP wants more data
543            *                  0xd0 = ZIP wants to send more data
544            *                  0xe0 = ZIP wants command
545            *                  0xf0 = end of transfer, ZIP is sending status
546            */
547 
548           ppb_MS_init_msq(wait_microseq, 2,
549                               WAIT_RET, (void *)&ret,
550                               WAIT_TMO, tmo);
551 
552           ppb_MS_microseq(ppbus, vpo->vpo_dev, wait_microseq, &err);
553 
554           if (err)
555                     return (0);          /* command timed out */
556 
557           return(ret);
558 }
559 
560 /*
561  * vpoio_probe()
562  *
563  * Low level probe of vpo device
564  *
565  */
566 int
vpoio_probe(device_t dev,struct vpoio_data * vpo)567 vpoio_probe(device_t dev, struct vpoio_data *vpo)
568 {
569           int error;
570 
571           /* ppbus dependent initialisation */
572           vpo->vpo_dev = dev;
573 
574           /*
575            * Initialize microsequence code
576            */
577           INIT_TRIG_MICROSEQ;
578 
579           /* now, try to initialise the drive */
580           if ((error = vpoio_detect(vpo))) {
581                     return (error);
582           }
583 
584           return (0);
585 }
586 
587 /*
588  * vpoio_attach()
589  *
590  * Low level attachment of vpo device
591  *
592  */
593 int
vpoio_attach(struct vpoio_data * vpo)594 vpoio_attach(struct vpoio_data *vpo)
595 {
596           DECLARE_NIBBLE_INBYTE_SUBMICROSEQ;
597           device_t ppbus = device_get_parent(vpo->vpo_dev);
598           int error = 0;
599 
600           vpo->vpo_nibble_inbyte_msq = kmalloc(sizeof(nibble_inbyte_submicroseq),
601                                                             M_DEVBUF, M_WAITOK);
602 
603           bcopy((void *)nibble_inbyte_submicroseq,
604                     (void *)vpo->vpo_nibble_inbyte_msq,
605                     sizeof(nibble_inbyte_submicroseq));
606 
607           ppb_MS_init_msq(vpo->vpo_nibble_inbyte_msq, 4,
608                     INB_NIBBLE_H, (void *)&(vpo)->vpo_nibble.h,
609                     INB_NIBBLE_L, (void *)&(vpo)->vpo_nibble.l,
610                     INB_NIBBLE_F, nibble_inbyte_hook,
611                     INB_NIBBLE_P, (void *)&(vpo)->vpo_nibble);
612 
613           /*
614            * Initialize mode dependent in/out microsequences
615            */
616           if ((error = ppb_request_bus(ppbus, vpo->vpo_dev, PPB_WAIT)))
617                     goto error;
618 
619           /* ppbus sets automatically the last mode entered during detection */
620           switch (vpo->vpo_mode_found) {
621           case VP0_MODE_EPP:
622                     ppb_MS_GET_init(ppbus, vpo->vpo_dev, epp17_instr_body);
623                     ppb_MS_PUT_init(ppbus, vpo->vpo_dev, epp17_outstr_body);
624                     kprintf("vpo%d: EPP mode\n", vpo->vpo_unit);
625                     break;
626           case VP0_MODE_PS2:
627                     ppb_MS_GET_init(ppbus, vpo->vpo_dev, ps2_inbyte_submicroseq);
628                     ppb_MS_PUT_init(ppbus, vpo->vpo_dev, spp_outbyte_submicroseq);
629                     kprintf("vpo%d: PS2 mode\n", vpo->vpo_unit);
630                     break;
631           case VP0_MODE_NIBBLE:
632                     ppb_MS_GET_init(ppbus, vpo->vpo_dev, vpo->vpo_nibble_inbyte_msq);
633                     ppb_MS_PUT_init(ppbus, vpo->vpo_dev, spp_outbyte_submicroseq);
634                     kprintf("vpo%d: NIBBLE mode\n", vpo->vpo_unit);
635                     break;
636           default:
637                     panic("vpo: unknown mode %d", vpo->vpo_mode_found);
638           }
639 
640           ppb_release_bus(ppbus, vpo->vpo_dev);
641 
642 error:
643           return (error);
644 }
645 
646 /*
647  * vpoio_reset_bus()
648  *
649  */
650 int
vpoio_reset_bus(struct vpoio_data * vpo)651 vpoio_reset_bus(struct vpoio_data *vpo)
652 {
653           /* first, connect to the drive */
654           if (vpoio_connect(vpo, PPB_WAIT|PPB_INTR) || !vpoio_in_disk_mode(vpo)) {
655 
656 #ifdef VP0_DEBUG
657                     kprintf("%s: not in disk mode!\n", __func__);
658 #endif
659                     /* release ppbus */
660                     vpoio_disconnect(vpo);
661                     return (1);
662           }
663 
664           /* reset the SCSI bus */
665           vpoio_reset(vpo);
666 
667           /* then disconnect */
668           vpoio_disconnect(vpo);
669 
670           return (0);
671 }
672 
673 /*
674  * vpoio_do_scsi()
675  *
676  * Send an SCSI command
677  *
678  */
679 int
vpoio_do_scsi(struct vpoio_data * vpo,int host,int target,char * command,int clen,char * buffer,int blen,int * result,int * count,int * ret)680 vpoio_do_scsi(struct vpoio_data *vpo, int host, int target, char *command,
681                     int clen, char *buffer, int blen, int *result, int *count,
682                     int *ret)
683 {
684           device_t ppbus = device_get_parent(vpo->vpo_dev);
685           char r;
686           char l, h = 0;
687           int len, error = 0;
688           int k;
689 
690           /*
691            * enter disk state, allocate the ppbus
692            *
693            * XXX
694            * Should we allow this call to be interruptible?
695            * The only way to report the interruption is to return
696            * EIO do upper SCSI code :^(
697            */
698           if ((error = vpoio_connect(vpo, PPB_WAIT|PPB_INTR)))
699                     return (error);
700 
701           if (!vpoio_in_disk_mode(vpo)) {
702                     *ret = VP0_ECONNECT; goto error;
703           }
704 
705           if ((*ret = vpoio_select(vpo,host,target)))
706                     goto error;
707 
708           /*
709            * Send the command ...
710            *
711            * set H_SELIN low for vpoio_wait().
712            */
713           ppb_wctr(ppbus, H_AUTO | H_nSELIN | H_INIT | H_STROBE);
714 
715           for (k = 0; k < clen; k++) {
716                     if (vpoio_wait(vpo, VP0_FAST_SPINTMO) != (char)0xe0) {
717                               *ret = VP0_ECMD_TIMEOUT;
718                               goto error;
719                     }
720                     if (vpoio_outstr(vpo, &command[k], 1)) {
721                               *ret = VP0_EPPDATA_TIMEOUT;
722                               goto error;
723                     }
724           }
725 
726           /*
727            * Completion ...
728            */
729 
730           *count = 0;
731           for (;;) {
732 
733                     if (!(r = vpoio_wait(vpo, VP0_LOW_SPINTMO))) {
734                               *ret = VP0_ESTATUS_TIMEOUT; goto error;
735                     }
736 
737                     /* stop when the ZIP wants to send status */
738                     if (r == (char)0xf0)
739                               break;
740 
741                     if (*count >= blen) {
742                               *ret = VP0_EDATA_OVERFLOW;
743                               goto error;
744                     }
745 
746                     /* if in EPP mode or writing bytes, try to transfer a sector
747                      * otherwise, just send one byte
748                      */
749                     if (PPB_IN_EPP_MODE(ppbus) || r == (char)0xc0)
750                               len = (((blen - *count) >= VP0_SECTOR_SIZE)) ?
751                                         VP0_SECTOR_SIZE : 1;
752                     else
753                               len = 1;
754 
755                     /* ZIP wants to send data? */
756                     if (r == (char)0xc0)
757                               error = vpoio_outstr(vpo, &buffer[*count], len);
758                     else
759                               error = vpoio_instr(vpo, &buffer[*count], len);
760 
761                     if (error) {
762                               *ret = error;
763                               goto error;
764                     }
765 
766                     *count += len;
767           }
768 
769           if (vpoio_instr(vpo, &l, 1)) {
770                     *ret = VP0_EOTHER; goto error;
771           }
772 
773           /* check if the ZIP wants to send more status */
774           if (vpoio_wait(vpo, VP0_FAST_SPINTMO) == (char)0xf0)
775                     if (vpoio_instr(vpo, &h, 1)) {
776                               *ret = VP0_EOTHER+2; goto error;
777                     }
778 
779           *result = ((int) h << 8) | ((int) l & 0xff);
780 
781 error:
782           /* return to printer state, release the ppbus */
783           vpoio_disconnect(vpo);
784           return (0);
785 }
786