immio.c revision 153586
139136Snsouch/*- 255939Snsouch * Copyright (c) 1998, 1999 Nicolas Souchu 371633Snsouch * Copyright (c) 2001 Alcove - Nicolas Souchu 439136Snsouch * All rights reserved. 539136Snsouch * 639136Snsouch * Redistribution and use in source and binary forms, with or without 739136Snsouch * modification, are permitted provided that the following conditions 839136Snsouch * are met: 939136Snsouch * 1. Redistributions of source code must retain the above copyright 1039136Snsouch * notice, this list of conditions and the following disclaimer. 1139136Snsouch * 2. Redistributions in binary form must reproduce the above copyright 1239136Snsouch * notice, this list of conditions and the following disclaimer in the 1339136Snsouch * documentation and/or other materials provided with the distribution. 1439136Snsouch * 1539136Snsouch * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1639136Snsouch * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1739136Snsouch * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1839136Snsouch * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1939136Snsouch * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2039136Snsouch * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2139136Snsouch * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2239136Snsouch * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2339136Snsouch * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2439136Snsouch * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2539136Snsouch * SUCH DAMAGE. 2639136Snsouch * 2739136Snsouch * 2839136Snsouch */ 2939136Snsouch 30119418Sobrien#include <sys/cdefs.h> 31119418Sobrien__FBSDID("$FreeBSD: head/sys/dev/ppbus/immio.c 153586 2005-12-21 00:09:11Z sam $"); 32119418Sobrien 3339136Snsouch/* 3439136Snsouch * Iomega ZIP+ Matchmaker Parallel Port Interface driver 3539136Snsouch * 3639136Snsouch * Thanks to David Campbell work on the Linux driver and the Iomega specs 3739136Snsouch * Thanks to Thiebault Moeglin for the drive 3839136Snsouch */ 3955205Speter#ifdef _KERNEL 4039136Snsouch#include <sys/param.h> 4139136Snsouch#include <sys/systm.h> 4255939Snsouch#include <sys/module.h> 4355939Snsouch#include <sys/bus.h> 4439136Snsouch#include <sys/malloc.h> 4539136Snsouch 4655205Speter#endif /* _KERNEL */ 4739136Snsouch 4842475Snsouch#include "opt_vpo.h" 4942475Snsouch 5055939Snsouch#include <dev/ppbus/ppbio.h> 5139136Snsouch#include <dev/ppbus/ppbconf.h> 5239136Snsouch#include <dev/ppbus/ppb_msq.h> 5339136Snsouch#include <dev/ppbus/vpoio.h> 5439136Snsouch#include <dev/ppbus/ppb_1284.h> 5539136Snsouch 5655939Snsouch#include "ppbus_if.h" 5755939Snsouch 5839136Snsouch#define VP0_SELTMO 5000 /* select timeout */ 5939136Snsouch#define VP0_FAST_SPINTMO 500000 /* wait status timeout */ 6039136Snsouch#define VP0_LOW_SPINTMO 5000000 /* wait status timeout */ 6139136Snsouch 6239136Snsouch#define VP0_SECTOR_SIZE 512 6339136Snsouch 6439136Snsouch/* 6539136Snsouch * Microcode to execute very fast I/O sequences at the lowest bus level. 6639136Snsouch */ 6739136Snsouch 6878645Snsouch#define WAIT_RET MS_PARAM(7, 2, MS_TYP_PTR) 6978645Snsouch#define WAIT_TMO MS_PARAM(1, 0, MS_TYP_INT) 7078645Snsouch 7178645Snsouch#define DECLARE_WAIT_MICROSEQUENCE \ 7278645Snsouchstruct ppb_microseq wait_microseq[] = { \ 7378645Snsouch MS_CASS(0x0c), \ 7478645Snsouch MS_SET(MS_UNKNOWN), \ 7578645Snsouch /* loop */ \ 7678645Snsouch MS_BRSET(nBUSY, 4 /* ready */), \ 7778645Snsouch MS_DBRA(-2 /* loop */), \ 7878645Snsouch MS_CASS(0x04), \ 7978645Snsouch MS_RET(1), /* timed out */ \ 8078645Snsouch /* ready */ \ 8178645Snsouch MS_CASS(0x04), \ 8278645Snsouch MS_RFETCH(MS_REG_STR, 0xb8, MS_UNKNOWN ), \ 8378645Snsouch MS_RET(0) /* no error */ \ 8478645Snsouch} 8578645Snsouch 8639136Snsouch#define SELECT_TARGET MS_PARAM(6, 1, MS_TYP_CHA) 8739136Snsouch 8878645Snsouch#define DECLARE_SELECT_MICROSEQUENCE \ 8939136Snsouchstruct ppb_microseq select_microseq[] = { \ 9039136Snsouch MS_CASS(0xc), \ 9139136Snsouch /* first, check there is nothing holding onto the bus */ \ 9239136Snsouch MS_SET(VP0_SELTMO), \ 9339136Snsouch/* _loop: */ \ 9443433Snsouch MS_BRCLEAR(0x8, 2 /* _ready */), \ 9543433Snsouch MS_DBRA(-2 /* _loop */), \ 9639136Snsouch MS_RET(2), /* bus busy */ \ 9739136Snsouch/* _ready: */ \ 9839136Snsouch MS_CASS(0x4), \ 9939136Snsouch MS_DASS(MS_UNKNOWN /* 0x80 | 1 << target */), \ 10039136Snsouch MS_DELAY(1), \ 10139136Snsouch MS_CASS(0xc), \ 10239136Snsouch MS_CASS(0xd), \ 10339136Snsouch /* now, wait until the drive is ready */ \ 10439136Snsouch MS_SET(VP0_SELTMO), \ 10539136Snsouch/* loop: */ \ 10643433Snsouch MS_BRSET(0x8, 3 /* ready */), \ 10743433Snsouch MS_DBRA(-2 /* loop */), \ 10839136Snsouch/* error: */ \ 10939136Snsouch MS_CASS(0xc), \ 11039136Snsouch MS_RET(VP0_ESELECT_TIMEOUT), \ 11139136Snsouch/* ready: */ \ 11239136Snsouch MS_CASS(0xc), \ 11339136Snsouch MS_RET(0) \ 11439136Snsouch} 11539136Snsouch 11639136Snsouchstatic struct ppb_microseq transfer_epilog[] = { 11739136Snsouch MS_CASS(0x4), 11839136Snsouch MS_CASS(0xc), 11939136Snsouch MS_CASS(0xe), 12039136Snsouch MS_CASS(0x4), 12139136Snsouch MS_RET(0) 12239136Snsouch}; 12339136Snsouch 12439136Snsouch#define CPP_S1 MS_PARAM(10, 2, MS_TYP_PTR) 12539136Snsouch#define CPP_S2 MS_PARAM(13, 2, MS_TYP_PTR) 12639136Snsouch#define CPP_S3 MS_PARAM(16, 2, MS_TYP_PTR) 12739136Snsouch#define CPP_PARAM MS_PARAM(17, 1, MS_TYP_CHA) 12839136Snsouch 12939136Snsouch#define DECLARE_CPP_MICROSEQ \ 13039136Snsouchstruct ppb_microseq cpp_microseq[] = { \ 13139136Snsouch MS_CASS(0x0c), MS_DELAY(2), \ 13239136Snsouch MS_DASS(0xaa), MS_DELAY(10), \ 13339136Snsouch MS_DASS(0x55), MS_DELAY(10), \ 13439136Snsouch MS_DASS(0x00), MS_DELAY(10), \ 13539136Snsouch MS_DASS(0xff), MS_DELAY(10), \ 13639136Snsouch MS_RFETCH(MS_REG_STR, 0xb8, MS_UNKNOWN /* &s1 */), \ 13739136Snsouch MS_DASS(0x87), MS_DELAY(10), \ 13839136Snsouch MS_RFETCH(MS_REG_STR, 0xb8, MS_UNKNOWN /* &s2 */), \ 13939136Snsouch MS_DASS(0x78), MS_DELAY(10), \ 14039136Snsouch MS_RFETCH(MS_REG_STR, 0x38, MS_UNKNOWN /* &s3 */), \ 14139136Snsouch MS_DASS(MS_UNKNOWN /* param */), \ 14239136Snsouch MS_DELAY(2), \ 14339136Snsouch MS_CASS(0x0c), MS_DELAY(10), \ 14439136Snsouch MS_CASS(0x0d), MS_DELAY(2), \ 14539136Snsouch MS_CASS(0x0c), MS_DELAY(10), \ 14639136Snsouch MS_DASS(0xff), MS_DELAY(10), \ 14739136Snsouch MS_RET(0) \ 14839136Snsouch} 14939136Snsouch 15039136Snsouch#define NEGOCIATED_MODE MS_PARAM(2, 1, MS_TYP_CHA) 15139136Snsouch 15239136Snsouch#define DECLARE_NEGOCIATE_MICROSEQ \ 15378645Snsouchstruct ppb_microseq negociate_microseq[] = { \ 15439136Snsouch MS_CASS(0x4), \ 15539136Snsouch MS_DELAY(5), \ 15639136Snsouch MS_DASS(MS_UNKNOWN /* mode */), \ 15739136Snsouch MS_DELAY(100), \ 15839136Snsouch MS_CASS(0x6), \ 15939136Snsouch MS_DELAY(5), \ 16043433Snsouch MS_BRSET(0x20, 5 /* continue */), \ 16139136Snsouch MS_DELAY(5), \ 16239136Snsouch MS_CASS(0x7), \ 16339136Snsouch MS_DELAY(5), \ 16439136Snsouch MS_CASS(0x6), \ 16539136Snsouch MS_RET(VP0_ENEGOCIATE), \ 16639136Snsouch/* continue: */ \ 16739136Snsouch MS_DELAY(5), \ 16839136Snsouch MS_CASS(0x7), \ 16939136Snsouch MS_DELAY(5), \ 17039136Snsouch MS_CASS(0x6), \ 17139136Snsouch MS_RET(0) \ 17239136Snsouch} 17339136Snsouch 17478645Snsouch#define INB_NIBBLE_L MS_PARAM(3, 2, MS_TYP_PTR) 17578645Snsouch#define INB_NIBBLE_H MS_PARAM(6, 2, MS_TYP_PTR) 17678645Snsouch#define INB_NIBBLE_F MS_PARAM(9, 0, MS_TYP_FUN) 17778645Snsouch#define INB_NIBBLE_P MS_PARAM(9, 1, MS_TYP_PTR) 17878645Snsouch 17978645Snsouch/* 18078645Snsouch * This is the sub-microseqence for MS_GET in NIBBLE mode 18178645Snsouch * Retrieve the two nibbles and call the C function to generate the character 18278645Snsouch * and store it in the buffer (see nibble_inbyte_hook()) 18378645Snsouch */ 18478645Snsouch 18578645Snsouch#define DECLARE_NIBBLE_INBYTE_SUBMICROSEQ \ 18678645Snsouchstruct ppb_microseq nibble_inbyte_submicroseq[] = { \ 18778645Snsouch MS_CASS(0x4), \ 18878645Snsouch/* loop: */ \ 18978645Snsouch MS_CASS(0x6), \ 19078645Snsouch MS_DELAY(1), \ 19178645Snsouch MS_RFETCH(MS_REG_STR, MS_FETCH_ALL, MS_UNKNOWN /* low nibble */),\ 19278645Snsouch MS_CASS(0x5), \ 19378645Snsouch MS_DELAY(1), \ 19478645Snsouch MS_RFETCH(MS_REG_STR, MS_FETCH_ALL, MS_UNKNOWN /* high nibble */),\ 19578645Snsouch MS_CASS(0x4), \ 19678645Snsouch MS_DELAY(1), \ 19778645Snsouch /* do a C call to format the received nibbles */ \ 19878645Snsouch MS_C_CALL(MS_UNKNOWN /* C hook */, MS_UNKNOWN /* param */), \ 19978645Snsouch MS_DBRA(-7 /* loop */), \ 20078645Snsouch MS_RET(0) \ 20178645Snsouch} 20278645Snsouch 20339136Snsouchstatic struct ppb_microseq reset_microseq[] = { 20439136Snsouch MS_CASS(0x04), 20539136Snsouch MS_DASS(0x40), 20639136Snsouch MS_DELAY(1), 20739136Snsouch MS_CASS(0x0c), 20839136Snsouch MS_CASS(0x0d), 20939136Snsouch MS_DELAY(50), 21039136Snsouch MS_CASS(0x0c), 21139136Snsouch MS_CASS(0x04), 21239136Snsouch MS_RET(0) 21339136Snsouch}; 21439136Snsouch 21539136Snsouch/* 21639136Snsouch * nibble_inbyte_hook() 21739136Snsouch * 21839136Snsouch * Formats high and low nibble into a character 21939136Snsouch */ 22039136Snsouchstatic int 22139136Snsouchnibble_inbyte_hook (void *p, char *ptr) 22239136Snsouch{ 22339136Snsouch struct vpo_nibble *s = (struct vpo_nibble *)p; 22439136Snsouch 22539136Snsouch /* increment the buffer pointer */ 22639136Snsouch *ptr = ((s->l >> 4) & 0x0f) + (s->h & 0xf0); 22739136Snsouch 22839136Snsouch return (0); 22939136Snsouch} 23039136Snsouch 23139136Snsouch/* 23239136Snsouch * This is the sub-microseqence for MS_GET in PS2 mode 23339136Snsouch */ 23439136Snsouchstatic struct ppb_microseq ps2_inbyte_submicroseq[] = { 23539136Snsouch MS_CASS(0x4), 23639136Snsouch 23739136Snsouch/* loop: */ 23839136Snsouch MS_CASS(PCD | 0x6), 23939136Snsouch MS_RFETCH_P(1, MS_REG_DTR, MS_FETCH_ALL), 24039136Snsouch MS_CASS(PCD | 0x5), 24143433Snsouch MS_DBRA(-4 /* loop */), 24239136Snsouch 24339136Snsouch MS_RET(0) 24439136Snsouch}; 24539136Snsouch 24639136Snsouch/* 24739136Snsouch * This is the sub-microsequence for MS_PUT in both NIBBLE and PS2 modes 24839136Snsouch */ 24939136Snsouchstatic struct ppb_microseq spp_outbyte_submicroseq[] = { 25039136Snsouch MS_CASS(0x4), 25139136Snsouch 25239136Snsouch/* loop: */ 25339136Snsouch MS_RASSERT_P(1, MS_REG_DTR), 25439136Snsouch MS_CASS(0x5), 25543433Snsouch MS_DBRA(0), /* decrement counter */ 25639136Snsouch MS_RASSERT_P(1, MS_REG_DTR), 25739136Snsouch MS_CASS(0x0), 25843433Snsouch MS_DBRA(-6 /* loop */), 25939136Snsouch 26039136Snsouch /* return from the put call */ 26139136Snsouch MS_CASS(0x4), 26239136Snsouch MS_RET(0) 26339136Snsouch}; 26439136Snsouch 26539136Snsouch/* EPP 1.7 microsequences, ptr and len set at runtime */ 26639136Snsouchstatic struct ppb_microseq epp17_outstr[] = { 26739136Snsouch MS_CASS(0x4), 26843433Snsouch MS_RASSERT_P(MS_ACCUM, MS_REG_EPP_D), 26939136Snsouch MS_CASS(0xc), 27039136Snsouch MS_RET(0), 27139136Snsouch}; 27239136Snsouch 27339136Snsouchstatic struct ppb_microseq epp17_instr[] = { 27439136Snsouch MS_CASS(PCD | 0x4), 27543433Snsouch MS_RFETCH_P(MS_ACCUM, MS_REG_EPP_D, MS_FETCH_ALL), 27639136Snsouch MS_CASS(PCD | 0xc), 27739136Snsouch MS_RET(0), 27839136Snsouch}; 27939136Snsouch 28039136Snsouchstatic int 28139520Snsouchimm_disconnect(struct vpoio_data *vpo, int *connected, int release_bus) 28239136Snsouch{ 28339136Snsouch DECLARE_CPP_MICROSEQ; 28439136Snsouch 28555939Snsouch device_t ppbus = device_get_parent(vpo->vpo_dev); 28639136Snsouch char s1, s2, s3; 28739136Snsouch int ret; 28839136Snsouch 28939136Snsouch /* all should be ok */ 29039520Snsouch if (connected) 29139520Snsouch *connected = 0; 29239136Snsouch 29339136Snsouch ppb_MS_init_msq(cpp_microseq, 4, CPP_S1, (void *)&s1, 29439136Snsouch CPP_S2, (void *)&s2, CPP_S3, (void *)&s3, 29539136Snsouch CPP_PARAM, 0x30); 29639136Snsouch 29755939Snsouch ppb_MS_microseq(ppbus, vpo->vpo_dev, cpp_microseq, &ret); 29839136Snsouch 29940783Snsouch if ((s1 != (char)0xb8 || s2 != (char)0x18 || s3 != (char)0x38)) { 30040783Snsouch if (bootverbose) 30140783Snsouch printf("imm%d: (disconnect) s1=0x%x s2=0x%x, s3=0x%x\n", 30240783Snsouch vpo->vpo_unit, s1 & 0xff, s2 & 0xff, s3 & 0xff); 30340783Snsouch if (connected) 30440783Snsouch *connected = VP0_ECONNECT; 30540783Snsouch } 30639136Snsouch 30739520Snsouch if (release_bus) 30855939Snsouch return (ppb_release_bus(ppbus, vpo->vpo_dev)); 30939520Snsouch else 31039520Snsouch return (0); 31139136Snsouch} 31239136Snsouch 31339136Snsouch/* 31439136Snsouch * how : PPB_WAIT or PPB_DONTWAIT 31539136Snsouch */ 31639136Snsouchstatic int 31739520Snsouchimm_connect(struct vpoio_data *vpo, int how, int *disconnected, int request_bus) 31839136Snsouch{ 31939136Snsouch DECLARE_CPP_MICROSEQ; 32039136Snsouch 32155939Snsouch device_t ppbus = device_get_parent(vpo->vpo_dev); 32239136Snsouch char s1, s2, s3; 32339136Snsouch int error; 32439136Snsouch int ret; 32539136Snsouch 32639136Snsouch /* all should be ok */ 32739520Snsouch if (disconnected) 32839520Snsouch *disconnected = 0; 32939136Snsouch 33039520Snsouch if (request_bus) 33155939Snsouch if ((error = ppb_request_bus(ppbus, vpo->vpo_dev, how))) 33239520Snsouch return (error); 33339136Snsouch 33439136Snsouch ppb_MS_init_msq(cpp_microseq, 3, CPP_S1, (void *)&s1, 33539136Snsouch CPP_S2, (void *)&s2, CPP_S3, (void *)&s3); 33639136Snsouch 33739136Snsouch /* select device 0 in compatible mode */ 33839136Snsouch ppb_MS_init_msq(cpp_microseq, 1, CPP_PARAM, 0xe0); 33955939Snsouch ppb_MS_microseq(ppbus, vpo->vpo_dev, cpp_microseq, &ret); 34039136Snsouch 34139136Snsouch /* disconnect all devices */ 34239136Snsouch ppb_MS_init_msq(cpp_microseq, 1, CPP_PARAM, 0x30); 34355939Snsouch ppb_MS_microseq(ppbus, vpo->vpo_dev, cpp_microseq, &ret); 34439136Snsouch 34555939Snsouch if (PPB_IN_EPP_MODE(ppbus)) 34639136Snsouch ppb_MS_init_msq(cpp_microseq, 1, CPP_PARAM, 0x28); 34739136Snsouch else 34839136Snsouch ppb_MS_init_msq(cpp_microseq, 1, CPP_PARAM, 0xe0); 34939136Snsouch 35055939Snsouch ppb_MS_microseq(ppbus, vpo->vpo_dev, cpp_microseq, &ret); 35139136Snsouch 35240783Snsouch if ((s1 != (char)0xb8 || s2 != (char)0x18 || s3 != (char)0x30)) { 35340783Snsouch if (bootverbose) 35440783Snsouch printf("imm%d: (connect) s1=0x%x s2=0x%x, s3=0x%x\n", 35540783Snsouch vpo->vpo_unit, s1 & 0xff, s2 & 0xff, s3 & 0xff); 35640783Snsouch if (disconnected) 35740783Snsouch *disconnected = VP0_ECONNECT; 35840783Snsouch } 35939136Snsouch 36039136Snsouch return (0); 36139136Snsouch} 36239136Snsouch 36339136Snsouch/* 36439136Snsouch * imm_detect() 36539136Snsouch * 36639136Snsouch * Detect and initialise the VP0 adapter. 36739136Snsouch */ 36839136Snsouchstatic int 36939136Snsouchimm_detect(struct vpoio_data *vpo) 37039136Snsouch{ 37155939Snsouch device_t ppbus = device_get_parent(vpo->vpo_dev); 37239136Snsouch int error; 37339136Snsouch 37455939Snsouch if ((error = ppb_request_bus(ppbus, vpo->vpo_dev, PPB_DONTWAIT))) 37539520Snsouch return (error); 37639136Snsouch 37739520Snsouch /* disconnect the drive, keep the bus */ 37839520Snsouch imm_disconnect(vpo, NULL, 0); 37939136Snsouch 38070608Snsouch vpo->vpo_mode_found = VP0_MODE_UNDEFINED; 38170608Snsouch error = 1; 38239520Snsouch 38370608Snsouch /* try to enter EPP mode since vpoio failure put the bus in NIBBLE */ 38470608Snsouch if (ppb_set_mode(ppbus, PPB_EPP) != -1) { 38570608Snsouch imm_connect(vpo, PPB_DONTWAIT, &error, 0); 38670608Snsouch } 38770608Snsouch 38870608Snsouch /* if connection failed try PS/2 then NIBBLE modes */ 38939520Snsouch if (error) { 39070608Snsouch if (ppb_set_mode(ppbus, PPB_PS2) != -1) { 39170608Snsouch imm_connect(vpo, PPB_DONTWAIT, &error, 0); 39270608Snsouch } 39370608Snsouch if (error) { 39470608Snsouch if (ppb_set_mode(ppbus, PPB_NIBBLE) != -1) { 39570608Snsouch imm_connect(vpo, PPB_DONTWAIT, &error, 0); 39670608Snsouch if (error) 39770608Snsouch goto error; 39870608Snsouch vpo->vpo_mode_found = VP0_MODE_NIBBLE; 39970608Snsouch } else { 40070608Snsouch printf("imm%d: NIBBLE mode unavailable!\n", vpo->vpo_unit); 40170608Snsouch goto error; 40270608Snsouch } 40370608Snsouch } else { 40470608Snsouch vpo->vpo_mode_found = VP0_MODE_PS2; 40570608Snsouch } 40670608Snsouch } else { 40770608Snsouch vpo->vpo_mode_found = VP0_MODE_EPP; 40839520Snsouch } 40939520Snsouch 41039136Snsouch /* send SCSI reset signal */ 41155939Snsouch ppb_MS_microseq(ppbus, vpo->vpo_dev, reset_microseq, NULL); 41239136Snsouch 41339520Snsouch /* release the bus now */ 41439520Snsouch imm_disconnect(vpo, &error, 1); 41539136Snsouch 41639136Snsouch /* ensure we are disconnected or daisy chained peripheral 41739136Snsouch * may cause serious problem to the disk */ 41839136Snsouch 41939520Snsouch if (error) { 42039520Snsouch if (bootverbose) 42139520Snsouch printf("imm%d: can't disconnect from the drive\n", 42239520Snsouch vpo->vpo_unit); 42339900Snsouch goto error; 42439520Snsouch } 42539136Snsouch 42639136Snsouch return (0); 42739520Snsouch 42839520Snsoucherror: 42955939Snsouch ppb_release_bus(ppbus, vpo->vpo_dev); 43039520Snsouch return (VP0_EINITFAILED); 43139136Snsouch} 43239136Snsouch 43339136Snsouch/* 43439136Snsouch * imm_outstr() 43539136Snsouch */ 43639136Snsouchstatic int 43739136Snsouchimm_outstr(struct vpoio_data *vpo, char *buffer, int size) 43839136Snsouch{ 43955939Snsouch device_t ppbus = device_get_parent(vpo->vpo_dev); 44039136Snsouch int error = 0; 44139136Snsouch 44255939Snsouch if (PPB_IN_EPP_MODE(ppbus)) 44355939Snsouch ppb_reset_epp_timeout(ppbus); 44439136Snsouch 44555939Snsouch ppb_MS_exec(ppbus, vpo->vpo_dev, MS_OP_PUT, (union ppb_insarg)buffer, 44645342Speter (union ppb_insarg)size, (union ppb_insarg)MS_UNKNOWN, &error); 44739136Snsouch 44839136Snsouch return (error); 44939136Snsouch} 45039136Snsouch 45139136Snsouch/* 45239136Snsouch * imm_instr() 45339136Snsouch */ 45439136Snsouchstatic int 45539136Snsouchimm_instr(struct vpoio_data *vpo, char *buffer, int size) 45639136Snsouch{ 45755939Snsouch device_t ppbus = device_get_parent(vpo->vpo_dev); 45839136Snsouch int error = 0; 45939136Snsouch 46055939Snsouch if (PPB_IN_EPP_MODE(ppbus)) 46155939Snsouch ppb_reset_epp_timeout(ppbus); 46239136Snsouch 46355939Snsouch ppb_MS_exec(ppbus, vpo->vpo_dev, MS_OP_GET, (union ppb_insarg)buffer, 46445342Speter (union ppb_insarg)size, (union ppb_insarg)MS_UNKNOWN, &error); 46539136Snsouch 46639136Snsouch return (error); 46739136Snsouch} 46839136Snsouch 46939136Snsouchstatic char 47039136Snsouchimm_select(struct vpoio_data *vpo, int initiator, int target) 47139136Snsouch{ 47239136Snsouch DECLARE_SELECT_MICROSEQUENCE; 47355939Snsouch device_t ppbus = device_get_parent(vpo->vpo_dev); 47439136Snsouch int ret; 47539136Snsouch 47639136Snsouch /* initialize the select microsequence */ 47739136Snsouch ppb_MS_init_msq(select_microseq, 1, 47839136Snsouch SELECT_TARGET, 1 << initiator | 1 << target); 47939136Snsouch 48055939Snsouch ppb_MS_microseq(ppbus, vpo->vpo_dev, select_microseq, &ret); 48139136Snsouch 48239136Snsouch return (ret); 48339136Snsouch} 48439136Snsouch 48539136Snsouch/* 48639136Snsouch * imm_wait() 48739136Snsouch * 48839136Snsouch * H_SELIN must be low. 48939136Snsouch * 49039136Snsouch */ 49139136Snsouchstatic char 49239136Snsouchimm_wait(struct vpoio_data *vpo, int tmo) 49339136Snsouch{ 49478645Snsouch DECLARE_WAIT_MICROSEQUENCE; 49578645Snsouch 49655939Snsouch device_t ppbus = device_get_parent(vpo->vpo_dev); 49778645Snsouch int ret, err; 49839136Snsouch 49939136Snsouch /* 50039136Snsouch * Return some status information. 50139136Snsouch * Semantics : 0x88 = ZIP+ wants more data 50239136Snsouch * 0x98 = ZIP+ wants to send more data 50339136Snsouch * 0xa8 = ZIP+ wants command 50439136Snsouch * 0xb8 = end of transfer, ZIP+ is sending status 50539136Snsouch */ 50639136Snsouch 50778645Snsouch ppb_MS_init_msq(wait_microseq, 2, 50878645Snsouch WAIT_RET, (void *)&ret, 50978645Snsouch WAIT_TMO, tmo); 51078645Snsouch 51178645Snsouch ppb_MS_microseq(ppbus, vpo->vpo_dev, wait_microseq, &err); 51278645Snsouch 51378645Snsouch if (err) 51478645Snsouch return (0); /* command timed out */ 51578645Snsouch 51678645Snsouch return(ret); 51739136Snsouch} 51839136Snsouch 51939136Snsouchstatic int 52039136Snsouchimm_negociate(struct vpoio_data *vpo) 52139136Snsouch{ 52239136Snsouch DECLARE_NEGOCIATE_MICROSEQ; 52355939Snsouch device_t ppbus = device_get_parent(vpo->vpo_dev); 52439136Snsouch int negociate_mode; 52539136Snsouch int ret; 52639136Snsouch 52755939Snsouch if (PPB_IN_NIBBLE_MODE(ppbus)) 52839136Snsouch negociate_mode = 0; 52955939Snsouch else if (PPB_IN_PS2_MODE(ppbus)) 53039136Snsouch negociate_mode = 1; 53139136Snsouch else 53239136Snsouch return (0); 53339136Snsouch 53439136Snsouch#if 0 /* XXX use standalone code not to depend on ppb_1284 code yet */ 53555939Snsouch ret = ppb_1284_negociate(ppbus, negociate_mode); 53639136Snsouch 53739136Snsouch if (ret) 53839136Snsouch return (VP0_ENEGOCIATE); 53939136Snsouch#endif 54039136Snsouch 54155939Snsouch ppb_MS_init_msq(negociate_microseq, 1, 54255939Snsouch NEGOCIATED_MODE, negociate_mode); 54339136Snsouch 54455939Snsouch ppb_MS_microseq(ppbus, vpo->vpo_dev, negociate_microseq, &ret); 54539136Snsouch 54639136Snsouch return (ret); 54739136Snsouch} 54839136Snsouch 54939136Snsouch/* 55039136Snsouch * imm_probe() 55139136Snsouch * 55239136Snsouch * Low level probe of vpo device 55339136Snsouch * 55439136Snsouch */ 55555939Snsouchint 55655939Snsouchimm_probe(device_t dev, struct vpoio_data *vpo) 55739136Snsouch{ 55855939Snsouch int error; 55939136Snsouch 56039136Snsouch /* ppbus dependent initialisation */ 56155939Snsouch vpo->vpo_dev = dev; 56239136Snsouch 56339136Snsouch /* now, try to initialise the drive */ 56455939Snsouch if ((error = imm_detect(vpo))) { 56555939Snsouch return (error); 56639136Snsouch } 56739136Snsouch 56855939Snsouch return (0); 56939136Snsouch} 57039136Snsouch 57139136Snsouch/* 57239136Snsouch * imm_attach() 57339136Snsouch * 57439136Snsouch * Low level attachment of vpo device 57539136Snsouch * 57639136Snsouch */ 57739136Snsouchint 57839136Snsouchimm_attach(struct vpoio_data *vpo) 57939136Snsouch{ 58078645Snsouch DECLARE_NIBBLE_INBYTE_SUBMICROSEQ; 58155939Snsouch device_t ppbus = device_get_parent(vpo->vpo_dev); 58278645Snsouch int error = 0; 58339136Snsouch 58439136Snsouch /* 58539136Snsouch * Initialize microsequence code 58639136Snsouch */ 58739136Snsouch vpo->vpo_nibble_inbyte_msq = (struct ppb_microseq *)malloc( 58839136Snsouch sizeof(nibble_inbyte_submicroseq), M_DEVBUF, M_NOWAIT); 58939136Snsouch 59039136Snsouch if (!vpo->vpo_nibble_inbyte_msq) 59155939Snsouch return (ENXIO); 59239136Snsouch 59339136Snsouch bcopy((void *)nibble_inbyte_submicroseq, 59439136Snsouch (void *)vpo->vpo_nibble_inbyte_msq, 59539136Snsouch sizeof(nibble_inbyte_submicroseq)); 59639136Snsouch 59778645Snsouch ppb_MS_init_msq(vpo->vpo_nibble_inbyte_msq, 4, 59878645Snsouch INB_NIBBLE_H, (void *)&(vpo)->vpo_nibble.h, 59978645Snsouch INB_NIBBLE_L, (void *)&(vpo)->vpo_nibble.l, 60078645Snsouch INB_NIBBLE_F, nibble_inbyte_hook, 60178645Snsouch INB_NIBBLE_P, (void *)&(vpo)->vpo_nibble); 60239136Snsouch 60339136Snsouch /* 60439136Snsouch * Initialize mode dependent in/out microsequences 60539136Snsouch */ 60678645Snsouch if ((error = ppb_request_bus(ppbus, vpo->vpo_dev, PPB_WAIT))) 60778645Snsouch goto error; 60839136Snsouch 60970608Snsouch /* ppbus automatically restore the last mode entered during detection */ 61070608Snsouch switch (vpo->vpo_mode_found) { 61170608Snsouch case VP0_MODE_EPP: 61270608Snsouch ppb_MS_GET_init(ppbus, vpo->vpo_dev, epp17_instr); 61370608Snsouch ppb_MS_PUT_init(ppbus, vpo->vpo_dev, epp17_outstr); 61470608Snsouch printf("imm%d: EPP mode\n", vpo->vpo_unit); 61570608Snsouch break; 61670608Snsouch case VP0_MODE_PS2: 61755939Snsouch ppb_MS_GET_init(ppbus, vpo->vpo_dev, ps2_inbyte_submicroseq); 61855939Snsouch ppb_MS_PUT_init(ppbus, vpo->vpo_dev, spp_outbyte_submicroseq); 61939136Snsouch printf("imm%d: PS2 mode\n", vpo->vpo_unit); 62070608Snsouch break; 62170608Snsouch case VP0_MODE_NIBBLE: 62270608Snsouch ppb_MS_GET_init(ppbus, vpo->vpo_dev, vpo->vpo_nibble_inbyte_msq); 62370608Snsouch ppb_MS_PUT_init(ppbus, vpo->vpo_dev, spp_outbyte_submicroseq); 62439136Snsouch printf("imm%d: NIBBLE mode\n", vpo->vpo_unit); 62570608Snsouch break; 62670608Snsouch default: 62770608Snsouch panic("imm: unknown mode %d", vpo->vpo_mode_found); 62839136Snsouch } 62939136Snsouch 63055939Snsouch ppb_release_bus(ppbus, vpo->vpo_dev); 63178645Snsouch error: 63278645Snsouch return (error); 63339136Snsouch} 63439136Snsouch 63539136Snsouch/* 63639136Snsouch * imm_reset_bus() 63739136Snsouch * 63839136Snsouch */ 63939136Snsouchint 64039136Snsouchimm_reset_bus(struct vpoio_data *vpo) 64139136Snsouch{ 64255939Snsouch device_t ppbus = device_get_parent(vpo->vpo_dev); 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 */ 65155939Snsouch ppb_MS_microseq(ppbus, 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{ 67155939Snsouch device_t ppbus = device_get_parent(vpo->vpo_dev); 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 68478645Snsouch * EIO to 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 { 75155939Snsouch if (!PPB_IN_EPP_MODE(ppbus)) 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 76855939Snsouch if ((PPB_IN_NIBBLE_MODE(ppbus) || 76955939Snsouch PPB_IN_PS2_MODE(ppbus)) && negociated) 77055939Snsouch ppb_MS_microseq(ppbus, 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 79171633Snsouch /* Experience showed that we should discard this */ 792153586Ssam if (h == (char) -1) 79371633Snsouch h = 0; 79471633Snsouch 79539136Snsouch *result = ((int) h << 8) | ((int) l & 0xff); 79639136Snsouch 79739136Snsoucherror: 79855939Snsouch if ((PPB_IN_NIBBLE_MODE(ppbus) || 79955939Snsouch PPB_IN_PS2_MODE(ppbus)) && negociated) 80055939Snsouch ppb_MS_microseq(ppbus, vpo->vpo_dev, transfer_epilog, NULL); 80139136Snsouch 80239136Snsouch /* return to printer state, release the ppbus */ 80339520Snsouch imm_disconnect(vpo, NULL, 1); 80439136Snsouch 80539136Snsouch return (0); 80639136Snsouch} 807