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