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$"); 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: */ 253185003Sjhb MS_RASSERT_P(1, MS_REG_DTR), 25439136Snsouch MS_CASS(0x5), 25543433Snsouch MS_DBRA(0), /* decrement counter */ 256185003Sjhb 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), 268185003Sjhb 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), 275185003Sjhb 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) 301184130Sjhb device_printf(vpo->vpo_dev, 302184130Sjhb "(disconnect) s1=0x%x s2=0x%x, s3=0x%x\n", 303184130Sjhb s1 & 0xff, s2 & 0xff, s3 & 0xff); 30440783Snsouch if (connected) 30540783Snsouch *connected = VP0_ECONNECT; 30640783Snsouch } 30739136Snsouch 30839520Snsouch if (release_bus) 30955939Snsouch return (ppb_release_bus(ppbus, vpo->vpo_dev)); 31039520Snsouch else 31139520Snsouch return (0); 31239136Snsouch} 31339136Snsouch 31439136Snsouch/* 31539136Snsouch * how : PPB_WAIT or PPB_DONTWAIT 31639136Snsouch */ 31739136Snsouchstatic int 31839520Snsouchimm_connect(struct vpoio_data *vpo, int how, int *disconnected, int request_bus) 31939136Snsouch{ 32039136Snsouch DECLARE_CPP_MICROSEQ; 32139136Snsouch 32255939Snsouch device_t ppbus = device_get_parent(vpo->vpo_dev); 32339136Snsouch char s1, s2, s3; 32439136Snsouch int error; 32539136Snsouch int ret; 32639136Snsouch 32739136Snsouch /* all should be ok */ 32839520Snsouch if (disconnected) 32939520Snsouch *disconnected = 0; 33039136Snsouch 33139520Snsouch if (request_bus) 33255939Snsouch if ((error = ppb_request_bus(ppbus, vpo->vpo_dev, how))) 33339520Snsouch return (error); 33439136Snsouch 33539136Snsouch ppb_MS_init_msq(cpp_microseq, 3, CPP_S1, (void *)&s1, 33639136Snsouch CPP_S2, (void *)&s2, CPP_S3, (void *)&s3); 33739136Snsouch 33839136Snsouch /* select device 0 in compatible mode */ 33939136Snsouch ppb_MS_init_msq(cpp_microseq, 1, CPP_PARAM, 0xe0); 34055939Snsouch ppb_MS_microseq(ppbus, vpo->vpo_dev, cpp_microseq, &ret); 34139136Snsouch 34239136Snsouch /* disconnect all devices */ 34339136Snsouch ppb_MS_init_msq(cpp_microseq, 1, CPP_PARAM, 0x30); 34455939Snsouch ppb_MS_microseq(ppbus, vpo->vpo_dev, cpp_microseq, &ret); 34539136Snsouch 34655939Snsouch if (PPB_IN_EPP_MODE(ppbus)) 34739136Snsouch ppb_MS_init_msq(cpp_microseq, 1, CPP_PARAM, 0x28); 34839136Snsouch else 34939136Snsouch ppb_MS_init_msq(cpp_microseq, 1, CPP_PARAM, 0xe0); 35039136Snsouch 35155939Snsouch ppb_MS_microseq(ppbus, vpo->vpo_dev, cpp_microseq, &ret); 35239136Snsouch 35340783Snsouch if ((s1 != (char)0xb8 || s2 != (char)0x18 || s3 != (char)0x30)) { 35440783Snsouch if (bootverbose) 355184130Sjhb device_printf(vpo->vpo_dev, 356184130Sjhb "(connect) s1=0x%x s2=0x%x, s3=0x%x\n", 357184130Sjhb s1 & 0xff, s2 & 0xff, s3 & 0xff); 35840783Snsouch if (disconnected) 35940783Snsouch *disconnected = VP0_ECONNECT; 36040783Snsouch } 36139136Snsouch 36239136Snsouch return (0); 36339136Snsouch} 36439136Snsouch 36539136Snsouch/* 36639136Snsouch * imm_detect() 36739136Snsouch * 36839136Snsouch * Detect and initialise the VP0 adapter. 36939136Snsouch */ 37039136Snsouchstatic int 37139136Snsouchimm_detect(struct vpoio_data *vpo) 37239136Snsouch{ 37355939Snsouch device_t ppbus = device_get_parent(vpo->vpo_dev); 37439136Snsouch int error; 37539136Snsouch 37655939Snsouch if ((error = ppb_request_bus(ppbus, vpo->vpo_dev, PPB_DONTWAIT))) 37739520Snsouch return (error); 37839136Snsouch 37939520Snsouch /* disconnect the drive, keep the bus */ 38039520Snsouch imm_disconnect(vpo, NULL, 0); 38139136Snsouch 38270608Snsouch vpo->vpo_mode_found = VP0_MODE_UNDEFINED; 38370608Snsouch error = 1; 38439520Snsouch 38570608Snsouch /* try to enter EPP mode since vpoio failure put the bus in NIBBLE */ 38670608Snsouch if (ppb_set_mode(ppbus, PPB_EPP) != -1) { 38770608Snsouch imm_connect(vpo, PPB_DONTWAIT, &error, 0); 38870608Snsouch } 38970608Snsouch 39070608Snsouch /* if connection failed try PS/2 then NIBBLE modes */ 39139520Snsouch if (error) { 39270608Snsouch if (ppb_set_mode(ppbus, PPB_PS2) != -1) { 39370608Snsouch imm_connect(vpo, PPB_DONTWAIT, &error, 0); 39470608Snsouch } 39570608Snsouch if (error) { 39670608Snsouch if (ppb_set_mode(ppbus, PPB_NIBBLE) != -1) { 39770608Snsouch imm_connect(vpo, PPB_DONTWAIT, &error, 0); 39870608Snsouch if (error) 39970608Snsouch goto error; 40070608Snsouch vpo->vpo_mode_found = VP0_MODE_NIBBLE; 40170608Snsouch } else { 402184130Sjhb device_printf(vpo->vpo_dev, 403184130Sjhb "NIBBLE mode unavailable!\n"); 40470608Snsouch goto error; 40570608Snsouch } 40670608Snsouch } else { 40770608Snsouch vpo->vpo_mode_found = VP0_MODE_PS2; 40870608Snsouch } 40970608Snsouch } else { 41070608Snsouch vpo->vpo_mode_found = VP0_MODE_EPP; 41139520Snsouch } 41239520Snsouch 41339136Snsouch /* send SCSI reset signal */ 41455939Snsouch ppb_MS_microseq(ppbus, vpo->vpo_dev, reset_microseq, NULL); 41539136Snsouch 41639520Snsouch /* release the bus now */ 41739520Snsouch imm_disconnect(vpo, &error, 1); 41839136Snsouch 419185003Sjhb /* ensure we are disconnected or daisy chained peripheral 42039136Snsouch * may cause serious problem to the disk */ 42139136Snsouch 42239520Snsouch if (error) { 42339520Snsouch if (bootverbose) 424184130Sjhb device_printf(vpo->vpo_dev, 425184130Sjhb "can't disconnect from the drive\n"); 42639900Snsouch goto error; 42739520Snsouch } 42839136Snsouch 42939136Snsouch return (0); 43039520Snsouch 43139520Snsoucherror: 43255939Snsouch ppb_release_bus(ppbus, vpo->vpo_dev); 43339520Snsouch return (VP0_EINITFAILED); 43439136Snsouch} 43539136Snsouch 43639136Snsouch/* 43739136Snsouch * imm_outstr() 43839136Snsouch */ 43939136Snsouchstatic int 44039136Snsouchimm_outstr(struct vpoio_data *vpo, char *buffer, int size) 44139136Snsouch{ 44255939Snsouch device_t ppbus = device_get_parent(vpo->vpo_dev); 44339136Snsouch int error = 0; 44439136Snsouch 44555939Snsouch if (PPB_IN_EPP_MODE(ppbus)) 44655939Snsouch ppb_reset_epp_timeout(ppbus); 44739136Snsouch 44855939Snsouch ppb_MS_exec(ppbus, vpo->vpo_dev, MS_OP_PUT, (union ppb_insarg)buffer, 44945342Speter (union ppb_insarg)size, (union ppb_insarg)MS_UNKNOWN, &error); 45039136Snsouch 45139136Snsouch return (error); 45239136Snsouch} 45339136Snsouch 45439136Snsouch/* 45539136Snsouch * imm_instr() 45639136Snsouch */ 45739136Snsouchstatic int 45839136Snsouchimm_instr(struct vpoio_data *vpo, char *buffer, int size) 45939136Snsouch{ 46055939Snsouch device_t ppbus = device_get_parent(vpo->vpo_dev); 46139136Snsouch int error = 0; 46239136Snsouch 46355939Snsouch if (PPB_IN_EPP_MODE(ppbus)) 46455939Snsouch ppb_reset_epp_timeout(ppbus); 46539136Snsouch 46655939Snsouch ppb_MS_exec(ppbus, vpo->vpo_dev, MS_OP_GET, (union ppb_insarg)buffer, 46745342Speter (union ppb_insarg)size, (union ppb_insarg)MS_UNKNOWN, &error); 46839136Snsouch 46939136Snsouch return (error); 47039136Snsouch} 47139136Snsouch 47239136Snsouchstatic char 47339136Snsouchimm_select(struct vpoio_data *vpo, int initiator, int target) 47439136Snsouch{ 47539136Snsouch DECLARE_SELECT_MICROSEQUENCE; 47655939Snsouch device_t ppbus = device_get_parent(vpo->vpo_dev); 47739136Snsouch int ret; 47839136Snsouch 47939136Snsouch /* initialize the select microsequence */ 48039136Snsouch ppb_MS_init_msq(select_microseq, 1, 48139136Snsouch SELECT_TARGET, 1 << initiator | 1 << target); 482185003Sjhb 48355939Snsouch ppb_MS_microseq(ppbus, vpo->vpo_dev, select_microseq, &ret); 48439136Snsouch 48539136Snsouch return (ret); 48639136Snsouch} 48739136Snsouch 48839136Snsouch/* 48939136Snsouch * imm_wait() 49039136Snsouch * 49139136Snsouch * H_SELIN must be low. 49239136Snsouch * 49339136Snsouch */ 49439136Snsouchstatic char 49539136Snsouchimm_wait(struct vpoio_data *vpo, int tmo) 49639136Snsouch{ 49778645Snsouch DECLARE_WAIT_MICROSEQUENCE; 49878645Snsouch 49955939Snsouch device_t ppbus = device_get_parent(vpo->vpo_dev); 50078645Snsouch int ret, err; 50139136Snsouch 50239136Snsouch /* 50339136Snsouch * Return some status information. 50439136Snsouch * Semantics : 0x88 = ZIP+ wants more data 50539136Snsouch * 0x98 = ZIP+ wants to send more data 50639136Snsouch * 0xa8 = ZIP+ wants command 50739136Snsouch * 0xb8 = end of transfer, ZIP+ is sending status 50839136Snsouch */ 50939136Snsouch 51078645Snsouch ppb_MS_init_msq(wait_microseq, 2, 51178645Snsouch WAIT_RET, (void *)&ret, 51278645Snsouch WAIT_TMO, tmo); 51378645Snsouch 51478645Snsouch ppb_MS_microseq(ppbus, vpo->vpo_dev, wait_microseq, &err); 51578645Snsouch 51678645Snsouch if (err) 517185003Sjhb return (0); /* command timed out */ 51878645Snsouch 51978645Snsouch return(ret); 52039136Snsouch} 52139136Snsouch 52239136Snsouchstatic int 52339136Snsouchimm_negociate(struct vpoio_data *vpo) 52439136Snsouch{ 52539136Snsouch DECLARE_NEGOCIATE_MICROSEQ; 52655939Snsouch device_t ppbus = device_get_parent(vpo->vpo_dev); 52739136Snsouch int negociate_mode; 52839136Snsouch int ret; 52939136Snsouch 53055939Snsouch if (PPB_IN_NIBBLE_MODE(ppbus)) 53139136Snsouch negociate_mode = 0; 53255939Snsouch else if (PPB_IN_PS2_MODE(ppbus)) 53339136Snsouch negociate_mode = 1; 53439136Snsouch else 53539136Snsouch return (0); 53639136Snsouch 53739136Snsouch#if 0 /* XXX use standalone code not to depend on ppb_1284 code yet */ 53855939Snsouch ret = ppb_1284_negociate(ppbus, negociate_mode); 53939136Snsouch 54039136Snsouch if (ret) 54139136Snsouch return (VP0_ENEGOCIATE); 54239136Snsouch#endif 543185003Sjhb 54455939Snsouch ppb_MS_init_msq(negociate_microseq, 1, 54555939Snsouch NEGOCIATED_MODE, negociate_mode); 54639136Snsouch 54755939Snsouch ppb_MS_microseq(ppbus, vpo->vpo_dev, negociate_microseq, &ret); 54839136Snsouch 54939136Snsouch return (ret); 55039136Snsouch} 55139136Snsouch 55239136Snsouch/* 55339136Snsouch * imm_probe() 55439136Snsouch * 55539136Snsouch * Low level probe of vpo device 55639136Snsouch * 55739136Snsouch */ 55855939Snsouchint 55955939Snsouchimm_probe(device_t dev, struct vpoio_data *vpo) 56039136Snsouch{ 56155939Snsouch int error; 56239136Snsouch 56339136Snsouch /* ppbus dependent initialisation */ 56455939Snsouch vpo->vpo_dev = dev; 56539136Snsouch 56639136Snsouch /* now, try to initialise the drive */ 56755939Snsouch if ((error = imm_detect(vpo))) { 56855939Snsouch return (error); 56939136Snsouch } 57039136Snsouch 57155939Snsouch return (0); 57239136Snsouch} 57339136Snsouch 57439136Snsouch/* 57539136Snsouch * imm_attach() 57639136Snsouch * 57739136Snsouch * Low level attachment of vpo device 57839136Snsouch * 57939136Snsouch */ 58039136Snsouchint 58139136Snsouchimm_attach(struct vpoio_data *vpo) 58239136Snsouch{ 583185003Sjhb DECLARE_NIBBLE_INBYTE_SUBMICROSEQ; 58455939Snsouch device_t ppbus = device_get_parent(vpo->vpo_dev); 58578645Snsouch int error = 0; 58639136Snsouch 58739136Snsouch /* 58839136Snsouch * Initialize microsequence code 58939136Snsouch */ 59039136Snsouch vpo->vpo_nibble_inbyte_msq = (struct ppb_microseq *)malloc( 59139136Snsouch sizeof(nibble_inbyte_submicroseq), M_DEVBUF, M_NOWAIT); 59239136Snsouch 59339136Snsouch if (!vpo->vpo_nibble_inbyte_msq) 59455939Snsouch return (ENXIO); 59539136Snsouch 59639136Snsouch bcopy((void *)nibble_inbyte_submicroseq, 59739136Snsouch (void *)vpo->vpo_nibble_inbyte_msq, 59839136Snsouch sizeof(nibble_inbyte_submicroseq)); 59939136Snsouch 60078645Snsouch ppb_MS_init_msq(vpo->vpo_nibble_inbyte_msq, 4, 60178645Snsouch INB_NIBBLE_H, (void *)&(vpo)->vpo_nibble.h, 60278645Snsouch INB_NIBBLE_L, (void *)&(vpo)->vpo_nibble.l, 60378645Snsouch INB_NIBBLE_F, nibble_inbyte_hook, 604185003Sjhb INB_NIBBLE_P, (void *)&(vpo)->vpo_nibble); 60539136Snsouch 60639136Snsouch /* 60739136Snsouch * Initialize mode dependent in/out microsequences 60839136Snsouch */ 609187576Sjhb ppb_lock(ppbus); 61078645Snsouch if ((error = ppb_request_bus(ppbus, vpo->vpo_dev, PPB_WAIT))) 61178645Snsouch goto error; 61239136Snsouch 61370608Snsouch /* ppbus automatically restore the last mode entered during detection */ 61470608Snsouch switch (vpo->vpo_mode_found) { 61570608Snsouch case VP0_MODE_EPP: 61670608Snsouch ppb_MS_GET_init(ppbus, vpo->vpo_dev, epp17_instr); 61770608Snsouch ppb_MS_PUT_init(ppbus, vpo->vpo_dev, epp17_outstr); 618184130Sjhb device_printf(vpo->vpo_dev, "EPP mode\n"); 61970608Snsouch break; 62070608Snsouch case VP0_MODE_PS2: 62155939Snsouch ppb_MS_GET_init(ppbus, vpo->vpo_dev, ps2_inbyte_submicroseq); 62255939Snsouch ppb_MS_PUT_init(ppbus, vpo->vpo_dev, spp_outbyte_submicroseq); 623184130Sjhb device_printf(vpo->vpo_dev, "PS2 mode\n"); 62470608Snsouch break; 62570608Snsouch case VP0_MODE_NIBBLE: 62670608Snsouch ppb_MS_GET_init(ppbus, vpo->vpo_dev, vpo->vpo_nibble_inbyte_msq); 62770608Snsouch ppb_MS_PUT_init(ppbus, vpo->vpo_dev, spp_outbyte_submicroseq); 628184130Sjhb device_printf(vpo->vpo_dev, "NIBBLE mode\n"); 62970608Snsouch break; 63070608Snsouch default: 63170608Snsouch panic("imm: unknown mode %d", vpo->vpo_mode_found); 63239136Snsouch } 63339136Snsouch 63455939Snsouch ppb_release_bus(ppbus, vpo->vpo_dev); 63578645Snsouch error: 636187576Sjhb ppb_unlock(ppbus); 63778645Snsouch return (error); 63839136Snsouch} 63939136Snsouch 64039136Snsouch/* 64139136Snsouch * imm_reset_bus() 64239136Snsouch * 64339136Snsouch */ 64439136Snsouchint 64539136Snsouchimm_reset_bus(struct vpoio_data *vpo) 64639136Snsouch{ 64755939Snsouch device_t ppbus = device_get_parent(vpo->vpo_dev); 64839520Snsouch int disconnected; 64939136Snsouch 65039520Snsouch /* first, connect to the drive and request the bus */ 65139520Snsouch imm_connect(vpo, PPB_WAIT|PPB_INTR, &disconnected, 1); 65239136Snsouch 65339520Snsouch if (!disconnected) { 65439136Snsouch 65539136Snsouch /* reset the SCSI bus */ 65655939Snsouch ppb_MS_microseq(ppbus, vpo->vpo_dev, reset_microseq, NULL); 65739136Snsouch 65839136Snsouch /* then disconnect */ 65939520Snsouch imm_disconnect(vpo, NULL, 1); 66039136Snsouch } 66139136Snsouch 66239136Snsouch return (0); 66339136Snsouch} 66439136Snsouch 66539136Snsouch/* 66639136Snsouch * imm_do_scsi() 66739136Snsouch * 66839136Snsouch * Send an SCSI command 66939136Snsouch * 67039136Snsouch */ 671185003Sjhbint 67239136Snsouchimm_do_scsi(struct vpoio_data *vpo, int host, int target, char *command, 67339136Snsouch int clen, char *buffer, int blen, int *result, int *count, 67439136Snsouch int *ret) 67539136Snsouch{ 67655939Snsouch device_t ppbus = device_get_parent(vpo->vpo_dev); 67739136Snsouch register char r; 67839136Snsouch char l, h = 0; 67939136Snsouch int len, error = 0, not_connected = 0; 68039136Snsouch register int k; 68139136Snsouch int negociated = 0; 68239136Snsouch 68339136Snsouch /* 68439136Snsouch * enter disk state, allocate the ppbus 68539136Snsouch * 68639136Snsouch * XXX 68739136Snsouch * Should we allow this call to be interruptible? 68839136Snsouch * The only way to report the interruption is to return 68978645Snsouch * EIO to upper SCSI code :^( 69039136Snsouch */ 69139520Snsouch if ((error = imm_connect(vpo, PPB_WAIT|PPB_INTR, ¬_connected, 1))) 69239136Snsouch return (error); 69339136Snsouch 69439136Snsouch if (not_connected) { 695185003Sjhb *ret = VP0_ECONNECT; 696185003Sjhb goto error; 69739136Snsouch } 69839136Snsouch 69939136Snsouch /* 70039136Snsouch * Select the drive ... 70139136Snsouch */ 70239136Snsouch if ((*ret = imm_select(vpo,host,target))) 70339136Snsouch goto error; 70439136Snsouch 70539136Snsouch /* 70639136Snsouch * Send the command ... 70739136Snsouch */ 70839136Snsouch for (k = 0; k < clen; k+=2) { 70939136Snsouch if (imm_wait(vpo, VP0_FAST_SPINTMO) != (char)0xa8) { 71039136Snsouch *ret = VP0_ECMD_TIMEOUT; 71139136Snsouch goto error; 71239136Snsouch } 71339136Snsouch if (imm_outstr(vpo, &command[k], 2)) { 71439136Snsouch *ret = VP0_EPPDATA_TIMEOUT; 71539136Snsouch goto error; 71639136Snsouch } 71739136Snsouch } 71839136Snsouch 71939136Snsouch if (!(r = imm_wait(vpo, VP0_LOW_SPINTMO))) { 720185003Sjhb *ret = VP0_ESTATUS_TIMEOUT; 721185003Sjhb goto error; 72239136Snsouch } 72339136Snsouch 72439136Snsouch if ((r & 0x30) == 0x10) { 72539136Snsouch if (imm_negociate(vpo)) { 72639136Snsouch *ret = VP0_ENEGOCIATE; 72739136Snsouch goto error; 72839136Snsouch } else 72939136Snsouch negociated = 1; 73039136Snsouch } 73139136Snsouch 732185003Sjhb /* 733185003Sjhb * Complete transfer ... 73439136Snsouch */ 73539136Snsouch *count = 0; 73639136Snsouch for (;;) { 73739136Snsouch 73839136Snsouch if (!(r = imm_wait(vpo, VP0_LOW_SPINTMO))) { 739185003Sjhb *ret = VP0_ESTATUS_TIMEOUT; 740185003Sjhb goto error; 74139136Snsouch } 74239136Snsouch 74339136Snsouch /* stop when the ZIP+ wants to send status */ 74439136Snsouch if (r == (char)0xb8) 74539136Snsouch break; 74639136Snsouch 74739136Snsouch if (*count >= blen) { 74839136Snsouch *ret = VP0_EDATA_OVERFLOW; 74939136Snsouch goto error; 75039136Snsouch } 75139136Snsouch 75239136Snsouch /* ZIP+ wants to send data? */ 75339136Snsouch if (r == (char)0x88) { 75439136Snsouch len = (((blen - *count) >= VP0_SECTOR_SIZE)) ? 75539136Snsouch VP0_SECTOR_SIZE : 2; 75639136Snsouch 75739136Snsouch error = imm_outstr(vpo, &buffer[*count], len); 75839136Snsouch } else { 75955939Snsouch if (!PPB_IN_EPP_MODE(ppbus)) 76039136Snsouch len = 1; 76139136Snsouch else 76239136Snsouch len = (((blen - *count) >= VP0_SECTOR_SIZE)) ? 76339136Snsouch VP0_SECTOR_SIZE : 1; 76439136Snsouch 76539136Snsouch error = imm_instr(vpo, &buffer[*count], len); 76639136Snsouch } 76739136Snsouch 76839136Snsouch if (error) { 76939136Snsouch *ret = error; 77039136Snsouch goto error; 77139136Snsouch } 77239136Snsouch 77339136Snsouch *count += len; 77439136Snsouch } 77539136Snsouch 77655939Snsouch if ((PPB_IN_NIBBLE_MODE(ppbus) || 77755939Snsouch PPB_IN_PS2_MODE(ppbus)) && negociated) 77855939Snsouch ppb_MS_microseq(ppbus, vpo->vpo_dev, transfer_epilog, NULL); 77939136Snsouch 78039136Snsouch /* 78139136Snsouch * Retrieve status ... 78239136Snsouch */ 78339136Snsouch if (imm_negociate(vpo)) { 78439136Snsouch *ret = VP0_ENEGOCIATE; 78539136Snsouch goto error; 78639136Snsouch } else 78739136Snsouch negociated = 1; 78839136Snsouch 78939136Snsouch if (imm_instr(vpo, &l, 1)) { 790185003Sjhb *ret = VP0_EOTHER; 791185003Sjhb goto error; 79239136Snsouch } 79339136Snsouch 79439136Snsouch /* check if the ZIP+ wants to send more status */ 79539136Snsouch if (imm_wait(vpo, VP0_FAST_SPINTMO) == (char)0xb8) 79639136Snsouch if (imm_instr(vpo, &h, 1)) { 797185003Sjhb *ret = VP0_EOTHER + 2; 798185003Sjhb goto error; 79939136Snsouch } 80039136Snsouch 80171633Snsouch /* Experience showed that we should discard this */ 802153586Ssam if (h == (char) -1) 80371633Snsouch h = 0; 80471633Snsouch 80539136Snsouch *result = ((int) h << 8) | ((int) l & 0xff); 80639136Snsouch 80739136Snsoucherror: 80855939Snsouch if ((PPB_IN_NIBBLE_MODE(ppbus) || 80955939Snsouch PPB_IN_PS2_MODE(ppbus)) && negociated) 81055939Snsouch ppb_MS_microseq(ppbus, vpo->vpo_dev, transfer_epilog, NULL); 81139136Snsouch 81239136Snsouch /* return to printer state, release the ppbus */ 81339520Snsouch imm_disconnect(vpo, NULL, 1); 81439136Snsouch 81539136Snsouch return (0); 81639136Snsouch} 817