immio.c revision 59368
1326949Sdim/*-
2326949Sdim * Copyright (c) 1998, 1999 Nicolas Souchu
3353358Sdim * All rights reserved.
4353358Sdim *
5353358Sdim * Redistribution and use in source and binary forms, with or without
6326949Sdim * modification, are permitted provided that the following conditions
7326949Sdim * are met:
8326949Sdim * 1. Redistributions of source code must retain the above copyright
9326949Sdim *    notice, this list of conditions and the following disclaimer.
10326949Sdim * 2. Redistributions in binary form must reproduce the above copyright
11326949Sdim *    notice, this list of conditions and the following disclaimer in the
12326949Sdim *    documentation and/or other materials provided with the distribution.
13344779Sdim *
14326949Sdim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15326949Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16326949Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17326949Sdim * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18353358Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19353358Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20326949Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21326949Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22326949Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23326949Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24326949Sdim * SUCH DAMAGE.
25326949Sdim *
26353358Sdim * $FreeBSD: head/sys/dev/ppbus/immio.c 59368 2000-04-18 15:15:39Z phk $
27353358Sdim *
28326949Sdim */
29326949Sdim
30326949Sdim/*
31326949Sdim * Iomega ZIP+ Matchmaker Parallel Port Interface driver
32326949Sdim *
33353358Sdim * Thanks to David Campbell work on the Linux driver and the Iomega specs
34353358Sdim * Thanks to Thiebault Moeglin for the drive
35326949Sdim */
36326949Sdim#ifdef _KERNEL
37326949Sdim#include <sys/param.h>
38326949Sdim#include <sys/systm.h>
39353358Sdim#include <sys/module.h>
40353358Sdim#include <sys/bus.h>
41326949Sdim#include <sys/malloc.h>
42326949Sdim
43326949Sdim#include <machine/clock.h>
44326949Sdim
45353358Sdim#endif	/* _KERNEL */
46353358Sdim
47326949Sdim#ifdef	_KERNEL
48326949Sdim#include <sys/kernel.h>
49326949Sdim#endif /* _KERNEL */
50326949Sdim
51326949Sdim#include "opt_vpo.h"
52326949Sdim
53326949Sdim#include <dev/ppbus/ppbio.h>
54326949Sdim#include <dev/ppbus/ppbconf.h>
55326949Sdim#include <dev/ppbus/ppb_msq.h>
56326949Sdim#include <dev/ppbus/vpoio.h>
57326949Sdim#include <dev/ppbus/ppb_1284.h>
58326949Sdim
59326949Sdim#include "ppbus_if.h"
60326949Sdim
61326949Sdim#define VP0_SELTMO		5000	/* select timeout */
62326949Sdim#define VP0_FAST_SPINTMO	500000	/* wait status timeout */
63326949Sdim#define VP0_LOW_SPINTMO		5000000	/* wait status timeout */
64326949Sdim
65326949Sdim#define VP0_SECTOR_SIZE	512
66326949Sdim
67326949Sdim/*
68326949Sdim * Microcode to execute very fast I/O sequences at the lowest bus level.
69326949Sdim */
70326949Sdim
71326949Sdim#define SELECT_TARGET		MS_PARAM(6, 1, MS_TYP_CHA)
72326949Sdim
73326949Sdim#define DECLARE_SELECT_MICROSEQUENCE					\
74326949Sdimstruct ppb_microseq select_microseq[] = {				\
75326949Sdim	MS_CASS(0xc),							\
76326949Sdim	/* first, check there is nothing holding onto the bus */	\
77326949Sdim	MS_SET(VP0_SELTMO),						\
78326949Sdim/* _loop: */								\
79326949Sdim	MS_BRCLEAR(0x8, 2 /* _ready */),				\
80326949Sdim	MS_DBRA(-2 /* _loop */),					\
81326949Sdim	MS_RET(2),			/* bus busy */			\
82326949Sdim/* _ready: */								\
83326949Sdim	MS_CASS(0x4),							\
84326949Sdim	MS_DASS(MS_UNKNOWN /* 0x80 | 1 << target */),			\
85326949Sdim	MS_DELAY(1),							\
86326949Sdim	MS_CASS(0xc),							\
87326949Sdim	MS_CASS(0xd),							\
88326949Sdim	/* now, wait until the drive is ready */			\
89326949Sdim	MS_SET(VP0_SELTMO),						\
90326949Sdim/* loop: */								\
91326949Sdim	MS_BRSET(0x8, 3 /* ready */),					\
92326949Sdim	MS_DBRA(-2 /* loop */),						\
93326949Sdim/* error: */								\
94326949Sdim	MS_CASS(0xc),							\
95326949Sdim	MS_RET(VP0_ESELECT_TIMEOUT),					\
96326949Sdim/* ready: */								\
97326949Sdim	MS_CASS(0xc),							\
98326949Sdim	MS_RET(0)							\
99326949Sdim}
100326949Sdim
101326949Sdimstatic struct ppb_microseq transfer_epilog[] = {
102326949Sdim	MS_CASS(0x4),
103326949Sdim	MS_CASS(0xc),
104326949Sdim	MS_CASS(0xe),
105326949Sdim	MS_CASS(0x4),
106326949Sdim	MS_RET(0)
107326949Sdim};
108326949Sdim
109326949Sdim#define CPP_S1		MS_PARAM(10, 2, MS_TYP_PTR)
110326949Sdim#define CPP_S2		MS_PARAM(13, 2, MS_TYP_PTR)
111326949Sdim#define CPP_S3		MS_PARAM(16, 2, MS_TYP_PTR)
112326949Sdim#define CPP_PARAM	MS_PARAM(17, 1, MS_TYP_CHA)
113326949Sdim
114326949Sdim#define DECLARE_CPP_MICROSEQ \
115326949Sdimstruct ppb_microseq cpp_microseq[] = {					\
116326949Sdim	MS_CASS(0x0c), MS_DELAY(2),					\
117326949Sdim	MS_DASS(0xaa), MS_DELAY(10),					\
118326949Sdim	MS_DASS(0x55), MS_DELAY(10),					\
119326949Sdim	MS_DASS(0x00), MS_DELAY(10),					\
120326949Sdim	MS_DASS(0xff), MS_DELAY(10),					\
121326949Sdim	MS_RFETCH(MS_REG_STR, 0xb8, MS_UNKNOWN /* &s1 */),		\
122326949Sdim	MS_DASS(0x87), MS_DELAY(10),					\
123326949Sdim	MS_RFETCH(MS_REG_STR, 0xb8, MS_UNKNOWN /* &s2 */),		\
124326949Sdim	MS_DASS(0x78), MS_DELAY(10),					\
125326949Sdim	MS_RFETCH(MS_REG_STR, 0x38, MS_UNKNOWN /* &s3 */),		\
126326949Sdim	MS_DASS(MS_UNKNOWN /* param */),				\
127326949Sdim	MS_DELAY(2),							\
128326949Sdim	MS_CASS(0x0c), MS_DELAY(10),					\
129326949Sdim	MS_CASS(0x0d), MS_DELAY(2),					\
130326949Sdim	MS_CASS(0x0c), MS_DELAY(10),					\
131326949Sdim	MS_DASS(0xff), MS_DELAY(10),					\
132326949Sdim	MS_RET(0)							\
133326949Sdim}
134
135#define NEGOCIATED_MODE		MS_PARAM(2, 1, MS_TYP_CHA)
136
137#define DECLARE_NEGOCIATE_MICROSEQ \
138static struct ppb_microseq negociate_microseq[] = { 			\
139	MS_CASS(0x4),							\
140	MS_DELAY(5),							\
141	MS_DASS(MS_UNKNOWN /* mode */),					\
142	MS_DELAY(100),							\
143	MS_CASS(0x6),							\
144	MS_DELAY(5),							\
145	MS_BRSET(0x20, 5 /* continue */),				\
146	MS_DELAY(5),							\
147	MS_CASS(0x7),							\
148	MS_DELAY(5),							\
149	MS_CASS(0x6),							\
150	MS_RET(VP0_ENEGOCIATE),						\
151/* continue: */								\
152	MS_DELAY(5),							\
153	MS_CASS(0x7),							\
154	MS_DELAY(5),							\
155	MS_CASS(0x6),							\
156	MS_RET(0)							\
157}
158
159static struct ppb_microseq reset_microseq[] = {
160	MS_CASS(0x04),
161	MS_DASS(0x40),
162	MS_DELAY(1),
163	MS_CASS(0x0c),
164	MS_CASS(0x0d),
165	MS_DELAY(50),
166	MS_CASS(0x0c),
167	MS_CASS(0x04),
168	MS_RET(0)
169};
170
171/*
172 * nibble_inbyte_hook()
173 *
174 * Formats high and low nibble into a character
175 */
176static int
177nibble_inbyte_hook (void *p, char *ptr)
178{
179	struct vpo_nibble *s = (struct vpo_nibble *)p;
180
181	/* increment the buffer pointer */
182	*ptr = ((s->l >> 4) & 0x0f) + (s->h & 0xf0);
183
184	return (0);
185}
186
187/*
188 * Macro used to initialize each vpoio_data structure during
189 * low level attachment
190 *
191 * XXX should be converted to ppb_MS_init_msq()
192 */
193#define INIT_NIBBLE_INBYTE_SUBMICROSEQ(vpo) {		    	\
194	(vpo)->vpo_nibble_inbyte_msq[6].arg[2].p =		\
195			(void *)&(vpo)->vpo_nibble.h;		\
196	(vpo)->vpo_nibble_inbyte_msq[3].arg[2].p =		\
197			(void *)&(vpo)->vpo_nibble.l;		\
198	(vpo)->vpo_nibble_inbyte_msq[9].arg[0].f =		\
199			nibble_inbyte_hook;			\
200	(vpo)->vpo_nibble_inbyte_msq[9].arg[1].p =		\
201			(void *)&(vpo)->vpo_nibble;		\
202}
203
204/*
205 * This is the sub-microseqence for MS_GET in NIBBLE mode
206 * Retrieve the two nibbles and call the C function to generate the character
207 * and store it in the buffer (see nibble_inbyte_hook())
208 */
209static struct ppb_microseq nibble_inbyte_submicroseq[] = {
210	  MS_CASS(0x4),
211
212/* loop: */
213	  MS_CASS(0x6),
214	  MS_DELAY(1),
215	  MS_RFETCH(MS_REG_STR, MS_FETCH_ALL, MS_UNKNOWN /* low nibble */),
216	  MS_CASS(0x5),
217	  MS_DELAY(1),
218	  MS_RFETCH(MS_REG_STR, MS_FETCH_ALL, MS_UNKNOWN /* high nibble */),
219	  MS_CASS(0x4),
220	  MS_DELAY(1),
221
222	  /* do a C call to format the received nibbles */
223	  MS_C_CALL(MS_UNKNOWN /* C hook */, MS_UNKNOWN /* param */),
224	  MS_DBRA(-7 /* loop */),
225	  MS_RET(0)
226};
227
228/*
229 * This is the sub-microseqence for MS_GET in PS2 mode
230 */
231static struct ppb_microseq ps2_inbyte_submicroseq[] = {
232	  MS_CASS(0x4),
233
234/* loop: */
235	  MS_CASS(PCD | 0x6),
236	  MS_RFETCH_P(1, MS_REG_DTR, MS_FETCH_ALL),
237	  MS_CASS(PCD | 0x5),
238	  MS_DBRA(-4 /* loop */),
239
240	  MS_RET(0)
241};
242
243/*
244 * This is the sub-microsequence for MS_PUT in both NIBBLE and PS2 modes
245 */
246static struct ppb_microseq spp_outbyte_submicroseq[] = {
247	  MS_CASS(0x4),
248
249/* loop: */
250	  MS_RASSERT_P(1, MS_REG_DTR),
251	  MS_CASS(0x5),
252	  MS_DBRA(0),				/* decrement counter */
253	  MS_RASSERT_P(1, MS_REG_DTR),
254	  MS_CASS(0x0),
255	  MS_DBRA(-6 /* loop */),
256
257	  /* return from the put call */
258	  MS_CASS(0x4),
259	  MS_RET(0)
260};
261
262/* EPP 1.7 microsequences, ptr and len set at runtime */
263static struct ppb_microseq epp17_outstr[] = {
264	  MS_CASS(0x4),
265	  MS_RASSERT_P(MS_ACCUM, MS_REG_EPP_D),
266	  MS_CASS(0xc),
267	  MS_RET(0),
268};
269
270static struct ppb_microseq epp17_instr[] = {
271	  MS_CASS(PCD | 0x4),
272	  MS_RFETCH_P(MS_ACCUM, MS_REG_EPP_D, MS_FETCH_ALL),
273	  MS_CASS(PCD | 0xc),
274	  MS_RET(0),
275};
276
277static int
278imm_disconnect(struct vpoio_data *vpo, int *connected, int release_bus)
279{
280	DECLARE_CPP_MICROSEQ;
281
282	device_t ppbus = device_get_parent(vpo->vpo_dev);
283	char s1, s2, s3;
284	int ret;
285
286	/* all should be ok */
287	if (connected)
288		*connected = 0;
289
290	ppb_MS_init_msq(cpp_microseq, 4, CPP_S1, (void *)&s1,
291			CPP_S2, (void *)&s2, CPP_S3, (void *)&s3,
292			CPP_PARAM, 0x30);
293
294	ppb_MS_microseq(ppbus, vpo->vpo_dev, cpp_microseq, &ret);
295
296	if ((s1 != (char)0xb8 || s2 != (char)0x18 || s3 != (char)0x38)) {
297		if (bootverbose)
298			printf("imm%d: (disconnect) s1=0x%x s2=0x%x, s3=0x%x\n",
299				vpo->vpo_unit, s1 & 0xff, s2 & 0xff, s3 & 0xff);
300		if (connected)
301			*connected = VP0_ECONNECT;
302	}
303
304	if (release_bus)
305		return (ppb_release_bus(ppbus, vpo->vpo_dev));
306	else
307		return (0);
308}
309
310/*
311 * how	: PPB_WAIT or PPB_DONTWAIT
312 */
313static int
314imm_connect(struct vpoio_data *vpo, int how, int *disconnected, int request_bus)
315{
316	DECLARE_CPP_MICROSEQ;
317
318	device_t ppbus = device_get_parent(vpo->vpo_dev);
319	char s1, s2, s3;
320	int error;
321	int ret;
322
323	/* all should be ok */
324	if (disconnected)
325		*disconnected = 0;
326
327	if (request_bus)
328		if ((error = ppb_request_bus(ppbus, vpo->vpo_dev, how)))
329			return (error);
330
331	ppb_MS_init_msq(cpp_microseq, 3, CPP_S1, (void *)&s1,
332			CPP_S2, (void *)&s2, CPP_S3, (void *)&s3);
333
334	/* select device 0 in compatible mode */
335	ppb_MS_init_msq(cpp_microseq, 1, CPP_PARAM, 0xe0);
336	ppb_MS_microseq(ppbus, vpo->vpo_dev, cpp_microseq, &ret);
337
338	/* disconnect all devices */
339	ppb_MS_init_msq(cpp_microseq, 1, CPP_PARAM, 0x30);
340	ppb_MS_microseq(ppbus, vpo->vpo_dev, cpp_microseq, &ret);
341
342	if (PPB_IN_EPP_MODE(ppbus))
343		ppb_MS_init_msq(cpp_microseq, 1, CPP_PARAM, 0x28);
344	else
345		ppb_MS_init_msq(cpp_microseq, 1, CPP_PARAM, 0xe0);
346
347	ppb_MS_microseq(ppbus, vpo->vpo_dev, cpp_microseq, &ret);
348
349	if ((s1 != (char)0xb8 || s2 != (char)0x18 || s3 != (char)0x30)) {
350		if (bootverbose)
351			printf("imm%d: (connect) s1=0x%x s2=0x%x, s3=0x%x\n",
352				vpo->vpo_unit, s1 & 0xff, s2 & 0xff, s3 & 0xff);
353		if (disconnected)
354			*disconnected = VP0_ECONNECT;
355	}
356
357	return (0);
358}
359
360/*
361 * imm_detect()
362 *
363 * Detect and initialise the VP0 adapter.
364 */
365static int
366imm_detect(struct vpoio_data *vpo)
367{
368	device_t ppbus = device_get_parent(vpo->vpo_dev);
369	int error;
370
371	if ((error = ppb_request_bus(ppbus, vpo->vpo_dev, PPB_DONTWAIT)))
372		return (error);
373
374	/* disconnect the drive, keep the bus */
375	imm_disconnect(vpo, NULL, 0);
376
377	/* we already have the bus, just connect */
378	imm_connect(vpo, PPB_DONTWAIT, &error, 0);
379
380	if (error) {
381		if (bootverbose)
382			printf("imm%d: can't connect to the drive\n",
383				vpo->vpo_unit);
384		goto error;
385	}
386
387	/* send SCSI reset signal */
388	ppb_MS_microseq(ppbus, vpo->vpo_dev, reset_microseq, NULL);
389
390	/* release the bus now */
391	imm_disconnect(vpo, &error, 1);
392
393	/* ensure we are disconnected or daisy chained peripheral
394	 * may cause serious problem to the disk */
395
396	if (error) {
397		if (bootverbose)
398			printf("imm%d: can't disconnect from the drive\n",
399				vpo->vpo_unit);
400		goto error;
401	}
402
403	return (0);
404
405error:
406	ppb_release_bus(ppbus, vpo->vpo_dev);
407	return (VP0_EINITFAILED);
408}
409
410/*
411 * imm_outstr()
412 */
413static int
414imm_outstr(struct vpoio_data *vpo, char *buffer, int size)
415{
416	device_t ppbus = device_get_parent(vpo->vpo_dev);
417	int error = 0;
418
419	if (PPB_IN_EPP_MODE(ppbus))
420		ppb_reset_epp_timeout(ppbus);
421
422	ppb_MS_exec(ppbus, vpo->vpo_dev, MS_OP_PUT, (union ppb_insarg)buffer,
423		(union ppb_insarg)size, (union ppb_insarg)MS_UNKNOWN, &error);
424
425	return (error);
426}
427
428/*
429 * imm_instr()
430 */
431static int
432imm_instr(struct vpoio_data *vpo, char *buffer, int size)
433{
434	device_t ppbus = device_get_parent(vpo->vpo_dev);
435	int error = 0;
436
437	if (PPB_IN_EPP_MODE(ppbus))
438		ppb_reset_epp_timeout(ppbus);
439
440	ppb_MS_exec(ppbus, vpo->vpo_dev, MS_OP_GET, (union ppb_insarg)buffer,
441		(union ppb_insarg)size, (union ppb_insarg)MS_UNKNOWN, &error);
442
443	return (error);
444}
445
446static char
447imm_select(struct vpoio_data *vpo, int initiator, int target)
448{
449	DECLARE_SELECT_MICROSEQUENCE;
450	device_t ppbus = device_get_parent(vpo->vpo_dev);
451	int ret;
452
453	/* initialize the select microsequence */
454	ppb_MS_init_msq(select_microseq, 1,
455			SELECT_TARGET, 1 << initiator | 1 << target);
456
457	ppb_MS_microseq(ppbus, vpo->vpo_dev, select_microseq, &ret);
458
459	return (ret);
460}
461
462/*
463 * imm_wait()
464 *
465 * H_SELIN must be low.
466 *
467 * XXX should be ported to microseq
468 */
469static char
470imm_wait(struct vpoio_data *vpo, int tmo)
471{
472	device_t ppbus = device_get_parent(vpo->vpo_dev);
473	register int	k;
474	register char	r;
475
476	ppb_wctr(ppbus, 0xc);
477
478	/* XXX should be ported to microseq */
479	k = 0;
480	while (!((r = ppb_rstr(ppbus)) & 0x80) && (k++ < tmo))
481		DELAY(1);
482
483	/*
484	 * Return some status information.
485	 * Semantics :	0x88 = ZIP+ wants more data
486	 *		0x98 = ZIP+ wants to send more data
487	 *		0xa8 = ZIP+ wants command
488	 *		0xb8 = end of transfer, ZIP+ is sending status
489	 */
490	ppb_wctr(ppbus, 0x4);
491	if (k < tmo)
492	  return (r & 0xb8);
493
494	return (0);			   /* command timed out */
495}
496
497static int
498imm_negociate(struct vpoio_data *vpo)
499{
500	DECLARE_NEGOCIATE_MICROSEQ;
501	device_t ppbus = device_get_parent(vpo->vpo_dev);
502	int negociate_mode;
503	int ret;
504
505	if (PPB_IN_NIBBLE_MODE(ppbus))
506		negociate_mode = 0;
507	else if (PPB_IN_PS2_MODE(ppbus))
508		negociate_mode = 1;
509	else
510		return (0);
511
512#if 0 /* XXX use standalone code not to depend on ppb_1284 code yet */
513	ret = ppb_1284_negociate(ppbus, negociate_mode);
514
515	if (ret)
516		return (VP0_ENEGOCIATE);
517#endif
518
519	ppb_MS_init_msq(negociate_microseq, 1,
520			NEGOCIATED_MODE, negociate_mode);
521
522	ppb_MS_microseq(ppbus, vpo->vpo_dev, negociate_microseq, &ret);
523
524	return (ret);
525}
526
527/*
528 * imm_probe()
529 *
530 * Low level probe of vpo device
531 *
532 */
533int
534imm_probe(device_t dev, struct vpoio_data *vpo)
535{
536	int error;
537
538	/* ppbus dependent initialisation */
539	vpo->vpo_dev = dev;
540
541	/* now, try to initialise the drive */
542	if ((error = imm_detect(vpo))) {
543		return (error);
544	}
545
546	return (0);
547}
548
549/*
550 * imm_attach()
551 *
552 * Low level attachment of vpo device
553 *
554 */
555int
556imm_attach(struct vpoio_data *vpo)
557{
558	device_t ppbus = device_get_parent(vpo->vpo_dev);
559	int epp;
560
561	/*
562	 * Initialize microsequence code
563	 */
564	vpo->vpo_nibble_inbyte_msq = (struct ppb_microseq *)malloc(
565		sizeof(nibble_inbyte_submicroseq), M_DEVBUF, M_NOWAIT);
566
567	if (!vpo->vpo_nibble_inbyte_msq)
568		return (ENXIO);
569
570	bcopy((void *)nibble_inbyte_submicroseq,
571		(void *)vpo->vpo_nibble_inbyte_msq,
572		sizeof(nibble_inbyte_submicroseq));
573
574	INIT_NIBBLE_INBYTE_SUBMICROSEQ(vpo);
575
576	/*
577	 * Initialize mode dependent in/out microsequences
578	 */
579	ppb_request_bus(ppbus, vpo->vpo_dev, PPB_WAIT);
580
581	/* enter NIBBLE mode to configure submsq */
582	if (ppb_set_mode(ppbus, PPB_NIBBLE) != -1) {
583
584		ppb_MS_GET_init(ppbus, vpo->vpo_dev, vpo->vpo_nibble_inbyte_msq);
585		ppb_MS_PUT_init(ppbus, vpo->vpo_dev, spp_outbyte_submicroseq);
586	}
587
588	/* enter PS2 mode to configure submsq */
589	if (ppb_set_mode(ppbus, PPB_PS2) != -1) {
590
591		ppb_MS_GET_init(ppbus, vpo->vpo_dev, ps2_inbyte_submicroseq);
592		ppb_MS_PUT_init(ppbus, vpo->vpo_dev, spp_outbyte_submicroseq);
593	}
594
595	epp = ppb_get_epp_protocol(ppbus);
596
597	/* enter EPP mode to configure submsq */
598	if (ppb_set_mode(ppbus, PPB_EPP) != -1) {
599
600		switch (epp) {
601		case EPP_1_9:
602		case EPP_1_7:
603			ppb_MS_GET_init(ppbus, vpo->vpo_dev, epp17_instr);
604			ppb_MS_PUT_init(ppbus, vpo->vpo_dev, epp17_outstr);
605			break;
606		default:
607			panic("%s: unknown EPP protocol (0x%x)", __FUNCTION__,
608				epp);
609		}
610	}
611
612	/* try to enter EPP or PS/2 mode, NIBBLE otherwise */
613	if (ppb_set_mode(ppbus, PPB_EPP) != -1) {
614		switch (epp) {
615		case EPP_1_9:
616			printf("imm%d: EPP 1.9 mode\n", vpo->vpo_unit);
617			break;
618		case EPP_1_7:
619			printf("imm%d: EPP 1.7 mode\n", vpo->vpo_unit);
620			break;
621		default:
622			panic("%s: unknown EPP protocol (0x%x)", __FUNCTION__,
623				epp);
624		}
625	} else if (ppb_set_mode(ppbus, PPB_PS2) != -1)
626		printf("imm%d: PS2 mode\n", vpo->vpo_unit);
627
628	else if (ppb_set_mode(ppbus, PPB_NIBBLE) != -1)
629		printf("imm%d: NIBBLE mode\n", vpo->vpo_unit);
630
631	else {
632		printf("imm%d: can't enter NIBBLE, PS2 or EPP mode\n",
633			vpo->vpo_unit);
634
635		ppb_release_bus(ppbus, vpo->vpo_dev);
636
637		free(vpo->vpo_nibble_inbyte_msq, M_DEVBUF);
638		return (ENXIO);
639	}
640
641	ppb_release_bus(ppbus, vpo->vpo_dev);
642
643	return (0);
644}
645
646/*
647 * imm_reset_bus()
648 *
649 */
650int
651imm_reset_bus(struct vpoio_data *vpo)
652{
653	device_t ppbus = device_get_parent(vpo->vpo_dev);
654	int disconnected;
655
656	/* first, connect to the drive and request the bus */
657	imm_connect(vpo, PPB_WAIT|PPB_INTR, &disconnected, 1);
658
659	if (!disconnected) {
660
661		/* reset the SCSI bus */
662		ppb_MS_microseq(ppbus, vpo->vpo_dev, reset_microseq, NULL);
663
664		/* then disconnect */
665		imm_disconnect(vpo, NULL, 1);
666	}
667
668	return (0);
669}
670
671/*
672 * imm_do_scsi()
673 *
674 * Send an SCSI command
675 *
676 */
677int
678imm_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, not_connected = 0;
686	register int k;
687	int negociated = 0;
688
689	/*
690	 * enter disk state, allocate the ppbus
691	 *
692	 * XXX
693	 * Should we allow this call to be interruptible?
694	 * The only way to report the interruption is to return
695	 * EIO do upper SCSI code :^(
696	 */
697	if ((error = imm_connect(vpo, PPB_WAIT|PPB_INTR, &not_connected, 1)))
698		return (error);
699
700	if (not_connected) {
701		*ret = VP0_ECONNECT; goto error;
702	}
703
704	/*
705	 * Select the drive ...
706	 */
707	if ((*ret = imm_select(vpo,host,target)))
708		goto error;
709
710	/*
711	 * Send the command ...
712	 */
713	for (k = 0; k < clen; k+=2) {
714		if (imm_wait(vpo, VP0_FAST_SPINTMO) != (char)0xa8) {
715			*ret = VP0_ECMD_TIMEOUT;
716			goto error;
717		}
718		if (imm_outstr(vpo, &command[k], 2)) {
719			*ret = VP0_EPPDATA_TIMEOUT;
720			goto error;
721		}
722	}
723
724	if (!(r = imm_wait(vpo, VP0_LOW_SPINTMO))) {
725		*ret = VP0_ESTATUS_TIMEOUT; goto error;
726	}
727
728	if ((r & 0x30) == 0x10) {
729		if (imm_negociate(vpo)) {
730			*ret = VP0_ENEGOCIATE;
731			goto error;
732		} else
733			negociated = 1;
734	}
735
736	/*
737	 * Complete transfer ...
738	 */
739	*count = 0;
740	for (;;) {
741
742		if (!(r = imm_wait(vpo, VP0_LOW_SPINTMO))) {
743			*ret = VP0_ESTATUS_TIMEOUT; goto error;
744		}
745
746		/* stop when the ZIP+ wants to send status */
747		if (r == (char)0xb8)
748			break;
749
750		if (*count >= blen) {
751			*ret = VP0_EDATA_OVERFLOW;
752			goto error;
753		}
754
755		/* ZIP+ wants to send data? */
756		if (r == (char)0x88) {
757			len = (((blen - *count) >= VP0_SECTOR_SIZE)) ?
758				VP0_SECTOR_SIZE : 2;
759
760			error = imm_outstr(vpo, &buffer[*count], len);
761		} else {
762			if (!PPB_IN_EPP_MODE(ppbus))
763				len = 1;
764			else
765				len = (((blen - *count) >= VP0_SECTOR_SIZE)) ?
766					VP0_SECTOR_SIZE : 1;
767
768			error = imm_instr(vpo, &buffer[*count], len);
769		}
770
771		if (error) {
772			*ret = error;
773			goto error;
774		}
775
776		*count += len;
777	}
778
779	if ((PPB_IN_NIBBLE_MODE(ppbus) ||
780			PPB_IN_PS2_MODE(ppbus)) && negociated)
781		ppb_MS_microseq(ppbus, vpo->vpo_dev, transfer_epilog, NULL);
782
783	/*
784	 * Retrieve status ...
785	 */
786	if (imm_negociate(vpo)) {
787		*ret = VP0_ENEGOCIATE;
788		goto error;
789	} else
790		negociated = 1;
791
792	if (imm_instr(vpo, &l, 1)) {
793		*ret = VP0_EOTHER; goto error;
794	}
795
796	/* check if the ZIP+ wants to send more status */
797	if (imm_wait(vpo, VP0_FAST_SPINTMO) == (char)0xb8)
798		if (imm_instr(vpo, &h, 1)) {
799			*ret = VP0_EOTHER+2; goto error;
800		}
801
802	*result = ((int) h << 8) | ((int) l & 0xff);
803
804error:
805	if ((PPB_IN_NIBBLE_MODE(ppbus) ||
806			PPB_IN_PS2_MODE(ppbus)) && negociated)
807		ppb_MS_microseq(ppbus, vpo->vpo_dev, transfer_epilog, NULL);
808
809	/* return to printer state, release the ppbus */
810	imm_disconnect(vpo, NULL, 1);
811
812	return (0);
813}
814