vpoio.c revision 78645
138061Smsmith/*- 255939Snsouch * Copyright (c) 1998, 1999 Nicolas Souchu 370608Snsouch * Copyright (c) 2000 Alcove - Nicolas Souchu 438061Smsmith * All rights reserved. 538061Smsmith * 638061Smsmith * Redistribution and use in source and binary forms, with or without 738061Smsmith * modification, are permitted provided that the following conditions 838061Smsmith * are met: 938061Smsmith * 1. Redistributions of source code must retain the above copyright 1038061Smsmith * notice, this list of conditions and the following disclaimer. 1138061Smsmith * 2. Redistributions in binary form must reproduce the above copyright 1238061Smsmith * notice, this list of conditions and the following disclaimer in the 1338061Smsmith * documentation and/or other materials provided with the distribution. 1438061Smsmith * 1538061Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1638061Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1738061Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1838061Smsmith * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1938061Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2038061Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2138061Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2238061Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2338061Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2438061Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2538061Smsmith * SUCH DAMAGE. 2638061Smsmith * 2750477Speter * $FreeBSD: head/sys/dev/ppbus/vpoio.c 78645 2001-06-23 06:51:52Z nsouch $ 2838061Smsmith * 2938061Smsmith */ 3038061Smsmith 3155205Speter#ifdef _KERNEL 3238061Smsmith#include <sys/param.h> 3338061Smsmith#include <sys/systm.h> 3455939Snsouch#include <sys/module.h> 3555939Snsouch#include <sys/bus.h> 3638061Smsmith#include <sys/malloc.h> 3738061Smsmith 3878645Snsouch#include <machine/clock.h> 3938061Smsmith 4055205Speter#endif 4138061Smsmith 4255205Speter#ifdef _KERNEL 4355205Speter#endif 4438061Smsmith 4542475Snsouch#include "opt_vpo.h" 4642475Snsouch 4755939Snsouch#include <dev/ppbus/ppbio.h> 4838061Smsmith#include <dev/ppbus/ppbconf.h> 4938061Smsmith#include <dev/ppbus/ppb_msq.h> 5038061Smsmith#include <dev/ppbus/vpoio.h> 5138061Smsmith 5255939Snsouch#include "ppbus_if.h" 5355939Snsouch 5438061Smsmith/* 5538061Smsmith * The driver pools the drive. We may add a timeout queue to avoid 5638061Smsmith * active polling on nACK. I've tried this but it leads to unreliable 5738061Smsmith * transfers 5838061Smsmith */ 5938061Smsmith#define VP0_SELTMO 5000 /* select timeout */ 6038061Smsmith#define VP0_FAST_SPINTMO 500000 /* wait status timeout */ 6138061Smsmith#define VP0_LOW_SPINTMO 5000000 /* wait status timeout */ 6238061Smsmith 6338061Smsmith/* 6438061Smsmith * Actually, VP0 timings are more accurate (about few 16MHZ cycles), 6538061Smsmith * but succeeding in respecting such timings leads to architecture 6638061Smsmith * dependent considerations. 6738061Smsmith */ 6838061Smsmith#define VP0_PULSE 1 6938061Smsmith 7038061Smsmith#define VP0_SECTOR_SIZE 512 7138061Smsmith#define VP0_BUFFER_SIZE 0x12000 7238061Smsmith 7338061Smsmith#define n(flags) (~(flags) & (flags)) 7438061Smsmith 7538061Smsmith/* 7638061Smsmith * VP0 connections. 7738061Smsmith */ 7838061Smsmith#define H_AUTO n(AUTOFEED) 7938061Smsmith#define H_nAUTO AUTOFEED 8038061Smsmith#define H_STROBE n(STROBE) 8138061Smsmith#define H_nSTROBE STROBE 8238061Smsmith#define H_BSY n(nBUSY) 8338061Smsmith#define H_nBSY nBUSY 8438061Smsmith#define H_SEL SELECT 8538061Smsmith#define H_nSEL n(SELECT) 8639134Snsouch#define H_ERR PERROR 8739134Snsouch#define H_nERR n(PERROR) 8838061Smsmith#define H_ACK nACK 8938061Smsmith#define H_nACK n(nACK) 9038061Smsmith#define H_FLT nFAULT 9138061Smsmith#define H_nFLT n(nFAULT) 9238061Smsmith#define H_SELIN n(SELECTIN) 9338061Smsmith#define H_nSELIN SELECTIN 9438061Smsmith#define H_INIT nINIT 9538061Smsmith#define H_nINIT n(nINIT) 9638061Smsmith 9738061Smsmith/* 9838061Smsmith * Microcode to execute very fast I/O sequences at the lowest bus level. 9938061Smsmith */ 10038061Smsmith 10178645Snsouch#define WAIT_RET MS_PARAM(4, 2, MS_TYP_PTR) 10278645Snsouch#define WAIT_TMO MS_PARAM(0, 0, MS_TYP_INT) 10378645Snsouch 10478645Snsouch#define DECLARE_WAIT_MICROSEQUENCE \ 10578645Snsouchstruct ppb_microseq wait_microseq[] = { \ 10678645Snsouch MS_SET(MS_UNKNOWN), \ 10778645Snsouch /* loop */ \ 10878645Snsouch MS_BRSET(nBUSY, 2 /* ready */), \ 10978645Snsouch MS_DBRA(-2 /* loop */), \ 11078645Snsouch MS_RET(1), /* timed out */ \ 11178645Snsouch /* ready */ \ 11278645Snsouch MS_RFETCH(MS_REG_STR, 0xf0, MS_UNKNOWN), \ 11378645Snsouch MS_RET(0) /* no error */ \ 11478645Snsouch} 11578645Snsouch 11639134Snsouch/* call this macro to initialize connect/disconnect microsequences */ 11739134Snsouch#define INIT_TRIG_MICROSEQ { \ 11839134Snsouch int i; \ 11939134Snsouch for (i=1; i <= 7; i+=2) { \ 12045342Speter disconnect_microseq[i].arg[2] = (union ppb_insarg)d_pulse; \ 12139134Snsouch connect_epp_microseq[i].arg[2] = \ 12245342Speter connect_spp_microseq[i].arg[2] = (union ppb_insarg)c_pulse; \ 12339134Snsouch } \ 12439134Snsouch} 12539134Snsouch 12639134Snsouch#define trig_d_pulse MS_TRIG(MS_REG_CTR,5,MS_UNKNOWN /* d_pulse */) 12739134Snsouchstatic char d_pulse[] = { 12838061Smsmith H_AUTO | H_nSELIN | H_INIT | H_STROBE, 0, 12938061Smsmith H_nAUTO | H_nSELIN | H_INIT | H_STROBE, VP0_PULSE, 13038061Smsmith H_AUTO | H_nSELIN | H_INIT | H_STROBE, 0, 13138061Smsmith H_AUTO | H_SELIN | H_INIT | H_STROBE, VP0_PULSE, 13238061Smsmith H_AUTO | H_nSELIN | H_INIT | H_STROBE, VP0_PULSE 13338061Smsmith}; 13438061Smsmith 13539134Snsouch#define trig_c_pulse MS_TRIG(MS_REG_CTR,5,MS_UNKNOWN /* c_pulse */) 13639134Snsouchstatic char c_pulse[] = { 13738061Smsmith H_AUTO | H_nSELIN | H_INIT | H_STROBE, 0, 13838061Smsmith H_AUTO | H_SELIN | H_INIT | H_STROBE, 0, 13938061Smsmith H_nAUTO | H_SELIN | H_INIT | H_STROBE, VP0_PULSE, 14038061Smsmith H_AUTO | H_SELIN | H_INIT | H_STROBE, 0, 14138061Smsmith H_AUTO | H_nSELIN | H_INIT | H_STROBE, VP0_PULSE 14238061Smsmith}; 14338061Smsmith 14439134Snsouchstatic struct ppb_microseq disconnect_microseq[] = { 14538061Smsmith MS_DASS(0x0), trig_d_pulse, MS_DASS(0x3c), trig_d_pulse, 14638061Smsmith MS_DASS(0x20), trig_d_pulse, MS_DASS(0xf), trig_d_pulse, MS_RET(0) 14738061Smsmith}; 14838061Smsmith 14939134Snsouchstatic struct ppb_microseq connect_epp_microseq[] = { 15038061Smsmith MS_DASS(0x0), trig_c_pulse, MS_DASS(0x3c), trig_c_pulse, 15138061Smsmith MS_DASS(0x20), trig_c_pulse, MS_DASS(0xcf), trig_c_pulse, MS_RET(0) 15238061Smsmith}; 15338061Smsmith 15439134Snsouchstatic struct ppb_microseq connect_spp_microseq[] = { 15538061Smsmith MS_DASS(0x0), trig_c_pulse, MS_DASS(0x3c), trig_c_pulse, 15638061Smsmith MS_DASS(0x20), trig_c_pulse, MS_DASS(0x8f), trig_c_pulse, MS_RET(0) 15738061Smsmith}; 15838061Smsmith 15938061Smsmith/* 16038061Smsmith * nibble_inbyte_hook() 16138061Smsmith * 16238061Smsmith * Formats high and low nibble into a character 16338061Smsmith */ 16438061Smsmithstatic int 16538061Smsmithnibble_inbyte_hook (void *p, char *ptr) 16638061Smsmith{ 16738061Smsmith struct vpo_nibble *s = (struct vpo_nibble *)p; 16838061Smsmith 16938061Smsmith /* increment the buffer pointer */ 17038061Smsmith *ptr++ = ((s->l >> 4) & 0x0f) + (s->h & 0xf0); 17138061Smsmith 17238061Smsmith return (0); 17338061Smsmith} 17438061Smsmith 17578645Snsouch#define INB_NIBBLE_H MS_PARAM(2, 2, MS_TYP_PTR) 17678645Snsouch#define INB_NIBBLE_L MS_PARAM(4, 2, MS_TYP_PTR) 17778645Snsouch#define INB_NIBBLE_F MS_PARAM(5, 0, MS_TYP_FUN) 17878645Snsouch#define INB_NIBBLE_P MS_PARAM(5, 1, MS_TYP_PTR) 17938061Smsmith 18038061Smsmith/* 18138061Smsmith * This is the sub-microseqence for MS_GET in NIBBLE mode 18238061Smsmith * Retrieve the two nibbles and call the C function to generate the character 18338061Smsmith * and store it in the buffer (see nibble_inbyte_hook()) 18438061Smsmith */ 18538061Smsmith 18678645Snsouch#define DECLARE_NIBBLE_INBYTE_SUBMICROSEQ \ 18778645Snsouchstruct ppb_microseq nibble_inbyte_submicroseq[] = { \ 18878645Snsouch/* loop: */ \ 18978645Snsouch MS_CASS( H_AUTO | H_SELIN | H_INIT | H_STROBE), \ 19078645Snsouch MS_DELAY(VP0_PULSE), \ 19178645Snsouch MS_RFETCH(MS_REG_STR, MS_FETCH_ALL, MS_UNKNOWN /* high nibble */),\ 19278645Snsouch MS_CASS(H_nAUTO | H_SELIN | H_INIT | H_STROBE), \ 19378645Snsouch MS_RFETCH(MS_REG_STR, MS_FETCH_ALL, MS_UNKNOWN /* low nibble */),\ 19478645Snsouch /* do a C call to format the received nibbles */ \ 19578645Snsouch MS_C_CALL(MS_UNKNOWN /* C hook */, MS_UNKNOWN /* param */),\ 19678645Snsouch MS_DBRA(-7 /* loop */), \ 19778645Snsouch MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE), \ 19878645Snsouch MS_RET(0) \ 19978645Snsouch} 20038061Smsmith 20138061Smsmith/* 20238061Smsmith * This is the sub-microseqence for MS_GET in PS2 mode 20338061Smsmith */ 20439134Snsouchstatic struct ppb_microseq ps2_inbyte_submicroseq[] = { 20538061Smsmith MS_CASS(PCD | H_AUTO | H_SELIN | H_INIT | H_nSTROBE), 20638061Smsmith 20738061Smsmith/* loop: */ 20838061Smsmith MS_RFETCH_P(1, MS_REG_DTR, MS_FETCH_ALL), 20938061Smsmith MS_CASS(PCD | H_nAUTO | H_SELIN | H_INIT | H_nSTROBE), 21038061Smsmith MS_CASS(PCD | H_AUTO | H_SELIN | H_INIT | H_nSTROBE), 21143433Snsouch MS_DBRA(-4 /* loop */), 21238061Smsmith 21338061Smsmith MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE), 21438061Smsmith MS_RET(0) 21538061Smsmith}; 21638061Smsmith 21738061Smsmith/* 21838061Smsmith * This is the sub-microsequence for MS_PUT in both NIBBLE and PS2 modes 21938061Smsmith */ 22039134Snsouchstatic struct ppb_microseq spp_outbyte_submicroseq[] = { 22138061Smsmith 22238061Smsmith/* loop: */ 22338061Smsmith MS_RASSERT_P(1, MS_REG_DTR), 22438061Smsmith MS_CASS(H_nAUTO | H_nSELIN | H_INIT | H_STROBE), 22538061Smsmith MS_CASS( H_AUTO | H_nSELIN | H_INIT | H_STROBE), 22638061Smsmith MS_DELAY(VP0_PULSE), 22743433Snsouch MS_DBRA(-5 /* loop */), 22838061Smsmith 22938061Smsmith /* return from the put call */ 23038061Smsmith MS_RET(0) 23138061Smsmith}; 23238061Smsmith 23338061Smsmith/* EPP 1.7 microsequences, ptr and len set at runtime */ 23439134Snsouchstatic struct ppb_microseq epp17_outstr_body[] = { 23538061Smsmith MS_CASS(H_AUTO | H_SELIN | H_INIT | H_STROBE), 23638061Smsmith 23738061Smsmith/* loop: */ 23843433Snsouch MS_RASSERT_P(1, MS_REG_EPP_D), 23943433Snsouch MS_BRSET(TIMEOUT, 3 /* error */), /* EPP timeout? */ 24043433Snsouch MS_DBRA(-3 /* loop */), 24138061Smsmith 24238061Smsmith MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE), 24338061Smsmith MS_RET(0), 24438061Smsmith/* error: */ 24538061Smsmith MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE), 24638061Smsmith MS_RET(1) 24738061Smsmith}; 24838061Smsmith 24939134Snsouchstatic struct ppb_microseq epp17_instr_body[] = { 25038061Smsmith MS_CASS(PCD | H_AUTO | H_SELIN | H_INIT | H_STROBE), 25138061Smsmith 25238061Smsmith/* loop: */ 25343433Snsouch MS_RFETCH_P(1, MS_REG_EPP_D, MS_FETCH_ALL), 25443433Snsouch MS_BRSET(TIMEOUT, 3 /* error */), /* EPP timeout? */ 25543433Snsouch MS_DBRA(-3 /* loop */), 25638061Smsmith 25738061Smsmith MS_CASS(PCD | H_AUTO | H_nSELIN | H_INIT | H_STROBE), 25838061Smsmith MS_RET(0), 25938061Smsmith/* error: */ 26038061Smsmith MS_CASS(PCD | H_AUTO | H_nSELIN | H_INIT | H_STROBE), 26138061Smsmith MS_RET(1) 26238061Smsmith}; 26338061Smsmith 26439134Snsouchstatic struct ppb_microseq in_disk_mode[] = { 26539134Snsouch MS_CASS( H_AUTO | H_nSELIN | H_INIT | H_STROBE), 26639134Snsouch MS_CASS(H_nAUTO | H_nSELIN | H_INIT | H_STROBE), 26739134Snsouch 26843433Snsouch MS_BRCLEAR(H_FLT, 3 /* error */), 26939134Snsouch MS_CASS( H_AUTO | H_nSELIN | H_INIT | H_STROBE), 27043433Snsouch MS_BRSET(H_FLT, 1 /* error */), 27139134Snsouch 27239520Snsouch MS_RET(1), 27339134Snsouch/* error: */ 27439520Snsouch MS_RET(0) 27539134Snsouch}; 27639134Snsouch 27738061Smsmithstatic int 27838061Smsmithvpoio_disconnect(struct vpoio_data *vpo) 27938061Smsmith{ 28055939Snsouch device_t ppbus = device_get_parent(vpo->vpo_dev); 28138061Smsmith int ret; 28238061Smsmith 28355939Snsouch ppb_MS_microseq(ppbus, vpo->vpo_dev, disconnect_microseq, &ret); 28455939Snsouch return (ppb_release_bus(ppbus, vpo->vpo_dev)); 28538061Smsmith} 28638061Smsmith 28738061Smsmith/* 28838061Smsmith * how : PPB_WAIT or PPB_DONTWAIT 28938061Smsmith */ 29038061Smsmithstatic int 29138061Smsmithvpoio_connect(struct vpoio_data *vpo, int how) 29238061Smsmith{ 29355939Snsouch device_t ppbus = device_get_parent(vpo->vpo_dev); 29438061Smsmith int error; 29538061Smsmith int ret; 29638061Smsmith 29755939Snsouch if ((error = ppb_request_bus(ppbus, vpo->vpo_dev, how))) { 29842475Snsouch 29942475Snsouch#ifdef VP0_DEBUG 30042475Snsouch printf("%s: can't request bus!\n", __FUNCTION__); 30142475Snsouch#endif 30238061Smsmith return error; 30342475Snsouch } 30438061Smsmith 30555939Snsouch if (PPB_IN_EPP_MODE(ppbus)) 30655939Snsouch ppb_MS_microseq(ppbus, vpo->vpo_dev, connect_epp_microseq, &ret); 30738061Smsmith else 30855939Snsouch ppb_MS_microseq(ppbus, vpo->vpo_dev, connect_spp_microseq, &ret); 30938061Smsmith 31038061Smsmith return (0); 31138061Smsmith} 31238061Smsmith 31338061Smsmith/* 31439134Snsouch * vpoio_reset() 31538061Smsmith * 31639134Snsouch * SCSI reset signal, the drive must be in disk mode 31738061Smsmith */ 31839134Snsouchstatic void 31939134Snsouchvpoio_reset (struct vpoio_data *vpo) 32038061Smsmith{ 32155939Snsouch device_t ppbus = device_get_parent(vpo->vpo_dev); 32239134Snsouch int ret; 32338061Smsmith 32439134Snsouch struct ppb_microseq reset_microseq[] = { 32538061Smsmith 32639134Snsouch #define INITIATOR MS_PARAM(0, 1, MS_TYP_INT) 32738061Smsmith 32839134Snsouch MS_DASS(MS_UNKNOWN), 32939134Snsouch MS_CASS(H_AUTO | H_nSELIN | H_nINIT | H_STROBE), 33039134Snsouch MS_DELAY(25), 33139134Snsouch MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE), 33239134Snsouch MS_RET(0) 33339134Snsouch }; 33438061Smsmith 33539134Snsouch ppb_MS_init_msq(reset_microseq, 1, INITIATOR, 1 << VP0_INITIATOR); 33655939Snsouch ppb_MS_microseq(ppbus, vpo->vpo_dev, reset_microseq, &ret); 33739134Snsouch 33839134Snsouch return; 33938061Smsmith} 34038061Smsmith 34138061Smsmith/* 34239134Snsouch * vpoio_in_disk_mode() 34338061Smsmith */ 34439134Snsouchstatic int 34539134Snsouchvpoio_in_disk_mode(struct vpoio_data *vpo) 34638061Smsmith{ 34755939Snsouch device_t ppbus = device_get_parent(vpo->vpo_dev); 34839134Snsouch int ret; 34938061Smsmith 35055939Snsouch ppb_MS_microseq(ppbus, vpo->vpo_dev, in_disk_mode, &ret); 35138061Smsmith 35239134Snsouch return (ret); 35338061Smsmith} 35438061Smsmith 35538061Smsmith/* 35638061Smsmith * vpoio_detect() 35738061Smsmith * 35838061Smsmith * Detect and initialise the VP0 adapter. 35938061Smsmith */ 36039134Snsouchstatic int 36138061Smsmithvpoio_detect(struct vpoio_data *vpo) 36238061Smsmith{ 36355939Snsouch device_t ppbus = device_get_parent(vpo->vpo_dev); 36439520Snsouch int error, ret; 36538061Smsmith 36639520Snsouch /* allocate the bus, then apply microsequences */ 36755939Snsouch if ((error = ppb_request_bus(ppbus, vpo->vpo_dev, PPB_DONTWAIT))) 36839520Snsouch return (error); 36939520Snsouch 37078645Snsouch /* Force disconnection */ 37155939Snsouch ppb_MS_microseq(ppbus, vpo->vpo_dev, disconnect_microseq, &ret); 37239520Snsouch 37370608Snsouch /* Try to enter EPP mode, then connect to the drive in EPP mode */ 37470608Snsouch if (ppb_set_mode(ppbus, PPB_EPP) != -1) { 37570608Snsouch /* call manually the microseq instead of using the appropriate function 37670608Snsouch * since we already requested the ppbus */ 37755939Snsouch ppb_MS_microseq(ppbus, vpo->vpo_dev, connect_epp_microseq, &ret); 37870608Snsouch } 37939520Snsouch 38070608Snsouch /* If EPP mode switch failed or ZIP connection in EPP mode failed, 38170608Snsouch * try to connect in NIBBLE mode */ 38270608Snsouch if (!vpoio_in_disk_mode(vpo)) { 38339520Snsouch 38470608Snsouch /* The interface must be at least PS/2 or NIBBLE capable. 38570608Snsouch * There is no way to know if the ZIP will work with 38670608Snsouch * PS/2 mode since PS/2 and SPP both use the same connect 38770608Snsouch * sequence. One must supress PS/2 with boot flags if 38870608Snsouch * PS/2 mode fails (see ppc(4)). 38939520Snsouch */ 39070608Snsouch if (ppb_set_mode(ppbus, PPB_PS2) != -1) { 39170608Snsouch vpo->vpo_mode_found = VP0_MODE_PS2; 39270608Snsouch } else { 39370608Snsouch if (ppb_set_mode(ppbus, PPB_NIBBLE) == -1) 39470608Snsouch goto error; 39570608Snsouch 39670608Snsouch vpo->vpo_mode_found = VP0_MODE_NIBBLE; 39770608Snsouch } 39870608Snsouch 39970608Snsouch /* Can't know if the interface is capable of PS/2 yet */ 40055939Snsouch ppb_MS_microseq(ppbus, vpo->vpo_dev, connect_spp_microseq, &ret); 40170608Snsouch if (!vpoio_in_disk_mode(vpo)) { 40270608Snsouch vpo->vpo_mode_found = VP0_MODE_UNDEFINED; 40339520Snsouch if (bootverbose) 40439520Snsouch printf("vpo%d: can't connect to the drive\n", 40539520Snsouch vpo->vpo_unit); 40639520Snsouch 40739520Snsouch /* disconnect and release the bus */ 40855939Snsouch ppb_MS_microseq(ppbus, vpo->vpo_dev, disconnect_microseq, 40939520Snsouch &ret); 41039520Snsouch goto error; 41139520Snsouch } 41270608Snsouch } else { 41370608Snsouch vpo->vpo_mode_found = VP0_MODE_EPP; 41438061Smsmith } 41538061Smsmith 41638061Smsmith /* send SCSI reset signal */ 41738061Smsmith vpoio_reset(vpo); 41838061Smsmith 41955939Snsouch ppb_MS_microseq(ppbus, vpo->vpo_dev, disconnect_microseq, &ret); 42038061Smsmith 42139134Snsouch /* ensure we are disconnected or daisy chained peripheral 42239134Snsouch * may cause serious problem to the disk */ 42370608Snsouch if (vpoio_in_disk_mode(vpo)) { 42439520Snsouch if (bootverbose) 42539520Snsouch printf("vpo%d: can't disconnect from the drive\n", 42639520Snsouch vpo->vpo_unit); 42739520Snsouch goto error; 42839520Snsouch } 42938061Smsmith 43055939Snsouch ppb_release_bus(ppbus, vpo->vpo_dev); 43138061Smsmith return (0); 43239520Snsouch 43339520Snsoucherror: 43455939Snsouch ppb_release_bus(ppbus, vpo->vpo_dev); 43539520Snsouch return (VP0_EINITFAILED); 43638061Smsmith} 43738061Smsmith 43838061Smsmith/* 43938061Smsmith * vpoio_outstr() 44038061Smsmith */ 44138061Smsmithstatic int 44238061Smsmithvpoio_outstr(struct vpoio_data *vpo, char *buffer, int size) 44338061Smsmith{ 44455939Snsouch device_t ppbus = device_get_parent(vpo->vpo_dev); 44538061Smsmith int error = 0; 44638061Smsmith 44755939Snsouch ppb_MS_exec(ppbus, vpo->vpo_dev, MS_OP_PUT, (union ppb_insarg)buffer, 44845342Speter (union ppb_insarg)size, (union ppb_insarg)MS_UNKNOWN, &error); 44938061Smsmith 45055939Snsouch ppb_ecp_sync(ppbus); 45138061Smsmith 45238061Smsmith return (error); 45338061Smsmith} 45438061Smsmith 45538061Smsmith/* 45638061Smsmith * vpoio_instr() 45738061Smsmith */ 45838061Smsmithstatic int 45938061Smsmithvpoio_instr(struct vpoio_data *vpo, char *buffer, int size) 46038061Smsmith{ 46155939Snsouch device_t ppbus = device_get_parent(vpo->vpo_dev); 46238061Smsmith int error = 0; 46338061Smsmith 46455939Snsouch ppb_MS_exec(ppbus, vpo->vpo_dev, MS_OP_GET, (union ppb_insarg)buffer, 46545342Speter (union ppb_insarg)size, (union ppb_insarg)MS_UNKNOWN, &error); 46638061Smsmith 46755939Snsouch ppb_ecp_sync(ppbus); 46838061Smsmith 46938061Smsmith return (error); 47038061Smsmith} 47138061Smsmith 47238061Smsmithstatic char 47338061Smsmithvpoio_select(struct vpoio_data *vpo, int initiator, int target) 47438061Smsmith{ 47555939Snsouch device_t ppbus = device_get_parent(vpo->vpo_dev); 47638061Smsmith int ret; 47738061Smsmith 47838061Smsmith struct ppb_microseq select_microseq[] = { 47938061Smsmith 48038061Smsmith /* parameter list 48138061Smsmith */ 48238061Smsmith #define SELECT_TARGET MS_PARAM(0, 1, MS_TYP_INT) 48338061Smsmith #define SELECT_INITIATOR MS_PARAM(3, 1, MS_TYP_INT) 48438061Smsmith 48538061Smsmith /* send the select command to the drive */ 48638061Smsmith MS_DASS(MS_UNKNOWN), 48738061Smsmith MS_CASS(H_nAUTO | H_nSELIN | H_INIT | H_STROBE), 48838061Smsmith MS_CASS( H_AUTO | H_nSELIN | H_INIT | H_STROBE), 48938061Smsmith MS_DASS(MS_UNKNOWN), 49038061Smsmith MS_CASS( H_AUTO | H_nSELIN | H_nINIT | H_STROBE), 49138061Smsmith 49238061Smsmith /* now, wait until the drive is ready */ 49338061Smsmith MS_SET(VP0_SELTMO), 49443433Snsouch/* loop: */ MS_BRSET(H_ACK, 2 /* ready */), 49543433Snsouch MS_DBRA(-2 /* loop */), 49638061Smsmith/* error: */ MS_RET(1), 49738061Smsmith/* ready: */ MS_RET(0) 49838061Smsmith }; 49938061Smsmith 50038061Smsmith /* initialize the select microsequence */ 50138061Smsmith ppb_MS_init_msq(select_microseq, 2, 50238061Smsmith SELECT_TARGET, 1 << target, 50338061Smsmith SELECT_INITIATOR, 1 << initiator); 50438061Smsmith 50555939Snsouch ppb_MS_microseq(ppbus, vpo->vpo_dev, select_microseq, &ret); 50638061Smsmith 50738061Smsmith if (ret) 50838061Smsmith return (VP0_ESELECT_TIMEOUT); 50938061Smsmith 51038061Smsmith return (0); 51138061Smsmith} 51238061Smsmith 51338061Smsmith/* 51438061Smsmith * vpoio_wait() 51538061Smsmith * 51638061Smsmith * H_SELIN must be low. 51738061Smsmith * 51838061Smsmith * XXX should be ported to microseq 51938061Smsmith */ 52038061Smsmithstatic char 52138061Smsmithvpoio_wait(struct vpoio_data *vpo, int tmo) 52238061Smsmith{ 52378645Snsouch DECLARE_WAIT_MICROSEQUENCE; 52478645Snsouch 52555939Snsouch device_t ppbus = device_get_parent(vpo->vpo_dev); 52678645Snsouch int ret, err; 52738061Smsmith 52838061Smsmith#if 0 /* broken */ 52955939Snsouch if (ppb_poll_device(ppbus, 150, nBUSY, nBUSY, PPB_INTR)) 53038061Smsmith return (0); 53138061Smsmith 53255939Snsouch return (ppb_rstr(ppbus) & 0xf0); 53338061Smsmith#endif 53438061Smsmith 53538061Smsmith /* 53638061Smsmith * Return some status information. 53738061Smsmith * Semantics : 0xc0 = ZIP wants more data 53838061Smsmith * 0xd0 = ZIP wants to send more data 53938061Smsmith * 0xe0 = ZIP wants command 54038061Smsmith * 0xf0 = end of transfer, ZIP is sending status 54138061Smsmith */ 54238061Smsmith 54378645Snsouch ppb_MS_init_msq(wait_microseq, 2, 54478645Snsouch WAIT_RET, (void *)&ret, 54578645Snsouch WAIT_TMO, tmo); 54678645Snsouch 54778645Snsouch ppb_MS_microseq(ppbus, vpo->vpo_dev, wait_microseq, &err); 54878645Snsouch 54978645Snsouch if (err) 55078645Snsouch return (0); /* command timed out */ 55178645Snsouch 55278645Snsouch return(ret); 55338061Smsmith} 55438061Smsmith 55538061Smsmith/* 55638061Smsmith * vpoio_probe() 55738061Smsmith * 55838061Smsmith * Low level probe of vpo device 55938061Smsmith * 56038061Smsmith */ 56155939Snsouchint 56255939Snsouchvpoio_probe(device_t dev, struct vpoio_data *vpo) 56338061Smsmith{ 56455939Snsouch int error; 56538061Smsmith 56638061Smsmith /* ppbus dependent initialisation */ 56755939Snsouch vpo->vpo_dev = dev; 56838061Smsmith 56939134Snsouch /* 57039134Snsouch * Initialize microsequence code 57139134Snsouch */ 57239134Snsouch INIT_TRIG_MICROSEQ; 57339134Snsouch 57438061Smsmith /* now, try to initialise the drive */ 57555939Snsouch if ((error = vpoio_detect(vpo))) { 57655939Snsouch return (error); 57738061Smsmith } 57838061Smsmith 57955939Snsouch return (0); 58038061Smsmith} 58138061Smsmith 58238061Smsmith/* 58338061Smsmith * vpoio_attach() 58438061Smsmith * 58538061Smsmith * Low level attachment of vpo device 58638061Smsmith * 58738061Smsmith */ 58838061Smsmithint 58938061Smsmithvpoio_attach(struct vpoio_data *vpo) 59038061Smsmith{ 59178645Snsouch DECLARE_NIBBLE_INBYTE_SUBMICROSEQ; 59255939Snsouch device_t ppbus = device_get_parent(vpo->vpo_dev); 59370608Snsouch int error = 0; 59438061Smsmith 59538061Smsmith vpo->vpo_nibble_inbyte_msq = (struct ppb_microseq *)malloc( 59638061Smsmith sizeof(nibble_inbyte_submicroseq), M_DEVBUF, M_NOWAIT); 59738061Smsmith 59838061Smsmith if (!vpo->vpo_nibble_inbyte_msq) 59955939Snsouch return (ENXIO); 60038061Smsmith 60138061Smsmith bcopy((void *)nibble_inbyte_submicroseq, 60238061Smsmith (void *)vpo->vpo_nibble_inbyte_msq, 60338061Smsmith sizeof(nibble_inbyte_submicroseq)); 60438061Smsmith 60578645Snsouch ppb_MS_init_msq(vpo->vpo_nibble_inbyte_msq, 4, 60678645Snsouch INB_NIBBLE_H, (void *)&(vpo)->vpo_nibble.h, 60778645Snsouch INB_NIBBLE_L, (void *)&(vpo)->vpo_nibble.l, 60878645Snsouch INB_NIBBLE_F, nibble_inbyte_hook, 60978645Snsouch INB_NIBBLE_P, (void *)&(vpo)->vpo_nibble); 61038061Smsmith 61138061Smsmith /* 61238061Smsmith * Initialize mode dependent in/out microsequences 61338061Smsmith */ 61470608Snsouch if ((error = ppb_request_bus(ppbus, vpo->vpo_dev, PPB_WAIT))) 61570608Snsouch goto error; 61638061Smsmith 61770608Snsouch /* ppbus sets automatically the last mode entered during detection */ 61870608Snsouch switch (vpo->vpo_mode_found) { 61970608Snsouch case VP0_MODE_EPP: 62070608Snsouch ppb_MS_GET_init(ppbus, vpo->vpo_dev, epp17_instr_body); 62170608Snsouch ppb_MS_PUT_init(ppbus, vpo->vpo_dev, epp17_outstr_body); 62270608Snsouch printf("vpo%d: EPP mode\n", vpo->vpo_unit); 62370608Snsouch break; 62470608Snsouch case VP0_MODE_PS2: 62555939Snsouch ppb_MS_GET_init(ppbus, vpo->vpo_dev, ps2_inbyte_submicroseq); 62655939Snsouch ppb_MS_PUT_init(ppbus, vpo->vpo_dev, spp_outbyte_submicroseq); 62738061Smsmith printf("vpo%d: PS2 mode\n", vpo->vpo_unit); 62870608Snsouch break; 62970608Snsouch case VP0_MODE_NIBBLE: 63070608Snsouch ppb_MS_GET_init(ppbus, vpo->vpo_dev, vpo->vpo_nibble_inbyte_msq); 63170608Snsouch ppb_MS_PUT_init(ppbus, vpo->vpo_dev, spp_outbyte_submicroseq); 63238061Smsmith printf("vpo%d: NIBBLE mode\n", vpo->vpo_unit); 63370608Snsouch break; 63470608Snsouch default: 63570608Snsouch panic("vpo: unknown mode %d", vpo->vpo_mode_found); 63638061Smsmith } 63738061Smsmith 63855939Snsouch ppb_release_bus(ppbus, vpo->vpo_dev); 63938061Smsmith 64070608Snsoucherror: 64170608Snsouch return (error); 64238061Smsmith} 64338061Smsmith 64438061Smsmith/* 64538061Smsmith * vpoio_reset_bus() 64638061Smsmith * 64738061Smsmith */ 64838061Smsmithint 64938061Smsmithvpoio_reset_bus(struct vpoio_data *vpo) 65038061Smsmith{ 65138061Smsmith /* first, connect to the drive */ 65239520Snsouch if (vpoio_connect(vpo, PPB_WAIT|PPB_INTR) || !vpoio_in_disk_mode(vpo)) { 65342475Snsouch 65442475Snsouch#ifdef VP0_DEBUG 65542475Snsouch printf("%s: not in disk mode!\n", __FUNCTION__); 65642475Snsouch#endif 65738061Smsmith /* release ppbus */ 65838061Smsmith vpoio_disconnect(vpo); 65938061Smsmith return (1); 66038061Smsmith } 66138061Smsmith 66238061Smsmith /* reset the SCSI bus */ 66338061Smsmith vpoio_reset(vpo); 66438061Smsmith 66538061Smsmith /* then disconnect */ 66638061Smsmith vpoio_disconnect(vpo); 66738061Smsmith 66838061Smsmith return (0); 66938061Smsmith} 67038061Smsmith 67138061Smsmith/* 67238061Smsmith * vpoio_do_scsi() 67338061Smsmith * 67438061Smsmith * Send an SCSI command 67538061Smsmith * 67638061Smsmith */ 67738061Smsmithint 67838061Smsmithvpoio_do_scsi(struct vpoio_data *vpo, int host, int target, char *command, 67938061Smsmith int clen, char *buffer, int blen, int *result, int *count, 68038061Smsmith int *ret) 68138061Smsmith{ 68255939Snsouch device_t ppbus = device_get_parent(vpo->vpo_dev); 68338061Smsmith register char r; 68438061Smsmith char l, h = 0; 68538061Smsmith int len, error = 0; 68638061Smsmith register int k; 68738061Smsmith 68838061Smsmith /* 68938061Smsmith * enter disk state, allocate the ppbus 69038061Smsmith * 69138061Smsmith * XXX 69238061Smsmith * Should we allow this call to be interruptible? 69338061Smsmith * The only way to report the interruption is to return 69438061Smsmith * EIO do upper SCSI code :^( 69538061Smsmith */ 69638061Smsmith if ((error = vpoio_connect(vpo, PPB_WAIT|PPB_INTR))) 69738061Smsmith return (error); 69838061Smsmith 69939520Snsouch if (!vpoio_in_disk_mode(vpo)) { 70038061Smsmith *ret = VP0_ECONNECT; goto error; 70138061Smsmith } 70238061Smsmith 70338061Smsmith if ((*ret = vpoio_select(vpo,host,target))) 70438061Smsmith goto error; 70538061Smsmith 70638061Smsmith /* 70738061Smsmith * Send the command ... 70838061Smsmith * 70938061Smsmith * set H_SELIN low for vpoio_wait(). 71038061Smsmith */ 71155939Snsouch ppb_wctr(ppbus, H_AUTO | H_nSELIN | H_INIT | H_STROBE); 71238061Smsmith 71338061Smsmith for (k = 0; k < clen; k++) { 71438061Smsmith if (vpoio_wait(vpo, VP0_FAST_SPINTMO) != (char)0xe0) { 71538061Smsmith *ret = VP0_ECMD_TIMEOUT; 71638061Smsmith goto error; 71738061Smsmith } 71838061Smsmith if (vpoio_outstr(vpo, &command[k], 1)) { 71938061Smsmith *ret = VP0_EPPDATA_TIMEOUT; 72038061Smsmith goto error; 72138061Smsmith } 72238061Smsmith } 72338061Smsmith 72438061Smsmith /* 72538061Smsmith * Completion ... 72638061Smsmith */ 72738061Smsmith 72838061Smsmith *count = 0; 72938061Smsmith for (;;) { 73038061Smsmith 73138061Smsmith if (!(r = vpoio_wait(vpo, VP0_LOW_SPINTMO))) { 73238061Smsmith *ret = VP0_ESTATUS_TIMEOUT; goto error; 73338061Smsmith } 73438061Smsmith 73538061Smsmith /* stop when the ZIP wants to send status */ 73638061Smsmith if (r == (char)0xf0) 73738061Smsmith break; 73838061Smsmith 73938061Smsmith if (*count >= blen) { 74038061Smsmith *ret = VP0_EDATA_OVERFLOW; 74138061Smsmith goto error; 74238061Smsmith } 74338061Smsmith 74439520Snsouch /* if in EPP mode or writing bytes, try to transfer a sector 74539520Snsouch * otherwise, just send one byte 74639520Snsouch */ 74755939Snsouch if (PPB_IN_EPP_MODE(ppbus) || r == (char)0xc0) 74839520Snsouch len = (((blen - *count) >= VP0_SECTOR_SIZE)) ? 74939520Snsouch VP0_SECTOR_SIZE : 1; 75039520Snsouch else 75139520Snsouch len = 1; 75239520Snsouch 75338061Smsmith /* ZIP wants to send data? */ 75438061Smsmith if (r == (char)0xc0) 75538061Smsmith error = vpoio_outstr(vpo, &buffer[*count], len); 75638061Smsmith else 75738061Smsmith error = vpoio_instr(vpo, &buffer[*count], len); 75838061Smsmith 75938061Smsmith if (error) { 76038061Smsmith *ret = error; 76138061Smsmith goto error; 76238061Smsmith } 76338061Smsmith 76438061Smsmith *count += len; 76538061Smsmith } 76638061Smsmith 76738061Smsmith if (vpoio_instr(vpo, &l, 1)) { 76838061Smsmith *ret = VP0_EOTHER; goto error; 76938061Smsmith } 77038061Smsmith 77138061Smsmith /* check if the ZIP wants to send more status */ 77238061Smsmith if (vpoio_wait(vpo, VP0_FAST_SPINTMO) == (char)0xf0) 77338061Smsmith if (vpoio_instr(vpo, &h, 1)) { 77438061Smsmith *ret = VP0_EOTHER+2; goto error; 77538061Smsmith } 77638061Smsmith 77738061Smsmith *result = ((int) h << 8) | ((int) l & 0xff); 77838061Smsmith 77938061Smsmitherror: 78038061Smsmith /* return to printer state, release the ppbus */ 78138061Smsmith vpoio_disconnect(vpo); 78238061Smsmith return (0); 78338061Smsmith} 784