138061Smsmith/*-
255939Snsouch * Copyright (c) 1998, 1999 Nicolas Souchu
370608Snsouch * Copyright (c) 2000 Alcove - Nicolas Souchu
438061Smsmith * All rights reserved.
538061Smsmith *
638061Smsmith * Redistribution and use in source and binary forms, with or without
738061Smsmith * modification, are permitted provided that the following conditions
838061Smsmith * are met:
938061Smsmith * 1. Redistributions of source code must retain the above copyright
1038061Smsmith *    notice, this list of conditions and the following disclaimer.
1138061Smsmith * 2. Redistributions in binary form must reproduce the above copyright
1238061Smsmith *    notice, this list of conditions and the following disclaimer in the
1338061Smsmith *    documentation and/or other materials provided with the distribution.
1438061Smsmith *
1538061Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1638061Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1738061Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1838061Smsmith * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1938061Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2038061Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2138061Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2238061Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2338061Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2438061Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2538061Smsmith * SUCH DAMAGE.
2638061Smsmith *
2738061Smsmith *
2838061Smsmith */
2938061Smsmith
30119418Sobrien#include <sys/cdefs.h>
31119418Sobrien__FBSDID("$FreeBSD$");
32119418Sobrien
3355205Speter#ifdef _KERNEL
3438061Smsmith#include <sys/param.h>
3538061Smsmith#include <sys/systm.h>
3655939Snsouch#include <sys/module.h>
3755939Snsouch#include <sys/bus.h>
3838061Smsmith#include <sys/malloc.h>
3938061Smsmith
4038061Smsmith
4155205Speter#endif
4238061Smsmith
4342475Snsouch#include "opt_vpo.h"
4442475Snsouch
4555939Snsouch#include <dev/ppbus/ppbio.h>
4638061Smsmith#include <dev/ppbus/ppbconf.h>
4738061Smsmith#include <dev/ppbus/ppb_msq.h>
4838061Smsmith#include <dev/ppbus/vpoio.h>
4938061Smsmith
5055939Snsouch#include "ppbus_if.h"
5155939Snsouch
5238061Smsmith/*
5338061Smsmith * The driver pools the drive. We may add a timeout queue to avoid
5438061Smsmith * active polling on nACK. I've tried this but it leads to unreliable
5538061Smsmith * transfers
5638061Smsmith */
5738061Smsmith#define VP0_SELTMO		5000	/* select timeout */
5838061Smsmith#define VP0_FAST_SPINTMO	500000	/* wait status timeout */
5938061Smsmith#define VP0_LOW_SPINTMO		5000000	/* wait status timeout */
6038061Smsmith
6138061Smsmith/*
6238061Smsmith * Actually, VP0 timings are more accurate (about few 16MHZ cycles),
6338061Smsmith * but succeeding in respecting such timings leads to architecture
6438061Smsmith * dependent considerations.
6538061Smsmith */
6638061Smsmith#define VP0_PULSE		1
6738061Smsmith
6838061Smsmith#define VP0_SECTOR_SIZE	512
6938061Smsmith#define VP0_BUFFER_SIZE	0x12000
7038061Smsmith
7138061Smsmith#define n(flags) (~(flags) & (flags))
7238061Smsmith
7338061Smsmith/*
7438061Smsmith * VP0 connections.
7538061Smsmith */
7638061Smsmith#define H_AUTO		n(AUTOFEED)
7738061Smsmith#define H_nAUTO		AUTOFEED
7838061Smsmith#define H_STROBE	n(STROBE)
7938061Smsmith#define H_nSTROBE	STROBE
8038061Smsmith#define H_BSY		n(nBUSY)
8138061Smsmith#define H_nBSY		nBUSY
8238061Smsmith#define H_SEL		SELECT
8338061Smsmith#define H_nSEL		n(SELECT)
8439134Snsouch#define H_ERR		PERROR
8539134Snsouch#define H_nERR		n(PERROR)
8638061Smsmith#define H_ACK		nACK
8738061Smsmith#define H_nACK		n(nACK)
8838061Smsmith#define H_FLT		nFAULT
8938061Smsmith#define H_nFLT		n(nFAULT)
9038061Smsmith#define H_SELIN		n(SELECTIN)
9138061Smsmith#define H_nSELIN	SELECTIN
9238061Smsmith#define H_INIT		nINIT
9338061Smsmith#define H_nINIT		n(nINIT)
9438061Smsmith
9538061Smsmith/*
9638061Smsmith * Microcode to execute very fast I/O sequences at the lowest bus level.
9738061Smsmith */
9838061Smsmith
9978645Snsouch#define WAIT_RET	MS_PARAM(4, 2, MS_TYP_PTR)
10078645Snsouch#define WAIT_TMO	MS_PARAM(0, 0, MS_TYP_INT)
10178645Snsouch
10278645Snsouch#define DECLARE_WAIT_MICROSEQUENCE			\
10378645Snsouchstruct ppb_microseq wait_microseq[] = {			\
10478645Snsouch	MS_SET(MS_UNKNOWN),				\
10578645Snsouch	/* loop */					\
10678645Snsouch	MS_BRSET(nBUSY, 2 /* ready */),			\
10778645Snsouch	MS_DBRA(-2 /* loop */),				\
10878645Snsouch	MS_RET(1), /* timed out */			\
10978645Snsouch	/* ready */					\
11078645Snsouch	MS_RFETCH(MS_REG_STR, 0xf0, MS_UNKNOWN),	\
11178645Snsouch	MS_RET(0) /* no error */			\
11278645Snsouch}
11378645Snsouch
11439134Snsouch/* call this macro to initialize connect/disconnect microsequences */
11539134Snsouch#define INIT_TRIG_MICROSEQ {						\
11639134Snsouch	int i;								\
11739134Snsouch	for (i=1; i <= 7; i+=2) {					\
11845342Speter		disconnect_microseq[i].arg[2] = (union ppb_insarg)d_pulse; \
11939134Snsouch		connect_epp_microseq[i].arg[2] = 			\
12045342Speter		connect_spp_microseq[i].arg[2] = (union ppb_insarg)c_pulse; \
12139134Snsouch	}								\
12239134Snsouch}
12339134Snsouch
12439134Snsouch#define trig_d_pulse MS_TRIG(MS_REG_CTR,5,MS_UNKNOWN /* d_pulse */)
12539134Snsouchstatic char d_pulse[] = {
12638061Smsmith	 H_AUTO | H_nSELIN | H_INIT | H_STROBE, 0,
12738061Smsmith	H_nAUTO | H_nSELIN | H_INIT | H_STROBE, VP0_PULSE,
12838061Smsmith	 H_AUTO | H_nSELIN | H_INIT | H_STROBE, 0,
12938061Smsmith	 H_AUTO |  H_SELIN | H_INIT | H_STROBE, VP0_PULSE,
13038061Smsmith	 H_AUTO | H_nSELIN | H_INIT | H_STROBE, VP0_PULSE
13138061Smsmith};
13238061Smsmith
13339134Snsouch#define trig_c_pulse MS_TRIG(MS_REG_CTR,5,MS_UNKNOWN /* c_pulse */)
13439134Snsouchstatic char c_pulse[] = {
13538061Smsmith	 H_AUTO | H_nSELIN | H_INIT | H_STROBE, 0,
13638061Smsmith	 H_AUTO |  H_SELIN | H_INIT | H_STROBE, 0,
13738061Smsmith	H_nAUTO |  H_SELIN | H_INIT | H_STROBE, VP0_PULSE,
13838061Smsmith	 H_AUTO |  H_SELIN | H_INIT | H_STROBE, 0,
13938061Smsmith	 H_AUTO | H_nSELIN | H_INIT | H_STROBE, VP0_PULSE
14038061Smsmith};
14138061Smsmith
14239134Snsouchstatic struct ppb_microseq disconnect_microseq[] = {
14338061Smsmith	  MS_DASS(0x0), trig_d_pulse, MS_DASS(0x3c), trig_d_pulse,
14438061Smsmith	  MS_DASS(0x20), trig_d_pulse, MS_DASS(0xf), trig_d_pulse, MS_RET(0)
14538061Smsmith};
14638061Smsmith
14739134Snsouchstatic struct ppb_microseq connect_epp_microseq[] = {
14838061Smsmith	  MS_DASS(0x0), trig_c_pulse, MS_DASS(0x3c), trig_c_pulse,
14938061Smsmith	  MS_DASS(0x20), trig_c_pulse, MS_DASS(0xcf), trig_c_pulse, MS_RET(0)
15038061Smsmith};
15138061Smsmith
15239134Snsouchstatic struct ppb_microseq connect_spp_microseq[] = {
15338061Smsmith	  MS_DASS(0x0), trig_c_pulse, MS_DASS(0x3c), trig_c_pulse,
15438061Smsmith	  MS_DASS(0x20), trig_c_pulse, MS_DASS(0x8f), trig_c_pulse, MS_RET(0)
15538061Smsmith};
15638061Smsmith
15738061Smsmith/*
15838061Smsmith * nibble_inbyte_hook()
15938061Smsmith *
16038061Smsmith * Formats high and low nibble into a character
16138061Smsmith */
16238061Smsmithstatic int
16338061Smsmithnibble_inbyte_hook (void *p, char *ptr)
16438061Smsmith{
16538061Smsmith	struct vpo_nibble *s = (struct vpo_nibble *)p;
16638061Smsmith
16738061Smsmith	/* increment the buffer pointer */
16838061Smsmith	*ptr++ = ((s->l >> 4) & 0x0f) + (s->h & 0xf0);
16938061Smsmith
17038061Smsmith	return (0);
17138061Smsmith}
17238061Smsmith
17378645Snsouch#define INB_NIBBLE_H MS_PARAM(2, 2, MS_TYP_PTR)
17478645Snsouch#define INB_NIBBLE_L MS_PARAM(4, 2, MS_TYP_PTR)
17578645Snsouch#define INB_NIBBLE_F MS_PARAM(5, 0, MS_TYP_FUN)
17678645Snsouch#define INB_NIBBLE_P MS_PARAM(5, 1, MS_TYP_PTR)
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 */
18338061Smsmith
18478645Snsouch#define DECLARE_NIBBLE_INBYTE_SUBMICROSEQ			\
18578645Snsouchstruct ppb_microseq nibble_inbyte_submicroseq[] = {		\
18678645Snsouch/* loop: */							\
18778645Snsouch	  MS_CASS( H_AUTO | H_SELIN | H_INIT | H_STROBE),	\
18878645Snsouch	  MS_DELAY(VP0_PULSE),					\
18978645Snsouch	  MS_RFETCH(MS_REG_STR, MS_FETCH_ALL, MS_UNKNOWN /* high nibble */),\
19078645Snsouch	  MS_CASS(H_nAUTO | H_SELIN | H_INIT | H_STROBE),	\
19178645Snsouch	  MS_RFETCH(MS_REG_STR, MS_FETCH_ALL, MS_UNKNOWN /* low nibble */),\
19278645Snsouch	  /* do a C call to format the received nibbles */	\
19378645Snsouch	  MS_C_CALL(MS_UNKNOWN /* C hook */, MS_UNKNOWN /* param */),\
19478645Snsouch	  MS_DBRA(-7 /* loop */),				\
19578645Snsouch	  MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE),	\
19678645Snsouch	  MS_RET(0)						\
19778645Snsouch}
19838061Smsmith
19938061Smsmith/*
20038061Smsmith * This is the sub-microseqence for MS_GET in PS2 mode
20138061Smsmith */
20239134Snsouchstatic struct ppb_microseq ps2_inbyte_submicroseq[] = {
20338061Smsmith	  MS_CASS(PCD | H_AUTO | H_SELIN | H_INIT | H_nSTROBE),
20438061Smsmith
20538061Smsmith/* loop: */
20638061Smsmith	  MS_RFETCH_P(1, MS_REG_DTR, MS_FETCH_ALL),
20738061Smsmith	  MS_CASS(PCD | H_nAUTO | H_SELIN | H_INIT | H_nSTROBE),
20838061Smsmith	  MS_CASS(PCD |  H_AUTO | H_SELIN | H_INIT | H_nSTROBE),
20943433Snsouch	  MS_DBRA(-4 /* loop */),
21038061Smsmith
21138061Smsmith	  MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE),
21238061Smsmith	  MS_RET(0)
21338061Smsmith};
21438061Smsmith
21538061Smsmith/*
21638061Smsmith * This is the sub-microsequence for MS_PUT in both NIBBLE and PS2 modes
21738061Smsmith */
21839134Snsouchstatic struct ppb_microseq spp_outbyte_submicroseq[] = {
21938061Smsmith
22038061Smsmith/* loop: */
221185003Sjhb	  MS_RASSERT_P(1, MS_REG_DTR),
22238061Smsmith	  MS_CASS(H_nAUTO | H_nSELIN | H_INIT | H_STROBE),
22338061Smsmith	  MS_CASS( H_AUTO | H_nSELIN | H_INIT | H_STROBE),
22438061Smsmith	  MS_DELAY(VP0_PULSE),
22543433Snsouch	  MS_DBRA(-5 /* loop */),
22638061Smsmith
22738061Smsmith	  /* return from the put call */
22838061Smsmith	  MS_RET(0)
22938061Smsmith};
23038061Smsmith
23138061Smsmith/* EPP 1.7 microsequences, ptr and len set at runtime */
23239134Snsouchstatic struct ppb_microseq epp17_outstr_body[] = {
23338061Smsmith	  MS_CASS(H_AUTO | H_SELIN | H_INIT | H_STROBE),
23438061Smsmith
23538061Smsmith/* loop: */
236185003Sjhb	  MS_RASSERT_P(1, MS_REG_EPP_D),
23743433Snsouch	  MS_BRSET(TIMEOUT, 3 /* error */),	/* EPP timeout? */
23843433Snsouch	  MS_DBRA(-3 /* loop */),
23938061Smsmith
24038061Smsmith	  MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE),
24138061Smsmith	  MS_RET(0),
24238061Smsmith/* error: */
24338061Smsmith	  MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE),
24438061Smsmith	  MS_RET(1)
24538061Smsmith};
24638061Smsmith
24739134Snsouchstatic struct ppb_microseq epp17_instr_body[] = {
24838061Smsmith	  MS_CASS(PCD | H_AUTO | H_SELIN | H_INIT | H_STROBE),
24938061Smsmith
25038061Smsmith/* loop: */
251185003Sjhb	  MS_RFETCH_P(1, MS_REG_EPP_D, MS_FETCH_ALL),
25243433Snsouch	  MS_BRSET(TIMEOUT, 3 /* error */),	/* EPP timeout? */
25343433Snsouch	  MS_DBRA(-3 /* loop */),
25438061Smsmith
25538061Smsmith	  MS_CASS(PCD | H_AUTO | H_nSELIN | H_INIT | H_STROBE),
25638061Smsmith	  MS_RET(0),
25738061Smsmith/* error: */
25838061Smsmith	  MS_CASS(PCD | H_AUTO | H_nSELIN | H_INIT | H_STROBE),
25938061Smsmith	  MS_RET(1)
26038061Smsmith};
26138061Smsmith
26239134Snsouchstatic struct ppb_microseq in_disk_mode[] = {
26339134Snsouch	  MS_CASS( H_AUTO | H_nSELIN | H_INIT | H_STROBE),
26439134Snsouch	  MS_CASS(H_nAUTO | H_nSELIN | H_INIT | H_STROBE),
26539134Snsouch
26643433Snsouch	  MS_BRCLEAR(H_FLT, 3 /* error */),
26739134Snsouch	  MS_CASS( H_AUTO | H_nSELIN | H_INIT | H_STROBE),
26843433Snsouch	  MS_BRSET(H_FLT, 1 /* error */),
26939134Snsouch
27039520Snsouch	  MS_RET(1),
27139134Snsouch/* error: */
27239520Snsouch	  MS_RET(0)
27339134Snsouch};
27439134Snsouch
27538061Smsmithstatic int
27638061Smsmithvpoio_disconnect(struct vpoio_data *vpo)
27738061Smsmith{
27855939Snsouch	device_t ppbus = device_get_parent(vpo->vpo_dev);
27938061Smsmith	int ret;
28038061Smsmith
28155939Snsouch	ppb_MS_microseq(ppbus, vpo->vpo_dev, disconnect_microseq, &ret);
28255939Snsouch	return (ppb_release_bus(ppbus, vpo->vpo_dev));
28338061Smsmith}
28438061Smsmith
28538061Smsmith/*
28638061Smsmith * how	: PPB_WAIT or PPB_DONTWAIT
28738061Smsmith */
28838061Smsmithstatic int
28938061Smsmithvpoio_connect(struct vpoio_data *vpo, int how)
29038061Smsmith{
29155939Snsouch	device_t ppbus = device_get_parent(vpo->vpo_dev);
29238061Smsmith	int error;
29338061Smsmith	int ret;
29438061Smsmith
29555939Snsouch	if ((error = ppb_request_bus(ppbus, vpo->vpo_dev, how))) {
29642475Snsouch
29742475Snsouch#ifdef VP0_DEBUG
29887599Sobrien		printf("%s: can't request bus!\n", __func__);
29942475Snsouch#endif
300185003Sjhb		return (error);
30142475Snsouch	}
30238061Smsmith
30355939Snsouch	if (PPB_IN_EPP_MODE(ppbus))
30455939Snsouch		ppb_MS_microseq(ppbus, vpo->vpo_dev, connect_epp_microseq, &ret);
30538061Smsmith	else
30655939Snsouch		ppb_MS_microseq(ppbus, vpo->vpo_dev, connect_spp_microseq, &ret);
30738061Smsmith
30838061Smsmith	return (0);
30938061Smsmith}
31038061Smsmith
31138061Smsmith/*
31239134Snsouch * vpoio_reset()
31338061Smsmith *
31439134Snsouch * SCSI reset signal, the drive must be in disk mode
31538061Smsmith */
31639134Snsouchstatic void
317185003Sjhbvpoio_reset(struct vpoio_data *vpo)
31838061Smsmith{
31955939Snsouch	device_t ppbus = device_get_parent(vpo->vpo_dev);
32039134Snsouch	int ret;
32138061Smsmith
32239134Snsouch	struct ppb_microseq reset_microseq[] = {
32338061Smsmith
32439134Snsouch		#define INITIATOR	MS_PARAM(0, 1, MS_TYP_INT)
32538061Smsmith
32639134Snsouch		MS_DASS(MS_UNKNOWN),
32739134Snsouch		MS_CASS(H_AUTO | H_nSELIN | H_nINIT | H_STROBE),
32839134Snsouch		MS_DELAY(25),
32939134Snsouch		MS_CASS(H_AUTO | H_nSELIN |  H_INIT | H_STROBE),
33039134Snsouch		MS_RET(0)
33139134Snsouch	};
33238061Smsmith
33339134Snsouch	ppb_MS_init_msq(reset_microseq, 1, INITIATOR, 1 << VP0_INITIATOR);
33455939Snsouch	ppb_MS_microseq(ppbus, vpo->vpo_dev, reset_microseq, &ret);
33539134Snsouch
33639134Snsouch	return;
33738061Smsmith}
33838061Smsmith
33938061Smsmith/*
34039134Snsouch * vpoio_in_disk_mode()
34138061Smsmith */
34239134Snsouchstatic int
34339134Snsouchvpoio_in_disk_mode(struct vpoio_data *vpo)
34438061Smsmith{
34555939Snsouch	device_t ppbus = device_get_parent(vpo->vpo_dev);
34639134Snsouch	int ret;
34738061Smsmith
34855939Snsouch	ppb_MS_microseq(ppbus, vpo->vpo_dev, in_disk_mode, &ret);
34938061Smsmith
35039134Snsouch	return (ret);
35138061Smsmith}
35238061Smsmith
35338061Smsmith/*
35438061Smsmith * vpoio_detect()
35538061Smsmith *
35638061Smsmith * Detect and initialise the VP0 adapter.
35738061Smsmith */
35839134Snsouchstatic int
35938061Smsmithvpoio_detect(struct vpoio_data *vpo)
36038061Smsmith{
36155939Snsouch	device_t ppbus = device_get_parent(vpo->vpo_dev);
36239520Snsouch	int error, ret;
36338061Smsmith
36439520Snsouch	/* allocate the bus, then apply microsequences */
36555939Snsouch	if ((error = ppb_request_bus(ppbus, vpo->vpo_dev, PPB_DONTWAIT)))
366185003Sjhb		return (error);
36739520Snsouch
36878645Snsouch	/* Force disconnection */
36955939Snsouch	ppb_MS_microseq(ppbus, vpo->vpo_dev, disconnect_microseq, &ret);
37039520Snsouch
37170608Snsouch	/* Try to enter EPP mode, then connect to the drive in EPP mode */
37270608Snsouch	if (ppb_set_mode(ppbus, PPB_EPP) != -1) {
37370608Snsouch		/* call manually the microseq instead of using the appropriate function
37470608Snsouch		 * since we already requested the ppbus */
37555939Snsouch		ppb_MS_microseq(ppbus, vpo->vpo_dev, connect_epp_microseq, &ret);
37670608Snsouch	}
37739520Snsouch
37870608Snsouch	/* If EPP mode switch failed or ZIP connection in EPP mode failed,
37970608Snsouch	 * try to connect in NIBBLE mode */
38070608Snsouch	if (!vpoio_in_disk_mode(vpo)) {
38139520Snsouch
38270608Snsouch		/* The interface must be at least PS/2 or NIBBLE capable.
38370608Snsouch		 * There is no way to know if the ZIP will work with
38470608Snsouch		 * PS/2 mode since PS/2 and SPP both use the same connect
38570608Snsouch		 * sequence. One must supress PS/2 with boot flags if
38670608Snsouch		 * PS/2 mode fails (see ppc(4)).
38739520Snsouch		 */
38870608Snsouch		if (ppb_set_mode(ppbus, PPB_PS2) != -1) {
38970608Snsouch			vpo->vpo_mode_found = VP0_MODE_PS2;
39070608Snsouch		} else {
39170608Snsouch			if (ppb_set_mode(ppbus, PPB_NIBBLE) == -1)
39270608Snsouch				goto error;
39370608Snsouch
39470608Snsouch			vpo->vpo_mode_found = VP0_MODE_NIBBLE;
39570608Snsouch		}
39670608Snsouch
39770608Snsouch		/* Can't know if the interface is capable of PS/2 yet */
39855939Snsouch		ppb_MS_microseq(ppbus, vpo->vpo_dev, connect_spp_microseq, &ret);
39970608Snsouch		if (!vpoio_in_disk_mode(vpo)) {
40070608Snsouch			vpo->vpo_mode_found = VP0_MODE_UNDEFINED;
40139520Snsouch			if (bootverbose)
402184130Sjhb				device_printf(vpo->vpo_dev,
403184130Sjhb				    "can't connect to the drive\n");
40439520Snsouch
40539520Snsouch			/* disconnect and release the bus */
40655939Snsouch			ppb_MS_microseq(ppbus, vpo->vpo_dev, disconnect_microseq,
40739520Snsouch					&ret);
40839520Snsouch			goto error;
40939520Snsouch		}
41070608Snsouch	} else {
41170608Snsouch		vpo->vpo_mode_found = VP0_MODE_EPP;
41238061Smsmith	}
41338061Smsmith
41438061Smsmith	/* send SCSI reset signal */
41538061Smsmith	vpoio_reset(vpo);
41638061Smsmith
41755939Snsouch	ppb_MS_microseq(ppbus, vpo->vpo_dev, disconnect_microseq, &ret);
41838061Smsmith
419185003Sjhb	/* ensure we are disconnected or daisy chained peripheral
42039134Snsouch	 * may cause serious problem to the disk */
42170608Snsouch	if (vpoio_in_disk_mode(vpo)) {
42239520Snsouch		if (bootverbose)
423184130Sjhb			device_printf(vpo->vpo_dev,
424184130Sjhb			    "can't disconnect from the drive\n");
42539520Snsouch		goto error;
42639520Snsouch	}
42738061Smsmith
42855939Snsouch	ppb_release_bus(ppbus, vpo->vpo_dev);
42938061Smsmith	return (0);
43039520Snsouch
43139520Snsoucherror:
43255939Snsouch	ppb_release_bus(ppbus, vpo->vpo_dev);
43339520Snsouch	return (VP0_EINITFAILED);
43438061Smsmith}
43538061Smsmith
43638061Smsmith/*
43738061Smsmith * vpoio_outstr()
43838061Smsmith */
43938061Smsmithstatic int
44038061Smsmithvpoio_outstr(struct vpoio_data *vpo, char *buffer, int size)
44138061Smsmith{
44255939Snsouch	device_t ppbus = device_get_parent(vpo->vpo_dev);
44338061Smsmith	int error = 0;
44438061Smsmith
44555939Snsouch	ppb_MS_exec(ppbus, vpo->vpo_dev, MS_OP_PUT, (union ppb_insarg)buffer,
44645342Speter		(union ppb_insarg)size, (union ppb_insarg)MS_UNKNOWN, &error);
44738061Smsmith
44855939Snsouch	ppb_ecp_sync(ppbus);
44938061Smsmith
45038061Smsmith	return (error);
45138061Smsmith}
45238061Smsmith
45338061Smsmith/*
45438061Smsmith * vpoio_instr()
45538061Smsmith */
45638061Smsmithstatic int
45738061Smsmithvpoio_instr(struct vpoio_data *vpo, char *buffer, int size)
45838061Smsmith{
45955939Snsouch	device_t ppbus = device_get_parent(vpo->vpo_dev);
46038061Smsmith	int error = 0;
46138061Smsmith
46255939Snsouch	ppb_MS_exec(ppbus, vpo->vpo_dev, MS_OP_GET, (union ppb_insarg)buffer,
46345342Speter		(union ppb_insarg)size, (union ppb_insarg)MS_UNKNOWN, &error);
46438061Smsmith
46555939Snsouch	ppb_ecp_sync(ppbus);
46638061Smsmith
46738061Smsmith	return (error);
46838061Smsmith}
46938061Smsmith
47038061Smsmithstatic char
47138061Smsmithvpoio_select(struct vpoio_data *vpo, int initiator, int target)
47238061Smsmith{
47355939Snsouch	device_t ppbus = device_get_parent(vpo->vpo_dev);
47438061Smsmith	int ret;
47538061Smsmith
47638061Smsmith	struct ppb_microseq select_microseq[] = {
47738061Smsmith
47838061Smsmith		/* parameter list
47938061Smsmith		 */
48038061Smsmith		#define SELECT_TARGET		MS_PARAM(0, 1, MS_TYP_INT)
48138061Smsmith		#define SELECT_INITIATOR	MS_PARAM(3, 1, MS_TYP_INT)
48238061Smsmith
48338061Smsmith		/* send the select command to the drive */
48438061Smsmith		MS_DASS(MS_UNKNOWN),
48538061Smsmith		MS_CASS(H_nAUTO | H_nSELIN |  H_INIT | H_STROBE),
48638061Smsmith		MS_CASS( H_AUTO | H_nSELIN |  H_INIT | H_STROBE),
48738061Smsmith		MS_DASS(MS_UNKNOWN),
48838061Smsmith		MS_CASS( H_AUTO | H_nSELIN | H_nINIT | H_STROBE),
48938061Smsmith
49038061Smsmith		/* now, wait until the drive is ready */
49138061Smsmith		MS_SET(VP0_SELTMO),
49243433Snsouch/* loop: */	MS_BRSET(H_ACK, 2 /* ready */),
49343433Snsouch		MS_DBRA(-2 /* loop */),
49438061Smsmith/* error: */	MS_RET(1),
49538061Smsmith/* ready: */	MS_RET(0)
49638061Smsmith	};
49738061Smsmith
49838061Smsmith	/* initialize the select microsequence */
49938061Smsmith	ppb_MS_init_msq(select_microseq, 2,
50038061Smsmith			SELECT_TARGET, 1 << target,
50138061Smsmith			SELECT_INITIATOR, 1 << initiator);
502185003Sjhb
50355939Snsouch	ppb_MS_microseq(ppbus, vpo->vpo_dev, select_microseq, &ret);
50438061Smsmith
50538061Smsmith	if (ret)
50638061Smsmith		return (VP0_ESELECT_TIMEOUT);
50738061Smsmith
50838061Smsmith	return (0);
50938061Smsmith}
51038061Smsmith
51138061Smsmith/*
51238061Smsmith * vpoio_wait()
51338061Smsmith *
51438061Smsmith * H_SELIN must be low.
51538061Smsmith *
51638061Smsmith * XXX should be ported to microseq
51738061Smsmith */
51838061Smsmithstatic char
51938061Smsmithvpoio_wait(struct vpoio_data *vpo, int tmo)
52038061Smsmith{
52178645Snsouch	DECLARE_WAIT_MICROSEQUENCE;
52278645Snsouch
52355939Snsouch	device_t ppbus = device_get_parent(vpo->vpo_dev);
52478645Snsouch	int ret, err;
52538061Smsmith
52638061Smsmith#if 0	/* broken */
52755939Snsouch	if (ppb_poll_device(ppbus, 150, nBUSY, nBUSY, PPB_INTR))
52838061Smsmith		return (0);
52938061Smsmith
53055939Snsouch	return (ppb_rstr(ppbus) & 0xf0);
53138061Smsmith#endif
53238061Smsmith
53338061Smsmith	/*
53438061Smsmith	 * Return some status information.
53538061Smsmith	 * Semantics :	0xc0 = ZIP wants more data
53638061Smsmith	 *		0xd0 = ZIP wants to send more data
53738061Smsmith	 *		0xe0 = ZIP wants command
53838061Smsmith	 *		0xf0 = end of transfer, ZIP is sending status
53938061Smsmith	 */
54038061Smsmith
54178645Snsouch	ppb_MS_init_msq(wait_microseq, 2,
54278645Snsouch			WAIT_RET, (void *)&ret,
54378645Snsouch			WAIT_TMO, tmo);
54478645Snsouch
54578645Snsouch	ppb_MS_microseq(ppbus, vpo->vpo_dev, wait_microseq, &err);
54678645Snsouch
54778645Snsouch	if (err)
548185003Sjhb		return (0);	 /* command timed out */
54978645Snsouch
55078645Snsouch	return(ret);
55138061Smsmith}
55238061Smsmith
55338061Smsmith/*
55438061Smsmith * vpoio_probe()
55538061Smsmith *
55638061Smsmith * Low level probe of vpo device
55738061Smsmith *
55838061Smsmith */
55955939Snsouchint
56055939Snsouchvpoio_probe(device_t dev, struct vpoio_data *vpo)
56138061Smsmith{
56255939Snsouch	int error;
56338061Smsmith
56438061Smsmith	/* ppbus dependent initialisation */
56555939Snsouch	vpo->vpo_dev = dev;
56638061Smsmith
56739134Snsouch	/*
56839134Snsouch	 * Initialize microsequence code
56939134Snsouch	 */
57039134Snsouch	INIT_TRIG_MICROSEQ;
57139134Snsouch
57238061Smsmith	/* now, try to initialise the drive */
57355939Snsouch	if ((error = vpoio_detect(vpo))) {
57455939Snsouch		return (error);
57538061Smsmith	}
57638061Smsmith
57755939Snsouch	return (0);
57838061Smsmith}
57938061Smsmith
58038061Smsmith/*
58138061Smsmith * vpoio_attach()
58238061Smsmith *
58338061Smsmith * Low level attachment of vpo device
58438061Smsmith *
58538061Smsmith */
58638061Smsmithint
58738061Smsmithvpoio_attach(struct vpoio_data *vpo)
58838061Smsmith{
589185003Sjhb	DECLARE_NIBBLE_INBYTE_SUBMICROSEQ;
59055939Snsouch	device_t ppbus = device_get_parent(vpo->vpo_dev);
59170608Snsouch	int error = 0;
59238061Smsmith
59338061Smsmith	vpo->vpo_nibble_inbyte_msq = (struct ppb_microseq *)malloc(
59438061Smsmith		sizeof(nibble_inbyte_submicroseq), M_DEVBUF, M_NOWAIT);
59538061Smsmith
59638061Smsmith	if (!vpo->vpo_nibble_inbyte_msq)
59755939Snsouch		return (ENXIO);
59838061Smsmith
59938061Smsmith	bcopy((void *)nibble_inbyte_submicroseq,
60038061Smsmith		(void *)vpo->vpo_nibble_inbyte_msq,
60138061Smsmith		sizeof(nibble_inbyte_submicroseq));
60238061Smsmith
60378645Snsouch	ppb_MS_init_msq(vpo->vpo_nibble_inbyte_msq, 4,
60478645Snsouch		INB_NIBBLE_H, (void *)&(vpo)->vpo_nibble.h,
60578645Snsouch		INB_NIBBLE_L, (void *)&(vpo)->vpo_nibble.l,
60678645Snsouch		INB_NIBBLE_F, nibble_inbyte_hook,
607185003Sjhb		INB_NIBBLE_P, (void *)&(vpo)->vpo_nibble);
60838061Smsmith
60938061Smsmith	/*
61038061Smsmith	 * Initialize mode dependent in/out microsequences
61138061Smsmith	 */
612187576Sjhb	ppb_lock(ppbus);
61370608Snsouch	if ((error = ppb_request_bus(ppbus, vpo->vpo_dev, PPB_WAIT)))
61470608Snsouch		goto error;
61538061Smsmith
61670608Snsouch	/* ppbus sets automatically the last mode entered during detection */
61770608Snsouch	switch (vpo->vpo_mode_found) {
61870608Snsouch	case VP0_MODE_EPP:
61970608Snsouch		ppb_MS_GET_init(ppbus, vpo->vpo_dev, epp17_instr_body);
62070608Snsouch		ppb_MS_PUT_init(ppbus, vpo->vpo_dev, epp17_outstr_body);
621184130Sjhb		device_printf(vpo->vpo_dev, "EPP mode\n");
62270608Snsouch		break;
62370608Snsouch	case VP0_MODE_PS2:
62455939Snsouch		ppb_MS_GET_init(ppbus, vpo->vpo_dev, ps2_inbyte_submicroseq);
62555939Snsouch		ppb_MS_PUT_init(ppbus, vpo->vpo_dev, spp_outbyte_submicroseq);
626184130Sjhb		device_printf(vpo->vpo_dev, "PS2 mode\n");
62770608Snsouch		break;
62870608Snsouch	case VP0_MODE_NIBBLE:
62970608Snsouch		ppb_MS_GET_init(ppbus, vpo->vpo_dev, vpo->vpo_nibble_inbyte_msq);
63070608Snsouch		ppb_MS_PUT_init(ppbus, vpo->vpo_dev, spp_outbyte_submicroseq);
631184130Sjhb		device_printf(vpo->vpo_dev, "NIBBLE mode\n");
63270608Snsouch		break;
63370608Snsouch	default:
63470608Snsouch		panic("vpo: unknown mode %d", vpo->vpo_mode_found);
63538061Smsmith	}
63638061Smsmith
63755939Snsouch	ppb_release_bus(ppbus, vpo->vpo_dev);
63838061Smsmith
63970608Snsoucherror:
640187576Sjhb	ppb_unlock(ppbus);
64170608Snsouch	return (error);
64238061Smsmith}
64338061Smsmith
64438061Smsmith/*
64538061Smsmith * vpoio_reset_bus()
64638061Smsmith *
64738061Smsmith */
64838061Smsmithint
64938061Smsmithvpoio_reset_bus(struct vpoio_data *vpo)
65038061Smsmith{
65138061Smsmith	/* first, connect to the drive */
65239520Snsouch	if (vpoio_connect(vpo, PPB_WAIT|PPB_INTR) || !vpoio_in_disk_mode(vpo)) {
65342475Snsouch
65442475Snsouch#ifdef VP0_DEBUG
65587599Sobrien		printf("%s: not in disk mode!\n", __func__);
65642475Snsouch#endif
65738061Smsmith		/* release ppbus */
65838061Smsmith		vpoio_disconnect(vpo);
65938061Smsmith		return (1);
66038061Smsmith	}
66138061Smsmith
66238061Smsmith	/* reset the SCSI bus */
66338061Smsmith	vpoio_reset(vpo);
66438061Smsmith
66538061Smsmith	/* then disconnect */
66638061Smsmith	vpoio_disconnect(vpo);
66738061Smsmith
66838061Smsmith	return (0);
66938061Smsmith}
67038061Smsmith
67138061Smsmith/*
67238061Smsmith * vpoio_do_scsi()
67338061Smsmith *
67438061Smsmith * Send an SCSI command
67538061Smsmith *
67638061Smsmith */
677185003Sjhbint
67838061Smsmithvpoio_do_scsi(struct vpoio_data *vpo, int host, int target, char *command,
67938061Smsmith		int clen, char *buffer, int blen, int *result, int *count,
68038061Smsmith		int *ret)
68138061Smsmith{
68255939Snsouch	device_t ppbus = device_get_parent(vpo->vpo_dev);
68338061Smsmith	register char r;
68438061Smsmith	char l, h = 0;
68538061Smsmith	int len, error = 0;
68638061Smsmith	register int k;
68738061Smsmith
68838061Smsmith	/*
68938061Smsmith	 * enter disk state, allocate the ppbus
69038061Smsmith	 *
69138061Smsmith	 * XXX
69238061Smsmith	 * Should we allow this call to be interruptible?
69338061Smsmith	 * The only way to report the interruption is to return
69438061Smsmith	 * EIO do upper SCSI code :^(
69538061Smsmith	 */
69638061Smsmith	if ((error = vpoio_connect(vpo, PPB_WAIT|PPB_INTR)))
69738061Smsmith		return (error);
69838061Smsmith
69939520Snsouch	if (!vpoio_in_disk_mode(vpo)) {
700185003Sjhb		*ret = VP0_ECONNECT;
701185003Sjhb		goto error;
70238061Smsmith	}
70338061Smsmith
70438061Smsmith	if ((*ret = vpoio_select(vpo,host,target)))
70538061Smsmith		goto error;
70638061Smsmith
70738061Smsmith	/*
70838061Smsmith	 * Send the command ...
70938061Smsmith	 *
71038061Smsmith	 * set H_SELIN low for vpoio_wait().
71138061Smsmith	 */
71255939Snsouch	ppb_wctr(ppbus, H_AUTO | H_nSELIN | H_INIT | H_STROBE);
71338061Smsmith
71438061Smsmith	for (k = 0; k < clen; k++) {
71538061Smsmith		if (vpoio_wait(vpo, VP0_FAST_SPINTMO) != (char)0xe0) {
71638061Smsmith			*ret = VP0_ECMD_TIMEOUT;
71738061Smsmith			goto error;
71838061Smsmith		}
71938061Smsmith		if (vpoio_outstr(vpo, &command[k], 1)) {
72038061Smsmith			*ret = VP0_EPPDATA_TIMEOUT;
72138061Smsmith			goto error;
72238061Smsmith		}
72338061Smsmith	}
72438061Smsmith
725185003Sjhb	/*
726185003Sjhb	 * Completion ...
72738061Smsmith	 */
72838061Smsmith
72938061Smsmith	*count = 0;
73038061Smsmith	for (;;) {
73138061Smsmith
73238061Smsmith		if (!(r = vpoio_wait(vpo, VP0_LOW_SPINTMO))) {
733185003Sjhb			*ret = VP0_ESTATUS_TIMEOUT;
734185003Sjhb			goto error;
73538061Smsmith		}
73638061Smsmith
73738061Smsmith		/* stop when the ZIP wants to send status */
73838061Smsmith		if (r == (char)0xf0)
73938061Smsmith			break;
74038061Smsmith
74138061Smsmith		if (*count >= blen) {
74238061Smsmith			*ret = VP0_EDATA_OVERFLOW;
74338061Smsmith			goto error;
74438061Smsmith		}
74538061Smsmith
74639520Snsouch		/* if in EPP mode or writing bytes, try to transfer a sector
74739520Snsouch		 * otherwise, just send one byte
74839520Snsouch		 */
74955939Snsouch		if (PPB_IN_EPP_MODE(ppbus) || r == (char)0xc0)
75039520Snsouch			len = (((blen - *count) >= VP0_SECTOR_SIZE)) ?
75139520Snsouch				VP0_SECTOR_SIZE : 1;
75239520Snsouch		else
75339520Snsouch			len = 1;
75439520Snsouch
75538061Smsmith		/* ZIP wants to send data? */
75638061Smsmith		if (r == (char)0xc0)
75738061Smsmith			error = vpoio_outstr(vpo, &buffer[*count], len);
75838061Smsmith		else
75938061Smsmith			error = vpoio_instr(vpo, &buffer[*count], len);
76038061Smsmith
76138061Smsmith		if (error) {
76238061Smsmith			*ret = error;
76338061Smsmith			goto error;
76438061Smsmith		}
76538061Smsmith
76638061Smsmith		*count += len;
76738061Smsmith	}
76838061Smsmith
76938061Smsmith	if (vpoio_instr(vpo, &l, 1)) {
770185003Sjhb		*ret = VP0_EOTHER;
771185003Sjhb		goto error;
77238061Smsmith	}
77338061Smsmith
77438061Smsmith	/* check if the ZIP wants to send more status */
77538061Smsmith	if (vpoio_wait(vpo, VP0_FAST_SPINTMO) == (char)0xf0)
77638061Smsmith		if (vpoio_instr(vpo, &h, 1)) {
777185003Sjhb			*ret = VP0_EOTHER + 2;
778185003Sjhb			goto error;
77938061Smsmith		}
78038061Smsmith
78138061Smsmith	*result = ((int) h << 8) | ((int) l & 0xff);
78238061Smsmith
78338061Smsmitherror:
78438061Smsmith	/* return to printer state, release the ppbus */
78538061Smsmith	vpoio_disconnect(vpo);
78638061Smsmith	return (0);
78738061Smsmith}
788