vpoio.c revision 42475
138061Smsmith/*-
238061Smsmith * Copyright (c) 1998 Nicolas Souchu
338061Smsmith * All rights reserved.
438061Smsmith *
538061Smsmith * Redistribution and use in source and binary forms, with or without
638061Smsmith * modification, are permitted provided that the following conditions
738061Smsmith * are met:
838061Smsmith * 1. Redistributions of source code must retain the above copyright
938061Smsmith *    notice, this list of conditions and the following disclaimer.
1038061Smsmith * 2. Redistributions in binary form must reproduce the above copyright
1138061Smsmith *    notice, this list of conditions and the following disclaimer in the
1238061Smsmith *    documentation and/or other materials provided with the distribution.
1338061Smsmith *
1438061Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1538061Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1638061Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1738061Smsmith * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1838061Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1938061Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2038061Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2138061Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2238061Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2338061Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2438061Smsmith * SUCH DAMAGE.
2538061Smsmith *
2642475Snsouch *	$Id: vpoio.c,v 1.4 1998/12/07 21:58:16 archie Exp $
2738061Smsmith *
2838061Smsmith */
2938061Smsmith
3038061Smsmith#ifdef KERNEL
3138061Smsmith#include <sys/param.h>
3238061Smsmith#include <sys/systm.h>
3338061Smsmith#include <sys/malloc.h>
3438061Smsmith#include <sys/buf.h>
3538061Smsmith
3638061Smsmith#include <machine/clock.h>
3738061Smsmith
3838061Smsmith#endif	/* KERNEL */
3938061Smsmith
4038061Smsmith#ifdef	KERNEL
4138061Smsmith#include <sys/kernel.h>
4238061Smsmith#endif /*KERNEL */
4338061Smsmith
4442475Snsouch#include "opt_vpo.h"
4542475Snsouch
4638061Smsmith#include <dev/ppbus/ppbconf.h>
4738061Smsmith#include <dev/ppbus/ppb_msq.h>
4838061Smsmith#include <dev/ppbus/vpoio.h>
4938061Smsmith
5038061Smsmith/*
5138061Smsmith * The driver pools the drive. We may add a timeout queue to avoid
5238061Smsmith * active polling on nACK. I've tried this but it leads to unreliable
5338061Smsmith * transfers
5438061Smsmith */
5538061Smsmith#define VP0_SELTMO		5000	/* select timeout */
5638061Smsmith#define VP0_FAST_SPINTMO	500000	/* wait status timeout */
5738061Smsmith#define VP0_LOW_SPINTMO		5000000	/* wait status timeout */
5838061Smsmith
5938061Smsmith/*
6038061Smsmith * Actually, VP0 timings are more accurate (about few 16MHZ cycles),
6138061Smsmith * but succeeding in respecting such timings leads to architecture
6238061Smsmith * dependent considerations.
6338061Smsmith */
6438061Smsmith#define VP0_PULSE		1
6538061Smsmith
6638061Smsmith#define VP0_SECTOR_SIZE	512
6738061Smsmith#define VP0_BUFFER_SIZE	0x12000
6838061Smsmith
6938061Smsmith#define n(flags) (~(flags) & (flags))
7038061Smsmith
7138061Smsmith/*
7238061Smsmith * VP0 connections.
7338061Smsmith */
7438061Smsmith#define H_AUTO		n(AUTOFEED)
7538061Smsmith#define H_nAUTO		AUTOFEED
7638061Smsmith#define H_STROBE	n(STROBE)
7738061Smsmith#define H_nSTROBE	STROBE
7838061Smsmith#define H_BSY		n(nBUSY)
7938061Smsmith#define H_nBSY		nBUSY
8038061Smsmith#define H_SEL		SELECT
8138061Smsmith#define H_nSEL		n(SELECT)
8239134Snsouch#define H_ERR		PERROR
8339134Snsouch#define H_nERR		n(PERROR)
8438061Smsmith#define H_ACK		nACK
8538061Smsmith#define H_nACK		n(nACK)
8638061Smsmith#define H_FLT		nFAULT
8738061Smsmith#define H_nFLT		n(nFAULT)
8838061Smsmith#define H_SELIN		n(SELECTIN)
8938061Smsmith#define H_nSELIN	SELECTIN
9038061Smsmith#define H_INIT		nINIT
9138061Smsmith#define H_nINIT		n(nINIT)
9238061Smsmith
9338061Smsmith/*
9438061Smsmith * Microcode to execute very fast I/O sequences at the lowest bus level.
9538061Smsmith */
9638061Smsmith
9739134Snsouch/* call this macro to initialize connect/disconnect microsequences */
9839134Snsouch#define INIT_TRIG_MICROSEQ {						\
9939134Snsouch	int i;								\
10039134Snsouch	for (i=1; i <= 7; i+=2) {					\
10139134Snsouch		disconnect_microseq[i].arg[2] = (void *)d_pulse;	\
10239134Snsouch		connect_epp_microseq[i].arg[2] = 			\
10339134Snsouch		connect_spp_microseq[i].arg[2] = (void *)c_pulse;	\
10439134Snsouch	}								\
10539134Snsouch}
10639134Snsouch
10739134Snsouch#define trig_d_pulse MS_TRIG(MS_REG_CTR,5,MS_UNKNOWN /* d_pulse */)
10839134Snsouchstatic char d_pulse[] = {
10938061Smsmith	 H_AUTO | H_nSELIN | H_INIT | H_STROBE, 0,
11038061Smsmith	H_nAUTO | H_nSELIN | H_INIT | H_STROBE, VP0_PULSE,
11138061Smsmith	 H_AUTO | H_nSELIN | H_INIT | H_STROBE, 0,
11238061Smsmith	 H_AUTO |  H_SELIN | H_INIT | H_STROBE, VP0_PULSE,
11338061Smsmith	 H_AUTO | H_nSELIN | H_INIT | H_STROBE, VP0_PULSE
11438061Smsmith};
11538061Smsmith
11639134Snsouch#define trig_c_pulse MS_TRIG(MS_REG_CTR,5,MS_UNKNOWN /* c_pulse */)
11739134Snsouchstatic char c_pulse[] = {
11838061Smsmith	 H_AUTO | H_nSELIN | H_INIT | H_STROBE, 0,
11938061Smsmith	 H_AUTO |  H_SELIN | H_INIT | H_STROBE, 0,
12038061Smsmith	H_nAUTO |  H_SELIN | H_INIT | H_STROBE, VP0_PULSE,
12138061Smsmith	 H_AUTO |  H_SELIN | H_INIT | H_STROBE, 0,
12238061Smsmith	 H_AUTO | H_nSELIN | H_INIT | H_STROBE, VP0_PULSE
12338061Smsmith};
12438061Smsmith
12539134Snsouchstatic struct ppb_microseq disconnect_microseq[] = {
12638061Smsmith	  MS_DASS(0x0), trig_d_pulse, MS_DASS(0x3c), trig_d_pulse,
12738061Smsmith	  MS_DASS(0x20), trig_d_pulse, MS_DASS(0xf), trig_d_pulse, MS_RET(0)
12838061Smsmith};
12938061Smsmith
13039134Snsouchstatic struct ppb_microseq connect_epp_microseq[] = {
13138061Smsmith	  MS_DASS(0x0), trig_c_pulse, MS_DASS(0x3c), trig_c_pulse,
13238061Smsmith	  MS_DASS(0x20), trig_c_pulse, MS_DASS(0xcf), trig_c_pulse, MS_RET(0)
13338061Smsmith};
13438061Smsmith
13539134Snsouchstatic struct ppb_microseq connect_spp_microseq[] = {
13638061Smsmith	  MS_DASS(0x0), trig_c_pulse, MS_DASS(0x3c), trig_c_pulse,
13738061Smsmith	  MS_DASS(0x20), trig_c_pulse, MS_DASS(0x8f), trig_c_pulse, MS_RET(0)
13838061Smsmith};
13938061Smsmith
14038061Smsmith/*
14138061Smsmith * nibble_inbyte_hook()
14238061Smsmith *
14338061Smsmith * Formats high and low nibble into a character
14438061Smsmith */
14538061Smsmithstatic int
14638061Smsmithnibble_inbyte_hook (void *p, char *ptr)
14738061Smsmith{
14838061Smsmith	struct vpo_nibble *s = (struct vpo_nibble *)p;
14938061Smsmith
15038061Smsmith	/* increment the buffer pointer */
15138061Smsmith	*ptr++ = ((s->l >> 4) & 0x0f) + (s->h & 0xf0);
15238061Smsmith
15338061Smsmith	return (0);
15438061Smsmith}
15538061Smsmith
15638061Smsmith/*
15738061Smsmith * Macro used to initialize each vpoio_data structure during
15838061Smsmith * low level attachment
15939134Snsouch *
16039134Snsouch * XXX should be converted to ppb_MS_init_msq()
16138061Smsmith */
16238061Smsmith#define INIT_NIBBLE_INBYTE_SUBMICROSEQ(vpo) {		    	\
16338061Smsmith	(vpo)->vpo_nibble_inbyte_msq[2].arg[2].p =		\
16438061Smsmith			(void *)&(vpo)->vpo_nibble.h;		\
16538061Smsmith	(vpo)->vpo_nibble_inbyte_msq[4].arg[2].p =		\
16638061Smsmith			(void *)&(vpo)->vpo_nibble.l;		\
16738061Smsmith	(vpo)->vpo_nibble_inbyte_msq[5].arg[0].f =		\
16838061Smsmith			nibble_inbyte_hook;			\
16938061Smsmith	(vpo)->vpo_nibble_inbyte_msq[5].arg[1].p =		\
17038061Smsmith			(void *)&(vpo)->vpo_nibble;		\
17138061Smsmith}
17238061Smsmith
17338061Smsmith/*
17438061Smsmith * This is the sub-microseqence for MS_GET in NIBBLE mode
17538061Smsmith * Retrieve the two nibbles and call the C function to generate the character
17638061Smsmith * and store it in the buffer (see nibble_inbyte_hook())
17738061Smsmith */
17839134Snsouchstatic struct ppb_microseq nibble_inbyte_submicroseq[] = {
17938061Smsmith
18038061Smsmith/* loop: */
18138061Smsmith	  MS_CASS( H_AUTO | H_SELIN | H_INIT | H_STROBE),
18238061Smsmith	  MS_DELAY(VP0_PULSE),
18338061Smsmith	  MS_RFETCH(MS_REG_STR, MS_FETCH_ALL, MS_UNKNOWN /* high nibble */),
18438061Smsmith	  MS_CASS(H_nAUTO | H_SELIN | H_INIT | H_STROBE),
18538061Smsmith	  MS_RFETCH(MS_REG_STR, MS_FETCH_ALL, MS_UNKNOWN /* low nibble */),
18638061Smsmith
18738061Smsmith	  /* do a C call to format the received nibbles */
18838061Smsmith	  MS_C_CALL(MS_UNKNOWN /* C hook */, MS_UNKNOWN /* param */),
18938061Smsmith	  MS_DBRA(-6 /* loop */),
19038061Smsmith
19138061Smsmith	  MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE),
19238061Smsmith	  MS_RET(0)
19338061Smsmith};
19438061Smsmith
19538061Smsmith/*
19638061Smsmith * This is the sub-microseqence for MS_GET in PS2 mode
19738061Smsmith */
19839134Snsouchstatic struct ppb_microseq ps2_inbyte_submicroseq[] = {
19938061Smsmith	  MS_CASS(PCD | H_AUTO | H_SELIN | H_INIT | H_nSTROBE),
20038061Smsmith
20138061Smsmith/* loop: */
20238061Smsmith	  MS_RFETCH_P(1, MS_REG_DTR, MS_FETCH_ALL),
20338061Smsmith	  MS_CASS(PCD | H_nAUTO | H_SELIN | H_INIT | H_nSTROBE),
20438061Smsmith	  MS_CASS(PCD |  H_AUTO | H_SELIN | H_INIT | H_nSTROBE),
20538061Smsmith	  MS_DBRA(-3 /* loop */),
20638061Smsmith
20738061Smsmith	  MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE),
20838061Smsmith	  MS_RET(0)
20938061Smsmith};
21038061Smsmith
21138061Smsmith/*
21238061Smsmith * This is the sub-microsequence for MS_PUT in both NIBBLE and PS2 modes
21338061Smsmith */
21439134Snsouchstatic struct ppb_microseq spp_outbyte_submicroseq[] = {
21538061Smsmith
21638061Smsmith/* loop: */
21738061Smsmith	  MS_RASSERT_P(1, MS_REG_DTR),
21838061Smsmith	  MS_CASS(H_nAUTO | H_nSELIN | H_INIT | H_STROBE),
21938061Smsmith	  MS_CASS( H_AUTO | H_nSELIN | H_INIT | H_STROBE),
22038061Smsmith	  MS_DELAY(VP0_PULSE),
22138061Smsmith	  MS_DBRA(-4 /* loop */),
22238061Smsmith
22338061Smsmith	  /* return from the put call */
22438061Smsmith	  MS_RET(0)
22538061Smsmith};
22638061Smsmith
22738061Smsmith/* EPP 1.7 microsequences, ptr and len set at runtime */
22839134Snsouchstatic struct ppb_microseq epp17_outstr_body[] = {
22938061Smsmith	  MS_CASS(H_AUTO | H_SELIN | H_INIT | H_STROBE),
23038061Smsmith
23138061Smsmith/* loop: */
23238061Smsmith	  MS_RASSERT_P(1, MS_REG_EPP),
23338061Smsmith	  MS_BRSET(TIMEOUT, 4 /* error */),	/* EPP timeout? */
23438061Smsmith	  MS_DBRA(-2 /* loop */),
23538061Smsmith
23638061Smsmith	  MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE),
23738061Smsmith	  MS_RET(0),
23838061Smsmith/* error: */
23938061Smsmith	  MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE),
24038061Smsmith	  MS_RET(1)
24138061Smsmith};
24238061Smsmith
24339134Snsouchstatic struct ppb_microseq epp17_instr_body[] = {
24438061Smsmith	  MS_CASS(PCD | H_AUTO | H_SELIN | H_INIT | H_STROBE),
24538061Smsmith
24638061Smsmith/* loop: */
24738061Smsmith	  MS_RFETCH_P(1, MS_REG_EPP, MS_FETCH_ALL),
24838061Smsmith	  MS_BRSET(TIMEOUT, 4 /* error */),	/* EPP timeout? */
24938061Smsmith	  MS_DBRA(-2 /* loop */),
25038061Smsmith
25138061Smsmith	  MS_CASS(PCD | H_AUTO | H_nSELIN | H_INIT | H_STROBE),
25238061Smsmith	  MS_RET(0),
25338061Smsmith/* error: */
25438061Smsmith	  MS_CASS(PCD | H_AUTO | H_nSELIN | H_INIT | H_STROBE),
25538061Smsmith	  MS_RET(1)
25638061Smsmith};
25738061Smsmith
25839134Snsouchstatic struct ppb_microseq in_disk_mode[] = {
25939134Snsouch	  MS_CASS( H_AUTO | H_nSELIN | H_INIT | H_STROBE),
26039134Snsouch	  MS_CASS(H_nAUTO | H_nSELIN | H_INIT | H_STROBE),
26139134Snsouch
26239134Snsouch	  MS_BRCLEAR(H_FLT, 4 /* error */),
26339134Snsouch	  MS_CASS( H_AUTO | H_nSELIN | H_INIT | H_STROBE),
26439134Snsouch	  MS_BRSET(H_FLT, 2 /* error */),
26539134Snsouch
26639520Snsouch	  MS_RET(1),
26739134Snsouch/* error: */
26839520Snsouch	  MS_RET(0)
26939134Snsouch};
27039134Snsouch
27138061Smsmithstatic int
27238061Smsmithvpoio_disconnect(struct vpoio_data *vpo)
27338061Smsmith{
27438061Smsmith	int ret;
27538061Smsmith
27638061Smsmith	ppb_MS_microseq(&vpo->vpo_dev, disconnect_microseq, &ret);
27738061Smsmith	return (ppb_release_bus(&vpo->vpo_dev));
27838061Smsmith}
27938061Smsmith
28038061Smsmith/*
28138061Smsmith * how	: PPB_WAIT or PPB_DONTWAIT
28238061Smsmith */
28338061Smsmithstatic int
28438061Smsmithvpoio_connect(struct vpoio_data *vpo, int how)
28538061Smsmith{
28638061Smsmith	int error;
28738061Smsmith	int ret;
28838061Smsmith
28942475Snsouch	if ((error = ppb_request_bus(&vpo->vpo_dev, how))) {
29042475Snsouch
29142475Snsouch#ifdef VP0_DEBUG
29242475Snsouch		printf("%s: can't request bus!\n", __FUNCTION__);
29342475Snsouch#endif
29438061Smsmith		return error;
29542475Snsouch	}
29638061Smsmith
29738061Smsmith	if (PPB_IN_EPP_MODE(&vpo->vpo_dev))
29838061Smsmith		ppb_MS_microseq(&vpo->vpo_dev, connect_epp_microseq, &ret);
29938061Smsmith	else
30038061Smsmith		ppb_MS_microseq(&vpo->vpo_dev, connect_spp_microseq, &ret);
30138061Smsmith
30238061Smsmith	return (0);
30338061Smsmith}
30438061Smsmith
30538061Smsmith/*
30639134Snsouch * vpoio_reset()
30738061Smsmith *
30839134Snsouch * SCSI reset signal, the drive must be in disk mode
30938061Smsmith */
31039134Snsouchstatic void
31139134Snsouchvpoio_reset (struct vpoio_data *vpo)
31238061Smsmith{
31339134Snsouch	int ret;
31438061Smsmith
31539134Snsouch	struct ppb_microseq reset_microseq[] = {
31638061Smsmith
31739134Snsouch		#define INITIATOR	MS_PARAM(0, 1, MS_TYP_INT)
31838061Smsmith
31939134Snsouch		MS_DASS(MS_UNKNOWN),
32039134Snsouch		MS_CASS(H_AUTO | H_nSELIN | H_nINIT | H_STROBE),
32139134Snsouch		MS_DELAY(25),
32239134Snsouch		MS_CASS(H_AUTO | H_nSELIN |  H_INIT | H_STROBE),
32339134Snsouch		MS_RET(0)
32439134Snsouch	};
32538061Smsmith
32639134Snsouch	ppb_MS_init_msq(reset_microseq, 1, INITIATOR, 1 << VP0_INITIATOR);
32739134Snsouch	ppb_MS_microseq(&vpo->vpo_dev, reset_microseq, &ret);
32839134Snsouch
32939134Snsouch	return;
33038061Smsmith}
33138061Smsmith
33238061Smsmith/*
33339134Snsouch * vpoio_in_disk_mode()
33438061Smsmith */
33539134Snsouchstatic int
33639134Snsouchvpoio_in_disk_mode(struct vpoio_data *vpo)
33738061Smsmith{
33839134Snsouch	int ret;
33938061Smsmith
34039134Snsouch	ppb_MS_microseq(&vpo->vpo_dev, in_disk_mode, &ret);
34138061Smsmith
34239134Snsouch	return (ret);
34338061Smsmith}
34438061Smsmith
34538061Smsmith/*
34638061Smsmith * vpoio_detect()
34738061Smsmith *
34838061Smsmith * Detect and initialise the VP0 adapter.
34938061Smsmith */
35039134Snsouchstatic int
35138061Smsmithvpoio_detect(struct vpoio_data *vpo)
35238061Smsmith{
35339520Snsouch	int error, ret;
35438061Smsmith
35539520Snsouch	/* allocate the bus, then apply microsequences */
35639520Snsouch	if ((error = ppb_request_bus(&vpo->vpo_dev, PPB_DONTWAIT)))
35739520Snsouch                return (error);
35839520Snsouch
35939520Snsouch	ppb_MS_microseq(&vpo->vpo_dev, disconnect_microseq, &ret);
36039520Snsouch
36139520Snsouch	if (PPB_IN_EPP_MODE(&vpo->vpo_dev))
36239520Snsouch		ppb_MS_microseq(&vpo->vpo_dev, connect_epp_microseq, &ret);
36339520Snsouch	else
36439520Snsouch		ppb_MS_microseq(&vpo->vpo_dev, connect_spp_microseq, &ret);
36539520Snsouch
36639520Snsouch	ppb_MS_microseq(&vpo->vpo_dev, in_disk_mode, &ret);
36739520Snsouch	if (!ret) {
36839520Snsouch
36939520Snsouch		/* try spp mode (maybe twice or because previous mode was PS2)
37039520Snsouch		 * NIBBLE mode will be restored on next transfers if detection
37139520Snsouch		 * succeed
37239520Snsouch		 */
37339520Snsouch		ppb_set_mode(&vpo->vpo_dev, PPB_NIBBLE);
37439520Snsouch		ppb_MS_microseq(&vpo->vpo_dev, connect_spp_microseq, &ret);
37539520Snsouch
37639520Snsouch		ppb_MS_microseq(&vpo->vpo_dev, in_disk_mode, &ret);
37739520Snsouch		if (!ret) {
37839520Snsouch			if (bootverbose)
37939520Snsouch				printf("vpo%d: can't connect to the drive\n",
38039520Snsouch					vpo->vpo_unit);
38139520Snsouch
38239520Snsouch			/* disconnect and release the bus */
38339520Snsouch			ppb_MS_microseq(&vpo->vpo_dev, disconnect_microseq,
38439520Snsouch					&ret);
38539520Snsouch			goto error;
38639520Snsouch		}
38738061Smsmith	}
38838061Smsmith
38938061Smsmith	/* send SCSI reset signal */
39038061Smsmith	vpoio_reset(vpo);
39138061Smsmith
39239520Snsouch	ppb_MS_microseq(&vpo->vpo_dev, disconnect_microseq, &ret);
39338061Smsmith
39439134Snsouch	/* ensure we are disconnected or daisy chained peripheral
39539134Snsouch	 * may cause serious problem to the disk */
39639134Snsouch
39739520Snsouch	ppb_MS_microseq(&vpo->vpo_dev, in_disk_mode, &ret);
39839520Snsouch	if (ret) {
39939520Snsouch		if (bootverbose)
40039520Snsouch			printf("vpo%d: can't disconnect from the drive\n",
40139520Snsouch				vpo->vpo_unit);
40239520Snsouch		goto error;
40339520Snsouch	}
40438061Smsmith
40539520Snsouch	ppb_release_bus(&vpo->vpo_dev);
40638061Smsmith	return (0);
40739520Snsouch
40839520Snsoucherror:
40939520Snsouch	ppb_release_bus(&vpo->vpo_dev);
41039520Snsouch	return (VP0_EINITFAILED);
41138061Smsmith}
41238061Smsmith
41338061Smsmith/*
41438061Smsmith * vpoio_outstr()
41538061Smsmith */
41638061Smsmithstatic int
41738061Smsmithvpoio_outstr(struct vpoio_data *vpo, char *buffer, int size)
41838061Smsmith{
41938061Smsmith
42038061Smsmith	int error = 0;
42138061Smsmith
42238061Smsmith	ppb_MS_exec(&vpo->vpo_dev, MS_OP_PUT, buffer, size, MS_UNKNOWN, &error);
42338061Smsmith
42438061Smsmith#if 0
42538061Smsmith		/* XXX EPP 1.9 not implemented with microsequences */
42638061Smsmith		else {
42738061Smsmith
42838061Smsmith			ppb_reset_epp_timeout(&vpo->vpo_dev);
42938061Smsmith			ppb_wctr(&vpo->vpo_dev,
43038061Smsmith				H_AUTO | H_SELIN | H_INIT | H_STROBE);
43138061Smsmith
43238061Smsmith			if (((long) buffer | size) & 0x03)
43338061Smsmith				ppb_outsb_epp(&vpo->vpo_dev,
43438061Smsmith						buffer, size);
43538061Smsmith			else
43638061Smsmith				ppb_outsl_epp(&vpo->vpo_dev,
43738061Smsmith						buffer, size/4);
43838061Smsmith
43938061Smsmith			if ((ppb_rstr(&vpo->vpo_dev) & TIMEOUT)) {
44038061Smsmith				error = VP0_EPPDATA_TIMEOUT;
44138061Smsmith				goto error;
44238061Smsmith			}
44338061Smsmith
44438061Smsmith			ppb_wctr(&vpo->vpo_dev,
44538061Smsmith				H_AUTO | H_nSELIN | H_INIT | H_STROBE);
44638061Smsmith		}
44738061Smsmith#endif
44842475Snsouch	ppb_ecp_sync(&vpo->vpo_dev);
44938061Smsmith
45038061Smsmith	return (error);
45138061Smsmith}
45238061Smsmith
45338061Smsmith/*
45438061Smsmith * vpoio_instr()
45538061Smsmith */
45638061Smsmithstatic int
45738061Smsmithvpoio_instr(struct vpoio_data *vpo, char *buffer, int size)
45838061Smsmith{
45938061Smsmith	int error = 0;
46038061Smsmith
46138061Smsmith	ppb_MS_exec(&vpo->vpo_dev, MS_OP_GET, buffer, size, MS_UNKNOWN, &error);
46238061Smsmith
46338061Smsmith#if 0
46438061Smsmith		/* XXX EPP 1.9 not implemented with microsequences */
46538061Smsmith		else {
46638061Smsmith
46738061Smsmith			ppb_reset_epp_timeout(&vpo->vpo_dev);
46838061Smsmith			ppb_wctr(&vpo->vpo_dev, PCD |
46938061Smsmith				H_AUTO | H_SELIN | H_INIT | H_STROBE);
47038061Smsmith
47138061Smsmith			if (((long) buffer | size) & 0x03)
47238061Smsmith				ppb_insb_epp(&vpo->vpo_dev,
47338061Smsmith						buffer, size);
47438061Smsmith			else
47538061Smsmith				ppb_insl_epp(&vpo->vpo_dev,
47638061Smsmith						buffer, size/4);
47738061Smsmith
47838061Smsmith			if ((ppb_rstr(&vpo->vpo_dev) & TIMEOUT)) {
47938061Smsmith				error = VP0_EPPDATA_TIMEOUT;
48038061Smsmith				goto error;
48138061Smsmith			}
48238061Smsmith
48338061Smsmith			ppb_wctr(&vpo->vpo_dev, PCD |
48438061Smsmith				H_AUTO | H_nSELIN | H_INIT | H_STROBE);
48538061Smsmith		}
48638061Smsmith#endif
48742475Snsouch	ppb_ecp_sync(&vpo->vpo_dev);
48838061Smsmith
48938061Smsmith	return (error);
49038061Smsmith}
49138061Smsmith
49238061Smsmithstatic char
49338061Smsmithvpoio_select(struct vpoio_data *vpo, int initiator, int target)
49438061Smsmith{
49538061Smsmith	int ret;
49638061Smsmith
49738061Smsmith	struct ppb_microseq select_microseq[] = {
49838061Smsmith
49938061Smsmith		/* parameter list
50038061Smsmith		 */
50138061Smsmith		#define SELECT_TARGET		MS_PARAM(0, 1, MS_TYP_INT)
50238061Smsmith		#define SELECT_INITIATOR	MS_PARAM(3, 1, MS_TYP_INT)
50338061Smsmith
50438061Smsmith		/* send the select command to the drive */
50538061Smsmith		MS_DASS(MS_UNKNOWN),
50638061Smsmith		MS_CASS(H_nAUTO | H_nSELIN |  H_INIT | H_STROBE),
50738061Smsmith		MS_CASS( H_AUTO | H_nSELIN |  H_INIT | H_STROBE),
50838061Smsmith		MS_DASS(MS_UNKNOWN),
50938061Smsmith		MS_CASS( H_AUTO | H_nSELIN | H_nINIT | H_STROBE),
51038061Smsmith
51138061Smsmith		/* now, wait until the drive is ready */
51238061Smsmith		MS_SET(VP0_SELTMO),
51338061Smsmith/* loop: */	MS_BRSET(H_ACK, 3 /* ready */),
51438061Smsmith		MS_DBRA(-1 /* loop */),
51538061Smsmith/* error: */	MS_RET(1),
51638061Smsmith/* ready: */	MS_RET(0)
51738061Smsmith	};
51838061Smsmith
51938061Smsmith	/* initialize the select microsequence */
52038061Smsmith	ppb_MS_init_msq(select_microseq, 2,
52138061Smsmith			SELECT_TARGET, 1 << target,
52238061Smsmith			SELECT_INITIATOR, 1 << initiator);
52338061Smsmith
52438061Smsmith	ppb_MS_microseq(&vpo->vpo_dev, select_microseq, &ret);
52538061Smsmith
52638061Smsmith	if (ret)
52738061Smsmith		return (VP0_ESELECT_TIMEOUT);
52838061Smsmith
52938061Smsmith	return (0);
53038061Smsmith}
53138061Smsmith
53238061Smsmith/*
53338061Smsmith * vpoio_wait()
53438061Smsmith *
53538061Smsmith * H_SELIN must be low.
53638061Smsmith *
53738061Smsmith * XXX should be ported to microseq
53838061Smsmith */
53938061Smsmithstatic char
54038061Smsmithvpoio_wait(struct vpoio_data *vpo, int tmo)
54138061Smsmith{
54238061Smsmith
54338061Smsmith	register int	k;
54438061Smsmith	register char	r;
54538061Smsmith
54638061Smsmith#if 0	/* broken */
54738061Smsmith	if (ppb_poll_device(&vpo->vpo_dev, 150, nBUSY, nBUSY, PPB_INTR))
54838061Smsmith		return (0);
54938061Smsmith
55038061Smsmith	return (ppb_rstr(&vpo->vpo_dev) & 0xf0);
55138061Smsmith#endif
55238061Smsmith
55338061Smsmith	/* XXX should be ported to microseq */
55438061Smsmith	k = 0;
55538061Smsmith	while (!((r = ppb_rstr(&vpo->vpo_dev)) & nBUSY) && (k++ < tmo))
55638061Smsmith		;
55738061Smsmith
55838061Smsmith	/*
55938061Smsmith	 * Return some status information.
56038061Smsmith	 * Semantics :	0xc0 = ZIP wants more data
56138061Smsmith	 *		0xd0 = ZIP wants to send more data
56238061Smsmith	 *		0xe0 = ZIP wants command
56338061Smsmith	 *		0xf0 = end of transfer, ZIP is sending status
56438061Smsmith	 */
56538061Smsmith	if (k < tmo)
56638061Smsmith	  return (r & 0xf0);
56738061Smsmith
56838061Smsmith	return (0);			   /* command timed out */
56938061Smsmith}
57038061Smsmith
57138061Smsmith/*
57238061Smsmith * vpoio_probe()
57338061Smsmith *
57438061Smsmith * Low level probe of vpo device
57538061Smsmith *
57638061Smsmith */
57738061Smsmithstruct ppb_device *
57838061Smsmithvpoio_probe(struct ppb_data *ppb, struct vpoio_data *vpo)
57938061Smsmith{
58038061Smsmith
58138061Smsmith	/* ppbus dependent initialisation */
58238061Smsmith	vpo->vpo_dev.id_unit = vpo->vpo_unit;
58338061Smsmith	vpo->vpo_dev.name = "vpo";
58438061Smsmith	vpo->vpo_dev.ppb = ppb;
58538061Smsmith
58639134Snsouch	/*
58739134Snsouch	 * Initialize microsequence code
58839134Snsouch	 */
58939134Snsouch	INIT_TRIG_MICROSEQ;
59039134Snsouch
59138061Smsmith	/* now, try to initialise the drive */
59238061Smsmith	if (vpoio_detect(vpo)) {
59338061Smsmith		return (NULL);
59438061Smsmith	}
59538061Smsmith
59638061Smsmith	return (&vpo->vpo_dev);
59738061Smsmith}
59838061Smsmith
59938061Smsmith/*
60038061Smsmith * vpoio_attach()
60138061Smsmith *
60238061Smsmith * Low level attachment of vpo device
60338061Smsmith *
60438061Smsmith */
60538061Smsmithint
60638061Smsmithvpoio_attach(struct vpoio_data *vpo)
60738061Smsmith{
60838061Smsmith	int epp;
60938061Smsmith
61038061Smsmith	/*
61138061Smsmith	 * Report ourselves
61238061Smsmith	 */
61339134Snsouch	printf("vpo%d: <Iomega VPI0 Parallel to SCSI interface> on ppbus %d\n",
61438061Smsmith		vpo->vpo_dev.id_unit, vpo->vpo_dev.ppb->ppb_link->adapter_unit);
61538061Smsmith
61638061Smsmith	vpo->vpo_nibble_inbyte_msq = (struct ppb_microseq *)malloc(
61738061Smsmith		sizeof(nibble_inbyte_submicroseq), M_DEVBUF, M_NOWAIT);
61838061Smsmith
61938061Smsmith	if (!vpo->vpo_nibble_inbyte_msq)
62038061Smsmith		return (0);
62138061Smsmith
62238061Smsmith	bcopy((void *)nibble_inbyte_submicroseq,
62338061Smsmith		(void *)vpo->vpo_nibble_inbyte_msq,
62438061Smsmith		sizeof(nibble_inbyte_submicroseq));
62538061Smsmith
62638061Smsmith	INIT_NIBBLE_INBYTE_SUBMICROSEQ(vpo);
62738061Smsmith
62838061Smsmith	/*
62938061Smsmith	 * Initialize mode dependent in/out microsequences
63038061Smsmith	 */
63138061Smsmith	ppb_request_bus(&vpo->vpo_dev, PPB_WAIT);
63238061Smsmith
63338061Smsmith	/* enter NIBBLE mode to configure submsq */
63438061Smsmith	if (ppb_set_mode(&vpo->vpo_dev, PPB_NIBBLE) != -1) {
63538061Smsmith
63638061Smsmith		ppb_MS_GET_init(&vpo->vpo_dev, vpo->vpo_nibble_inbyte_msq);
63738061Smsmith
63838061Smsmith		ppb_MS_PUT_init(&vpo->vpo_dev, spp_outbyte_submicroseq);
63938061Smsmith	}
64038061Smsmith
64138061Smsmith	/* enter PS2 mode to configure submsq */
64238061Smsmith	if (ppb_set_mode(&vpo->vpo_dev, PPB_PS2) != -1) {
64338061Smsmith
64438061Smsmith		ppb_MS_GET_init(&vpo->vpo_dev, ps2_inbyte_submicroseq);
64538061Smsmith
64638061Smsmith		ppb_MS_PUT_init(&vpo->vpo_dev, spp_outbyte_submicroseq);
64738061Smsmith	}
64838061Smsmith
64938061Smsmith	epp = ppb_get_epp_protocol(&vpo->vpo_dev);
65038061Smsmith
65138061Smsmith	/* enter EPP mode to configure submsq */
65238061Smsmith	if (ppb_set_mode(&vpo->vpo_dev, PPB_EPP) != -1) {
65338061Smsmith
65438061Smsmith		switch (epp) {
65538061Smsmith		case EPP_1_9:
65638061Smsmith			/* XXX EPP 1.9 support should be improved */
65738061Smsmith		case EPP_1_7:
65838061Smsmith			ppb_MS_GET_init(&vpo->vpo_dev, epp17_instr_body);
65938061Smsmith
66038061Smsmith			ppb_MS_PUT_init(&vpo->vpo_dev, epp17_outstr_body);
66138061Smsmith			break;
66238061Smsmith		default:
66338061Smsmith			panic("%s: unknown EPP protocol (0x%x)", __FUNCTION__,
66438061Smsmith				epp);
66538061Smsmith		}
66638061Smsmith	}
66738061Smsmith
66838061Smsmith	/* try to enter EPP or PS/2 mode, NIBBLE otherwise */
66938061Smsmith	if (ppb_set_mode(&vpo->vpo_dev, PPB_EPP) != -1) {
67038061Smsmith		switch (epp) {
67138061Smsmith		case EPP_1_9:
67238061Smsmith			printf("vpo%d: EPP 1.9 mode\n", vpo->vpo_unit);
67338061Smsmith			break;
67438061Smsmith		case EPP_1_7:
67538061Smsmith			printf("vpo%d: EPP 1.7 mode\n", vpo->vpo_unit);
67638061Smsmith			break;
67738061Smsmith		default:
67838061Smsmith			panic("%s: unknown EPP protocol (0x%x)", __FUNCTION__,
67938061Smsmith				epp);
68038061Smsmith		}
68138061Smsmith	} else if (ppb_set_mode(&vpo->vpo_dev, PPB_PS2) != -1)
68238061Smsmith		printf("vpo%d: PS2 mode\n", vpo->vpo_unit);
68338061Smsmith
68438061Smsmith	else if (ppb_set_mode(&vpo->vpo_dev, PPB_NIBBLE) != -1)
68538061Smsmith		printf("vpo%d: NIBBLE mode\n", vpo->vpo_unit);
68638061Smsmith
68738061Smsmith	else {
68838061Smsmith		printf("vpo%d: can't enter NIBBLE, PS2 or EPP mode\n",
68938061Smsmith			vpo->vpo_unit);
69038061Smsmith
69138061Smsmith		ppb_release_bus(&vpo->vpo_dev);
69238061Smsmith
69338061Smsmith		free(vpo->vpo_nibble_inbyte_msq, M_DEVBUF);
69438061Smsmith		return (0);
69538061Smsmith	}
69638061Smsmith
69738061Smsmith	ppb_release_bus(&vpo->vpo_dev);
69838061Smsmith
69938061Smsmith	return (1);
70038061Smsmith}
70138061Smsmith
70238061Smsmith/*
70338061Smsmith * vpoio_reset_bus()
70438061Smsmith *
70538061Smsmith */
70638061Smsmithint
70738061Smsmithvpoio_reset_bus(struct vpoio_data *vpo)
70838061Smsmith{
70938061Smsmith	/* first, connect to the drive */
71039520Snsouch	if (vpoio_connect(vpo, PPB_WAIT|PPB_INTR) || !vpoio_in_disk_mode(vpo)) {
71142475Snsouch
71242475Snsouch#ifdef VP0_DEBUG
71342475Snsouch		printf("%s: not in disk mode!\n", __FUNCTION__);
71442475Snsouch#endif
71538061Smsmith		/* release ppbus */
71638061Smsmith		vpoio_disconnect(vpo);
71738061Smsmith		return (1);
71838061Smsmith	}
71938061Smsmith
72038061Smsmith	/* reset the SCSI bus */
72138061Smsmith	vpoio_reset(vpo);
72238061Smsmith
72338061Smsmith	/* then disconnect */
72438061Smsmith	vpoio_disconnect(vpo);
72538061Smsmith
72638061Smsmith	return (0);
72738061Smsmith}
72838061Smsmith
72938061Smsmith/*
73038061Smsmith * vpoio_do_scsi()
73138061Smsmith *
73238061Smsmith * Send an SCSI command
73338061Smsmith *
73438061Smsmith */
73538061Smsmithint
73638061Smsmithvpoio_do_scsi(struct vpoio_data *vpo, int host, int target, char *command,
73738061Smsmith		int clen, char *buffer, int blen, int *result, int *count,
73838061Smsmith		int *ret)
73938061Smsmith{
74038061Smsmith
74138061Smsmith	register char r;
74238061Smsmith	char l, h = 0;
74338061Smsmith	int len, error = 0;
74438061Smsmith	register int k;
74538061Smsmith
74638061Smsmith	/*
74738061Smsmith	 * enter disk state, allocate the ppbus
74838061Smsmith	 *
74938061Smsmith	 * XXX
75038061Smsmith	 * Should we allow this call to be interruptible?
75138061Smsmith	 * The only way to report the interruption is to return
75238061Smsmith	 * EIO do upper SCSI code :^(
75338061Smsmith	 */
75438061Smsmith	if ((error = vpoio_connect(vpo, PPB_WAIT|PPB_INTR)))
75538061Smsmith		return (error);
75638061Smsmith
75739520Snsouch	if (!vpoio_in_disk_mode(vpo)) {
75838061Smsmith		*ret = VP0_ECONNECT; goto error;
75938061Smsmith	}
76038061Smsmith
76138061Smsmith	if ((*ret = vpoio_select(vpo,host,target)))
76238061Smsmith		goto error;
76338061Smsmith
76438061Smsmith	/*
76538061Smsmith	 * Send the command ...
76638061Smsmith	 *
76738061Smsmith	 * set H_SELIN low for vpoio_wait().
76838061Smsmith	 */
76938061Smsmith	ppb_wctr(&vpo->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE);
77038061Smsmith
77138061Smsmith	for (k = 0; k < clen; k++) {
77238061Smsmith		if (vpoio_wait(vpo, VP0_FAST_SPINTMO) != (char)0xe0) {
77338061Smsmith			*ret = VP0_ECMD_TIMEOUT;
77438061Smsmith			goto error;
77538061Smsmith		}
77638061Smsmith		if (vpoio_outstr(vpo, &command[k], 1)) {
77738061Smsmith			*ret = VP0_EPPDATA_TIMEOUT;
77838061Smsmith			goto error;
77938061Smsmith		}
78038061Smsmith	}
78138061Smsmith
78238061Smsmith	/*
78338061Smsmith	 * Completion ...
78438061Smsmith	 */
78538061Smsmith
78638061Smsmith	*count = 0;
78738061Smsmith	for (;;) {
78838061Smsmith
78938061Smsmith		if (!(r = vpoio_wait(vpo, VP0_LOW_SPINTMO))) {
79038061Smsmith			*ret = VP0_ESTATUS_TIMEOUT; goto error;
79138061Smsmith		}
79238061Smsmith
79338061Smsmith		/* stop when the ZIP wants to send status */
79438061Smsmith		if (r == (char)0xf0)
79538061Smsmith			break;
79638061Smsmith
79738061Smsmith		if (*count >= blen) {
79838061Smsmith			*ret = VP0_EDATA_OVERFLOW;
79938061Smsmith			goto error;
80038061Smsmith		}
80138061Smsmith
80239520Snsouch		/* if in EPP mode or writing bytes, try to transfer a sector
80339520Snsouch		 * otherwise, just send one byte
80439520Snsouch		 */
80539520Snsouch		if (PPB_IN_EPP_MODE(&vpo->vpo_dev) || r == (char)0xc0)
80639520Snsouch			len = (((blen - *count) >= VP0_SECTOR_SIZE)) ?
80739520Snsouch				VP0_SECTOR_SIZE : 1;
80839520Snsouch		else
80939520Snsouch			len = 1;
81039520Snsouch
81138061Smsmith		/* ZIP wants to send data? */
81238061Smsmith		if (r == (char)0xc0)
81338061Smsmith			error = vpoio_outstr(vpo, &buffer[*count], len);
81438061Smsmith		else
81538061Smsmith			error = vpoio_instr(vpo, &buffer[*count], len);
81638061Smsmith
81738061Smsmith		if (error) {
81838061Smsmith			*ret = error;
81938061Smsmith			goto error;
82038061Smsmith		}
82138061Smsmith
82238061Smsmith		*count += len;
82338061Smsmith	}
82438061Smsmith
82538061Smsmith	if (vpoio_instr(vpo, &l, 1)) {
82638061Smsmith		*ret = VP0_EOTHER; goto error;
82738061Smsmith	}
82838061Smsmith
82938061Smsmith	/* check if the ZIP wants to send more status */
83038061Smsmith	if (vpoio_wait(vpo, VP0_FAST_SPINTMO) == (char)0xf0)
83138061Smsmith		if (vpoio_instr(vpo, &h, 1)) {
83238061Smsmith			*ret = VP0_EOTHER+2; goto error;
83338061Smsmith		}
83438061Smsmith
83538061Smsmith	*result = ((int) h << 8) | ((int) l & 0xff);
83638061Smsmith
83738061Smsmitherror:
83838061Smsmith	/* return to printer state, release the ppbus */
83938061Smsmith	vpoio_disconnect(vpo);
84038061Smsmith	return (0);
84138061Smsmith}
842