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