vpoio.c revision 45342
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 *
2645342Speter *	$Id: vpoio.c,v 1.6 1999/01/30 15:35:39 nsouch 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) {					\
10145342Speter		disconnect_microseq[i].arg[2] = (union ppb_insarg)d_pulse; \
10239134Snsouch		connect_epp_microseq[i].arg[2] = 			\
10345342Speter		connect_spp_microseq[i].arg[2] = (union ppb_insarg)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 */),
18943433Snsouch	  MS_DBRA(-7 /* 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),
20543433Snsouch	  MS_DBRA(-4 /* 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),
22143433Snsouch	  MS_DBRA(-5 /* 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: */
23243433Snsouch	  MS_RASSERT_P(1, MS_REG_EPP_D),
23343433Snsouch	  MS_BRSET(TIMEOUT, 3 /* error */),	/* EPP timeout? */
23443433Snsouch	  MS_DBRA(-3 /* 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: */
24743433Snsouch	  MS_RFETCH_P(1, MS_REG_EPP_D, MS_FETCH_ALL),
24843433Snsouch	  MS_BRSET(TIMEOUT, 3 /* error */),	/* EPP timeout? */
24943433Snsouch	  MS_DBRA(-3 /* 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
26243433Snsouch	  MS_BRCLEAR(H_FLT, 3 /* error */),
26339134Snsouch	  MS_CASS( H_AUTO | H_nSELIN | H_INIT | H_STROBE),
26443433Snsouch	  MS_BRSET(H_FLT, 1 /* 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
42245342Speter	ppb_MS_exec(&vpo->vpo_dev, MS_OP_PUT, (union ppb_insarg)buffer,
42345342Speter		(union ppb_insarg)size, (union ppb_insarg)MS_UNKNOWN, &error);
42438061Smsmith
42538061Smsmith#if 0
42638061Smsmith		/* XXX EPP 1.9 not implemented with microsequences */
42738061Smsmith		else {
42838061Smsmith
42938061Smsmith			ppb_reset_epp_timeout(&vpo->vpo_dev);
43038061Smsmith			ppb_wctr(&vpo->vpo_dev,
43138061Smsmith				H_AUTO | H_SELIN | H_INIT | H_STROBE);
43238061Smsmith
43338061Smsmith			if (((long) buffer | size) & 0x03)
43438061Smsmith				ppb_outsb_epp(&vpo->vpo_dev,
43538061Smsmith						buffer, size);
43638061Smsmith			else
43738061Smsmith				ppb_outsl_epp(&vpo->vpo_dev,
43838061Smsmith						buffer, size/4);
43938061Smsmith
44038061Smsmith			if ((ppb_rstr(&vpo->vpo_dev) & TIMEOUT)) {
44138061Smsmith				error = VP0_EPPDATA_TIMEOUT;
44238061Smsmith				goto error;
44338061Smsmith			}
44438061Smsmith
44538061Smsmith			ppb_wctr(&vpo->vpo_dev,
44638061Smsmith				H_AUTO | H_nSELIN | H_INIT | H_STROBE);
44738061Smsmith		}
44838061Smsmith#endif
44942475Snsouch	ppb_ecp_sync(&vpo->vpo_dev);
45038061Smsmith
45138061Smsmith	return (error);
45238061Smsmith}
45338061Smsmith
45438061Smsmith/*
45538061Smsmith * vpoio_instr()
45638061Smsmith */
45738061Smsmithstatic int
45838061Smsmithvpoio_instr(struct vpoio_data *vpo, char *buffer, int size)
45938061Smsmith{
46038061Smsmith	int error = 0;
46138061Smsmith
46245342Speter	ppb_MS_exec(&vpo->vpo_dev, MS_OP_GET, (union ppb_insarg)buffer,
46345342Speter		(union ppb_insarg)size, (union ppb_insarg)MS_UNKNOWN, &error);
46438061Smsmith
46538061Smsmith#if 0
46638061Smsmith		/* XXX EPP 1.9 not implemented with microsequences */
46738061Smsmith		else {
46838061Smsmith
46938061Smsmith			ppb_reset_epp_timeout(&vpo->vpo_dev);
47038061Smsmith			ppb_wctr(&vpo->vpo_dev, PCD |
47138061Smsmith				H_AUTO | H_SELIN | H_INIT | H_STROBE);
47238061Smsmith
47338061Smsmith			if (((long) buffer | size) & 0x03)
47438061Smsmith				ppb_insb_epp(&vpo->vpo_dev,
47538061Smsmith						buffer, size);
47638061Smsmith			else
47738061Smsmith				ppb_insl_epp(&vpo->vpo_dev,
47838061Smsmith						buffer, size/4);
47938061Smsmith
48038061Smsmith			if ((ppb_rstr(&vpo->vpo_dev) & TIMEOUT)) {
48138061Smsmith				error = VP0_EPPDATA_TIMEOUT;
48238061Smsmith				goto error;
48338061Smsmith			}
48438061Smsmith
48538061Smsmith			ppb_wctr(&vpo->vpo_dev, PCD |
48638061Smsmith				H_AUTO | H_nSELIN | H_INIT | H_STROBE);
48738061Smsmith		}
48838061Smsmith#endif
48942475Snsouch	ppb_ecp_sync(&vpo->vpo_dev);
49038061Smsmith
49138061Smsmith	return (error);
49238061Smsmith}
49338061Smsmith
49438061Smsmithstatic char
49538061Smsmithvpoio_select(struct vpoio_data *vpo, int initiator, int target)
49638061Smsmith{
49738061Smsmith	int ret;
49838061Smsmith
49938061Smsmith	struct ppb_microseq select_microseq[] = {
50038061Smsmith
50138061Smsmith		/* parameter list
50238061Smsmith		 */
50338061Smsmith		#define SELECT_TARGET		MS_PARAM(0, 1, MS_TYP_INT)
50438061Smsmith		#define SELECT_INITIATOR	MS_PARAM(3, 1, MS_TYP_INT)
50538061Smsmith
50638061Smsmith		/* send the select command to the drive */
50738061Smsmith		MS_DASS(MS_UNKNOWN),
50838061Smsmith		MS_CASS(H_nAUTO | H_nSELIN |  H_INIT | H_STROBE),
50938061Smsmith		MS_CASS( H_AUTO | H_nSELIN |  H_INIT | H_STROBE),
51038061Smsmith		MS_DASS(MS_UNKNOWN),
51138061Smsmith		MS_CASS( H_AUTO | H_nSELIN | H_nINIT | H_STROBE),
51238061Smsmith
51338061Smsmith		/* now, wait until the drive is ready */
51438061Smsmith		MS_SET(VP0_SELTMO),
51543433Snsouch/* loop: */	MS_BRSET(H_ACK, 2 /* ready */),
51643433Snsouch		MS_DBRA(-2 /* loop */),
51738061Smsmith/* error: */	MS_RET(1),
51838061Smsmith/* ready: */	MS_RET(0)
51938061Smsmith	};
52038061Smsmith
52138061Smsmith	/* initialize the select microsequence */
52238061Smsmith	ppb_MS_init_msq(select_microseq, 2,
52338061Smsmith			SELECT_TARGET, 1 << target,
52438061Smsmith			SELECT_INITIATOR, 1 << initiator);
52538061Smsmith
52638061Smsmith	ppb_MS_microseq(&vpo->vpo_dev, select_microseq, &ret);
52738061Smsmith
52838061Smsmith	if (ret)
52938061Smsmith		return (VP0_ESELECT_TIMEOUT);
53038061Smsmith
53138061Smsmith	return (0);
53238061Smsmith}
53338061Smsmith
53438061Smsmith/*
53538061Smsmith * vpoio_wait()
53638061Smsmith *
53738061Smsmith * H_SELIN must be low.
53838061Smsmith *
53938061Smsmith * XXX should be ported to microseq
54038061Smsmith */
54138061Smsmithstatic char
54238061Smsmithvpoio_wait(struct vpoio_data *vpo, int tmo)
54338061Smsmith{
54438061Smsmith
54538061Smsmith	register int	k;
54638061Smsmith	register char	r;
54738061Smsmith
54838061Smsmith#if 0	/* broken */
54938061Smsmith	if (ppb_poll_device(&vpo->vpo_dev, 150, nBUSY, nBUSY, PPB_INTR))
55038061Smsmith		return (0);
55138061Smsmith
55238061Smsmith	return (ppb_rstr(&vpo->vpo_dev) & 0xf0);
55338061Smsmith#endif
55438061Smsmith
55538061Smsmith	/* XXX should be ported to microseq */
55638061Smsmith	k = 0;
55738061Smsmith	while (!((r = ppb_rstr(&vpo->vpo_dev)) & nBUSY) && (k++ < tmo))
55838061Smsmith		;
55938061Smsmith
56038061Smsmith	/*
56138061Smsmith	 * Return some status information.
56238061Smsmith	 * Semantics :	0xc0 = ZIP wants more data
56338061Smsmith	 *		0xd0 = ZIP wants to send more data
56438061Smsmith	 *		0xe0 = ZIP wants command
56538061Smsmith	 *		0xf0 = end of transfer, ZIP is sending status
56638061Smsmith	 */
56738061Smsmith	if (k < tmo)
56838061Smsmith	  return (r & 0xf0);
56938061Smsmith
57038061Smsmith	return (0);			   /* command timed out */
57138061Smsmith}
57238061Smsmith
57338061Smsmith/*
57438061Smsmith * vpoio_probe()
57538061Smsmith *
57638061Smsmith * Low level probe of vpo device
57738061Smsmith *
57838061Smsmith */
57938061Smsmithstruct ppb_device *
58038061Smsmithvpoio_probe(struct ppb_data *ppb, struct vpoio_data *vpo)
58138061Smsmith{
58238061Smsmith
58338061Smsmith	/* ppbus dependent initialisation */
58438061Smsmith	vpo->vpo_dev.id_unit = vpo->vpo_unit;
58538061Smsmith	vpo->vpo_dev.name = "vpo";
58638061Smsmith	vpo->vpo_dev.ppb = ppb;
58738061Smsmith
58839134Snsouch	/*
58939134Snsouch	 * Initialize microsequence code
59039134Snsouch	 */
59139134Snsouch	INIT_TRIG_MICROSEQ;
59239134Snsouch
59338061Smsmith	/* now, try to initialise the drive */
59438061Smsmith	if (vpoio_detect(vpo)) {
59538061Smsmith		return (NULL);
59638061Smsmith	}
59738061Smsmith
59838061Smsmith	return (&vpo->vpo_dev);
59938061Smsmith}
60038061Smsmith
60138061Smsmith/*
60238061Smsmith * vpoio_attach()
60338061Smsmith *
60438061Smsmith * Low level attachment of vpo device
60538061Smsmith *
60638061Smsmith */
60738061Smsmithint
60838061Smsmithvpoio_attach(struct vpoio_data *vpo)
60938061Smsmith{
61038061Smsmith	int epp;
61138061Smsmith
61238061Smsmith	/*
61338061Smsmith	 * Report ourselves
61438061Smsmith	 */
61539134Snsouch	printf("vpo%d: <Iomega VPI0 Parallel to SCSI interface> on ppbus %d\n",
61638061Smsmith		vpo->vpo_dev.id_unit, vpo->vpo_dev.ppb->ppb_link->adapter_unit);
61738061Smsmith
61838061Smsmith	vpo->vpo_nibble_inbyte_msq = (struct ppb_microseq *)malloc(
61938061Smsmith		sizeof(nibble_inbyte_submicroseq), M_DEVBUF, M_NOWAIT);
62038061Smsmith
62138061Smsmith	if (!vpo->vpo_nibble_inbyte_msq)
62238061Smsmith		return (0);
62338061Smsmith
62438061Smsmith	bcopy((void *)nibble_inbyte_submicroseq,
62538061Smsmith		(void *)vpo->vpo_nibble_inbyte_msq,
62638061Smsmith		sizeof(nibble_inbyte_submicroseq));
62738061Smsmith
62838061Smsmith	INIT_NIBBLE_INBYTE_SUBMICROSEQ(vpo);
62938061Smsmith
63038061Smsmith	/*
63138061Smsmith	 * Initialize mode dependent in/out microsequences
63238061Smsmith	 */
63338061Smsmith	ppb_request_bus(&vpo->vpo_dev, PPB_WAIT);
63438061Smsmith
63538061Smsmith	/* enter NIBBLE mode to configure submsq */
63638061Smsmith	if (ppb_set_mode(&vpo->vpo_dev, PPB_NIBBLE) != -1) {
63738061Smsmith
63838061Smsmith		ppb_MS_GET_init(&vpo->vpo_dev, vpo->vpo_nibble_inbyte_msq);
63938061Smsmith
64038061Smsmith		ppb_MS_PUT_init(&vpo->vpo_dev, spp_outbyte_submicroseq);
64138061Smsmith	}
64238061Smsmith
64338061Smsmith	/* enter PS2 mode to configure submsq */
64438061Smsmith	if (ppb_set_mode(&vpo->vpo_dev, PPB_PS2) != -1) {
64538061Smsmith
64638061Smsmith		ppb_MS_GET_init(&vpo->vpo_dev, ps2_inbyte_submicroseq);
64738061Smsmith
64838061Smsmith		ppb_MS_PUT_init(&vpo->vpo_dev, spp_outbyte_submicroseq);
64938061Smsmith	}
65038061Smsmith
65138061Smsmith	epp = ppb_get_epp_protocol(&vpo->vpo_dev);
65238061Smsmith
65338061Smsmith	/* enter EPP mode to configure submsq */
65438061Smsmith	if (ppb_set_mode(&vpo->vpo_dev, PPB_EPP) != -1) {
65538061Smsmith
65638061Smsmith		switch (epp) {
65738061Smsmith		case EPP_1_9:
65838061Smsmith			/* XXX EPP 1.9 support should be improved */
65938061Smsmith		case EPP_1_7:
66038061Smsmith			ppb_MS_GET_init(&vpo->vpo_dev, epp17_instr_body);
66138061Smsmith
66238061Smsmith			ppb_MS_PUT_init(&vpo->vpo_dev, epp17_outstr_body);
66338061Smsmith			break;
66438061Smsmith		default:
66538061Smsmith			panic("%s: unknown EPP protocol (0x%x)", __FUNCTION__,
66638061Smsmith				epp);
66738061Smsmith		}
66838061Smsmith	}
66938061Smsmith
67038061Smsmith	/* try to enter EPP or PS/2 mode, NIBBLE otherwise */
67138061Smsmith	if (ppb_set_mode(&vpo->vpo_dev, PPB_EPP) != -1) {
67238061Smsmith		switch (epp) {
67338061Smsmith		case EPP_1_9:
67438061Smsmith			printf("vpo%d: EPP 1.9 mode\n", vpo->vpo_unit);
67538061Smsmith			break;
67638061Smsmith		case EPP_1_7:
67738061Smsmith			printf("vpo%d: EPP 1.7 mode\n", vpo->vpo_unit);
67838061Smsmith			break;
67938061Smsmith		default:
68038061Smsmith			panic("%s: unknown EPP protocol (0x%x)", __FUNCTION__,
68138061Smsmith				epp);
68238061Smsmith		}
68338061Smsmith	} else if (ppb_set_mode(&vpo->vpo_dev, PPB_PS2) != -1)
68438061Smsmith		printf("vpo%d: PS2 mode\n", vpo->vpo_unit);
68538061Smsmith
68638061Smsmith	else if (ppb_set_mode(&vpo->vpo_dev, PPB_NIBBLE) != -1)
68738061Smsmith		printf("vpo%d: NIBBLE mode\n", vpo->vpo_unit);
68838061Smsmith
68938061Smsmith	else {
69038061Smsmith		printf("vpo%d: can't enter NIBBLE, PS2 or EPP mode\n",
69138061Smsmith			vpo->vpo_unit);
69238061Smsmith
69338061Smsmith		ppb_release_bus(&vpo->vpo_dev);
69438061Smsmith
69538061Smsmith		free(vpo->vpo_nibble_inbyte_msq, M_DEVBUF);
69638061Smsmith		return (0);
69738061Smsmith	}
69838061Smsmith
69938061Smsmith	ppb_release_bus(&vpo->vpo_dev);
70038061Smsmith
70138061Smsmith	return (1);
70238061Smsmith}
70338061Smsmith
70438061Smsmith/*
70538061Smsmith * vpoio_reset_bus()
70638061Smsmith *
70738061Smsmith */
70838061Smsmithint
70938061Smsmithvpoio_reset_bus(struct vpoio_data *vpo)
71038061Smsmith{
71138061Smsmith	/* first, connect to the drive */
71239520Snsouch	if (vpoio_connect(vpo, PPB_WAIT|PPB_INTR) || !vpoio_in_disk_mode(vpo)) {
71342475Snsouch
71442475Snsouch#ifdef VP0_DEBUG
71542475Snsouch		printf("%s: not in disk mode!\n", __FUNCTION__);
71642475Snsouch#endif
71738061Smsmith		/* release ppbus */
71838061Smsmith		vpoio_disconnect(vpo);
71938061Smsmith		return (1);
72038061Smsmith	}
72138061Smsmith
72238061Smsmith	/* reset the SCSI bus */
72338061Smsmith	vpoio_reset(vpo);
72438061Smsmith
72538061Smsmith	/* then disconnect */
72638061Smsmith	vpoio_disconnect(vpo);
72738061Smsmith
72838061Smsmith	return (0);
72938061Smsmith}
73038061Smsmith
73138061Smsmith/*
73238061Smsmith * vpoio_do_scsi()
73338061Smsmith *
73438061Smsmith * Send an SCSI command
73538061Smsmith *
73638061Smsmith */
73738061Smsmithint
73838061Smsmithvpoio_do_scsi(struct vpoio_data *vpo, int host, int target, char *command,
73938061Smsmith		int clen, char *buffer, int blen, int *result, int *count,
74038061Smsmith		int *ret)
74138061Smsmith{
74238061Smsmith
74338061Smsmith	register char r;
74438061Smsmith	char l, h = 0;
74538061Smsmith	int len, error = 0;
74638061Smsmith	register int k;
74738061Smsmith
74838061Smsmith	/*
74938061Smsmith	 * enter disk state, allocate the ppbus
75038061Smsmith	 *
75138061Smsmith	 * XXX
75238061Smsmith	 * Should we allow this call to be interruptible?
75338061Smsmith	 * The only way to report the interruption is to return
75438061Smsmith	 * EIO do upper SCSI code :^(
75538061Smsmith	 */
75638061Smsmith	if ((error = vpoio_connect(vpo, PPB_WAIT|PPB_INTR)))
75738061Smsmith		return (error);
75838061Smsmith
75939520Snsouch	if (!vpoio_in_disk_mode(vpo)) {
76038061Smsmith		*ret = VP0_ECONNECT; goto error;
76138061Smsmith	}
76238061Smsmith
76338061Smsmith	if ((*ret = vpoio_select(vpo,host,target)))
76438061Smsmith		goto error;
76538061Smsmith
76638061Smsmith	/*
76738061Smsmith	 * Send the command ...
76838061Smsmith	 *
76938061Smsmith	 * set H_SELIN low for vpoio_wait().
77038061Smsmith	 */
77138061Smsmith	ppb_wctr(&vpo->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE);
77238061Smsmith
77338061Smsmith	for (k = 0; k < clen; k++) {
77438061Smsmith		if (vpoio_wait(vpo, VP0_FAST_SPINTMO) != (char)0xe0) {
77538061Smsmith			*ret = VP0_ECMD_TIMEOUT;
77638061Smsmith			goto error;
77738061Smsmith		}
77838061Smsmith		if (vpoio_outstr(vpo, &command[k], 1)) {
77938061Smsmith			*ret = VP0_EPPDATA_TIMEOUT;
78038061Smsmith			goto error;
78138061Smsmith		}
78238061Smsmith	}
78338061Smsmith
78438061Smsmith	/*
78538061Smsmith	 * Completion ...
78638061Smsmith	 */
78738061Smsmith
78838061Smsmith	*count = 0;
78938061Smsmith	for (;;) {
79038061Smsmith
79138061Smsmith		if (!(r = vpoio_wait(vpo, VP0_LOW_SPINTMO))) {
79238061Smsmith			*ret = VP0_ESTATUS_TIMEOUT; goto error;
79338061Smsmith		}
79438061Smsmith
79538061Smsmith		/* stop when the ZIP wants to send status */
79638061Smsmith		if (r == (char)0xf0)
79738061Smsmith			break;
79838061Smsmith
79938061Smsmith		if (*count >= blen) {
80038061Smsmith			*ret = VP0_EDATA_OVERFLOW;
80138061Smsmith			goto error;
80238061Smsmith		}
80338061Smsmith
80439520Snsouch		/* if in EPP mode or writing bytes, try to transfer a sector
80539520Snsouch		 * otherwise, just send one byte
80639520Snsouch		 */
80739520Snsouch		if (PPB_IN_EPP_MODE(&vpo->vpo_dev) || r == (char)0xc0)
80839520Snsouch			len = (((blen - *count) >= VP0_SECTOR_SIZE)) ?
80939520Snsouch				VP0_SECTOR_SIZE : 1;
81039520Snsouch		else
81139520Snsouch			len = 1;
81239520Snsouch
81338061Smsmith		/* ZIP wants to send data? */
81438061Smsmith		if (r == (char)0xc0)
81538061Smsmith			error = vpoio_outstr(vpo, &buffer[*count], len);
81638061Smsmith		else
81738061Smsmith			error = vpoio_instr(vpo, &buffer[*count], len);
81838061Smsmith
81938061Smsmith		if (error) {
82038061Smsmith			*ret = error;
82138061Smsmith			goto error;
82238061Smsmith		}
82338061Smsmith
82438061Smsmith		*count += len;
82538061Smsmith	}
82638061Smsmith
82738061Smsmith	if (vpoio_instr(vpo, &l, 1)) {
82838061Smsmith		*ret = VP0_EOTHER; goto error;
82938061Smsmith	}
83038061Smsmith
83138061Smsmith	/* check if the ZIP wants to send more status */
83238061Smsmith	if (vpoio_wait(vpo, VP0_FAST_SPINTMO) == (char)0xf0)
83338061Smsmith		if (vpoio_instr(vpo, &h, 1)) {
83438061Smsmith			*ret = VP0_EOTHER+2; goto error;
83538061Smsmith		}
83638061Smsmith
83738061Smsmith	*result = ((int) h << 8) | ((int) l & 0xff);
83838061Smsmith
83938061Smsmitherror:
84038061Smsmith	/* return to printer state, release the ppbus */
84138061Smsmith	vpoio_disconnect(vpo);
84238061Smsmith	return (0);
84338061Smsmith}
844