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