vpoio.c revision 55939
138061Smsmith/*-
255939Snsouch * Copyright (c) 1998, 1999 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 *
2650477Speter * $FreeBSD: head/sys/dev/ppbus/vpoio.c 55939 2000-01-14 00:18:06Z nsouch $
2738061Smsmith *
2838061Smsmith */
2938061Smsmith
3055205Speter#ifdef _KERNEL
3138061Smsmith#include <sys/param.h>
3238061Smsmith#include <sys/systm.h>
3355939Snsouch#include <sys/module.h>
3455939Snsouch#include <sys/bus.h>
3538061Smsmith#include <sys/malloc.h>
3638061Smsmith#include <sys/buf.h>
3738061Smsmith
3838061Smsmith#include <machine/clock.h>
3938061Smsmith
4055205Speter#endif
4138061Smsmith
4255205Speter#ifdef	_KERNEL
4338061Smsmith#include <sys/kernel.h>
4455205Speter#endif
4538061Smsmith
4642475Snsouch#include "opt_vpo.h"
4742475Snsouch
4855939Snsouch#include <dev/ppbus/ppbio.h>
4938061Smsmith#include <dev/ppbus/ppbconf.h>
5038061Smsmith#include <dev/ppbus/ppb_msq.h>
5138061Smsmith#include <dev/ppbus/vpoio.h>
5238061Smsmith
5355939Snsouch#include "ppbus_if.h"
5455939Snsouch
5538061Smsmith/*
5638061Smsmith * The driver pools the drive. We may add a timeout queue to avoid
5738061Smsmith * active polling on nACK. I've tried this but it leads to unreliable
5838061Smsmith * transfers
5938061Smsmith */
6038061Smsmith#define VP0_SELTMO		5000	/* select timeout */
6138061Smsmith#define VP0_FAST_SPINTMO	500000	/* wait status timeout */
6238061Smsmith#define VP0_LOW_SPINTMO		5000000	/* wait status timeout */
6338061Smsmith
6438061Smsmith/*
6538061Smsmith * Actually, VP0 timings are more accurate (about few 16MHZ cycles),
6638061Smsmith * but succeeding in respecting such timings leads to architecture
6738061Smsmith * dependent considerations.
6838061Smsmith */
6938061Smsmith#define VP0_PULSE		1
7038061Smsmith
7138061Smsmith#define VP0_SECTOR_SIZE	512
7238061Smsmith#define VP0_BUFFER_SIZE	0x12000
7338061Smsmith
7438061Smsmith#define n(flags) (~(flags) & (flags))
7538061Smsmith
7638061Smsmith/*
7738061Smsmith * VP0 connections.
7838061Smsmith */
7938061Smsmith#define H_AUTO		n(AUTOFEED)
8038061Smsmith#define H_nAUTO		AUTOFEED
8138061Smsmith#define H_STROBE	n(STROBE)
8238061Smsmith#define H_nSTROBE	STROBE
8338061Smsmith#define H_BSY		n(nBUSY)
8438061Smsmith#define H_nBSY		nBUSY
8538061Smsmith#define H_SEL		SELECT
8638061Smsmith#define H_nSEL		n(SELECT)
8739134Snsouch#define H_ERR		PERROR
8839134Snsouch#define H_nERR		n(PERROR)
8938061Smsmith#define H_ACK		nACK
9038061Smsmith#define H_nACK		n(nACK)
9138061Smsmith#define H_FLT		nFAULT
9238061Smsmith#define H_nFLT		n(nFAULT)
9338061Smsmith#define H_SELIN		n(SELECTIN)
9438061Smsmith#define H_nSELIN	SELECTIN
9538061Smsmith#define H_INIT		nINIT
9638061Smsmith#define H_nINIT		n(nINIT)
9738061Smsmith
9838061Smsmith/*
9938061Smsmith * Microcode to execute very fast I/O sequences at the lowest bus level.
10038061Smsmith */
10138061Smsmith
10239134Snsouch/* call this macro to initialize connect/disconnect microsequences */
10339134Snsouch#define INIT_TRIG_MICROSEQ {						\
10439134Snsouch	int i;								\
10539134Snsouch	for (i=1; i <= 7; i+=2) {					\
10645342Speter		disconnect_microseq[i].arg[2] = (union ppb_insarg)d_pulse; \
10739134Snsouch		connect_epp_microseq[i].arg[2] = 			\
10845342Speter		connect_spp_microseq[i].arg[2] = (union ppb_insarg)c_pulse; \
10939134Snsouch	}								\
11039134Snsouch}
11139134Snsouch
11239134Snsouch#define trig_d_pulse MS_TRIG(MS_REG_CTR,5,MS_UNKNOWN /* d_pulse */)
11339134Snsouchstatic char d_pulse[] = {
11438061Smsmith	 H_AUTO | H_nSELIN | H_INIT | H_STROBE, 0,
11538061Smsmith	H_nAUTO | H_nSELIN | H_INIT | H_STROBE, VP0_PULSE,
11638061Smsmith	 H_AUTO | H_nSELIN | H_INIT | H_STROBE, 0,
11738061Smsmith	 H_AUTO |  H_SELIN | H_INIT | H_STROBE, VP0_PULSE,
11838061Smsmith	 H_AUTO | H_nSELIN | H_INIT | H_STROBE, VP0_PULSE
11938061Smsmith};
12038061Smsmith
12139134Snsouch#define trig_c_pulse MS_TRIG(MS_REG_CTR,5,MS_UNKNOWN /* c_pulse */)
12239134Snsouchstatic char c_pulse[] = {
12338061Smsmith	 H_AUTO | H_nSELIN | H_INIT | H_STROBE, 0,
12438061Smsmith	 H_AUTO |  H_SELIN | H_INIT | H_STROBE, 0,
12538061Smsmith	H_nAUTO |  H_SELIN | H_INIT | H_STROBE, VP0_PULSE,
12638061Smsmith	 H_AUTO |  H_SELIN | H_INIT | H_STROBE, 0,
12738061Smsmith	 H_AUTO | H_nSELIN | H_INIT | H_STROBE, VP0_PULSE
12838061Smsmith};
12938061Smsmith
13039134Snsouchstatic struct ppb_microseq disconnect_microseq[] = {
13138061Smsmith	  MS_DASS(0x0), trig_d_pulse, MS_DASS(0x3c), trig_d_pulse,
13238061Smsmith	  MS_DASS(0x20), trig_d_pulse, MS_DASS(0xf), trig_d_pulse, MS_RET(0)
13338061Smsmith};
13438061Smsmith
13539134Snsouchstatic struct ppb_microseq connect_epp_microseq[] = {
13638061Smsmith	  MS_DASS(0x0), trig_c_pulse, MS_DASS(0x3c), trig_c_pulse,
13738061Smsmith	  MS_DASS(0x20), trig_c_pulse, MS_DASS(0xcf), trig_c_pulse, MS_RET(0)
13838061Smsmith};
13938061Smsmith
14039134Snsouchstatic struct ppb_microseq connect_spp_microseq[] = {
14138061Smsmith	  MS_DASS(0x0), trig_c_pulse, MS_DASS(0x3c), trig_c_pulse,
14238061Smsmith	  MS_DASS(0x20), trig_c_pulse, MS_DASS(0x8f), trig_c_pulse, MS_RET(0)
14338061Smsmith};
14438061Smsmith
14538061Smsmith/*
14638061Smsmith * nibble_inbyte_hook()
14738061Smsmith *
14838061Smsmith * Formats high and low nibble into a character
14938061Smsmith */
15038061Smsmithstatic int
15138061Smsmithnibble_inbyte_hook (void *p, char *ptr)
15238061Smsmith{
15338061Smsmith	struct vpo_nibble *s = (struct vpo_nibble *)p;
15438061Smsmith
15538061Smsmith	/* increment the buffer pointer */
15638061Smsmith	*ptr++ = ((s->l >> 4) & 0x0f) + (s->h & 0xf0);
15738061Smsmith
15838061Smsmith	return (0);
15938061Smsmith}
16038061Smsmith
16138061Smsmith/*
16238061Smsmith * Macro used to initialize each vpoio_data structure during
16338061Smsmith * low level attachment
16439134Snsouch *
16539134Snsouch * XXX should be converted to ppb_MS_init_msq()
16638061Smsmith */
16738061Smsmith#define INIT_NIBBLE_INBYTE_SUBMICROSEQ(vpo) {		    	\
16838061Smsmith	(vpo)->vpo_nibble_inbyte_msq[2].arg[2].p =		\
16938061Smsmith			(void *)&(vpo)->vpo_nibble.h;		\
17038061Smsmith	(vpo)->vpo_nibble_inbyte_msq[4].arg[2].p =		\
17138061Smsmith			(void *)&(vpo)->vpo_nibble.l;		\
17238061Smsmith	(vpo)->vpo_nibble_inbyte_msq[5].arg[0].f =		\
17338061Smsmith			nibble_inbyte_hook;			\
17438061Smsmith	(vpo)->vpo_nibble_inbyte_msq[5].arg[1].p =		\
17538061Smsmith			(void *)&(vpo)->vpo_nibble;		\
17638061Smsmith}
17738061Smsmith
17838061Smsmith/*
17938061Smsmith * This is the sub-microseqence for MS_GET in NIBBLE mode
18038061Smsmith * Retrieve the two nibbles and call the C function to generate the character
18138061Smsmith * and store it in the buffer (see nibble_inbyte_hook())
18238061Smsmith */
18339134Snsouchstatic struct ppb_microseq nibble_inbyte_submicroseq[] = {
18438061Smsmith
18538061Smsmith/* loop: */
18638061Smsmith	  MS_CASS( H_AUTO | H_SELIN | H_INIT | H_STROBE),
18738061Smsmith	  MS_DELAY(VP0_PULSE),
18838061Smsmith	  MS_RFETCH(MS_REG_STR, MS_FETCH_ALL, MS_UNKNOWN /* high nibble */),
18938061Smsmith	  MS_CASS(H_nAUTO | H_SELIN | H_INIT | H_STROBE),
19038061Smsmith	  MS_RFETCH(MS_REG_STR, MS_FETCH_ALL, MS_UNKNOWN /* low nibble */),
19138061Smsmith
19238061Smsmith	  /* do a C call to format the received nibbles */
19338061Smsmith	  MS_C_CALL(MS_UNKNOWN /* C hook */, MS_UNKNOWN /* param */),
19443433Snsouch	  MS_DBRA(-7 /* loop */),
19538061Smsmith
19638061Smsmith	  MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE),
19738061Smsmith	  MS_RET(0)
19838061Smsmith};
19938061Smsmith
20038061Smsmith/*
20138061Smsmith * This is the sub-microseqence for MS_GET in PS2 mode
20238061Smsmith */
20339134Snsouchstatic struct ppb_microseq ps2_inbyte_submicroseq[] = {
20438061Smsmith	  MS_CASS(PCD | H_AUTO | H_SELIN | H_INIT | H_nSTROBE),
20538061Smsmith
20638061Smsmith/* loop: */
20738061Smsmith	  MS_RFETCH_P(1, MS_REG_DTR, MS_FETCH_ALL),
20838061Smsmith	  MS_CASS(PCD | H_nAUTO | H_SELIN | H_INIT | H_nSTROBE),
20938061Smsmith	  MS_CASS(PCD |  H_AUTO | H_SELIN | H_INIT | H_nSTROBE),
21043433Snsouch	  MS_DBRA(-4 /* loop */),
21138061Smsmith
21238061Smsmith	  MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE),
21338061Smsmith	  MS_RET(0)
21438061Smsmith};
21538061Smsmith
21638061Smsmith/*
21738061Smsmith * This is the sub-microsequence for MS_PUT in both NIBBLE and PS2 modes
21838061Smsmith */
21939134Snsouchstatic struct ppb_microseq spp_outbyte_submicroseq[] = {
22038061Smsmith
22138061Smsmith/* loop: */
22238061Smsmith	  MS_RASSERT_P(1, MS_REG_DTR),
22338061Smsmith	  MS_CASS(H_nAUTO | H_nSELIN | H_INIT | H_STROBE),
22438061Smsmith	  MS_CASS( H_AUTO | H_nSELIN | H_INIT | H_STROBE),
22538061Smsmith	  MS_DELAY(VP0_PULSE),
22643433Snsouch	  MS_DBRA(-5 /* loop */),
22738061Smsmith
22838061Smsmith	  /* return from the put call */
22938061Smsmith	  MS_RET(0)
23038061Smsmith};
23138061Smsmith
23238061Smsmith/* EPP 1.7 microsequences, ptr and len set at runtime */
23339134Snsouchstatic struct ppb_microseq epp17_outstr_body[] = {
23438061Smsmith	  MS_CASS(H_AUTO | H_SELIN | H_INIT | H_STROBE),
23538061Smsmith
23638061Smsmith/* loop: */
23743433Snsouch	  MS_RASSERT_P(1, MS_REG_EPP_D),
23843433Snsouch	  MS_BRSET(TIMEOUT, 3 /* error */),	/* EPP timeout? */
23943433Snsouch	  MS_DBRA(-3 /* loop */),
24038061Smsmith
24138061Smsmith	  MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE),
24238061Smsmith	  MS_RET(0),
24338061Smsmith/* error: */
24438061Smsmith	  MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE),
24538061Smsmith	  MS_RET(1)
24638061Smsmith};
24738061Smsmith
24839134Snsouchstatic struct ppb_microseq epp17_instr_body[] = {
24938061Smsmith	  MS_CASS(PCD | H_AUTO | H_SELIN | H_INIT | H_STROBE),
25038061Smsmith
25138061Smsmith/* loop: */
25243433Snsouch	  MS_RFETCH_P(1, MS_REG_EPP_D, MS_FETCH_ALL),
25343433Snsouch	  MS_BRSET(TIMEOUT, 3 /* error */),	/* EPP timeout? */
25443433Snsouch	  MS_DBRA(-3 /* loop */),
25538061Smsmith
25638061Smsmith	  MS_CASS(PCD | H_AUTO | H_nSELIN | H_INIT | H_STROBE),
25738061Smsmith	  MS_RET(0),
25838061Smsmith/* error: */
25938061Smsmith	  MS_CASS(PCD | H_AUTO | H_nSELIN | H_INIT | H_STROBE),
26038061Smsmith	  MS_RET(1)
26138061Smsmith};
26238061Smsmith
26339134Snsouchstatic struct ppb_microseq in_disk_mode[] = {
26439134Snsouch	  MS_CASS( H_AUTO | H_nSELIN | H_INIT | H_STROBE),
26539134Snsouch	  MS_CASS(H_nAUTO | H_nSELIN | H_INIT | H_STROBE),
26639134Snsouch
26743433Snsouch	  MS_BRCLEAR(H_FLT, 3 /* error */),
26839134Snsouch	  MS_CASS( H_AUTO | H_nSELIN | H_INIT | H_STROBE),
26943433Snsouch	  MS_BRSET(H_FLT, 1 /* error */),
27039134Snsouch
27139520Snsouch	  MS_RET(1),
27239134Snsouch/* error: */
27339520Snsouch	  MS_RET(0)
27439134Snsouch};
27539134Snsouch
27638061Smsmithstatic int
27738061Smsmithvpoio_disconnect(struct vpoio_data *vpo)
27838061Smsmith{
27955939Snsouch	device_t ppbus = device_get_parent(vpo->vpo_dev);
28038061Smsmith	int ret;
28138061Smsmith
28255939Snsouch	ppb_MS_microseq(ppbus, vpo->vpo_dev, disconnect_microseq, &ret);
28355939Snsouch	return (ppb_release_bus(ppbus, vpo->vpo_dev));
28438061Smsmith}
28538061Smsmith
28638061Smsmith/*
28738061Smsmith * how	: PPB_WAIT or PPB_DONTWAIT
28838061Smsmith */
28938061Smsmithstatic int
29038061Smsmithvpoio_connect(struct vpoio_data *vpo, int how)
29138061Smsmith{
29255939Snsouch	device_t ppbus = device_get_parent(vpo->vpo_dev);
29338061Smsmith	int error;
29438061Smsmith	int ret;
29538061Smsmith
29655939Snsouch	if ((error = ppb_request_bus(ppbus, vpo->vpo_dev, how))) {
29742475Snsouch
29842475Snsouch#ifdef VP0_DEBUG
29942475Snsouch		printf("%s: can't request bus!\n", __FUNCTION__);
30042475Snsouch#endif
30138061Smsmith		return error;
30242475Snsouch	}
30338061Smsmith
30455939Snsouch	if (PPB_IN_EPP_MODE(ppbus))
30555939Snsouch		ppb_MS_microseq(ppbus, vpo->vpo_dev, connect_epp_microseq, &ret);
30638061Smsmith	else
30755939Snsouch		ppb_MS_microseq(ppbus, vpo->vpo_dev, connect_spp_microseq, &ret);
30838061Smsmith
30938061Smsmith	return (0);
31038061Smsmith}
31138061Smsmith
31238061Smsmith/*
31339134Snsouch * vpoio_reset()
31438061Smsmith *
31539134Snsouch * SCSI reset signal, the drive must be in disk mode
31638061Smsmith */
31739134Snsouchstatic void
31839134Snsouchvpoio_reset (struct vpoio_data *vpo)
31938061Smsmith{
32055939Snsouch	device_t ppbus = device_get_parent(vpo->vpo_dev);
32139134Snsouch	int ret;
32238061Smsmith
32339134Snsouch	struct ppb_microseq reset_microseq[] = {
32438061Smsmith
32539134Snsouch		#define INITIATOR	MS_PARAM(0, 1, MS_TYP_INT)
32638061Smsmith
32739134Snsouch		MS_DASS(MS_UNKNOWN),
32839134Snsouch		MS_CASS(H_AUTO | H_nSELIN | H_nINIT | H_STROBE),
32939134Snsouch		MS_DELAY(25),
33039134Snsouch		MS_CASS(H_AUTO | H_nSELIN |  H_INIT | H_STROBE),
33139134Snsouch		MS_RET(0)
33239134Snsouch	};
33338061Smsmith
33439134Snsouch	ppb_MS_init_msq(reset_microseq, 1, INITIATOR, 1 << VP0_INITIATOR);
33555939Snsouch	ppb_MS_microseq(ppbus, vpo->vpo_dev, reset_microseq, &ret);
33639134Snsouch
33739134Snsouch	return;
33838061Smsmith}
33938061Smsmith
34038061Smsmith/*
34139134Snsouch * vpoio_in_disk_mode()
34238061Smsmith */
34339134Snsouchstatic int
34439134Snsouchvpoio_in_disk_mode(struct vpoio_data *vpo)
34538061Smsmith{
34655939Snsouch	device_t ppbus = device_get_parent(vpo->vpo_dev);
34739134Snsouch	int ret;
34838061Smsmith
34955939Snsouch	ppb_MS_microseq(ppbus, vpo->vpo_dev, in_disk_mode, &ret);
35038061Smsmith
35139134Snsouch	return (ret);
35238061Smsmith}
35338061Smsmith
35438061Smsmith/*
35538061Smsmith * vpoio_detect()
35638061Smsmith *
35738061Smsmith * Detect and initialise the VP0 adapter.
35838061Smsmith */
35939134Snsouchstatic int
36038061Smsmithvpoio_detect(struct vpoio_data *vpo)
36138061Smsmith{
36255939Snsouch	device_t ppbus = device_get_parent(vpo->vpo_dev);
36339520Snsouch	int error, ret;
36438061Smsmith
36539520Snsouch	/* allocate the bus, then apply microsequences */
36655939Snsouch	if ((error = ppb_request_bus(ppbus, vpo->vpo_dev, PPB_DONTWAIT)))
36739520Snsouch                return (error);
36839520Snsouch
36955939Snsouch	ppb_MS_microseq(ppbus, vpo->vpo_dev, disconnect_microseq, &ret);
37039520Snsouch
37155939Snsouch	if (PPB_IN_EPP_MODE(ppbus))
37255939Snsouch		ppb_MS_microseq(ppbus, vpo->vpo_dev, connect_epp_microseq, &ret);
37339520Snsouch	else
37455939Snsouch		ppb_MS_microseq(ppbus, vpo->vpo_dev, connect_spp_microseq, &ret);
37539520Snsouch
37655939Snsouch	ppb_MS_microseq(ppbus, vpo->vpo_dev, in_disk_mode, &ret);
37739520Snsouch	if (!ret) {
37839520Snsouch
37939520Snsouch		/* try spp mode (maybe twice or because previous mode was PS2)
38039520Snsouch		 * NIBBLE mode will be restored on next transfers if detection
38139520Snsouch		 * succeed
38239520Snsouch		 */
38355939Snsouch		ppb_set_mode(ppbus, PPB_NIBBLE);
38455939Snsouch		ppb_MS_microseq(ppbus, vpo->vpo_dev, connect_spp_microseq, &ret);
38539520Snsouch
38655939Snsouch		ppb_MS_microseq(ppbus, vpo->vpo_dev, in_disk_mode, &ret);
38739520Snsouch		if (!ret) {
38839520Snsouch			if (bootverbose)
38939520Snsouch				printf("vpo%d: can't connect to the drive\n",
39039520Snsouch					vpo->vpo_unit);
39139520Snsouch
39239520Snsouch			/* disconnect and release the bus */
39355939Snsouch			ppb_MS_microseq(ppbus, vpo->vpo_dev, disconnect_microseq,
39439520Snsouch					&ret);
39539520Snsouch			goto error;
39639520Snsouch		}
39738061Smsmith	}
39838061Smsmith
39938061Smsmith	/* send SCSI reset signal */
40038061Smsmith	vpoio_reset(vpo);
40138061Smsmith
40255939Snsouch	ppb_MS_microseq(ppbus, vpo->vpo_dev, disconnect_microseq, &ret);
40338061Smsmith
40439134Snsouch	/* ensure we are disconnected or daisy chained peripheral
40539134Snsouch	 * may cause serious problem to the disk */
40639134Snsouch
40755939Snsouch	ppb_MS_microseq(ppbus, vpo->vpo_dev, in_disk_mode, &ret);
40839520Snsouch	if (ret) {
40939520Snsouch		if (bootverbose)
41039520Snsouch			printf("vpo%d: can't disconnect from the drive\n",
41139520Snsouch				vpo->vpo_unit);
41239520Snsouch		goto error;
41339520Snsouch	}
41438061Smsmith
41555939Snsouch	ppb_release_bus(ppbus, vpo->vpo_dev);
41638061Smsmith	return (0);
41739520Snsouch
41839520Snsoucherror:
41955939Snsouch	ppb_release_bus(ppbus, vpo->vpo_dev);
42039520Snsouch	return (VP0_EINITFAILED);
42138061Smsmith}
42238061Smsmith
42338061Smsmith/*
42438061Smsmith * vpoio_outstr()
42538061Smsmith */
42638061Smsmithstatic int
42738061Smsmithvpoio_outstr(struct vpoio_data *vpo, char *buffer, int size)
42838061Smsmith{
42955939Snsouch	device_t ppbus = device_get_parent(vpo->vpo_dev);
43038061Smsmith	int error = 0;
43138061Smsmith
43255939Snsouch	ppb_MS_exec(ppbus, vpo->vpo_dev, MS_OP_PUT, (union ppb_insarg)buffer,
43345342Speter		(union ppb_insarg)size, (union ppb_insarg)MS_UNKNOWN, &error);
43438061Smsmith
43538061Smsmith#if 0
43638061Smsmith		/* XXX EPP 1.9 not implemented with microsequences */
43738061Smsmith		else {
43838061Smsmith
43955939Snsouch			ppb_reset_epp_timeout(ppbus);
44055939Snsouch			ppb_wctr(ppbus, H_AUTO | H_SELIN | H_INIT | H_STROBE);
44138061Smsmith
44238061Smsmith			if (((long) buffer | size) & 0x03)
44355939Snsouch				ppb_outsb_epp(ppbus,
44438061Smsmith						buffer, size);
44538061Smsmith			else
44655939Snsouch				ppb_outsl_epp(ppbus,
44738061Smsmith						buffer, size/4);
44838061Smsmith
44955939Snsouch			if ((ppb_rstr(ppbus) & TIMEOUT)) {
45038061Smsmith				error = VP0_EPPDATA_TIMEOUT;
45138061Smsmith				goto error;
45238061Smsmith			}
45338061Smsmith
45455939Snsouch			ppb_wctr(ppbus, H_AUTO | H_nSELIN | H_INIT | H_STROBE);
45538061Smsmith		}
45638061Smsmith#endif
45755939Snsouch	ppb_ecp_sync(ppbus);
45838061Smsmith
45938061Smsmith	return (error);
46038061Smsmith}
46138061Smsmith
46238061Smsmith/*
46338061Smsmith * vpoio_instr()
46438061Smsmith */
46538061Smsmithstatic int
46638061Smsmithvpoio_instr(struct vpoio_data *vpo, char *buffer, int size)
46738061Smsmith{
46855939Snsouch	device_t ppbus = device_get_parent(vpo->vpo_dev);
46938061Smsmith	int error = 0;
47038061Smsmith
47155939Snsouch	ppb_MS_exec(ppbus, vpo->vpo_dev, MS_OP_GET, (union ppb_insarg)buffer,
47245342Speter		(union ppb_insarg)size, (union ppb_insarg)MS_UNKNOWN, &error);
47338061Smsmith
47438061Smsmith#if 0
47538061Smsmith		/* XXX EPP 1.9 not implemented with microsequences */
47638061Smsmith		else {
47738061Smsmith
47855939Snsouch			ppb_reset_epp_timeout(ppbus);
47955939Snsouch			ppb_wctr(ppbus, PCD |
48038061Smsmith				H_AUTO | H_SELIN | H_INIT | H_STROBE);
48138061Smsmith
48238061Smsmith			if (((long) buffer | size) & 0x03)
48355939Snsouch				ppb_insb_epp(ppbus,
48438061Smsmith						buffer, size);
48538061Smsmith			else
48655939Snsouch				ppb_insl_epp(ppbus,
48738061Smsmith						buffer, size/4);
48838061Smsmith
48955939Snsouch			if ((ppb_rstr(ppbus) & TIMEOUT)) {
49038061Smsmith				error = VP0_EPPDATA_TIMEOUT;
49138061Smsmith				goto error;
49238061Smsmith			}
49338061Smsmith
49455939Snsouch			ppb_wctr(ppbus, PCD |
49538061Smsmith				H_AUTO | H_nSELIN | H_INIT | H_STROBE);
49638061Smsmith		}
49738061Smsmith#endif
49855939Snsouch	ppb_ecp_sync(ppbus);
49938061Smsmith
50038061Smsmith	return (error);
50138061Smsmith}
50238061Smsmith
50338061Smsmithstatic char
50438061Smsmithvpoio_select(struct vpoio_data *vpo, int initiator, int target)
50538061Smsmith{
50655939Snsouch	device_t ppbus = device_get_parent(vpo->vpo_dev);
50738061Smsmith	int ret;
50838061Smsmith
50938061Smsmith	struct ppb_microseq select_microseq[] = {
51038061Smsmith
51138061Smsmith		/* parameter list
51238061Smsmith		 */
51338061Smsmith		#define SELECT_TARGET		MS_PARAM(0, 1, MS_TYP_INT)
51438061Smsmith		#define SELECT_INITIATOR	MS_PARAM(3, 1, MS_TYP_INT)
51538061Smsmith
51638061Smsmith		/* send the select command to the drive */
51738061Smsmith		MS_DASS(MS_UNKNOWN),
51838061Smsmith		MS_CASS(H_nAUTO | H_nSELIN |  H_INIT | H_STROBE),
51938061Smsmith		MS_CASS( H_AUTO | H_nSELIN |  H_INIT | H_STROBE),
52038061Smsmith		MS_DASS(MS_UNKNOWN),
52138061Smsmith		MS_CASS( H_AUTO | H_nSELIN | H_nINIT | H_STROBE),
52238061Smsmith
52338061Smsmith		/* now, wait until the drive is ready */
52438061Smsmith		MS_SET(VP0_SELTMO),
52543433Snsouch/* loop: */	MS_BRSET(H_ACK, 2 /* ready */),
52643433Snsouch		MS_DBRA(-2 /* loop */),
52738061Smsmith/* error: */	MS_RET(1),
52838061Smsmith/* ready: */	MS_RET(0)
52938061Smsmith	};
53038061Smsmith
53138061Smsmith	/* initialize the select microsequence */
53238061Smsmith	ppb_MS_init_msq(select_microseq, 2,
53338061Smsmith			SELECT_TARGET, 1 << target,
53438061Smsmith			SELECT_INITIATOR, 1 << initiator);
53538061Smsmith
53655939Snsouch	ppb_MS_microseq(ppbus, vpo->vpo_dev, select_microseq, &ret);
53738061Smsmith
53838061Smsmith	if (ret)
53938061Smsmith		return (VP0_ESELECT_TIMEOUT);
54038061Smsmith
54138061Smsmith	return (0);
54238061Smsmith}
54338061Smsmith
54438061Smsmith/*
54538061Smsmith * vpoio_wait()
54638061Smsmith *
54738061Smsmith * H_SELIN must be low.
54838061Smsmith *
54938061Smsmith * XXX should be ported to microseq
55038061Smsmith */
55138061Smsmithstatic char
55238061Smsmithvpoio_wait(struct vpoio_data *vpo, int tmo)
55338061Smsmith{
55455939Snsouch	device_t ppbus = device_get_parent(vpo->vpo_dev);
55538061Smsmith	register int	k;
55638061Smsmith	register char	r;
55738061Smsmith
55838061Smsmith#if 0	/* broken */
55955939Snsouch	if (ppb_poll_device(ppbus, 150, nBUSY, nBUSY, PPB_INTR))
56038061Smsmith		return (0);
56138061Smsmith
56255939Snsouch	return (ppb_rstr(ppbus) & 0xf0);
56338061Smsmith#endif
56438061Smsmith
56538061Smsmith	/* XXX should be ported to microseq */
56638061Smsmith	k = 0;
56755939Snsouch	while (!((r = ppb_rstr(ppbus)) & nBUSY) && (k++ < tmo))
56838061Smsmith		;
56938061Smsmith
57038061Smsmith	/*
57138061Smsmith	 * Return some status information.
57238061Smsmith	 * Semantics :	0xc0 = ZIP wants more data
57338061Smsmith	 *		0xd0 = ZIP wants to send more data
57438061Smsmith	 *		0xe0 = ZIP wants command
57538061Smsmith	 *		0xf0 = end of transfer, ZIP is sending status
57638061Smsmith	 */
57738061Smsmith	if (k < tmo)
57838061Smsmith	  return (r & 0xf0);
57938061Smsmith
58038061Smsmith	return (0);			   /* command timed out */
58138061Smsmith}
58238061Smsmith
58338061Smsmith/*
58438061Smsmith * vpoio_probe()
58538061Smsmith *
58638061Smsmith * Low level probe of vpo device
58738061Smsmith *
58838061Smsmith */
58955939Snsouchint
59055939Snsouchvpoio_probe(device_t dev, struct vpoio_data *vpo)
59138061Smsmith{
59255939Snsouch	int error;
59338061Smsmith
59438061Smsmith	/* ppbus dependent initialisation */
59555939Snsouch	vpo->vpo_dev = dev;
59638061Smsmith
59739134Snsouch	/*
59839134Snsouch	 * Initialize microsequence code
59939134Snsouch	 */
60039134Snsouch	INIT_TRIG_MICROSEQ;
60139134Snsouch
60238061Smsmith	/* now, try to initialise the drive */
60355939Snsouch	if ((error = vpoio_detect(vpo))) {
60455939Snsouch		return (error);
60538061Smsmith	}
60638061Smsmith
60755939Snsouch	return (0);
60838061Smsmith}
60938061Smsmith
61038061Smsmith/*
61138061Smsmith * vpoio_attach()
61238061Smsmith *
61338061Smsmith * Low level attachment of vpo device
61438061Smsmith *
61538061Smsmith */
61638061Smsmithint
61738061Smsmithvpoio_attach(struct vpoio_data *vpo)
61838061Smsmith{
61955939Snsouch	device_t ppbus = device_get_parent(vpo->vpo_dev);
62038061Smsmith	int epp;
62138061Smsmith
62238061Smsmith	vpo->vpo_nibble_inbyte_msq = (struct ppb_microseq *)malloc(
62338061Smsmith		sizeof(nibble_inbyte_submicroseq), M_DEVBUF, M_NOWAIT);
62438061Smsmith
62538061Smsmith	if (!vpo->vpo_nibble_inbyte_msq)
62655939Snsouch		return (ENXIO);
62738061Smsmith
62838061Smsmith	bcopy((void *)nibble_inbyte_submicroseq,
62938061Smsmith		(void *)vpo->vpo_nibble_inbyte_msq,
63038061Smsmith		sizeof(nibble_inbyte_submicroseq));
63138061Smsmith
63238061Smsmith	INIT_NIBBLE_INBYTE_SUBMICROSEQ(vpo);
63338061Smsmith
63438061Smsmith	/*
63538061Smsmith	 * Initialize mode dependent in/out microsequences
63638061Smsmith	 */
63755939Snsouch	ppb_request_bus(ppbus, vpo->vpo_dev, PPB_WAIT);
63838061Smsmith
63938061Smsmith	/* enter NIBBLE mode to configure submsq */
64055939Snsouch	if (ppb_set_mode(ppbus, PPB_NIBBLE) != -1) {
64138061Smsmith
64255939Snsouch		ppb_MS_GET_init(ppbus, vpo->vpo_dev, vpo->vpo_nibble_inbyte_msq);
64338061Smsmith
64455939Snsouch		ppb_MS_PUT_init(ppbus, vpo->vpo_dev, spp_outbyte_submicroseq);
64538061Smsmith	}
64638061Smsmith
64738061Smsmith	/* enter PS2 mode to configure submsq */
64855939Snsouch	if (ppb_set_mode(ppbus, PPB_PS2) != -1) {
64938061Smsmith
65055939Snsouch		ppb_MS_GET_init(ppbus, vpo->vpo_dev, ps2_inbyte_submicroseq);
65138061Smsmith
65255939Snsouch		ppb_MS_PUT_init(ppbus, vpo->vpo_dev, spp_outbyte_submicroseq);
65338061Smsmith	}
65438061Smsmith
65555939Snsouch	epp = ppb_get_epp_protocol(ppbus);
65638061Smsmith
65738061Smsmith	/* enter EPP mode to configure submsq */
65855939Snsouch	if (ppb_set_mode(ppbus, PPB_EPP) != -1) {
65938061Smsmith
66038061Smsmith		switch (epp) {
66138061Smsmith		case EPP_1_9:
66238061Smsmith			/* XXX EPP 1.9 support should be improved */
66338061Smsmith		case EPP_1_7:
66455939Snsouch			ppb_MS_GET_init(ppbus, vpo->vpo_dev, epp17_instr_body);
66538061Smsmith
66655939Snsouch			ppb_MS_PUT_init(ppbus, vpo->vpo_dev, epp17_outstr_body);
66738061Smsmith			break;
66838061Smsmith		default:
66938061Smsmith			panic("%s: unknown EPP protocol (0x%x)", __FUNCTION__,
67038061Smsmith				epp);
67138061Smsmith		}
67238061Smsmith	}
67338061Smsmith
67438061Smsmith	/* try to enter EPP or PS/2 mode, NIBBLE otherwise */
67555939Snsouch	if (ppb_set_mode(ppbus, PPB_EPP) != -1) {
67638061Smsmith		switch (epp) {
67738061Smsmith		case EPP_1_9:
67838061Smsmith			printf("vpo%d: EPP 1.9 mode\n", vpo->vpo_unit);
67938061Smsmith			break;
68038061Smsmith		case EPP_1_7:
68138061Smsmith			printf("vpo%d: EPP 1.7 mode\n", vpo->vpo_unit);
68238061Smsmith			break;
68338061Smsmith		default:
68438061Smsmith			panic("%s: unknown EPP protocol (0x%x)", __FUNCTION__,
68538061Smsmith				epp);
68638061Smsmith		}
68755939Snsouch	} else if (ppb_set_mode(ppbus, PPB_PS2) != -1)
68838061Smsmith		printf("vpo%d: PS2 mode\n", vpo->vpo_unit);
68938061Smsmith
69055939Snsouch	else if (ppb_set_mode(ppbus, PPB_NIBBLE) != -1)
69138061Smsmith		printf("vpo%d: NIBBLE mode\n", vpo->vpo_unit);
69238061Smsmith
69338061Smsmith	else {
69438061Smsmith		printf("vpo%d: can't enter NIBBLE, PS2 or EPP mode\n",
69538061Smsmith			vpo->vpo_unit);
69638061Smsmith
69755939Snsouch		ppb_release_bus(ppbus, vpo->vpo_dev);
69838061Smsmith
69938061Smsmith		free(vpo->vpo_nibble_inbyte_msq, M_DEVBUF);
70055939Snsouch		return (ENXIO);
70138061Smsmith	}
70238061Smsmith
70355939Snsouch	ppb_release_bus(ppbus, vpo->vpo_dev);
70438061Smsmith
70555939Snsouch	return (0);
70638061Smsmith}
70738061Smsmith
70838061Smsmith/*
70938061Smsmith * vpoio_reset_bus()
71038061Smsmith *
71138061Smsmith */
71238061Smsmithint
71338061Smsmithvpoio_reset_bus(struct vpoio_data *vpo)
71438061Smsmith{
71538061Smsmith	/* first, connect to the drive */
71639520Snsouch	if (vpoio_connect(vpo, PPB_WAIT|PPB_INTR) || !vpoio_in_disk_mode(vpo)) {
71742475Snsouch
71842475Snsouch#ifdef VP0_DEBUG
71942475Snsouch		printf("%s: not in disk mode!\n", __FUNCTION__);
72042475Snsouch#endif
72138061Smsmith		/* release ppbus */
72238061Smsmith		vpoio_disconnect(vpo);
72338061Smsmith		return (1);
72438061Smsmith	}
72538061Smsmith
72638061Smsmith	/* reset the SCSI bus */
72738061Smsmith	vpoio_reset(vpo);
72838061Smsmith
72938061Smsmith	/* then disconnect */
73038061Smsmith	vpoio_disconnect(vpo);
73138061Smsmith
73238061Smsmith	return (0);
73338061Smsmith}
73438061Smsmith
73538061Smsmith/*
73638061Smsmith * vpoio_do_scsi()
73738061Smsmith *
73838061Smsmith * Send an SCSI command
73938061Smsmith *
74038061Smsmith */
74138061Smsmithint
74238061Smsmithvpoio_do_scsi(struct vpoio_data *vpo, int host, int target, char *command,
74338061Smsmith		int clen, char *buffer, int blen, int *result, int *count,
74438061Smsmith		int *ret)
74538061Smsmith{
74655939Snsouch	device_t ppbus = device_get_parent(vpo->vpo_dev);
74738061Smsmith	register char r;
74838061Smsmith	char l, h = 0;
74938061Smsmith	int len, error = 0;
75038061Smsmith	register int k;
75138061Smsmith
75238061Smsmith	/*
75338061Smsmith	 * enter disk state, allocate the ppbus
75438061Smsmith	 *
75538061Smsmith	 * XXX
75638061Smsmith	 * Should we allow this call to be interruptible?
75738061Smsmith	 * The only way to report the interruption is to return
75838061Smsmith	 * EIO do upper SCSI code :^(
75938061Smsmith	 */
76038061Smsmith	if ((error = vpoio_connect(vpo, PPB_WAIT|PPB_INTR)))
76138061Smsmith		return (error);
76238061Smsmith
76339520Snsouch	if (!vpoio_in_disk_mode(vpo)) {
76438061Smsmith		*ret = VP0_ECONNECT; goto error;
76538061Smsmith	}
76638061Smsmith
76738061Smsmith	if ((*ret = vpoio_select(vpo,host,target)))
76838061Smsmith		goto error;
76938061Smsmith
77038061Smsmith	/*
77138061Smsmith	 * Send the command ...
77238061Smsmith	 *
77338061Smsmith	 * set H_SELIN low for vpoio_wait().
77438061Smsmith	 */
77555939Snsouch	ppb_wctr(ppbus, H_AUTO | H_nSELIN | H_INIT | H_STROBE);
77638061Smsmith
77738061Smsmith	for (k = 0; k < clen; k++) {
77838061Smsmith		if (vpoio_wait(vpo, VP0_FAST_SPINTMO) != (char)0xe0) {
77938061Smsmith			*ret = VP0_ECMD_TIMEOUT;
78038061Smsmith			goto error;
78138061Smsmith		}
78238061Smsmith		if (vpoio_outstr(vpo, &command[k], 1)) {
78338061Smsmith			*ret = VP0_EPPDATA_TIMEOUT;
78438061Smsmith			goto error;
78538061Smsmith		}
78638061Smsmith	}
78738061Smsmith
78838061Smsmith	/*
78938061Smsmith	 * Completion ...
79038061Smsmith	 */
79138061Smsmith
79238061Smsmith	*count = 0;
79338061Smsmith	for (;;) {
79438061Smsmith
79538061Smsmith		if (!(r = vpoio_wait(vpo, VP0_LOW_SPINTMO))) {
79638061Smsmith			*ret = VP0_ESTATUS_TIMEOUT; goto error;
79738061Smsmith		}
79838061Smsmith
79938061Smsmith		/* stop when the ZIP wants to send status */
80038061Smsmith		if (r == (char)0xf0)
80138061Smsmith			break;
80238061Smsmith
80338061Smsmith		if (*count >= blen) {
80438061Smsmith			*ret = VP0_EDATA_OVERFLOW;
80538061Smsmith			goto error;
80638061Smsmith		}
80738061Smsmith
80839520Snsouch		/* if in EPP mode or writing bytes, try to transfer a sector
80939520Snsouch		 * otherwise, just send one byte
81039520Snsouch		 */
81155939Snsouch		if (PPB_IN_EPP_MODE(ppbus) || r == (char)0xc0)
81239520Snsouch			len = (((blen - *count) >= VP0_SECTOR_SIZE)) ?
81339520Snsouch				VP0_SECTOR_SIZE : 1;
81439520Snsouch		else
81539520Snsouch			len = 1;
81639520Snsouch
81738061Smsmith		/* ZIP wants to send data? */
81838061Smsmith		if (r == (char)0xc0)
81938061Smsmith			error = vpoio_outstr(vpo, &buffer[*count], len);
82038061Smsmith		else
82138061Smsmith			error = vpoio_instr(vpo, &buffer[*count], len);
82238061Smsmith
82338061Smsmith		if (error) {
82438061Smsmith			*ret = error;
82538061Smsmith			goto error;
82638061Smsmith		}
82738061Smsmith
82838061Smsmith		*count += len;
82938061Smsmith	}
83038061Smsmith
83138061Smsmith	if (vpoio_instr(vpo, &l, 1)) {
83238061Smsmith		*ret = VP0_EOTHER; goto error;
83338061Smsmith	}
83438061Smsmith
83538061Smsmith	/* check if the ZIP wants to send more status */
83638061Smsmith	if (vpoio_wait(vpo, VP0_FAST_SPINTMO) == (char)0xf0)
83738061Smsmith		if (vpoio_instr(vpo, &h, 1)) {
83838061Smsmith			*ret = VP0_EOTHER+2; goto error;
83938061Smsmith		}
84038061Smsmith
84138061Smsmith	*result = ((int) h << 8) | ((int) l & 0xff);
84238061Smsmith
84338061Smsmitherror:
84438061Smsmith	/* return to printer state, release the ppbus */
84538061Smsmith	vpoio_disconnect(vpo);
84638061Smsmith	return (0);
84738061Smsmith}
848