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, ¬_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