immio.c revision 70608
139136Snsouch/*-
255939Snsouch * Copyright (c) 1998, 1999 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 *
2650477Speter * $FreeBSD: head/sys/dev/ppbus/immio.c 70608 2001-01-02 21:29:06Z nsouch $
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 */
3655205Speter#ifdef _KERNEL
3739136Snsouch#include <sys/param.h>
3839136Snsouch#include <sys/systm.h>
3955939Snsouch#include <sys/module.h>
4055939Snsouch#include <sys/bus.h>
4139136Snsouch#include <sys/malloc.h>
4239136Snsouch
4339136Snsouch
4455205Speter#endif	/* _KERNEL */
4539136Snsouch
4655205Speter#ifdef	_KERNEL
4755205Speter#endif /* _KERNEL */
4839136Snsouch
4942475Snsouch#include "opt_vpo.h"
5042475Snsouch
5155939Snsouch#include <dev/ppbus/ppbio.h>
5239136Snsouch#include <dev/ppbus/ppbconf.h>
5339136Snsouch#include <dev/ppbus/ppb_msq.h>
5439136Snsouch#include <dev/ppbus/vpoio.h>
5539136Snsouch#include <dev/ppbus/ppb_1284.h>
5639136Snsouch
5755939Snsouch#include "ppbus_if.h"
5855939Snsouch
5939136Snsouch#define VP0_SELTMO		5000	/* select timeout */
6039136Snsouch#define VP0_FAST_SPINTMO	500000	/* wait status timeout */
6139136Snsouch#define VP0_LOW_SPINTMO		5000000	/* wait status timeout */
6239136Snsouch
6339136Snsouch#define VP0_SECTOR_SIZE	512
6439136Snsouch
6539136Snsouch/*
6639136Snsouch * Microcode to execute very fast I/O sequences at the lowest bus level.
6739136Snsouch */
6839136Snsouch
6939136Snsouch#define SELECT_TARGET		MS_PARAM(6, 1, MS_TYP_CHA)
7039136Snsouch
7139136Snsouch#define DECLARE_SELECT_MICROSEQUENCE					\
7239136Snsouchstruct ppb_microseq select_microseq[] = {				\
7339136Snsouch	MS_CASS(0xc),							\
7439136Snsouch	/* first, check there is nothing holding onto the bus */	\
7539136Snsouch	MS_SET(VP0_SELTMO),						\
7639136Snsouch/* _loop: */								\
7743433Snsouch	MS_BRCLEAR(0x8, 2 /* _ready */),				\
7843433Snsouch	MS_DBRA(-2 /* _loop */),					\
7939136Snsouch	MS_RET(2),			/* bus busy */			\
8039136Snsouch/* _ready: */								\
8139136Snsouch	MS_CASS(0x4),							\
8239136Snsouch	MS_DASS(MS_UNKNOWN /* 0x80 | 1 << target */),			\
8339136Snsouch	MS_DELAY(1),							\
8439136Snsouch	MS_CASS(0xc),							\
8539136Snsouch	MS_CASS(0xd),							\
8639136Snsouch	/* now, wait until the drive is ready */			\
8739136Snsouch	MS_SET(VP0_SELTMO),						\
8839136Snsouch/* loop: */								\
8943433Snsouch	MS_BRSET(0x8, 3 /* ready */),					\
9043433Snsouch	MS_DBRA(-2 /* loop */),						\
9139136Snsouch/* error: */								\
9239136Snsouch	MS_CASS(0xc),							\
9339136Snsouch	MS_RET(VP0_ESELECT_TIMEOUT),					\
9439136Snsouch/* ready: */								\
9539136Snsouch	MS_CASS(0xc),							\
9639136Snsouch	MS_RET(0)							\
9739136Snsouch}
9839136Snsouch
9939136Snsouchstatic struct ppb_microseq transfer_epilog[] = {
10039136Snsouch	MS_CASS(0x4),
10139136Snsouch	MS_CASS(0xc),
10239136Snsouch	MS_CASS(0xe),
10339136Snsouch	MS_CASS(0x4),
10439136Snsouch	MS_RET(0)
10539136Snsouch};
10639136Snsouch
10739136Snsouch#define CPP_S1		MS_PARAM(10, 2, MS_TYP_PTR)
10839136Snsouch#define CPP_S2		MS_PARAM(13, 2, MS_TYP_PTR)
10939136Snsouch#define CPP_S3		MS_PARAM(16, 2, MS_TYP_PTR)
11039136Snsouch#define CPP_PARAM	MS_PARAM(17, 1, MS_TYP_CHA)
11139136Snsouch
11239136Snsouch#define DECLARE_CPP_MICROSEQ \
11339136Snsouchstruct ppb_microseq cpp_microseq[] = {					\
11439136Snsouch	MS_CASS(0x0c), MS_DELAY(2),					\
11539136Snsouch	MS_DASS(0xaa), MS_DELAY(10),					\
11639136Snsouch	MS_DASS(0x55), MS_DELAY(10),					\
11739136Snsouch	MS_DASS(0x00), MS_DELAY(10),					\
11839136Snsouch	MS_DASS(0xff), MS_DELAY(10),					\
11939136Snsouch	MS_RFETCH(MS_REG_STR, 0xb8, MS_UNKNOWN /* &s1 */),		\
12039136Snsouch	MS_DASS(0x87), MS_DELAY(10),					\
12139136Snsouch	MS_RFETCH(MS_REG_STR, 0xb8, MS_UNKNOWN /* &s2 */),		\
12239136Snsouch	MS_DASS(0x78), MS_DELAY(10),					\
12339136Snsouch	MS_RFETCH(MS_REG_STR, 0x38, MS_UNKNOWN /* &s3 */),		\
12439136Snsouch	MS_DASS(MS_UNKNOWN /* param */),				\
12539136Snsouch	MS_DELAY(2),							\
12639136Snsouch	MS_CASS(0x0c), MS_DELAY(10),					\
12739136Snsouch	MS_CASS(0x0d), MS_DELAY(2),					\
12839136Snsouch	MS_CASS(0x0c), MS_DELAY(10),					\
12939136Snsouch	MS_DASS(0xff), MS_DELAY(10),					\
13039136Snsouch	MS_RET(0)							\
13139136Snsouch}
13239136Snsouch
13339136Snsouch#define NEGOCIATED_MODE		MS_PARAM(2, 1, MS_TYP_CHA)
13439136Snsouch
13539136Snsouch#define DECLARE_NEGOCIATE_MICROSEQ \
13639136Snsouchstatic struct ppb_microseq negociate_microseq[] = { 			\
13739136Snsouch	MS_CASS(0x4),							\
13839136Snsouch	MS_DELAY(5),							\
13939136Snsouch	MS_DASS(MS_UNKNOWN /* mode */),					\
14039136Snsouch	MS_DELAY(100),							\
14139136Snsouch	MS_CASS(0x6),							\
14239136Snsouch	MS_DELAY(5),							\
14343433Snsouch	MS_BRSET(0x20, 5 /* continue */),				\
14439136Snsouch	MS_DELAY(5),							\
14539136Snsouch	MS_CASS(0x7),							\
14639136Snsouch	MS_DELAY(5),							\
14739136Snsouch	MS_CASS(0x6),							\
14839136Snsouch	MS_RET(VP0_ENEGOCIATE),						\
14939136Snsouch/* continue: */								\
15039136Snsouch	MS_DELAY(5),							\
15139136Snsouch	MS_CASS(0x7),							\
15239136Snsouch	MS_DELAY(5),							\
15339136Snsouch	MS_CASS(0x6),							\
15439136Snsouch	MS_RET(0)							\
15539136Snsouch}
15639136Snsouch
15739136Snsouchstatic struct ppb_microseq reset_microseq[] = {
15839136Snsouch	MS_CASS(0x04),
15939136Snsouch	MS_DASS(0x40),
16039136Snsouch	MS_DELAY(1),
16139136Snsouch	MS_CASS(0x0c),
16239136Snsouch	MS_CASS(0x0d),
16339136Snsouch	MS_DELAY(50),
16439136Snsouch	MS_CASS(0x0c),
16539136Snsouch	MS_CASS(0x04),
16639136Snsouch	MS_RET(0)
16739136Snsouch};
16839136Snsouch
16939136Snsouch/*
17039136Snsouch * nibble_inbyte_hook()
17139136Snsouch *
17239136Snsouch * Formats high and low nibble into a character
17339136Snsouch */
17439136Snsouchstatic int
17539136Snsouchnibble_inbyte_hook (void *p, char *ptr)
17639136Snsouch{
17739136Snsouch	struct vpo_nibble *s = (struct vpo_nibble *)p;
17839136Snsouch
17939136Snsouch	/* increment the buffer pointer */
18039136Snsouch	*ptr = ((s->l >> 4) & 0x0f) + (s->h & 0xf0);
18139136Snsouch
18239136Snsouch	return (0);
18339136Snsouch}
18439136Snsouch
18539136Snsouch/*
18639136Snsouch * Macro used to initialize each vpoio_data structure during
18739136Snsouch * low level attachment
18839136Snsouch *
18939136Snsouch * XXX should be converted to ppb_MS_init_msq()
19039136Snsouch */
19139136Snsouch#define INIT_NIBBLE_INBYTE_SUBMICROSEQ(vpo) {		    	\
19239136Snsouch	(vpo)->vpo_nibble_inbyte_msq[6].arg[2].p =		\
19339136Snsouch			(void *)&(vpo)->vpo_nibble.h;		\
19439136Snsouch	(vpo)->vpo_nibble_inbyte_msq[3].arg[2].p =		\
19539136Snsouch			(void *)&(vpo)->vpo_nibble.l;		\
19639136Snsouch	(vpo)->vpo_nibble_inbyte_msq[9].arg[0].f =		\
19739136Snsouch			nibble_inbyte_hook;			\
19839136Snsouch	(vpo)->vpo_nibble_inbyte_msq[9].arg[1].p =		\
19939136Snsouch			(void *)&(vpo)->vpo_nibble;		\
20039136Snsouch}
20139136Snsouch
20239136Snsouch/*
20339136Snsouch * This is the sub-microseqence for MS_GET in NIBBLE mode
20439136Snsouch * Retrieve the two nibbles and call the C function to generate the character
20539136Snsouch * and store it in the buffer (see nibble_inbyte_hook())
20639136Snsouch */
20739136Snsouchstatic struct ppb_microseq nibble_inbyte_submicroseq[] = {
20839136Snsouch	  MS_CASS(0x4),
20939136Snsouch
21039136Snsouch/* loop: */
21139136Snsouch	  MS_CASS(0x6),
21239136Snsouch	  MS_DELAY(1),
21339136Snsouch	  MS_RFETCH(MS_REG_STR, MS_FETCH_ALL, MS_UNKNOWN /* low nibble */),
21439136Snsouch	  MS_CASS(0x5),
21539136Snsouch	  MS_DELAY(1),
21639136Snsouch	  MS_RFETCH(MS_REG_STR, MS_FETCH_ALL, MS_UNKNOWN /* high nibble */),
21739136Snsouch	  MS_CASS(0x4),
21839136Snsouch	  MS_DELAY(1),
21939136Snsouch
22039136Snsouch	  /* do a C call to format the received nibbles */
22139136Snsouch	  MS_C_CALL(MS_UNKNOWN /* C hook */, MS_UNKNOWN /* param */),
22243433Snsouch	  MS_DBRA(-7 /* loop */),
22339136Snsouch	  MS_RET(0)
22439136Snsouch};
22539136Snsouch
22639136Snsouch/*
22739136Snsouch * This is the sub-microseqence for MS_GET in PS2 mode
22839136Snsouch */
22939136Snsouchstatic struct ppb_microseq ps2_inbyte_submicroseq[] = {
23039136Snsouch	  MS_CASS(0x4),
23139136Snsouch
23239136Snsouch/* loop: */
23339136Snsouch	  MS_CASS(PCD | 0x6),
23439136Snsouch	  MS_RFETCH_P(1, MS_REG_DTR, MS_FETCH_ALL),
23539136Snsouch	  MS_CASS(PCD | 0x5),
23643433Snsouch	  MS_DBRA(-4 /* loop */),
23739136Snsouch
23839136Snsouch	  MS_RET(0)
23939136Snsouch};
24039136Snsouch
24139136Snsouch/*
24239136Snsouch * This is the sub-microsequence for MS_PUT in both NIBBLE and PS2 modes
24339136Snsouch */
24439136Snsouchstatic struct ppb_microseq spp_outbyte_submicroseq[] = {
24539136Snsouch	  MS_CASS(0x4),
24639136Snsouch
24739136Snsouch/* loop: */
24839136Snsouch	  MS_RASSERT_P(1, MS_REG_DTR),
24939136Snsouch	  MS_CASS(0x5),
25043433Snsouch	  MS_DBRA(0),				/* decrement counter */
25139136Snsouch	  MS_RASSERT_P(1, MS_REG_DTR),
25239136Snsouch	  MS_CASS(0x0),
25343433Snsouch	  MS_DBRA(-6 /* loop */),
25439136Snsouch
25539136Snsouch	  /* return from the put call */
25639136Snsouch	  MS_CASS(0x4),
25739136Snsouch	  MS_RET(0)
25839136Snsouch};
25939136Snsouch
26039136Snsouch/* EPP 1.7 microsequences, ptr and len set at runtime */
26139136Snsouchstatic struct ppb_microseq epp17_outstr[] = {
26239136Snsouch	  MS_CASS(0x4),
26343433Snsouch	  MS_RASSERT_P(MS_ACCUM, MS_REG_EPP_D),
26439136Snsouch	  MS_CASS(0xc),
26539136Snsouch	  MS_RET(0),
26639136Snsouch};
26739136Snsouch
26839136Snsouchstatic struct ppb_microseq epp17_instr[] = {
26939136Snsouch	  MS_CASS(PCD | 0x4),
27043433Snsouch	  MS_RFETCH_P(MS_ACCUM, MS_REG_EPP_D, MS_FETCH_ALL),
27139136Snsouch	  MS_CASS(PCD | 0xc),
27239136Snsouch	  MS_RET(0),
27339136Snsouch};
27439136Snsouch
27539136Snsouchstatic int
27639520Snsouchimm_disconnect(struct vpoio_data *vpo, int *connected, int release_bus)
27739136Snsouch{
27839136Snsouch	DECLARE_CPP_MICROSEQ;
27939136Snsouch
28055939Snsouch	device_t ppbus = device_get_parent(vpo->vpo_dev);
28139136Snsouch	char s1, s2, s3;
28239136Snsouch	int ret;
28339136Snsouch
28439136Snsouch	/* all should be ok */
28539520Snsouch	if (connected)
28639520Snsouch		*connected = 0;
28739136Snsouch
28839136Snsouch	ppb_MS_init_msq(cpp_microseq, 4, CPP_S1, (void *)&s1,
28939136Snsouch			CPP_S2, (void *)&s2, CPP_S3, (void *)&s3,
29039136Snsouch			CPP_PARAM, 0x30);
29139136Snsouch
29255939Snsouch	ppb_MS_microseq(ppbus, vpo->vpo_dev, cpp_microseq, &ret);
29339136Snsouch
29440783Snsouch	if ((s1 != (char)0xb8 || s2 != (char)0x18 || s3 != (char)0x38)) {
29540783Snsouch		if (bootverbose)
29640783Snsouch			printf("imm%d: (disconnect) s1=0x%x s2=0x%x, s3=0x%x\n",
29740783Snsouch				vpo->vpo_unit, s1 & 0xff, s2 & 0xff, s3 & 0xff);
29840783Snsouch		if (connected)
29940783Snsouch			*connected = VP0_ECONNECT;
30040783Snsouch	}
30139136Snsouch
30239520Snsouch	if (release_bus)
30355939Snsouch		return (ppb_release_bus(ppbus, vpo->vpo_dev));
30439520Snsouch	else
30539520Snsouch		return (0);
30639136Snsouch}
30739136Snsouch
30839136Snsouch/*
30939136Snsouch * how	: PPB_WAIT or PPB_DONTWAIT
31039136Snsouch */
31139136Snsouchstatic int
31239520Snsouchimm_connect(struct vpoio_data *vpo, int how, int *disconnected, int request_bus)
31339136Snsouch{
31439136Snsouch	DECLARE_CPP_MICROSEQ;
31539136Snsouch
31655939Snsouch	device_t ppbus = device_get_parent(vpo->vpo_dev);
31739136Snsouch	char s1, s2, s3;
31839136Snsouch	int error;
31939136Snsouch	int ret;
32039136Snsouch
32139136Snsouch	/* all should be ok */
32239520Snsouch	if (disconnected)
32339520Snsouch		*disconnected = 0;
32439136Snsouch
32539520Snsouch	if (request_bus)
32655939Snsouch		if ((error = ppb_request_bus(ppbus, vpo->vpo_dev, how)))
32739520Snsouch			return (error);
32839136Snsouch
32939136Snsouch	ppb_MS_init_msq(cpp_microseq, 3, CPP_S1, (void *)&s1,
33039136Snsouch			CPP_S2, (void *)&s2, CPP_S3, (void *)&s3);
33139136Snsouch
33239136Snsouch	/* select device 0 in compatible mode */
33339136Snsouch	ppb_MS_init_msq(cpp_microseq, 1, CPP_PARAM, 0xe0);
33455939Snsouch	ppb_MS_microseq(ppbus, vpo->vpo_dev, cpp_microseq, &ret);
33539136Snsouch
33639136Snsouch	/* disconnect all devices */
33739136Snsouch	ppb_MS_init_msq(cpp_microseq, 1, CPP_PARAM, 0x30);
33855939Snsouch	ppb_MS_microseq(ppbus, vpo->vpo_dev, cpp_microseq, &ret);
33939136Snsouch
34055939Snsouch	if (PPB_IN_EPP_MODE(ppbus))
34139136Snsouch		ppb_MS_init_msq(cpp_microseq, 1, CPP_PARAM, 0x28);
34239136Snsouch	else
34339136Snsouch		ppb_MS_init_msq(cpp_microseq, 1, CPP_PARAM, 0xe0);
34439136Snsouch
34555939Snsouch	ppb_MS_microseq(ppbus, vpo->vpo_dev, cpp_microseq, &ret);
34639136Snsouch
34740783Snsouch	if ((s1 != (char)0xb8 || s2 != (char)0x18 || s3 != (char)0x30)) {
34840783Snsouch		if (bootverbose)
34940783Snsouch			printf("imm%d: (connect) s1=0x%x s2=0x%x, s3=0x%x\n",
35040783Snsouch				vpo->vpo_unit, s1 & 0xff, s2 & 0xff, s3 & 0xff);
35140783Snsouch		if (disconnected)
35240783Snsouch			*disconnected = VP0_ECONNECT;
35340783Snsouch	}
35439136Snsouch
35539136Snsouch	return (0);
35639136Snsouch}
35739136Snsouch
35839136Snsouch/*
35939136Snsouch * imm_detect()
36039136Snsouch *
36139136Snsouch * Detect and initialise the VP0 adapter.
36239136Snsouch */
36339136Snsouchstatic int
36439136Snsouchimm_detect(struct vpoio_data *vpo)
36539136Snsouch{
36655939Snsouch	device_t ppbus = device_get_parent(vpo->vpo_dev);
36739136Snsouch	int error;
36839136Snsouch
36955939Snsouch	if ((error = ppb_request_bus(ppbus, vpo->vpo_dev, PPB_DONTWAIT)))
37039520Snsouch		return (error);
37139136Snsouch
37239520Snsouch	/* disconnect the drive, keep the bus */
37339520Snsouch	imm_disconnect(vpo, NULL, 0);
37439136Snsouch
37570608Snsouch	vpo->vpo_mode_found = VP0_MODE_UNDEFINED;
37670608Snsouch	error = 1;
37739520Snsouch
37870608Snsouch	/* try to enter EPP mode since vpoio failure put the bus in NIBBLE */
37970608Snsouch	if (ppb_set_mode(ppbus, PPB_EPP) != -1) {
38070608Snsouch		imm_connect(vpo, PPB_DONTWAIT, &error, 0);
38170608Snsouch	}
38270608Snsouch
38370608Snsouch	/* if connection failed try PS/2 then NIBBLE modes */
38439520Snsouch	if (error) {
38570608Snsouch		if (ppb_set_mode(ppbus, PPB_PS2) != -1) {
38670608Snsouch			imm_connect(vpo, PPB_DONTWAIT, &error, 0);
38770608Snsouch		}
38870608Snsouch		if (error) {
38970608Snsouch			if (ppb_set_mode(ppbus, PPB_NIBBLE) != -1) {
39070608Snsouch				imm_connect(vpo, PPB_DONTWAIT, &error, 0);
39170608Snsouch				if (error)
39270608Snsouch					goto error;
39370608Snsouch				vpo->vpo_mode_found = VP0_MODE_NIBBLE;
39470608Snsouch			} else {
39570608Snsouch				printf("imm%d: NIBBLE mode unavailable!\n", vpo->vpo_unit);
39670608Snsouch				goto error;
39770608Snsouch			}
39870608Snsouch		} else {
39970608Snsouch			vpo->vpo_mode_found = VP0_MODE_PS2;
40070608Snsouch		}
40170608Snsouch	} else {
40270608Snsouch		vpo->vpo_mode_found = VP0_MODE_EPP;
40339520Snsouch	}
40439520Snsouch
40539136Snsouch	/* send SCSI reset signal */
40655939Snsouch	ppb_MS_microseq(ppbus, vpo->vpo_dev, reset_microseq, NULL);
40739136Snsouch
40839520Snsouch	/* release the bus now */
40939520Snsouch	imm_disconnect(vpo, &error, 1);
41039136Snsouch
41139136Snsouch	/* ensure we are disconnected or daisy chained peripheral
41239136Snsouch	 * may cause serious problem to the disk */
41339136Snsouch
41439520Snsouch	if (error) {
41539520Snsouch		if (bootverbose)
41639520Snsouch			printf("imm%d: can't disconnect from the drive\n",
41739520Snsouch				vpo->vpo_unit);
41839900Snsouch		goto error;
41939520Snsouch	}
42039136Snsouch
42139136Snsouch	return (0);
42239520Snsouch
42339520Snsoucherror:
42455939Snsouch	ppb_release_bus(ppbus, vpo->vpo_dev);
42539520Snsouch	return (VP0_EINITFAILED);
42639136Snsouch}
42739136Snsouch
42839136Snsouch/*
42939136Snsouch * imm_outstr()
43039136Snsouch */
43139136Snsouchstatic int
43239136Snsouchimm_outstr(struct vpoio_data *vpo, char *buffer, int size)
43339136Snsouch{
43455939Snsouch	device_t ppbus = device_get_parent(vpo->vpo_dev);
43539136Snsouch	int error = 0;
43639136Snsouch
43755939Snsouch	if (PPB_IN_EPP_MODE(ppbus))
43855939Snsouch		ppb_reset_epp_timeout(ppbus);
43939136Snsouch
44055939Snsouch	ppb_MS_exec(ppbus, vpo->vpo_dev, MS_OP_PUT, (union ppb_insarg)buffer,
44145342Speter		(union ppb_insarg)size, (union ppb_insarg)MS_UNKNOWN, &error);
44239136Snsouch
44339136Snsouch	return (error);
44439136Snsouch}
44539136Snsouch
44639136Snsouch/*
44739136Snsouch * imm_instr()
44839136Snsouch */
44939136Snsouchstatic int
45039136Snsouchimm_instr(struct vpoio_data *vpo, char *buffer, int size)
45139136Snsouch{
45255939Snsouch	device_t ppbus = device_get_parent(vpo->vpo_dev);
45339136Snsouch	int error = 0;
45439136Snsouch
45555939Snsouch	if (PPB_IN_EPP_MODE(ppbus))
45655939Snsouch		ppb_reset_epp_timeout(ppbus);
45739136Snsouch
45855939Snsouch	ppb_MS_exec(ppbus, vpo->vpo_dev, MS_OP_GET, (union ppb_insarg)buffer,
45945342Speter		(union ppb_insarg)size, (union ppb_insarg)MS_UNKNOWN, &error);
46039136Snsouch
46139136Snsouch	return (error);
46239136Snsouch}
46339136Snsouch
46439136Snsouchstatic char
46539136Snsouchimm_select(struct vpoio_data *vpo, int initiator, int target)
46639136Snsouch{
46739136Snsouch	DECLARE_SELECT_MICROSEQUENCE;
46855939Snsouch	device_t ppbus = device_get_parent(vpo->vpo_dev);
46939136Snsouch	int ret;
47039136Snsouch
47139136Snsouch	/* initialize the select microsequence */
47239136Snsouch	ppb_MS_init_msq(select_microseq, 1,
47339136Snsouch			SELECT_TARGET, 1 << initiator | 1 << target);
47439136Snsouch
47555939Snsouch	ppb_MS_microseq(ppbus, vpo->vpo_dev, select_microseq, &ret);
47639136Snsouch
47739136Snsouch	return (ret);
47839136Snsouch}
47939136Snsouch
48039136Snsouch/*
48139136Snsouch * imm_wait()
48239136Snsouch *
48339136Snsouch * H_SELIN must be low.
48439136Snsouch *
48539136Snsouch * XXX should be ported to microseq
48639136Snsouch */
48739136Snsouchstatic char
48839136Snsouchimm_wait(struct vpoio_data *vpo, int tmo)
48939136Snsouch{
49055939Snsouch	device_t ppbus = device_get_parent(vpo->vpo_dev);
49139136Snsouch	register int	k;
49239136Snsouch	register char	r;
49339136Snsouch
49455939Snsouch	ppb_wctr(ppbus, 0xc);
49539136Snsouch
49639136Snsouch	/* XXX should be ported to microseq */
49739136Snsouch	k = 0;
49855939Snsouch	while (!((r = ppb_rstr(ppbus)) & 0x80) && (k++ < tmo))
49939136Snsouch		DELAY(1);
50039136Snsouch
50139136Snsouch	/*
50239136Snsouch	 * Return some status information.
50339136Snsouch	 * Semantics :	0x88 = ZIP+ wants more data
50439136Snsouch	 *		0x98 = ZIP+ wants to send more data
50539136Snsouch	 *		0xa8 = ZIP+ wants command
50639136Snsouch	 *		0xb8 = end of transfer, ZIP+ is sending status
50739136Snsouch	 */
50855939Snsouch	ppb_wctr(ppbus, 0x4);
50939136Snsouch	if (k < tmo)
51039136Snsouch	  return (r & 0xb8);
51139136Snsouch
51239136Snsouch	return (0);			   /* command timed out */
51339136Snsouch}
51439136Snsouch
51539136Snsouchstatic int
51639136Snsouchimm_negociate(struct vpoio_data *vpo)
51739136Snsouch{
51839136Snsouch	DECLARE_NEGOCIATE_MICROSEQ;
51955939Snsouch	device_t ppbus = device_get_parent(vpo->vpo_dev);
52039136Snsouch	int negociate_mode;
52139136Snsouch	int ret;
52239136Snsouch
52355939Snsouch	if (PPB_IN_NIBBLE_MODE(ppbus))
52439136Snsouch		negociate_mode = 0;
52555939Snsouch	else if (PPB_IN_PS2_MODE(ppbus))
52639136Snsouch		negociate_mode = 1;
52739136Snsouch	else
52839136Snsouch		return (0);
52939136Snsouch
53039136Snsouch#if 0 /* XXX use standalone code not to depend on ppb_1284 code yet */
53155939Snsouch	ret = ppb_1284_negociate(ppbus, negociate_mode);
53239136Snsouch
53339136Snsouch	if (ret)
53439136Snsouch		return (VP0_ENEGOCIATE);
53539136Snsouch#endif
53639136Snsouch
53755939Snsouch	ppb_MS_init_msq(negociate_microseq, 1,
53855939Snsouch			NEGOCIATED_MODE, negociate_mode);
53939136Snsouch
54055939Snsouch	ppb_MS_microseq(ppbus, vpo->vpo_dev, negociate_microseq, &ret);
54139136Snsouch
54239136Snsouch	return (ret);
54339136Snsouch}
54439136Snsouch
54539136Snsouch/*
54639136Snsouch * imm_probe()
54739136Snsouch *
54839136Snsouch * Low level probe of vpo device
54939136Snsouch *
55039136Snsouch */
55155939Snsouchint
55255939Snsouchimm_probe(device_t dev, struct vpoio_data *vpo)
55339136Snsouch{
55455939Snsouch	int error;
55539136Snsouch
55639136Snsouch	/* ppbus dependent initialisation */
55755939Snsouch	vpo->vpo_dev = dev;
55839136Snsouch
55939136Snsouch	/* now, try to initialise the drive */
56055939Snsouch	if ((error = imm_detect(vpo))) {
56155939Snsouch		return (error);
56239136Snsouch	}
56339136Snsouch
56455939Snsouch	return (0);
56539136Snsouch}
56639136Snsouch
56739136Snsouch/*
56839136Snsouch * imm_attach()
56939136Snsouch *
57039136Snsouch * Low level attachment of vpo device
57139136Snsouch *
57239136Snsouch */
57339136Snsouchint
57439136Snsouchimm_attach(struct vpoio_data *vpo)
57539136Snsouch{
57655939Snsouch	device_t ppbus = device_get_parent(vpo->vpo_dev);
57739136Snsouch
57839136Snsouch	/*
57939136Snsouch	 * Initialize microsequence code
58039136Snsouch	 */
58139136Snsouch	vpo->vpo_nibble_inbyte_msq = (struct ppb_microseq *)malloc(
58239136Snsouch		sizeof(nibble_inbyte_submicroseq), M_DEVBUF, M_NOWAIT);
58339136Snsouch
58439136Snsouch	if (!vpo->vpo_nibble_inbyte_msq)
58555939Snsouch		return (ENXIO);
58639136Snsouch
58739136Snsouch	bcopy((void *)nibble_inbyte_submicroseq,
58839136Snsouch		(void *)vpo->vpo_nibble_inbyte_msq,
58939136Snsouch		sizeof(nibble_inbyte_submicroseq));
59039136Snsouch
59139136Snsouch	INIT_NIBBLE_INBYTE_SUBMICROSEQ(vpo);
59239136Snsouch
59339136Snsouch	/*
59439136Snsouch	 * Initialize mode dependent in/out microsequences
59539136Snsouch	 */
59655939Snsouch	ppb_request_bus(ppbus, vpo->vpo_dev, PPB_WAIT);
59739136Snsouch
59870608Snsouch	/* ppbus automatically restore the last mode entered during detection */
59970608Snsouch	switch (vpo->vpo_mode_found) {
60070608Snsouch	case VP0_MODE_EPP:
60170608Snsouch		ppb_MS_GET_init(ppbus, vpo->vpo_dev, epp17_instr);
60270608Snsouch		ppb_MS_PUT_init(ppbus, vpo->vpo_dev, epp17_outstr);
60370608Snsouch		printf("imm%d: EPP mode\n", vpo->vpo_unit);
60470608Snsouch		break;
60570608Snsouch	case VP0_MODE_PS2:
60655939Snsouch		ppb_MS_GET_init(ppbus, vpo->vpo_dev, ps2_inbyte_submicroseq);
60755939Snsouch		ppb_MS_PUT_init(ppbus, vpo->vpo_dev, spp_outbyte_submicroseq);
60839136Snsouch		printf("imm%d: PS2 mode\n", vpo->vpo_unit);
60970608Snsouch		break;
61070608Snsouch	case VP0_MODE_NIBBLE:
61170608Snsouch		ppb_MS_GET_init(ppbus, vpo->vpo_dev, vpo->vpo_nibble_inbyte_msq);
61270608Snsouch		ppb_MS_PUT_init(ppbus, vpo->vpo_dev, spp_outbyte_submicroseq);
61339136Snsouch		printf("imm%d: NIBBLE mode\n", vpo->vpo_unit);
61470608Snsouch		break;
61570608Snsouch	default:
61670608Snsouch		panic("imm: unknown mode %d", vpo->vpo_mode_found);
61739136Snsouch	}
61839136Snsouch
61955939Snsouch	ppb_release_bus(ppbus, vpo->vpo_dev);
62039136Snsouch
62155939Snsouch	return (0);
62239136Snsouch}
62339136Snsouch
62439136Snsouch/*
62539136Snsouch * imm_reset_bus()
62639136Snsouch *
62739136Snsouch */
62839136Snsouchint
62939136Snsouchimm_reset_bus(struct vpoio_data *vpo)
63039136Snsouch{
63155939Snsouch	device_t ppbus = device_get_parent(vpo->vpo_dev);
63239520Snsouch	int disconnected;
63339136Snsouch
63439520Snsouch	/* first, connect to the drive and request the bus */
63539520Snsouch	imm_connect(vpo, PPB_WAIT|PPB_INTR, &disconnected, 1);
63639136Snsouch
63739520Snsouch	if (!disconnected) {
63839136Snsouch
63939136Snsouch		/* reset the SCSI bus */
64055939Snsouch		ppb_MS_microseq(ppbus, vpo->vpo_dev, reset_microseq, NULL);
64139136Snsouch
64239136Snsouch		/* then disconnect */
64339520Snsouch		imm_disconnect(vpo, NULL, 1);
64439136Snsouch	}
64539136Snsouch
64639136Snsouch	return (0);
64739136Snsouch}
64839136Snsouch
64939136Snsouch/*
65039136Snsouch * imm_do_scsi()
65139136Snsouch *
65239136Snsouch * Send an SCSI command
65339136Snsouch *
65439136Snsouch */
65539136Snsouchint
65639136Snsouchimm_do_scsi(struct vpoio_data *vpo, int host, int target, char *command,
65739136Snsouch		int clen, char *buffer, int blen, int *result, int *count,
65839136Snsouch		int *ret)
65939136Snsouch{
66055939Snsouch	device_t ppbus = device_get_parent(vpo->vpo_dev);
66139136Snsouch	register char r;
66239136Snsouch	char l, h = 0;
66339136Snsouch	int len, error = 0, not_connected = 0;
66439136Snsouch	register int k;
66539136Snsouch	int negociated = 0;
66639136Snsouch
66739136Snsouch	/*
66839136Snsouch	 * enter disk state, allocate the ppbus
66939136Snsouch	 *
67039136Snsouch	 * XXX
67139136Snsouch	 * Should we allow this call to be interruptible?
67239136Snsouch	 * The only way to report the interruption is to return
67339136Snsouch	 * EIO do upper SCSI code :^(
67439136Snsouch	 */
67539520Snsouch	if ((error = imm_connect(vpo, PPB_WAIT|PPB_INTR, &not_connected, 1)))
67639136Snsouch		return (error);
67739136Snsouch
67839136Snsouch	if (not_connected) {
67939136Snsouch		*ret = VP0_ECONNECT; goto error;
68039136Snsouch	}
68139136Snsouch
68239136Snsouch	/*
68339136Snsouch	 * Select the drive ...
68439136Snsouch	 */
68539136Snsouch	if ((*ret = imm_select(vpo,host,target)))
68639136Snsouch		goto error;
68739136Snsouch
68839136Snsouch	/*
68939136Snsouch	 * Send the command ...
69039136Snsouch	 */
69139136Snsouch	for (k = 0; k < clen; k+=2) {
69239136Snsouch		if (imm_wait(vpo, VP0_FAST_SPINTMO) != (char)0xa8) {
69339136Snsouch			*ret = VP0_ECMD_TIMEOUT;
69439136Snsouch			goto error;
69539136Snsouch		}
69639136Snsouch		if (imm_outstr(vpo, &command[k], 2)) {
69739136Snsouch			*ret = VP0_EPPDATA_TIMEOUT;
69839136Snsouch			goto error;
69939136Snsouch		}
70039136Snsouch	}
70139136Snsouch
70239136Snsouch	if (!(r = imm_wait(vpo, VP0_LOW_SPINTMO))) {
70339136Snsouch		*ret = VP0_ESTATUS_TIMEOUT; goto error;
70439136Snsouch	}
70539136Snsouch
70639136Snsouch	if ((r & 0x30) == 0x10) {
70739136Snsouch		if (imm_negociate(vpo)) {
70839136Snsouch			*ret = VP0_ENEGOCIATE;
70939136Snsouch			goto error;
71039136Snsouch		} else
71139136Snsouch			negociated = 1;
71239136Snsouch	}
71339136Snsouch
71439136Snsouch	/*
71539136Snsouch	 * Complete transfer ...
71639136Snsouch	 */
71739136Snsouch	*count = 0;
71839136Snsouch	for (;;) {
71939136Snsouch
72039136Snsouch		if (!(r = imm_wait(vpo, VP0_LOW_SPINTMO))) {
72139136Snsouch			*ret = VP0_ESTATUS_TIMEOUT; goto error;
72239136Snsouch		}
72339136Snsouch
72439136Snsouch		/* stop when the ZIP+ wants to send status */
72539136Snsouch		if (r == (char)0xb8)
72639136Snsouch			break;
72739136Snsouch
72839136Snsouch		if (*count >= blen) {
72939136Snsouch			*ret = VP0_EDATA_OVERFLOW;
73039136Snsouch			goto error;
73139136Snsouch		}
73239136Snsouch
73339136Snsouch		/* ZIP+ wants to send data? */
73439136Snsouch		if (r == (char)0x88) {
73539136Snsouch			len = (((blen - *count) >= VP0_SECTOR_SIZE)) ?
73639136Snsouch				VP0_SECTOR_SIZE : 2;
73739136Snsouch
73839136Snsouch			error = imm_outstr(vpo, &buffer[*count], len);
73939136Snsouch		} else {
74055939Snsouch			if (!PPB_IN_EPP_MODE(ppbus))
74139136Snsouch				len = 1;
74239136Snsouch			else
74339136Snsouch				len = (((blen - *count) >= VP0_SECTOR_SIZE)) ?
74439136Snsouch					VP0_SECTOR_SIZE : 1;
74539136Snsouch
74639136Snsouch			error = imm_instr(vpo, &buffer[*count], len);
74739136Snsouch		}
74839136Snsouch
74939136Snsouch		if (error) {
75039136Snsouch			*ret = error;
75139136Snsouch			goto error;
75239136Snsouch		}
75339136Snsouch
75439136Snsouch		*count += len;
75539136Snsouch	}
75639136Snsouch
75755939Snsouch	if ((PPB_IN_NIBBLE_MODE(ppbus) ||
75855939Snsouch			PPB_IN_PS2_MODE(ppbus)) && negociated)
75955939Snsouch		ppb_MS_microseq(ppbus, vpo->vpo_dev, transfer_epilog, NULL);
76039136Snsouch
76139136Snsouch	/*
76239136Snsouch	 * Retrieve status ...
76339136Snsouch	 */
76439136Snsouch	if (imm_negociate(vpo)) {
76539136Snsouch		*ret = VP0_ENEGOCIATE;
76639136Snsouch		goto error;
76739136Snsouch	} else
76839136Snsouch		negociated = 1;
76939136Snsouch
77039136Snsouch	if (imm_instr(vpo, &l, 1)) {
77139136Snsouch		*ret = VP0_EOTHER; goto error;
77239136Snsouch	}
77339136Snsouch
77439136Snsouch	/* check if the ZIP+ wants to send more status */
77539136Snsouch	if (imm_wait(vpo, VP0_FAST_SPINTMO) == (char)0xb8)
77639136Snsouch		if (imm_instr(vpo, &h, 1)) {
77739136Snsouch			*ret = VP0_EOTHER+2; goto error;
77839136Snsouch		}
77939136Snsouch
78039136Snsouch	*result = ((int) h << 8) | ((int) l & 0xff);
78139136Snsouch
78239136Snsoucherror:
78355939Snsouch	if ((PPB_IN_NIBBLE_MODE(ppbus) ||
78455939Snsouch			PPB_IN_PS2_MODE(ppbus)) && negociated)
78555939Snsouch		ppb_MS_microseq(ppbus, vpo->vpo_dev, transfer_epilog, NULL);
78639136Snsouch
78739136Snsouch	/* return to printer state, release the ppbus */
78839520Snsouch	imm_disconnect(vpo, NULL, 1);
78939136Snsouch
79039136Snsouch	return (0);
79139136Snsouch}
792