vpoio.c revision 45342
138061Smsmith/*- 238061Smsmith * Copyright (c) 1998 Nicolas Souchu 338061Smsmith * All rights reserved. 438061Smsmith * 538061Smsmith * Redistribution and use in source and binary forms, with or without 638061Smsmith * modification, are permitted provided that the following conditions 738061Smsmith * are met: 838061Smsmith * 1. Redistributions of source code must retain the above copyright 938061Smsmith * notice, this list of conditions and the following disclaimer. 1038061Smsmith * 2. Redistributions in binary form must reproduce the above copyright 1138061Smsmith * notice, this list of conditions and the following disclaimer in the 1238061Smsmith * documentation and/or other materials provided with the distribution. 1338061Smsmith * 1438061Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1538061Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1638061Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1738061Smsmith * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1838061Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1938061Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2038061Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2138061Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2238061Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2338061Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2438061Smsmith * SUCH DAMAGE. 2538061Smsmith * 2645342Speter * $Id: vpoio.c,v 1.6 1999/01/30 15:35:39 nsouch Exp $ 2738061Smsmith * 2838061Smsmith */ 2938061Smsmith 3038061Smsmith#ifdef KERNEL 3138061Smsmith#include <sys/param.h> 3238061Smsmith#include <sys/systm.h> 3338061Smsmith#include <sys/malloc.h> 3438061Smsmith#include <sys/buf.h> 3538061Smsmith 3638061Smsmith#include <machine/clock.h> 3738061Smsmith 3838061Smsmith#endif /* KERNEL */ 3938061Smsmith 4038061Smsmith#ifdef KERNEL 4138061Smsmith#include <sys/kernel.h> 4238061Smsmith#endif /*KERNEL */ 4338061Smsmith 4442475Snsouch#include "opt_vpo.h" 4542475Snsouch 4638061Smsmith#include <dev/ppbus/ppbconf.h> 4738061Smsmith#include <dev/ppbus/ppb_msq.h> 4838061Smsmith#include <dev/ppbus/vpoio.h> 4938061Smsmith 5038061Smsmith/* 5138061Smsmith * The driver pools the drive. We may add a timeout queue to avoid 5238061Smsmith * active polling on nACK. I've tried this but it leads to unreliable 5338061Smsmith * transfers 5438061Smsmith */ 5538061Smsmith#define VP0_SELTMO 5000 /* select timeout */ 5638061Smsmith#define VP0_FAST_SPINTMO 500000 /* wait status timeout */ 5738061Smsmith#define VP0_LOW_SPINTMO 5000000 /* wait status timeout */ 5838061Smsmith 5938061Smsmith/* 6038061Smsmith * Actually, VP0 timings are more accurate (about few 16MHZ cycles), 6138061Smsmith * but succeeding in respecting such timings leads to architecture 6238061Smsmith * dependent considerations. 6338061Smsmith */ 6438061Smsmith#define VP0_PULSE 1 6538061Smsmith 6638061Smsmith#define VP0_SECTOR_SIZE 512 6738061Smsmith#define VP0_BUFFER_SIZE 0x12000 6838061Smsmith 6938061Smsmith#define n(flags) (~(flags) & (flags)) 7038061Smsmith 7138061Smsmith/* 7238061Smsmith * VP0 connections. 7338061Smsmith */ 7438061Smsmith#define H_AUTO n(AUTOFEED) 7538061Smsmith#define H_nAUTO AUTOFEED 7638061Smsmith#define H_STROBE n(STROBE) 7738061Smsmith#define H_nSTROBE STROBE 7838061Smsmith#define H_BSY n(nBUSY) 7938061Smsmith#define H_nBSY nBUSY 8038061Smsmith#define H_SEL SELECT 8138061Smsmith#define H_nSEL n(SELECT) 8239134Snsouch#define H_ERR PERROR 8339134Snsouch#define H_nERR n(PERROR) 8438061Smsmith#define H_ACK nACK 8538061Smsmith#define H_nACK n(nACK) 8638061Smsmith#define H_FLT nFAULT 8738061Smsmith#define H_nFLT n(nFAULT) 8838061Smsmith#define H_SELIN n(SELECTIN) 8938061Smsmith#define H_nSELIN SELECTIN 9038061Smsmith#define H_INIT nINIT 9138061Smsmith#define H_nINIT n(nINIT) 9238061Smsmith 9338061Smsmith/* 9438061Smsmith * Microcode to execute very fast I/O sequences at the lowest bus level. 9538061Smsmith */ 9638061Smsmith 9739134Snsouch/* call this macro to initialize connect/disconnect microsequences */ 9839134Snsouch#define INIT_TRIG_MICROSEQ { \ 9939134Snsouch int i; \ 10039134Snsouch for (i=1; i <= 7; i+=2) { \ 10145342Speter disconnect_microseq[i].arg[2] = (union ppb_insarg)d_pulse; \ 10239134Snsouch connect_epp_microseq[i].arg[2] = \ 10345342Speter connect_spp_microseq[i].arg[2] = (union ppb_insarg)c_pulse; \ 10439134Snsouch } \ 10539134Snsouch} 10639134Snsouch 10739134Snsouch#define trig_d_pulse MS_TRIG(MS_REG_CTR,5,MS_UNKNOWN /* d_pulse */) 10839134Snsouchstatic char d_pulse[] = { 10938061Smsmith H_AUTO | H_nSELIN | H_INIT | H_STROBE, 0, 11038061Smsmith H_nAUTO | H_nSELIN | H_INIT | H_STROBE, VP0_PULSE, 11138061Smsmith H_AUTO | H_nSELIN | H_INIT | H_STROBE, 0, 11238061Smsmith H_AUTO | H_SELIN | H_INIT | H_STROBE, VP0_PULSE, 11338061Smsmith H_AUTO | H_nSELIN | H_INIT | H_STROBE, VP0_PULSE 11438061Smsmith}; 11538061Smsmith 11639134Snsouch#define trig_c_pulse MS_TRIG(MS_REG_CTR,5,MS_UNKNOWN /* c_pulse */) 11739134Snsouchstatic char c_pulse[] = { 11838061Smsmith H_AUTO | H_nSELIN | H_INIT | H_STROBE, 0, 11938061Smsmith H_AUTO | H_SELIN | H_INIT | H_STROBE, 0, 12038061Smsmith H_nAUTO | H_SELIN | H_INIT | H_STROBE, VP0_PULSE, 12138061Smsmith H_AUTO | H_SELIN | H_INIT | H_STROBE, 0, 12238061Smsmith H_AUTO | H_nSELIN | H_INIT | H_STROBE, VP0_PULSE 12338061Smsmith}; 12438061Smsmith 12539134Snsouchstatic struct ppb_microseq disconnect_microseq[] = { 12638061Smsmith MS_DASS(0x0), trig_d_pulse, MS_DASS(0x3c), trig_d_pulse, 12738061Smsmith MS_DASS(0x20), trig_d_pulse, MS_DASS(0xf), trig_d_pulse, MS_RET(0) 12838061Smsmith}; 12938061Smsmith 13039134Snsouchstatic struct ppb_microseq connect_epp_microseq[] = { 13138061Smsmith MS_DASS(0x0), trig_c_pulse, MS_DASS(0x3c), trig_c_pulse, 13238061Smsmith MS_DASS(0x20), trig_c_pulse, MS_DASS(0xcf), trig_c_pulse, MS_RET(0) 13338061Smsmith}; 13438061Smsmith 13539134Snsouchstatic struct ppb_microseq connect_spp_microseq[] = { 13638061Smsmith MS_DASS(0x0), trig_c_pulse, MS_DASS(0x3c), trig_c_pulse, 13738061Smsmith MS_DASS(0x20), trig_c_pulse, MS_DASS(0x8f), trig_c_pulse, MS_RET(0) 13838061Smsmith}; 13938061Smsmith 14038061Smsmith/* 14138061Smsmith * nibble_inbyte_hook() 14238061Smsmith * 14338061Smsmith * Formats high and low nibble into a character 14438061Smsmith */ 14538061Smsmithstatic int 14638061Smsmithnibble_inbyte_hook (void *p, char *ptr) 14738061Smsmith{ 14838061Smsmith struct vpo_nibble *s = (struct vpo_nibble *)p; 14938061Smsmith 15038061Smsmith /* increment the buffer pointer */ 15138061Smsmith *ptr++ = ((s->l >> 4) & 0x0f) + (s->h & 0xf0); 15238061Smsmith 15338061Smsmith return (0); 15438061Smsmith} 15538061Smsmith 15638061Smsmith/* 15738061Smsmith * Macro used to initialize each vpoio_data structure during 15838061Smsmith * low level attachment 15939134Snsouch * 16039134Snsouch * XXX should be converted to ppb_MS_init_msq() 16138061Smsmith */ 16238061Smsmith#define INIT_NIBBLE_INBYTE_SUBMICROSEQ(vpo) { \ 16338061Smsmith (vpo)->vpo_nibble_inbyte_msq[2].arg[2].p = \ 16438061Smsmith (void *)&(vpo)->vpo_nibble.h; \ 16538061Smsmith (vpo)->vpo_nibble_inbyte_msq[4].arg[2].p = \ 16638061Smsmith (void *)&(vpo)->vpo_nibble.l; \ 16738061Smsmith (vpo)->vpo_nibble_inbyte_msq[5].arg[0].f = \ 16838061Smsmith nibble_inbyte_hook; \ 16938061Smsmith (vpo)->vpo_nibble_inbyte_msq[5].arg[1].p = \ 17038061Smsmith (void *)&(vpo)->vpo_nibble; \ 17138061Smsmith} 17238061Smsmith 17338061Smsmith/* 17438061Smsmith * This is the sub-microseqence for MS_GET in NIBBLE mode 17538061Smsmith * Retrieve the two nibbles and call the C function to generate the character 17638061Smsmith * and store it in the buffer (see nibble_inbyte_hook()) 17738061Smsmith */ 17839134Snsouchstatic struct ppb_microseq nibble_inbyte_submicroseq[] = { 17938061Smsmith 18038061Smsmith/* loop: */ 18138061Smsmith MS_CASS( H_AUTO | H_SELIN | H_INIT | H_STROBE), 18238061Smsmith MS_DELAY(VP0_PULSE), 18338061Smsmith MS_RFETCH(MS_REG_STR, MS_FETCH_ALL, MS_UNKNOWN /* high nibble */), 18438061Smsmith MS_CASS(H_nAUTO | H_SELIN | H_INIT | H_STROBE), 18538061Smsmith MS_RFETCH(MS_REG_STR, MS_FETCH_ALL, MS_UNKNOWN /* low nibble */), 18638061Smsmith 18738061Smsmith /* do a C call to format the received nibbles */ 18838061Smsmith MS_C_CALL(MS_UNKNOWN /* C hook */, MS_UNKNOWN /* param */), 18943433Snsouch MS_DBRA(-7 /* loop */), 19038061Smsmith 19138061Smsmith MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE), 19238061Smsmith MS_RET(0) 19338061Smsmith}; 19438061Smsmith 19538061Smsmith/* 19638061Smsmith * This is the sub-microseqence for MS_GET in PS2 mode 19738061Smsmith */ 19839134Snsouchstatic struct ppb_microseq ps2_inbyte_submicroseq[] = { 19938061Smsmith MS_CASS(PCD | H_AUTO | H_SELIN | H_INIT | H_nSTROBE), 20038061Smsmith 20138061Smsmith/* loop: */ 20238061Smsmith MS_RFETCH_P(1, MS_REG_DTR, MS_FETCH_ALL), 20338061Smsmith MS_CASS(PCD | H_nAUTO | H_SELIN | H_INIT | H_nSTROBE), 20438061Smsmith MS_CASS(PCD | H_AUTO | H_SELIN | H_INIT | H_nSTROBE), 20543433Snsouch MS_DBRA(-4 /* loop */), 20638061Smsmith 20738061Smsmith MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE), 20838061Smsmith MS_RET(0) 20938061Smsmith}; 21038061Smsmith 21138061Smsmith/* 21238061Smsmith * This is the sub-microsequence for MS_PUT in both NIBBLE and PS2 modes 21338061Smsmith */ 21439134Snsouchstatic struct ppb_microseq spp_outbyte_submicroseq[] = { 21538061Smsmith 21638061Smsmith/* loop: */ 21738061Smsmith MS_RASSERT_P(1, MS_REG_DTR), 21838061Smsmith MS_CASS(H_nAUTO | H_nSELIN | H_INIT | H_STROBE), 21938061Smsmith MS_CASS( H_AUTO | H_nSELIN | H_INIT | H_STROBE), 22038061Smsmith MS_DELAY(VP0_PULSE), 22143433Snsouch MS_DBRA(-5 /* loop */), 22238061Smsmith 22338061Smsmith /* return from the put call */ 22438061Smsmith MS_RET(0) 22538061Smsmith}; 22638061Smsmith 22738061Smsmith/* EPP 1.7 microsequences, ptr and len set at runtime */ 22839134Snsouchstatic struct ppb_microseq epp17_outstr_body[] = { 22938061Smsmith MS_CASS(H_AUTO | H_SELIN | H_INIT | H_STROBE), 23038061Smsmith 23138061Smsmith/* loop: */ 23243433Snsouch MS_RASSERT_P(1, MS_REG_EPP_D), 23343433Snsouch MS_BRSET(TIMEOUT, 3 /* error */), /* EPP timeout? */ 23443433Snsouch MS_DBRA(-3 /* loop */), 23538061Smsmith 23638061Smsmith MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE), 23738061Smsmith MS_RET(0), 23838061Smsmith/* error: */ 23938061Smsmith MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE), 24038061Smsmith MS_RET(1) 24138061Smsmith}; 24238061Smsmith 24339134Snsouchstatic struct ppb_microseq epp17_instr_body[] = { 24438061Smsmith MS_CASS(PCD | H_AUTO | H_SELIN | H_INIT | H_STROBE), 24538061Smsmith 24638061Smsmith/* loop: */ 24743433Snsouch MS_RFETCH_P(1, MS_REG_EPP_D, MS_FETCH_ALL), 24843433Snsouch MS_BRSET(TIMEOUT, 3 /* error */), /* EPP timeout? */ 24943433Snsouch MS_DBRA(-3 /* loop */), 25038061Smsmith 25138061Smsmith MS_CASS(PCD | H_AUTO | H_nSELIN | H_INIT | H_STROBE), 25238061Smsmith MS_RET(0), 25338061Smsmith/* error: */ 25438061Smsmith MS_CASS(PCD | H_AUTO | H_nSELIN | H_INIT | H_STROBE), 25538061Smsmith MS_RET(1) 25638061Smsmith}; 25738061Smsmith 25839134Snsouchstatic struct ppb_microseq in_disk_mode[] = { 25939134Snsouch MS_CASS( H_AUTO | H_nSELIN | H_INIT | H_STROBE), 26039134Snsouch MS_CASS(H_nAUTO | H_nSELIN | H_INIT | H_STROBE), 26139134Snsouch 26243433Snsouch MS_BRCLEAR(H_FLT, 3 /* error */), 26339134Snsouch MS_CASS( H_AUTO | H_nSELIN | H_INIT | H_STROBE), 26443433Snsouch MS_BRSET(H_FLT, 1 /* error */), 26539134Snsouch 26639520Snsouch MS_RET(1), 26739134Snsouch/* error: */ 26839520Snsouch MS_RET(0) 26939134Snsouch}; 27039134Snsouch 27138061Smsmithstatic int 27238061Smsmithvpoio_disconnect(struct vpoio_data *vpo) 27338061Smsmith{ 27438061Smsmith int ret; 27538061Smsmith 27638061Smsmith ppb_MS_microseq(&vpo->vpo_dev, disconnect_microseq, &ret); 27738061Smsmith return (ppb_release_bus(&vpo->vpo_dev)); 27838061Smsmith} 27938061Smsmith 28038061Smsmith/* 28138061Smsmith * how : PPB_WAIT or PPB_DONTWAIT 28238061Smsmith */ 28338061Smsmithstatic int 28438061Smsmithvpoio_connect(struct vpoio_data *vpo, int how) 28538061Smsmith{ 28638061Smsmith int error; 28738061Smsmith int ret; 28838061Smsmith 28942475Snsouch if ((error = ppb_request_bus(&vpo->vpo_dev, how))) { 29042475Snsouch 29142475Snsouch#ifdef VP0_DEBUG 29242475Snsouch printf("%s: can't request bus!\n", __FUNCTION__); 29342475Snsouch#endif 29438061Smsmith return error; 29542475Snsouch } 29638061Smsmith 29738061Smsmith if (PPB_IN_EPP_MODE(&vpo->vpo_dev)) 29838061Smsmith ppb_MS_microseq(&vpo->vpo_dev, connect_epp_microseq, &ret); 29938061Smsmith else 30038061Smsmith ppb_MS_microseq(&vpo->vpo_dev, connect_spp_microseq, &ret); 30138061Smsmith 30238061Smsmith return (0); 30338061Smsmith} 30438061Smsmith 30538061Smsmith/* 30639134Snsouch * vpoio_reset() 30738061Smsmith * 30839134Snsouch * SCSI reset signal, the drive must be in disk mode 30938061Smsmith */ 31039134Snsouchstatic void 31139134Snsouchvpoio_reset (struct vpoio_data *vpo) 31238061Smsmith{ 31339134Snsouch int ret; 31438061Smsmith 31539134Snsouch struct ppb_microseq reset_microseq[] = { 31638061Smsmith 31739134Snsouch #define INITIATOR MS_PARAM(0, 1, MS_TYP_INT) 31838061Smsmith 31939134Snsouch MS_DASS(MS_UNKNOWN), 32039134Snsouch MS_CASS(H_AUTO | H_nSELIN | H_nINIT | H_STROBE), 32139134Snsouch MS_DELAY(25), 32239134Snsouch MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE), 32339134Snsouch MS_RET(0) 32439134Snsouch }; 32538061Smsmith 32639134Snsouch ppb_MS_init_msq(reset_microseq, 1, INITIATOR, 1 << VP0_INITIATOR); 32739134Snsouch ppb_MS_microseq(&vpo->vpo_dev, reset_microseq, &ret); 32839134Snsouch 32939134Snsouch return; 33038061Smsmith} 33138061Smsmith 33238061Smsmith/* 33339134Snsouch * vpoio_in_disk_mode() 33438061Smsmith */ 33539134Snsouchstatic int 33639134Snsouchvpoio_in_disk_mode(struct vpoio_data *vpo) 33738061Smsmith{ 33839134Snsouch int ret; 33938061Smsmith 34039134Snsouch ppb_MS_microseq(&vpo->vpo_dev, in_disk_mode, &ret); 34138061Smsmith 34239134Snsouch return (ret); 34338061Smsmith} 34438061Smsmith 34538061Smsmith/* 34638061Smsmith * vpoio_detect() 34738061Smsmith * 34838061Smsmith * Detect and initialise the VP0 adapter. 34938061Smsmith */ 35039134Snsouchstatic int 35138061Smsmithvpoio_detect(struct vpoio_data *vpo) 35238061Smsmith{ 35339520Snsouch int error, ret; 35438061Smsmith 35539520Snsouch /* allocate the bus, then apply microsequences */ 35639520Snsouch if ((error = ppb_request_bus(&vpo->vpo_dev, PPB_DONTWAIT))) 35739520Snsouch return (error); 35839520Snsouch 35939520Snsouch ppb_MS_microseq(&vpo->vpo_dev, disconnect_microseq, &ret); 36039520Snsouch 36139520Snsouch if (PPB_IN_EPP_MODE(&vpo->vpo_dev)) 36239520Snsouch ppb_MS_microseq(&vpo->vpo_dev, connect_epp_microseq, &ret); 36339520Snsouch else 36439520Snsouch ppb_MS_microseq(&vpo->vpo_dev, connect_spp_microseq, &ret); 36539520Snsouch 36639520Snsouch ppb_MS_microseq(&vpo->vpo_dev, in_disk_mode, &ret); 36739520Snsouch if (!ret) { 36839520Snsouch 36939520Snsouch /* try spp mode (maybe twice or because previous mode was PS2) 37039520Snsouch * NIBBLE mode will be restored on next transfers if detection 37139520Snsouch * succeed 37239520Snsouch */ 37339520Snsouch ppb_set_mode(&vpo->vpo_dev, PPB_NIBBLE); 37439520Snsouch ppb_MS_microseq(&vpo->vpo_dev, connect_spp_microseq, &ret); 37539520Snsouch 37639520Snsouch ppb_MS_microseq(&vpo->vpo_dev, in_disk_mode, &ret); 37739520Snsouch if (!ret) { 37839520Snsouch if (bootverbose) 37939520Snsouch printf("vpo%d: can't connect to the drive\n", 38039520Snsouch vpo->vpo_unit); 38139520Snsouch 38239520Snsouch /* disconnect and release the bus */ 38339520Snsouch ppb_MS_microseq(&vpo->vpo_dev, disconnect_microseq, 38439520Snsouch &ret); 38539520Snsouch goto error; 38639520Snsouch } 38738061Smsmith } 38838061Smsmith 38938061Smsmith /* send SCSI reset signal */ 39038061Smsmith vpoio_reset(vpo); 39138061Smsmith 39239520Snsouch ppb_MS_microseq(&vpo->vpo_dev, disconnect_microseq, &ret); 39338061Smsmith 39439134Snsouch /* ensure we are disconnected or daisy chained peripheral 39539134Snsouch * may cause serious problem to the disk */ 39639134Snsouch 39739520Snsouch ppb_MS_microseq(&vpo->vpo_dev, in_disk_mode, &ret); 39839520Snsouch if (ret) { 39939520Snsouch if (bootverbose) 40039520Snsouch printf("vpo%d: can't disconnect from the drive\n", 40139520Snsouch vpo->vpo_unit); 40239520Snsouch goto error; 40339520Snsouch } 40438061Smsmith 40539520Snsouch ppb_release_bus(&vpo->vpo_dev); 40638061Smsmith return (0); 40739520Snsouch 40839520Snsoucherror: 40939520Snsouch ppb_release_bus(&vpo->vpo_dev); 41039520Snsouch return (VP0_EINITFAILED); 41138061Smsmith} 41238061Smsmith 41338061Smsmith/* 41438061Smsmith * vpoio_outstr() 41538061Smsmith */ 41638061Smsmithstatic int 41738061Smsmithvpoio_outstr(struct vpoio_data *vpo, char *buffer, int size) 41838061Smsmith{ 41938061Smsmith 42038061Smsmith int error = 0; 42138061Smsmith 42245342Speter ppb_MS_exec(&vpo->vpo_dev, MS_OP_PUT, (union ppb_insarg)buffer, 42345342Speter (union ppb_insarg)size, (union ppb_insarg)MS_UNKNOWN, &error); 42438061Smsmith 42538061Smsmith#if 0 42638061Smsmith /* XXX EPP 1.9 not implemented with microsequences */ 42738061Smsmith else { 42838061Smsmith 42938061Smsmith ppb_reset_epp_timeout(&vpo->vpo_dev); 43038061Smsmith ppb_wctr(&vpo->vpo_dev, 43138061Smsmith H_AUTO | H_SELIN | H_INIT | H_STROBE); 43238061Smsmith 43338061Smsmith if (((long) buffer | size) & 0x03) 43438061Smsmith ppb_outsb_epp(&vpo->vpo_dev, 43538061Smsmith buffer, size); 43638061Smsmith else 43738061Smsmith ppb_outsl_epp(&vpo->vpo_dev, 43838061Smsmith buffer, size/4); 43938061Smsmith 44038061Smsmith if ((ppb_rstr(&vpo->vpo_dev) & TIMEOUT)) { 44138061Smsmith error = VP0_EPPDATA_TIMEOUT; 44238061Smsmith goto error; 44338061Smsmith } 44438061Smsmith 44538061Smsmith ppb_wctr(&vpo->vpo_dev, 44638061Smsmith H_AUTO | H_nSELIN | H_INIT | H_STROBE); 44738061Smsmith } 44838061Smsmith#endif 44942475Snsouch ppb_ecp_sync(&vpo->vpo_dev); 45038061Smsmith 45138061Smsmith return (error); 45238061Smsmith} 45338061Smsmith 45438061Smsmith/* 45538061Smsmith * vpoio_instr() 45638061Smsmith */ 45738061Smsmithstatic int 45838061Smsmithvpoio_instr(struct vpoio_data *vpo, char *buffer, int size) 45938061Smsmith{ 46038061Smsmith int error = 0; 46138061Smsmith 46245342Speter ppb_MS_exec(&vpo->vpo_dev, MS_OP_GET, (union ppb_insarg)buffer, 46345342Speter (union ppb_insarg)size, (union ppb_insarg)MS_UNKNOWN, &error); 46438061Smsmith 46538061Smsmith#if 0 46638061Smsmith /* XXX EPP 1.9 not implemented with microsequences */ 46738061Smsmith else { 46838061Smsmith 46938061Smsmith ppb_reset_epp_timeout(&vpo->vpo_dev); 47038061Smsmith ppb_wctr(&vpo->vpo_dev, PCD | 47138061Smsmith H_AUTO | H_SELIN | H_INIT | H_STROBE); 47238061Smsmith 47338061Smsmith if (((long) buffer | size) & 0x03) 47438061Smsmith ppb_insb_epp(&vpo->vpo_dev, 47538061Smsmith buffer, size); 47638061Smsmith else 47738061Smsmith ppb_insl_epp(&vpo->vpo_dev, 47838061Smsmith buffer, size/4); 47938061Smsmith 48038061Smsmith if ((ppb_rstr(&vpo->vpo_dev) & TIMEOUT)) { 48138061Smsmith error = VP0_EPPDATA_TIMEOUT; 48238061Smsmith goto error; 48338061Smsmith } 48438061Smsmith 48538061Smsmith ppb_wctr(&vpo->vpo_dev, PCD | 48638061Smsmith H_AUTO | H_nSELIN | H_INIT | H_STROBE); 48738061Smsmith } 48838061Smsmith#endif 48942475Snsouch ppb_ecp_sync(&vpo->vpo_dev); 49038061Smsmith 49138061Smsmith return (error); 49238061Smsmith} 49338061Smsmith 49438061Smsmithstatic char 49538061Smsmithvpoio_select(struct vpoio_data *vpo, int initiator, int target) 49638061Smsmith{ 49738061Smsmith int ret; 49838061Smsmith 49938061Smsmith struct ppb_microseq select_microseq[] = { 50038061Smsmith 50138061Smsmith /* parameter list 50238061Smsmith */ 50338061Smsmith #define SELECT_TARGET MS_PARAM(0, 1, MS_TYP_INT) 50438061Smsmith #define SELECT_INITIATOR MS_PARAM(3, 1, MS_TYP_INT) 50538061Smsmith 50638061Smsmith /* send the select command to the drive */ 50738061Smsmith MS_DASS(MS_UNKNOWN), 50838061Smsmith MS_CASS(H_nAUTO | H_nSELIN | H_INIT | H_STROBE), 50938061Smsmith MS_CASS( H_AUTO | H_nSELIN | H_INIT | H_STROBE), 51038061Smsmith MS_DASS(MS_UNKNOWN), 51138061Smsmith MS_CASS( H_AUTO | H_nSELIN | H_nINIT | H_STROBE), 51238061Smsmith 51338061Smsmith /* now, wait until the drive is ready */ 51438061Smsmith MS_SET(VP0_SELTMO), 51543433Snsouch/* loop: */ MS_BRSET(H_ACK, 2 /* ready */), 51643433Snsouch MS_DBRA(-2 /* loop */), 51738061Smsmith/* error: */ MS_RET(1), 51838061Smsmith/* ready: */ MS_RET(0) 51938061Smsmith }; 52038061Smsmith 52138061Smsmith /* initialize the select microsequence */ 52238061Smsmith ppb_MS_init_msq(select_microseq, 2, 52338061Smsmith SELECT_TARGET, 1 << target, 52438061Smsmith SELECT_INITIATOR, 1 << initiator); 52538061Smsmith 52638061Smsmith ppb_MS_microseq(&vpo->vpo_dev, select_microseq, &ret); 52738061Smsmith 52838061Smsmith if (ret) 52938061Smsmith return (VP0_ESELECT_TIMEOUT); 53038061Smsmith 53138061Smsmith return (0); 53238061Smsmith} 53338061Smsmith 53438061Smsmith/* 53538061Smsmith * vpoio_wait() 53638061Smsmith * 53738061Smsmith * H_SELIN must be low. 53838061Smsmith * 53938061Smsmith * XXX should be ported to microseq 54038061Smsmith */ 54138061Smsmithstatic char 54238061Smsmithvpoio_wait(struct vpoio_data *vpo, int tmo) 54338061Smsmith{ 54438061Smsmith 54538061Smsmith register int k; 54638061Smsmith register char r; 54738061Smsmith 54838061Smsmith#if 0 /* broken */ 54938061Smsmith if (ppb_poll_device(&vpo->vpo_dev, 150, nBUSY, nBUSY, PPB_INTR)) 55038061Smsmith return (0); 55138061Smsmith 55238061Smsmith return (ppb_rstr(&vpo->vpo_dev) & 0xf0); 55338061Smsmith#endif 55438061Smsmith 55538061Smsmith /* XXX should be ported to microseq */ 55638061Smsmith k = 0; 55738061Smsmith while (!((r = ppb_rstr(&vpo->vpo_dev)) & nBUSY) && (k++ < tmo)) 55838061Smsmith ; 55938061Smsmith 56038061Smsmith /* 56138061Smsmith * Return some status information. 56238061Smsmith * Semantics : 0xc0 = ZIP wants more data 56338061Smsmith * 0xd0 = ZIP wants to send more data 56438061Smsmith * 0xe0 = ZIP wants command 56538061Smsmith * 0xf0 = end of transfer, ZIP is sending status 56638061Smsmith */ 56738061Smsmith if (k < tmo) 56838061Smsmith return (r & 0xf0); 56938061Smsmith 57038061Smsmith return (0); /* command timed out */ 57138061Smsmith} 57238061Smsmith 57338061Smsmith/* 57438061Smsmith * vpoio_probe() 57538061Smsmith * 57638061Smsmith * Low level probe of vpo device 57738061Smsmith * 57838061Smsmith */ 57938061Smsmithstruct ppb_device * 58038061Smsmithvpoio_probe(struct ppb_data *ppb, struct vpoio_data *vpo) 58138061Smsmith{ 58238061Smsmith 58338061Smsmith /* ppbus dependent initialisation */ 58438061Smsmith vpo->vpo_dev.id_unit = vpo->vpo_unit; 58538061Smsmith vpo->vpo_dev.name = "vpo"; 58638061Smsmith vpo->vpo_dev.ppb = ppb; 58738061Smsmith 58839134Snsouch /* 58939134Snsouch * Initialize microsequence code 59039134Snsouch */ 59139134Snsouch INIT_TRIG_MICROSEQ; 59239134Snsouch 59338061Smsmith /* now, try to initialise the drive */ 59438061Smsmith if (vpoio_detect(vpo)) { 59538061Smsmith return (NULL); 59638061Smsmith } 59738061Smsmith 59838061Smsmith return (&vpo->vpo_dev); 59938061Smsmith} 60038061Smsmith 60138061Smsmith/* 60238061Smsmith * vpoio_attach() 60338061Smsmith * 60438061Smsmith * Low level attachment of vpo device 60538061Smsmith * 60638061Smsmith */ 60738061Smsmithint 60838061Smsmithvpoio_attach(struct vpoio_data *vpo) 60938061Smsmith{ 61038061Smsmith int epp; 61138061Smsmith 61238061Smsmith /* 61338061Smsmith * Report ourselves 61438061Smsmith */ 61539134Snsouch printf("vpo%d: <Iomega VPI0 Parallel to SCSI interface> on ppbus %d\n", 61638061Smsmith vpo->vpo_dev.id_unit, vpo->vpo_dev.ppb->ppb_link->adapter_unit); 61738061Smsmith 61838061Smsmith vpo->vpo_nibble_inbyte_msq = (struct ppb_microseq *)malloc( 61938061Smsmith sizeof(nibble_inbyte_submicroseq), M_DEVBUF, M_NOWAIT); 62038061Smsmith 62138061Smsmith if (!vpo->vpo_nibble_inbyte_msq) 62238061Smsmith return (0); 62338061Smsmith 62438061Smsmith bcopy((void *)nibble_inbyte_submicroseq, 62538061Smsmith (void *)vpo->vpo_nibble_inbyte_msq, 62638061Smsmith sizeof(nibble_inbyte_submicroseq)); 62738061Smsmith 62838061Smsmith INIT_NIBBLE_INBYTE_SUBMICROSEQ(vpo); 62938061Smsmith 63038061Smsmith /* 63138061Smsmith * Initialize mode dependent in/out microsequences 63238061Smsmith */ 63338061Smsmith ppb_request_bus(&vpo->vpo_dev, PPB_WAIT); 63438061Smsmith 63538061Smsmith /* enter NIBBLE mode to configure submsq */ 63638061Smsmith if (ppb_set_mode(&vpo->vpo_dev, PPB_NIBBLE) != -1) { 63738061Smsmith 63838061Smsmith ppb_MS_GET_init(&vpo->vpo_dev, vpo->vpo_nibble_inbyte_msq); 63938061Smsmith 64038061Smsmith ppb_MS_PUT_init(&vpo->vpo_dev, spp_outbyte_submicroseq); 64138061Smsmith } 64238061Smsmith 64338061Smsmith /* enter PS2 mode to configure submsq */ 64438061Smsmith if (ppb_set_mode(&vpo->vpo_dev, PPB_PS2) != -1) { 64538061Smsmith 64638061Smsmith ppb_MS_GET_init(&vpo->vpo_dev, ps2_inbyte_submicroseq); 64738061Smsmith 64838061Smsmith ppb_MS_PUT_init(&vpo->vpo_dev, spp_outbyte_submicroseq); 64938061Smsmith } 65038061Smsmith 65138061Smsmith epp = ppb_get_epp_protocol(&vpo->vpo_dev); 65238061Smsmith 65338061Smsmith /* enter EPP mode to configure submsq */ 65438061Smsmith if (ppb_set_mode(&vpo->vpo_dev, PPB_EPP) != -1) { 65538061Smsmith 65638061Smsmith switch (epp) { 65738061Smsmith case EPP_1_9: 65838061Smsmith /* XXX EPP 1.9 support should be improved */ 65938061Smsmith case EPP_1_7: 66038061Smsmith ppb_MS_GET_init(&vpo->vpo_dev, epp17_instr_body); 66138061Smsmith 66238061Smsmith ppb_MS_PUT_init(&vpo->vpo_dev, epp17_outstr_body); 66338061Smsmith break; 66438061Smsmith default: 66538061Smsmith panic("%s: unknown EPP protocol (0x%x)", __FUNCTION__, 66638061Smsmith epp); 66738061Smsmith } 66838061Smsmith } 66938061Smsmith 67038061Smsmith /* try to enter EPP or PS/2 mode, NIBBLE otherwise */ 67138061Smsmith if (ppb_set_mode(&vpo->vpo_dev, PPB_EPP) != -1) { 67238061Smsmith switch (epp) { 67338061Smsmith case EPP_1_9: 67438061Smsmith printf("vpo%d: EPP 1.9 mode\n", vpo->vpo_unit); 67538061Smsmith break; 67638061Smsmith case EPP_1_7: 67738061Smsmith printf("vpo%d: EPP 1.7 mode\n", vpo->vpo_unit); 67838061Smsmith break; 67938061Smsmith default: 68038061Smsmith panic("%s: unknown EPP protocol (0x%x)", __FUNCTION__, 68138061Smsmith epp); 68238061Smsmith } 68338061Smsmith } else if (ppb_set_mode(&vpo->vpo_dev, PPB_PS2) != -1) 68438061Smsmith printf("vpo%d: PS2 mode\n", vpo->vpo_unit); 68538061Smsmith 68638061Smsmith else if (ppb_set_mode(&vpo->vpo_dev, PPB_NIBBLE) != -1) 68738061Smsmith printf("vpo%d: NIBBLE mode\n", vpo->vpo_unit); 68838061Smsmith 68938061Smsmith else { 69038061Smsmith printf("vpo%d: can't enter NIBBLE, PS2 or EPP mode\n", 69138061Smsmith vpo->vpo_unit); 69238061Smsmith 69338061Smsmith ppb_release_bus(&vpo->vpo_dev); 69438061Smsmith 69538061Smsmith free(vpo->vpo_nibble_inbyte_msq, M_DEVBUF); 69638061Smsmith return (0); 69738061Smsmith } 69838061Smsmith 69938061Smsmith ppb_release_bus(&vpo->vpo_dev); 70038061Smsmith 70138061Smsmith return (1); 70238061Smsmith} 70338061Smsmith 70438061Smsmith/* 70538061Smsmith * vpoio_reset_bus() 70638061Smsmith * 70738061Smsmith */ 70838061Smsmithint 70938061Smsmithvpoio_reset_bus(struct vpoio_data *vpo) 71038061Smsmith{ 71138061Smsmith /* first, connect to the drive */ 71239520Snsouch if (vpoio_connect(vpo, PPB_WAIT|PPB_INTR) || !vpoio_in_disk_mode(vpo)) { 71342475Snsouch 71442475Snsouch#ifdef VP0_DEBUG 71542475Snsouch printf("%s: not in disk mode!\n", __FUNCTION__); 71642475Snsouch#endif 71738061Smsmith /* release ppbus */ 71838061Smsmith vpoio_disconnect(vpo); 71938061Smsmith return (1); 72038061Smsmith } 72138061Smsmith 72238061Smsmith /* reset the SCSI bus */ 72338061Smsmith vpoio_reset(vpo); 72438061Smsmith 72538061Smsmith /* then disconnect */ 72638061Smsmith vpoio_disconnect(vpo); 72738061Smsmith 72838061Smsmith return (0); 72938061Smsmith} 73038061Smsmith 73138061Smsmith/* 73238061Smsmith * vpoio_do_scsi() 73338061Smsmith * 73438061Smsmith * Send an SCSI command 73538061Smsmith * 73638061Smsmith */ 73738061Smsmithint 73838061Smsmithvpoio_do_scsi(struct vpoio_data *vpo, int host, int target, char *command, 73938061Smsmith int clen, char *buffer, int blen, int *result, int *count, 74038061Smsmith int *ret) 74138061Smsmith{ 74238061Smsmith 74338061Smsmith register char r; 74438061Smsmith char l, h = 0; 74538061Smsmith int len, error = 0; 74638061Smsmith register int k; 74738061Smsmith 74838061Smsmith /* 74938061Smsmith * enter disk state, allocate the ppbus 75038061Smsmith * 75138061Smsmith * XXX 75238061Smsmith * Should we allow this call to be interruptible? 75338061Smsmith * The only way to report the interruption is to return 75438061Smsmith * EIO do upper SCSI code :^( 75538061Smsmith */ 75638061Smsmith if ((error = vpoio_connect(vpo, PPB_WAIT|PPB_INTR))) 75738061Smsmith return (error); 75838061Smsmith 75939520Snsouch if (!vpoio_in_disk_mode(vpo)) { 76038061Smsmith *ret = VP0_ECONNECT; goto error; 76138061Smsmith } 76238061Smsmith 76338061Smsmith if ((*ret = vpoio_select(vpo,host,target))) 76438061Smsmith goto error; 76538061Smsmith 76638061Smsmith /* 76738061Smsmith * Send the command ... 76838061Smsmith * 76938061Smsmith * set H_SELIN low for vpoio_wait(). 77038061Smsmith */ 77138061Smsmith ppb_wctr(&vpo->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE); 77238061Smsmith 77338061Smsmith for (k = 0; k < clen; k++) { 77438061Smsmith if (vpoio_wait(vpo, VP0_FAST_SPINTMO) != (char)0xe0) { 77538061Smsmith *ret = VP0_ECMD_TIMEOUT; 77638061Smsmith goto error; 77738061Smsmith } 77838061Smsmith if (vpoio_outstr(vpo, &command[k], 1)) { 77938061Smsmith *ret = VP0_EPPDATA_TIMEOUT; 78038061Smsmith goto error; 78138061Smsmith } 78238061Smsmith } 78338061Smsmith 78438061Smsmith /* 78538061Smsmith * Completion ... 78638061Smsmith */ 78738061Smsmith 78838061Smsmith *count = 0; 78938061Smsmith for (;;) { 79038061Smsmith 79138061Smsmith if (!(r = vpoio_wait(vpo, VP0_LOW_SPINTMO))) { 79238061Smsmith *ret = VP0_ESTATUS_TIMEOUT; goto error; 79338061Smsmith } 79438061Smsmith 79538061Smsmith /* stop when the ZIP wants to send status */ 79638061Smsmith if (r == (char)0xf0) 79738061Smsmith break; 79838061Smsmith 79938061Smsmith if (*count >= blen) { 80038061Smsmith *ret = VP0_EDATA_OVERFLOW; 80138061Smsmith goto error; 80238061Smsmith } 80338061Smsmith 80439520Snsouch /* if in EPP mode or writing bytes, try to transfer a sector 80539520Snsouch * otherwise, just send one byte 80639520Snsouch */ 80739520Snsouch if (PPB_IN_EPP_MODE(&vpo->vpo_dev) || r == (char)0xc0) 80839520Snsouch len = (((blen - *count) >= VP0_SECTOR_SIZE)) ? 80939520Snsouch VP0_SECTOR_SIZE : 1; 81039520Snsouch else 81139520Snsouch len = 1; 81239520Snsouch 81338061Smsmith /* ZIP wants to send data? */ 81438061Smsmith if (r == (char)0xc0) 81538061Smsmith error = vpoio_outstr(vpo, &buffer[*count], len); 81638061Smsmith else 81738061Smsmith error = vpoio_instr(vpo, &buffer[*count], len); 81838061Smsmith 81938061Smsmith if (error) { 82038061Smsmith *ret = error; 82138061Smsmith goto error; 82238061Smsmith } 82338061Smsmith 82438061Smsmith *count += len; 82538061Smsmith } 82638061Smsmith 82738061Smsmith if (vpoio_instr(vpo, &l, 1)) { 82838061Smsmith *ret = VP0_EOTHER; goto error; 82938061Smsmith } 83038061Smsmith 83138061Smsmith /* check if the ZIP wants to send more status */ 83238061Smsmith if (vpoio_wait(vpo, VP0_FAST_SPINTMO) == (char)0xf0) 83338061Smsmith if (vpoio_instr(vpo, &h, 1)) { 83438061Smsmith *ret = VP0_EOTHER+2; goto error; 83538061Smsmith } 83638061Smsmith 83738061Smsmith *result = ((int) h << 8) | ((int) l & 0xff); 83838061Smsmith 83938061Smsmitherror: 84038061Smsmith /* return to printer state, release the ppbus */ 84138061Smsmith vpoio_disconnect(vpo); 84238061Smsmith return (0); 84338061Smsmith} 844