immio.c revision 40783
139136Snsouch/*-
239136Snsouch * Copyright (c) 1998 Nicolas Souchu
339136Snsouch * All rights reserved.
439136Snsouch *
539136Snsouch * Redistribution and use in source and binary forms, with or without
639136Snsouch * modification, are permitted provided that the following conditions
739136Snsouch * are met:
839136Snsouch * 1. Redistributions of source code must retain the above copyright
939136Snsouch *    notice, this list of conditions and the following disclaimer.
1039136Snsouch * 2. Redistributions in binary form must reproduce the above copyright
1139136Snsouch *    notice, this list of conditions and the following disclaimer in the
1239136Snsouch *    documentation and/or other materials provided with the distribution.
1339136Snsouch *
1439136Snsouch * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1539136Snsouch * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1639136Snsouch * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1739136Snsouch * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1839136Snsouch * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1939136Snsouch * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2039136Snsouch * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2139136Snsouch * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2239136Snsouch * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2339136Snsouch * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2439136Snsouch * SUCH DAMAGE.
2539136Snsouch *
2640783Snsouch *	$Id: immio.c,v 1.3 1998/10/02 20:44:58 nsouch Exp $
2739136Snsouch *
2839136Snsouch */
2939136Snsouch
3039136Snsouch/*
3139136Snsouch * Iomega ZIP+ Matchmaker Parallel Port Interface driver
3239136Snsouch *
3339136Snsouch * Thanks to David Campbell work on the Linux driver and the Iomega specs
3439136Snsouch * Thanks to Thiebault Moeglin for the drive
3539136Snsouch */
3639136Snsouch#ifdef KERNEL
3739136Snsouch#include <sys/param.h>
3839136Snsouch#include <sys/systm.h>
3939136Snsouch#include <sys/malloc.h>
4039136Snsouch#include <sys/buf.h>
4139136Snsouch
4239136Snsouch#include <machine/clock.h>
4339136Snsouch
4439136Snsouch#endif	/* KERNEL */
4539136Snsouch
4639136Snsouch#ifdef	KERNEL
4739136Snsouch#include <sys/kernel.h>
4839136Snsouch#endif /*KERNEL */
4939136Snsouch
5039136Snsouch#include <dev/ppbus/ppbconf.h>
5139136Snsouch#include <dev/ppbus/ppb_msq.h>
5239136Snsouch#include <dev/ppbus/vpoio.h>
5339136Snsouch#include <dev/ppbus/ppb_1284.h>
5439136Snsouch
5539136Snsouch#define VP0_SELTMO		5000	/* select timeout */
5639136Snsouch#define VP0_FAST_SPINTMO	500000	/* wait status timeout */
5739136Snsouch#define VP0_LOW_SPINTMO		5000000	/* wait status timeout */
5839136Snsouch
5939136Snsouch#define VP0_SECTOR_SIZE	512
6039136Snsouch
6139136Snsouch/*
6239136Snsouch * Microcode to execute very fast I/O sequences at the lowest bus level.
6339136Snsouch */
6439136Snsouch
6539136Snsouch#define SELECT_TARGET		MS_PARAM(6, 1, MS_TYP_CHA)
6639136Snsouch
6739136Snsouch#define DECLARE_SELECT_MICROSEQUENCE					\
6839136Snsouchstruct ppb_microseq select_microseq[] = {				\
6939136Snsouch	MS_CASS(0xc),							\
7039136Snsouch	/* first, check there is nothing holding onto the bus */	\
7139136Snsouch	MS_SET(VP0_SELTMO),						\
7239136Snsouch/* _loop: */								\
7339136Snsouch	MS_BRCLEAR(0x8, 3 /* _ready */),				\
7439136Snsouch	MS_DBRA(-1 /* _loop */),					\
7539136Snsouch	MS_RET(2),			/* bus busy */			\
7639136Snsouch/* _ready: */								\
7739136Snsouch	MS_CASS(0x4),							\
7839136Snsouch	MS_DASS(MS_UNKNOWN /* 0x80 | 1 << target */),			\
7939136Snsouch	MS_DELAY(1),							\
8039136Snsouch	MS_CASS(0xc),							\
8139136Snsouch	MS_CASS(0xd),							\
8239136Snsouch	/* now, wait until the drive is ready */			\
8339136Snsouch	MS_SET(VP0_SELTMO),						\
8439136Snsouch/* loop: */								\
8539136Snsouch	MS_BRSET(0x8, 4 /* ready */),					\
8639136Snsouch	MS_DBRA(-1 /* loop */),						\
8739136Snsouch/* error: */								\
8839136Snsouch	MS_CASS(0xc),							\
8939136Snsouch	MS_RET(VP0_ESELECT_TIMEOUT),					\
9039136Snsouch/* ready: */								\
9139136Snsouch	MS_CASS(0xc),							\
9239136Snsouch	MS_RET(0)							\
9339136Snsouch}
9439136Snsouch
9539136Snsouchstatic struct ppb_microseq transfer_epilog[] = {
9639136Snsouch	MS_CASS(0x4),
9739136Snsouch	MS_CASS(0xc),
9839136Snsouch	MS_CASS(0xe),
9939136Snsouch	MS_CASS(0x4),
10039136Snsouch	MS_RET(0)
10139136Snsouch};
10239136Snsouch
10339136Snsouch#define CPP_S1		MS_PARAM(10, 2, MS_TYP_PTR)
10439136Snsouch#define CPP_S2		MS_PARAM(13, 2, MS_TYP_PTR)
10539136Snsouch#define CPP_S3		MS_PARAM(16, 2, MS_TYP_PTR)
10639136Snsouch#define CPP_PARAM	MS_PARAM(17, 1, MS_TYP_CHA)
10739136Snsouch
10839136Snsouch#define DECLARE_CPP_MICROSEQ \
10939136Snsouchstruct ppb_microseq cpp_microseq[] = {					\
11039136Snsouch	MS_CASS(0x0c), MS_DELAY(2),					\
11139136Snsouch	MS_DASS(0xaa), MS_DELAY(10),					\
11239136Snsouch	MS_DASS(0x55), MS_DELAY(10),					\
11339136Snsouch	MS_DASS(0x00), MS_DELAY(10),					\
11439136Snsouch	MS_DASS(0xff), MS_DELAY(10),					\
11539136Snsouch	MS_RFETCH(MS_REG_STR, 0xb8, MS_UNKNOWN /* &s1 */),		\
11639136Snsouch	MS_DASS(0x87), MS_DELAY(10),					\
11739136Snsouch	MS_RFETCH(MS_REG_STR, 0xb8, MS_UNKNOWN /* &s2 */),		\
11839136Snsouch	MS_DASS(0x78), MS_DELAY(10),					\
11939136Snsouch	MS_RFETCH(MS_REG_STR, 0x38, MS_UNKNOWN /* &s3 */),		\
12039136Snsouch	MS_DASS(MS_UNKNOWN /* param */),				\
12139136Snsouch	MS_DELAY(2),							\
12239136Snsouch	MS_CASS(0x0c), MS_DELAY(10),					\
12339136Snsouch	MS_CASS(0x0d), MS_DELAY(2),					\
12439136Snsouch	MS_CASS(0x0c), MS_DELAY(10),					\
12539136Snsouch	MS_DASS(0xff), MS_DELAY(10),					\
12639136Snsouch	MS_RET(0)							\
12739136Snsouch}
12839136Snsouch
12939136Snsouch#define NEGOCIATED_MODE		MS_PARAM(2, 1, MS_TYP_CHA)
13039136Snsouch
13139136Snsouch#define DECLARE_NEGOCIATE_MICROSEQ \
13239136Snsouchstatic struct ppb_microseq negociate_microseq[] = { 			\
13339136Snsouch	MS_CASS(0x4),							\
13439136Snsouch	MS_DELAY(5),							\
13539136Snsouch	MS_DASS(MS_UNKNOWN /* mode */),					\
13639136Snsouch	MS_DELAY(100),							\
13739136Snsouch	MS_CASS(0x6),							\
13839136Snsouch	MS_DELAY(5),							\
13939136Snsouch	MS_BRSET(0x20, 6 /* continue */),				\
14039136Snsouch	MS_DELAY(5),							\
14139136Snsouch	MS_CASS(0x7),							\
14239136Snsouch	MS_DELAY(5),							\
14339136Snsouch	MS_CASS(0x6),							\
14439136Snsouch	MS_RET(VP0_ENEGOCIATE),						\
14539136Snsouch/* continue: */								\
14639136Snsouch	MS_DELAY(5),							\
14739136Snsouch	MS_CASS(0x7),							\
14839136Snsouch	MS_DELAY(5),							\
14939136Snsouch	MS_CASS(0x6),							\
15039136Snsouch	MS_RET(0)							\
15139136Snsouch}
15239136Snsouch
15339136Snsouchstatic struct ppb_microseq reset_microseq[] = {
15439136Snsouch	MS_CASS(0x04),
15539136Snsouch	MS_DASS(0x40),
15639136Snsouch	MS_DELAY(1),
15739136Snsouch	MS_CASS(0x0c),
15839136Snsouch	MS_CASS(0x0d),
15939136Snsouch	MS_DELAY(50),
16039136Snsouch	MS_CASS(0x0c),
16139136Snsouch	MS_CASS(0x04),
16239136Snsouch	MS_RET(0)
16339136Snsouch};
16439136Snsouch
16539136Snsouch/*
16639136Snsouch * nibble_inbyte_hook()
16739136Snsouch *
16839136Snsouch * Formats high and low nibble into a character
16939136Snsouch */
17039136Snsouchstatic int
17139136Snsouchnibble_inbyte_hook (void *p, char *ptr)
17239136Snsouch{
17339136Snsouch	struct vpo_nibble *s = (struct vpo_nibble *)p;
17439136Snsouch
17539136Snsouch	/* increment the buffer pointer */
17639136Snsouch	*ptr = ((s->l >> 4) & 0x0f) + (s->h & 0xf0);
17739136Snsouch
17839136Snsouch	return (0);
17939136Snsouch}
18039136Snsouch
18139136Snsouch/*
18239136Snsouch * Macro used to initialize each vpoio_data structure during
18339136Snsouch * low level attachment
18439136Snsouch *
18539136Snsouch * XXX should be converted to ppb_MS_init_msq()
18639136Snsouch */
18739136Snsouch#define INIT_NIBBLE_INBYTE_SUBMICROSEQ(vpo) {		    	\
18839136Snsouch	(vpo)->vpo_nibble_inbyte_msq[6].arg[2].p =		\
18939136Snsouch			(void *)&(vpo)->vpo_nibble.h;		\
19039136Snsouch	(vpo)->vpo_nibble_inbyte_msq[3].arg[2].p =		\
19139136Snsouch			(void *)&(vpo)->vpo_nibble.l;		\
19239136Snsouch	(vpo)->vpo_nibble_inbyte_msq[9].arg[0].f =		\
19339136Snsouch			nibble_inbyte_hook;			\
19439136Snsouch	(vpo)->vpo_nibble_inbyte_msq[9].arg[1].p =		\
19539136Snsouch			(void *)&(vpo)->vpo_nibble;		\
19639136Snsouch}
19739136Snsouch
19839136Snsouch/*
19939136Snsouch * This is the sub-microseqence for MS_GET in NIBBLE mode
20039136Snsouch * Retrieve the two nibbles and call the C function to generate the character
20139136Snsouch * and store it in the buffer (see nibble_inbyte_hook())
20239136Snsouch */
20339136Snsouchstatic struct ppb_microseq nibble_inbyte_submicroseq[] = {
20439136Snsouch	  MS_CASS(0x4),
20539136Snsouch
20639136Snsouch/* loop: */
20739136Snsouch	  MS_CASS(0x6),
20839136Snsouch	  MS_DELAY(1),
20939136Snsouch	  MS_RFETCH(MS_REG_STR, MS_FETCH_ALL, MS_UNKNOWN /* low nibble */),
21039136Snsouch	  MS_CASS(0x5),
21139136Snsouch	  MS_DELAY(1),
21239136Snsouch	  MS_RFETCH(MS_REG_STR, MS_FETCH_ALL, MS_UNKNOWN /* high nibble */),
21339136Snsouch	  MS_CASS(0x4),
21439136Snsouch	  MS_DELAY(1),
21539136Snsouch
21639136Snsouch	  /* do a C call to format the received nibbles */
21739136Snsouch	  MS_C_CALL(MS_UNKNOWN /* C hook */, MS_UNKNOWN /* param */),
21839136Snsouch	  MS_DBRA(-6 /* loop */),
21939136Snsouch	  MS_RET(0)
22039136Snsouch};
22139136Snsouch
22239136Snsouch/*
22339136Snsouch * This is the sub-microseqence for MS_GET in PS2 mode
22439136Snsouch */
22539136Snsouchstatic struct ppb_microseq ps2_inbyte_submicroseq[] = {
22639136Snsouch	  MS_CASS(0x4),
22739136Snsouch
22839136Snsouch/* loop: */
22939136Snsouch	  MS_CASS(PCD | 0x6),
23039136Snsouch	  MS_RFETCH_P(1, MS_REG_DTR, MS_FETCH_ALL),
23139136Snsouch	  MS_CASS(PCD | 0x5),
23239136Snsouch	  MS_DBRA(-3 /* loop */),
23339136Snsouch
23439136Snsouch	  MS_RET(0)
23539136Snsouch};
23639136Snsouch
23739136Snsouch/*
23839136Snsouch * This is the sub-microsequence for MS_PUT in both NIBBLE and PS2 modes
23939136Snsouch */
24039136Snsouchstatic struct ppb_microseq spp_outbyte_submicroseq[] = {
24139136Snsouch	  MS_CASS(0x4),
24239136Snsouch
24339136Snsouch/* loop: */
24439136Snsouch	  MS_RASSERT_P(1, MS_REG_DTR),
24539136Snsouch	  MS_CASS(0x5),
24639136Snsouch	  MS_DBRA(1),				/* decrement counter */
24739136Snsouch	  MS_RASSERT_P(1, MS_REG_DTR),
24839136Snsouch	  MS_CASS(0x0),
24939136Snsouch	  MS_DBRA(-5 /* loop */),
25039136Snsouch
25139136Snsouch	  /* return from the put call */
25239136Snsouch	  MS_CASS(0x4),
25339136Snsouch	  MS_RET(0)
25439136Snsouch};
25539136Snsouch
25639136Snsouch/* EPP 1.7 microsequences, ptr and len set at runtime */
25739136Snsouchstatic struct ppb_microseq epp17_outstr[] = {
25839136Snsouch	  MS_CASS(0x4),
25939136Snsouch	  MS_RASSERT_P(MS_ACCUM, MS_REG_EPP),
26039136Snsouch	  MS_CASS(0xc),
26139136Snsouch	  MS_RET(0),
26239136Snsouch};
26339136Snsouch
26439136Snsouchstatic struct ppb_microseq epp17_instr[] = {
26539136Snsouch	  MS_CASS(PCD | 0x4),
26639136Snsouch	  MS_RFETCH_P(MS_ACCUM, MS_REG_EPP, MS_FETCH_ALL),
26739136Snsouch	  MS_CASS(PCD | 0xc),
26839136Snsouch	  MS_RET(0),
26939136Snsouch};
27039136Snsouch
27139136Snsouchstatic int
27239520Snsouchimm_disconnect(struct vpoio_data *vpo, int *connected, int release_bus)
27339136Snsouch{
27439136Snsouch	DECLARE_CPP_MICROSEQ;
27539136Snsouch
27639136Snsouch	char s1, s2, s3;
27739136Snsouch	int ret;
27839136Snsouch
27939136Snsouch	/* all should be ok */
28039520Snsouch	if (connected)
28139520Snsouch		*connected = 0;
28239136Snsouch
28339136Snsouch	ppb_MS_init_msq(cpp_microseq, 4, CPP_S1, (void *)&s1,
28439136Snsouch			CPP_S2, (void *)&s2, CPP_S3, (void *)&s3,
28539136Snsouch			CPP_PARAM, 0x30);
28639136Snsouch
28739136Snsouch	ppb_MS_microseq(&vpo->vpo_dev, cpp_microseq, &ret);
28839136Snsouch
28940783Snsouch	if ((s1 != (char)0xb8 || s2 != (char)0x18 || s3 != (char)0x38)) {
29040783Snsouch		if (bootverbose)
29140783Snsouch			printf("imm%d: (disconnect) s1=0x%x s2=0x%x, s3=0x%x\n",
29240783Snsouch				vpo->vpo_unit, s1 & 0xff, s2 & 0xff, s3 & 0xff);
29340783Snsouch		if (connected)
29440783Snsouch			*connected = VP0_ECONNECT;
29540783Snsouch	}
29639136Snsouch
29739520Snsouch	if (release_bus)
29839520Snsouch		return (ppb_release_bus(&vpo->vpo_dev));
29939520Snsouch	else
30039520Snsouch		return (0);
30139136Snsouch}
30239136Snsouch
30339136Snsouch/*
30439136Snsouch * how	: PPB_WAIT or PPB_DONTWAIT
30539136Snsouch */
30639136Snsouchstatic int
30739520Snsouchimm_connect(struct vpoio_data *vpo, int how, int *disconnected, int request_bus)
30839136Snsouch{
30939136Snsouch	DECLARE_CPP_MICROSEQ;
31039136Snsouch
31139136Snsouch	char s1, s2, s3;
31239136Snsouch	int error;
31339136Snsouch	int ret;
31439136Snsouch
31539136Snsouch	/* all should be ok */
31639520Snsouch	if (disconnected)
31739520Snsouch		*disconnected = 0;
31839136Snsouch
31939520Snsouch	if (request_bus)
32039520Snsouch		if ((error = ppb_request_bus(&vpo->vpo_dev, how)))
32139520Snsouch			return (error);
32239136Snsouch
32339136Snsouch	ppb_MS_init_msq(cpp_microseq, 3, CPP_S1, (void *)&s1,
32439136Snsouch			CPP_S2, (void *)&s2, CPP_S3, (void *)&s3);
32539136Snsouch
32639136Snsouch	/* select device 0 in compatible mode */
32739136Snsouch	ppb_MS_init_msq(cpp_microseq, 1, CPP_PARAM, 0xe0);
32839136Snsouch	ppb_MS_microseq(&vpo->vpo_dev, cpp_microseq, &ret);
32939136Snsouch
33039136Snsouch	/* disconnect all devices */
33139136Snsouch	ppb_MS_init_msq(cpp_microseq, 1, CPP_PARAM, 0x30);
33239136Snsouch	ppb_MS_microseq(&vpo->vpo_dev, cpp_microseq, &ret);
33339136Snsouch
33439136Snsouch	if (PPB_IN_EPP_MODE(&vpo->vpo_dev))
33539136Snsouch		ppb_MS_init_msq(cpp_microseq, 1, CPP_PARAM, 0x28);
33639136Snsouch	else
33739136Snsouch		ppb_MS_init_msq(cpp_microseq, 1, CPP_PARAM, 0xe0);
33839136Snsouch
33939136Snsouch	ppb_MS_microseq(&vpo->vpo_dev, cpp_microseq, &ret);
34039136Snsouch
34140783Snsouch	if ((s1 != (char)0xb8 || s2 != (char)0x18 || s3 != (char)0x30)) {
34240783Snsouch		if (bootverbose)
34340783Snsouch			printf("imm%d: (connect) s1=0x%x s2=0x%x, s3=0x%x\n",
34440783Snsouch				vpo->vpo_unit, s1 & 0xff, s2 & 0xff, s3 & 0xff);
34540783Snsouch		if (disconnected)
34640783Snsouch			*disconnected = VP0_ECONNECT;
34740783Snsouch	}
34839136Snsouch
34939136Snsouch	return (0);
35039136Snsouch}
35139136Snsouch
35239136Snsouch/*
35339136Snsouch * imm_detect()
35439136Snsouch *
35539136Snsouch * Detect and initialise the VP0 adapter.
35639136Snsouch */
35739136Snsouchstatic int
35839136Snsouchimm_detect(struct vpoio_data *vpo)
35939136Snsouch{
36039136Snsouch	int error;
36139136Snsouch
36239520Snsouch	if ((error = ppb_request_bus(&vpo->vpo_dev, PPB_DONTWAIT)))
36339520Snsouch		return (error);
36439136Snsouch
36539520Snsouch	/* disconnect the drive, keep the bus */
36639520Snsouch	imm_disconnect(vpo, NULL, 0);
36739136Snsouch
36839520Snsouch	/* we already have the bus, just connect */
36939520Snsouch	imm_connect(vpo, PPB_DONTWAIT, &error, 0);
37039520Snsouch
37139520Snsouch	if (error) {
37239520Snsouch		if (bootverbose)
37339520Snsouch			printf("imm%d: can't connect to the drive\n",
37439520Snsouch				vpo->vpo_unit);
37539520Snsouch		goto error;
37639520Snsouch	}
37739520Snsouch
37839136Snsouch	/* send SCSI reset signal */
37939136Snsouch	ppb_MS_microseq(&vpo->vpo_dev, reset_microseq, NULL);
38039136Snsouch
38139520Snsouch	/* release the bus now */
38239520Snsouch	imm_disconnect(vpo, &error, 1);
38339136Snsouch
38439136Snsouch	/* ensure we are disconnected or daisy chained peripheral
38539136Snsouch	 * may cause serious problem to the disk */
38639136Snsouch
38739520Snsouch	if (error) {
38839520Snsouch		if (bootverbose)
38939520Snsouch			printf("imm%d: can't disconnect from the drive\n",
39039520Snsouch				vpo->vpo_unit);
39139900Snsouch		goto error;
39239520Snsouch	}
39339136Snsouch
39439136Snsouch	return (0);
39539520Snsouch
39639520Snsoucherror:
39739520Snsouch	ppb_release_bus(&vpo->vpo_dev);
39839520Snsouch	return (VP0_EINITFAILED);
39939136Snsouch}
40039136Snsouch
40139136Snsouch/*
40239136Snsouch * imm_outstr()
40339136Snsouch */
40439136Snsouchstatic int
40539136Snsouchimm_outstr(struct vpoio_data *vpo, char *buffer, int size)
40639136Snsouch{
40739136Snsouch	int error = 0;
40839136Snsouch
40939136Snsouch	if (PPB_IN_EPP_MODE(&vpo->vpo_dev))
41039136Snsouch		ppb_reset_epp_timeout(&vpo->vpo_dev);
41139136Snsouch
41239136Snsouch	ppb_MS_exec(&vpo->vpo_dev, MS_OP_PUT, buffer, size, MS_UNKNOWN, &error);
41339136Snsouch
41439136Snsouch	return (error);
41539136Snsouch}
41639136Snsouch
41739136Snsouch/*
41839136Snsouch * imm_instr()
41939136Snsouch */
42039136Snsouchstatic int
42139136Snsouchimm_instr(struct vpoio_data *vpo, char *buffer, int size)
42239136Snsouch{
42339136Snsouch	int error = 0;
42439136Snsouch
42539136Snsouch	if (PPB_IN_EPP_MODE(&vpo->vpo_dev))
42639136Snsouch		ppb_reset_epp_timeout(&vpo->vpo_dev);
42739136Snsouch
42839136Snsouch	ppb_MS_exec(&vpo->vpo_dev, MS_OP_GET, buffer, size, MS_UNKNOWN, &error);
42939136Snsouch
43039136Snsouch	return (error);
43139136Snsouch}
43239136Snsouch
43339136Snsouchstatic char
43439136Snsouchimm_select(struct vpoio_data *vpo, int initiator, int target)
43539136Snsouch{
43639136Snsouch	DECLARE_SELECT_MICROSEQUENCE;
43739136Snsouch	int ret;
43839136Snsouch
43939136Snsouch	/* initialize the select microsequence */
44039136Snsouch	ppb_MS_init_msq(select_microseq, 1,
44139136Snsouch			SELECT_TARGET, 1 << initiator | 1 << target);
44239136Snsouch
44339136Snsouch	ppb_MS_microseq(&vpo->vpo_dev, select_microseq, &ret);
44439136Snsouch
44539136Snsouch	return (ret);
44639136Snsouch}
44739136Snsouch
44839136Snsouch/*
44939136Snsouch * imm_wait()
45039136Snsouch *
45139136Snsouch * H_SELIN must be low.
45239136Snsouch *
45339136Snsouch * XXX should be ported to microseq
45439136Snsouch */
45539136Snsouchstatic char
45639136Snsouchimm_wait(struct vpoio_data *vpo, int tmo)
45739136Snsouch{
45839136Snsouch
45939136Snsouch	register int	k;
46039136Snsouch	register char	r;
46139136Snsouch
46239136Snsouch	ppb_wctr(&vpo->vpo_dev, 0xc);
46339136Snsouch
46439136Snsouch	/* XXX should be ported to microseq */
46539136Snsouch	k = 0;
46639136Snsouch	while (!((r = ppb_rstr(&vpo->vpo_dev)) & 0x80) && (k++ < tmo))
46739136Snsouch		DELAY(1);
46839136Snsouch
46939136Snsouch	/*
47039136Snsouch	 * Return some status information.
47139136Snsouch	 * Semantics :	0x88 = ZIP+ wants more data
47239136Snsouch	 *		0x98 = ZIP+ wants to send more data
47339136Snsouch	 *		0xa8 = ZIP+ wants command
47439136Snsouch	 *		0xb8 = end of transfer, ZIP+ is sending status
47539136Snsouch	 */
47639136Snsouch	ppb_wctr(&vpo->vpo_dev, 0x4);
47739136Snsouch	if (k < tmo)
47839136Snsouch	  return (r & 0xb8);
47939136Snsouch
48039136Snsouch	return (0);			   /* command timed out */
48139136Snsouch}
48239136Snsouch
48339136Snsouchstatic int
48439136Snsouchimm_negociate(struct vpoio_data *vpo)
48539136Snsouch{
48639136Snsouch	DECLARE_NEGOCIATE_MICROSEQ;
48739136Snsouch	int negociate_mode;
48839136Snsouch	int ret;
48939136Snsouch
49039136Snsouch	if (PPB_IN_NIBBLE_MODE(&vpo->vpo_dev))
49139136Snsouch		negociate_mode = 0;
49239136Snsouch	else if (PPB_IN_PS2_MODE(&vpo->vpo_dev))
49339136Snsouch		negociate_mode = 1;
49439136Snsouch	else
49539136Snsouch		return (0);
49639136Snsouch
49739136Snsouch#if 0 /* XXX use standalone code not to depend on ppb_1284 code yet */
49839136Snsouch	ret = ppb_1284_negociate(&vpo->vpo_dev, negociate_mode);
49939136Snsouch
50039136Snsouch	if (ret)
50139136Snsouch		return (VP0_ENEGOCIATE);
50239136Snsouch#endif
50339136Snsouch
50439136Snsouch	ppb_MS_init_msq(negociate_microseq, 1, NEGOCIATED_MODE, negociate_mode);
50539136Snsouch
50639136Snsouch	ppb_MS_microseq(&vpo->vpo_dev, negociate_microseq, &ret);
50739136Snsouch
50839136Snsouch	return (ret);
50939136Snsouch}
51039136Snsouch
51139136Snsouch/*
51239136Snsouch * imm_probe()
51339136Snsouch *
51439136Snsouch * Low level probe of vpo device
51539136Snsouch *
51639136Snsouch */
51739136Snsouchstruct ppb_device *
51839136Snsouchimm_probe(struct ppb_data *ppb, struct vpoio_data *vpo)
51939136Snsouch{
52039136Snsouch
52139136Snsouch	/* ppbus dependent initialisation */
52239136Snsouch	vpo->vpo_dev.id_unit = vpo->vpo_unit;
52339136Snsouch	vpo->vpo_dev.name = "vpo";
52439136Snsouch	vpo->vpo_dev.ppb = ppb;
52539136Snsouch
52639136Snsouch	/* now, try to initialise the drive */
52739136Snsouch	if (imm_detect(vpo)) {
52839136Snsouch		return (NULL);
52939136Snsouch	}
53039136Snsouch
53139136Snsouch	return (&vpo->vpo_dev);
53239136Snsouch}
53339136Snsouch
53439136Snsouch/*
53539136Snsouch * imm_attach()
53639136Snsouch *
53739136Snsouch * Low level attachment of vpo device
53839136Snsouch *
53939136Snsouch */
54039136Snsouchint
54139136Snsouchimm_attach(struct vpoio_data *vpo)
54239136Snsouch{
54339136Snsouch	int epp;
54439136Snsouch
54539136Snsouch	/*
54639136Snsouch	 * Report ourselves
54739136Snsouch	 */
54839136Snsouch	printf("imm%d: <Iomega Matchmaker Parallel to SCSI interface> on ppbus %d\n",
54939136Snsouch		vpo->vpo_dev.id_unit, vpo->vpo_dev.ppb->ppb_link->adapter_unit);
55039136Snsouch
55139136Snsouch	/*
55239136Snsouch	 * Initialize microsequence code
55339136Snsouch	 */
55439136Snsouch	vpo->vpo_nibble_inbyte_msq = (struct ppb_microseq *)malloc(
55539136Snsouch		sizeof(nibble_inbyte_submicroseq), M_DEVBUF, M_NOWAIT);
55639136Snsouch
55739136Snsouch	if (!vpo->vpo_nibble_inbyte_msq)
55839136Snsouch		return (0);
55939136Snsouch
56039136Snsouch	bcopy((void *)nibble_inbyte_submicroseq,
56139136Snsouch		(void *)vpo->vpo_nibble_inbyte_msq,
56239136Snsouch		sizeof(nibble_inbyte_submicroseq));
56339136Snsouch
56439136Snsouch	INIT_NIBBLE_INBYTE_SUBMICROSEQ(vpo);
56539136Snsouch
56639136Snsouch	/*
56739136Snsouch	 * Initialize mode dependent in/out microsequences
56839136Snsouch	 */
56939136Snsouch	ppb_request_bus(&vpo->vpo_dev, PPB_WAIT);
57039136Snsouch
57139136Snsouch	/* enter NIBBLE mode to configure submsq */
57239136Snsouch	if (ppb_set_mode(&vpo->vpo_dev, PPB_NIBBLE) != -1) {
57339136Snsouch
57439136Snsouch		ppb_MS_GET_init(&vpo->vpo_dev, vpo->vpo_nibble_inbyte_msq);
57539136Snsouch		ppb_MS_PUT_init(&vpo->vpo_dev, spp_outbyte_submicroseq);
57639136Snsouch	}
57739136Snsouch
57839136Snsouch	/* enter PS2 mode to configure submsq */
57939136Snsouch	if (ppb_set_mode(&vpo->vpo_dev, PPB_PS2) != -1) {
58039136Snsouch
58139136Snsouch		ppb_MS_GET_init(&vpo->vpo_dev, ps2_inbyte_submicroseq);
58239136Snsouch		ppb_MS_PUT_init(&vpo->vpo_dev, spp_outbyte_submicroseq);
58339136Snsouch	}
58439136Snsouch
58539136Snsouch	epp = ppb_get_epp_protocol(&vpo->vpo_dev);
58639136Snsouch
58739136Snsouch	/* enter EPP mode to configure submsq */
58839136Snsouch	if (ppb_set_mode(&vpo->vpo_dev, PPB_EPP) != -1) {
58939136Snsouch
59039136Snsouch		switch (epp) {
59139136Snsouch		case EPP_1_9:
59239136Snsouch		case EPP_1_7:
59339136Snsouch			ppb_MS_GET_init(&vpo->vpo_dev, epp17_instr);
59439136Snsouch			ppb_MS_PUT_init(&vpo->vpo_dev, epp17_outstr);
59539136Snsouch			break;
59639136Snsouch		default:
59739136Snsouch			panic("%s: unknown EPP protocol (0x%x)", __FUNCTION__,
59839136Snsouch				epp);
59939136Snsouch		}
60039136Snsouch	}
60139136Snsouch
60239136Snsouch	/* try to enter EPP or PS/2 mode, NIBBLE otherwise */
60339136Snsouch	if (ppb_set_mode(&vpo->vpo_dev, PPB_EPP) != -1) {
60439136Snsouch		switch (epp) {
60539136Snsouch		case EPP_1_9:
60639136Snsouch			printf("imm%d: EPP 1.9 mode\n", vpo->vpo_unit);
60739136Snsouch			break;
60839136Snsouch		case EPP_1_7:
60939136Snsouch			printf("imm%d: EPP 1.7 mode\n", vpo->vpo_unit);
61039136Snsouch			break;
61139136Snsouch		default:
61239136Snsouch			panic("%s: unknown EPP protocol (0x%x)", __FUNCTION__,
61339136Snsouch				epp);
61439136Snsouch		}
61539136Snsouch	} else if (ppb_set_mode(&vpo->vpo_dev, PPB_PS2) != -1)
61639136Snsouch		printf("imm%d: PS2 mode\n", vpo->vpo_unit);
61739136Snsouch
61839136Snsouch	else if (ppb_set_mode(&vpo->vpo_dev, PPB_NIBBLE) != -1)
61939136Snsouch		printf("imm%d: NIBBLE mode\n", vpo->vpo_unit);
62039136Snsouch
62139136Snsouch	else {
62239136Snsouch		printf("imm%d: can't enter NIBBLE, PS2 or EPP mode\n",
62339136Snsouch			vpo->vpo_unit);
62439136Snsouch
62539136Snsouch		ppb_release_bus(&vpo->vpo_dev);
62639136Snsouch
62739136Snsouch		free(vpo->vpo_nibble_inbyte_msq, M_DEVBUF);
62839136Snsouch		return (0);
62939136Snsouch	}
63039136Snsouch
63139136Snsouch	ppb_release_bus(&vpo->vpo_dev);
63239136Snsouch
63339136Snsouch	return (1);
63439136Snsouch}
63539136Snsouch
63639136Snsouch/*
63739136Snsouch * imm_reset_bus()
63839136Snsouch *
63939136Snsouch */
64039136Snsouchint
64139136Snsouchimm_reset_bus(struct vpoio_data *vpo)
64239136Snsouch{
64339520Snsouch	int disconnected;
64439136Snsouch
64539520Snsouch	/* first, connect to the drive and request the bus */
64639520Snsouch	imm_connect(vpo, PPB_WAIT|PPB_INTR, &disconnected, 1);
64739136Snsouch
64839520Snsouch	if (!disconnected) {
64939136Snsouch
65039136Snsouch		/* reset the SCSI bus */
65139136Snsouch		ppb_MS_microseq(&vpo->vpo_dev, reset_microseq, NULL);
65239136Snsouch
65339136Snsouch		/* then disconnect */
65439520Snsouch		imm_disconnect(vpo, NULL, 1);
65539136Snsouch	}
65639136Snsouch
65739136Snsouch	return (0);
65839136Snsouch}
65939136Snsouch
66039136Snsouch/*
66139136Snsouch * imm_do_scsi()
66239136Snsouch *
66339136Snsouch * Send an SCSI command
66439136Snsouch *
66539136Snsouch */
66639136Snsouchint
66739136Snsouchimm_do_scsi(struct vpoio_data *vpo, int host, int target, char *command,
66839136Snsouch		int clen, char *buffer, int blen, int *result, int *count,
66939136Snsouch		int *ret)
67039136Snsouch{
67139136Snsouch
67239136Snsouch	register char r;
67339136Snsouch	char l, h = 0;
67439136Snsouch	int len, error = 0, not_connected = 0;
67539136Snsouch	register int k;
67639136Snsouch	int negociated = 0;
67739136Snsouch
67839136Snsouch	/*
67939136Snsouch	 * enter disk state, allocate the ppbus
68039136Snsouch	 *
68139136Snsouch	 * XXX
68239136Snsouch	 * Should we allow this call to be interruptible?
68339136Snsouch	 * The only way to report the interruption is to return
68439136Snsouch	 * EIO do upper SCSI code :^(
68539136Snsouch	 */
68639520Snsouch	if ((error = imm_connect(vpo, PPB_WAIT|PPB_INTR, &not_connected, 1)))
68739136Snsouch		return (error);
68839136Snsouch
68939136Snsouch	if (not_connected) {
69039136Snsouch		*ret = VP0_ECONNECT; goto error;
69139136Snsouch	}
69239136Snsouch
69339136Snsouch	/*
69439136Snsouch	 * Select the drive ...
69539136Snsouch	 */
69639136Snsouch	if ((*ret = imm_select(vpo,host,target)))
69739136Snsouch		goto error;
69839136Snsouch
69939136Snsouch	/*
70039136Snsouch	 * Send the command ...
70139136Snsouch	 */
70239136Snsouch	for (k = 0; k < clen; k+=2) {
70339136Snsouch		if (imm_wait(vpo, VP0_FAST_SPINTMO) != (char)0xa8) {
70439136Snsouch			*ret = VP0_ECMD_TIMEOUT;
70539136Snsouch			goto error;
70639136Snsouch		}
70739136Snsouch		if (imm_outstr(vpo, &command[k], 2)) {
70839136Snsouch			*ret = VP0_EPPDATA_TIMEOUT;
70939136Snsouch			goto error;
71039136Snsouch		}
71139136Snsouch	}
71239136Snsouch
71339136Snsouch	if (!(r = imm_wait(vpo, VP0_LOW_SPINTMO))) {
71439136Snsouch		*ret = VP0_ESTATUS_TIMEOUT; goto error;
71539136Snsouch	}
71639136Snsouch
71739136Snsouch	if ((r & 0x30) == 0x10) {
71839136Snsouch		if (imm_negociate(vpo)) {
71939136Snsouch			*ret = VP0_ENEGOCIATE;
72039136Snsouch			goto error;
72139136Snsouch		} else
72239136Snsouch			negociated = 1;
72339136Snsouch	}
72439136Snsouch
72539136Snsouch	/*
72639136Snsouch	 * Complete transfer ...
72739136Snsouch	 */
72839136Snsouch	*count = 0;
72939136Snsouch	for (;;) {
73039136Snsouch
73139136Snsouch		if (!(r = imm_wait(vpo, VP0_LOW_SPINTMO))) {
73239136Snsouch			*ret = VP0_ESTATUS_TIMEOUT; goto error;
73339136Snsouch		}
73439136Snsouch
73539136Snsouch		/* stop when the ZIP+ wants to send status */
73639136Snsouch		if (r == (char)0xb8)
73739136Snsouch			break;
73839136Snsouch
73939136Snsouch		if (*count >= blen) {
74039136Snsouch			*ret = VP0_EDATA_OVERFLOW;
74139136Snsouch			goto error;
74239136Snsouch		}
74339136Snsouch
74439136Snsouch		/* ZIP+ wants to send data? */
74539136Snsouch		if (r == (char)0x88) {
74639136Snsouch			len = (((blen - *count) >= VP0_SECTOR_SIZE)) ?
74739136Snsouch				VP0_SECTOR_SIZE : 2;
74839136Snsouch
74939136Snsouch			error = imm_outstr(vpo, &buffer[*count], len);
75039136Snsouch		} else {
75139136Snsouch			if (!PPB_IN_EPP_MODE(&vpo->vpo_dev))
75239136Snsouch				len = 1;
75339136Snsouch			else
75439136Snsouch				len = (((blen - *count) >= VP0_SECTOR_SIZE)) ?
75539136Snsouch					VP0_SECTOR_SIZE : 1;
75639136Snsouch
75739136Snsouch			error = imm_instr(vpo, &buffer[*count], len);
75839136Snsouch		}
75939136Snsouch
76039136Snsouch		if (error) {
76139136Snsouch			*ret = error;
76239136Snsouch			goto error;
76339136Snsouch		}
76439136Snsouch
76539136Snsouch		*count += len;
76639136Snsouch	}
76739136Snsouch
76839136Snsouch	if ((PPB_IN_NIBBLE_MODE(&vpo->vpo_dev) ||
76939136Snsouch			PPB_IN_PS2_MODE(&vpo->vpo_dev)) && negociated)
77039136Snsouch		ppb_MS_microseq(&vpo->vpo_dev, transfer_epilog, NULL);
77139136Snsouch
77239136Snsouch	/*
77339136Snsouch	 * Retrieve status ...
77439136Snsouch	 */
77539136Snsouch	if (imm_negociate(vpo)) {
77639136Snsouch		*ret = VP0_ENEGOCIATE;
77739136Snsouch		goto error;
77839136Snsouch	} else
77939136Snsouch		negociated = 1;
78039136Snsouch
78139136Snsouch	if (imm_instr(vpo, &l, 1)) {
78239136Snsouch		*ret = VP0_EOTHER; goto error;
78339136Snsouch	}
78439136Snsouch
78539136Snsouch	/* check if the ZIP+ wants to send more status */
78639136Snsouch	if (imm_wait(vpo, VP0_FAST_SPINTMO) == (char)0xb8)
78739136Snsouch		if (imm_instr(vpo, &h, 1)) {
78839136Snsouch			*ret = VP0_EOTHER+2; goto error;
78939136Snsouch		}
79039136Snsouch
79139136Snsouch	*result = ((int) h << 8) | ((int) l & 0xff);
79239136Snsouch
79339136Snsoucherror:
79439136Snsouch	if ((PPB_IN_NIBBLE_MODE(&vpo->vpo_dev) ||
79539136Snsouch			PPB_IN_PS2_MODE(&vpo->vpo_dev)) && negociated)
79639136Snsouch		ppb_MS_microseq(&vpo->vpo_dev, transfer_epilog, NULL);
79739136Snsouch
79839136Snsouch	/* return to printer state, release the ppbus */
79939520Snsouch	imm_disconnect(vpo, NULL, 1);
80039136Snsouch
80139136Snsouch	return (0);
80239136Snsouch}
803