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 * 2738061Smsmith * 2838061Smsmith */ 2938061Smsmith 30119418Sobrien#include <sys/cdefs.h> 31119418Sobrien__FBSDID("$FreeBSD$"); 32119418Sobrien 3355205Speter#ifdef _KERNEL 3438061Smsmith#include <sys/param.h> 3538061Smsmith#include <sys/systm.h> 3655939Snsouch#include <sys/module.h> 3755939Snsouch#include <sys/bus.h> 3838061Smsmith#include <sys/malloc.h> 3938061Smsmith 4038061Smsmith 4155205Speter#endif 4238061Smsmith 4342475Snsouch#include "opt_vpo.h" 4442475Snsouch 4555939Snsouch#include <dev/ppbus/ppbio.h> 4638061Smsmith#include <dev/ppbus/ppbconf.h> 4738061Smsmith#include <dev/ppbus/ppb_msq.h> 4838061Smsmith#include <dev/ppbus/vpoio.h> 4938061Smsmith 5055939Snsouch#include "ppbus_if.h" 5155939Snsouch 5238061Smsmith/* 5338061Smsmith * The driver pools the drive. We may add a timeout queue to avoid 5438061Smsmith * active polling on nACK. I've tried this but it leads to unreliable 5538061Smsmith * transfers 5638061Smsmith */ 5738061Smsmith#define VP0_SELTMO 5000 /* select timeout */ 5838061Smsmith#define VP0_FAST_SPINTMO 500000 /* wait status timeout */ 5938061Smsmith#define VP0_LOW_SPINTMO 5000000 /* wait status timeout */ 6038061Smsmith 6138061Smsmith/* 6238061Smsmith * Actually, VP0 timings are more accurate (about few 16MHZ cycles), 6338061Smsmith * but succeeding in respecting such timings leads to architecture 6438061Smsmith * dependent considerations. 6538061Smsmith */ 6638061Smsmith#define VP0_PULSE 1 6738061Smsmith 6838061Smsmith#define VP0_SECTOR_SIZE 512 6938061Smsmith#define VP0_BUFFER_SIZE 0x12000 7038061Smsmith 7138061Smsmith#define n(flags) (~(flags) & (flags)) 7238061Smsmith 7338061Smsmith/* 7438061Smsmith * VP0 connections. 7538061Smsmith */ 7638061Smsmith#define H_AUTO n(AUTOFEED) 7738061Smsmith#define H_nAUTO AUTOFEED 7838061Smsmith#define H_STROBE n(STROBE) 7938061Smsmith#define H_nSTROBE STROBE 8038061Smsmith#define H_BSY n(nBUSY) 8138061Smsmith#define H_nBSY nBUSY 8238061Smsmith#define H_SEL SELECT 8338061Smsmith#define H_nSEL n(SELECT) 8439134Snsouch#define H_ERR PERROR 8539134Snsouch#define H_nERR n(PERROR) 8638061Smsmith#define H_ACK nACK 8738061Smsmith#define H_nACK n(nACK) 8838061Smsmith#define H_FLT nFAULT 8938061Smsmith#define H_nFLT n(nFAULT) 9038061Smsmith#define H_SELIN n(SELECTIN) 9138061Smsmith#define H_nSELIN SELECTIN 9238061Smsmith#define H_INIT nINIT 9338061Smsmith#define H_nINIT n(nINIT) 9438061Smsmith 9538061Smsmith/* 9638061Smsmith * Microcode to execute very fast I/O sequences at the lowest bus level. 9738061Smsmith */ 9838061Smsmith 9978645Snsouch#define WAIT_RET MS_PARAM(4, 2, MS_TYP_PTR) 10078645Snsouch#define WAIT_TMO MS_PARAM(0, 0, MS_TYP_INT) 10178645Snsouch 10278645Snsouch#define DECLARE_WAIT_MICROSEQUENCE \ 10378645Snsouchstruct ppb_microseq wait_microseq[] = { \ 10478645Snsouch MS_SET(MS_UNKNOWN), \ 10578645Snsouch /* loop */ \ 10678645Snsouch MS_BRSET(nBUSY, 2 /* ready */), \ 10778645Snsouch MS_DBRA(-2 /* loop */), \ 10878645Snsouch MS_RET(1), /* timed out */ \ 10978645Snsouch /* ready */ \ 11078645Snsouch MS_RFETCH(MS_REG_STR, 0xf0, MS_UNKNOWN), \ 11178645Snsouch MS_RET(0) /* no error */ \ 11278645Snsouch} 11378645Snsouch 11439134Snsouch/* call this macro to initialize connect/disconnect microsequences */ 11539134Snsouch#define INIT_TRIG_MICROSEQ { \ 11639134Snsouch int i; \ 11739134Snsouch for (i=1; i <= 7; i+=2) { \ 11845342Speter disconnect_microseq[i].arg[2] = (union ppb_insarg)d_pulse; \ 11939134Snsouch connect_epp_microseq[i].arg[2] = \ 12045342Speter connect_spp_microseq[i].arg[2] = (union ppb_insarg)c_pulse; \ 12139134Snsouch } \ 12239134Snsouch} 12339134Snsouch 12439134Snsouch#define trig_d_pulse MS_TRIG(MS_REG_CTR,5,MS_UNKNOWN /* d_pulse */) 12539134Snsouchstatic char d_pulse[] = { 12638061Smsmith H_AUTO | H_nSELIN | H_INIT | H_STROBE, 0, 12738061Smsmith H_nAUTO | H_nSELIN | H_INIT | H_STROBE, VP0_PULSE, 12838061Smsmith H_AUTO | H_nSELIN | H_INIT | H_STROBE, 0, 12938061Smsmith H_AUTO | H_SELIN | H_INIT | H_STROBE, VP0_PULSE, 13038061Smsmith H_AUTO | H_nSELIN | H_INIT | H_STROBE, VP0_PULSE 13138061Smsmith}; 13238061Smsmith 13339134Snsouch#define trig_c_pulse MS_TRIG(MS_REG_CTR,5,MS_UNKNOWN /* c_pulse */) 13439134Snsouchstatic char c_pulse[] = { 13538061Smsmith H_AUTO | H_nSELIN | H_INIT | H_STROBE, 0, 13638061Smsmith H_AUTO | H_SELIN | H_INIT | H_STROBE, 0, 13738061Smsmith H_nAUTO | H_SELIN | H_INIT | H_STROBE, VP0_PULSE, 13838061Smsmith H_AUTO | H_SELIN | H_INIT | H_STROBE, 0, 13938061Smsmith H_AUTO | H_nSELIN | H_INIT | H_STROBE, VP0_PULSE 14038061Smsmith}; 14138061Smsmith 14239134Snsouchstatic struct ppb_microseq disconnect_microseq[] = { 14338061Smsmith MS_DASS(0x0), trig_d_pulse, MS_DASS(0x3c), trig_d_pulse, 14438061Smsmith MS_DASS(0x20), trig_d_pulse, MS_DASS(0xf), trig_d_pulse, MS_RET(0) 14538061Smsmith}; 14638061Smsmith 14739134Snsouchstatic struct ppb_microseq connect_epp_microseq[] = { 14838061Smsmith MS_DASS(0x0), trig_c_pulse, MS_DASS(0x3c), trig_c_pulse, 14938061Smsmith MS_DASS(0x20), trig_c_pulse, MS_DASS(0xcf), trig_c_pulse, MS_RET(0) 15038061Smsmith}; 15138061Smsmith 15239134Snsouchstatic struct ppb_microseq connect_spp_microseq[] = { 15338061Smsmith MS_DASS(0x0), trig_c_pulse, MS_DASS(0x3c), trig_c_pulse, 15438061Smsmith MS_DASS(0x20), trig_c_pulse, MS_DASS(0x8f), trig_c_pulse, MS_RET(0) 15538061Smsmith}; 15638061Smsmith 15738061Smsmith/* 15838061Smsmith * nibble_inbyte_hook() 15938061Smsmith * 16038061Smsmith * Formats high and low nibble into a character 16138061Smsmith */ 16238061Smsmithstatic int 16338061Smsmithnibble_inbyte_hook (void *p, char *ptr) 16438061Smsmith{ 16538061Smsmith struct vpo_nibble *s = (struct vpo_nibble *)p; 16638061Smsmith 16738061Smsmith /* increment the buffer pointer */ 16838061Smsmith *ptr++ = ((s->l >> 4) & 0x0f) + (s->h & 0xf0); 16938061Smsmith 17038061Smsmith return (0); 17138061Smsmith} 17238061Smsmith 17378645Snsouch#define INB_NIBBLE_H MS_PARAM(2, 2, MS_TYP_PTR) 17478645Snsouch#define INB_NIBBLE_L MS_PARAM(4, 2, MS_TYP_PTR) 17578645Snsouch#define INB_NIBBLE_F MS_PARAM(5, 0, MS_TYP_FUN) 17678645Snsouch#define INB_NIBBLE_P MS_PARAM(5, 1, MS_TYP_PTR) 17738061Smsmith 17838061Smsmith/* 17938061Smsmith * This is the sub-microseqence for MS_GET in NIBBLE mode 18038061Smsmith * Retrieve the two nibbles and call the C function to generate the character 18138061Smsmith * and store it in the buffer (see nibble_inbyte_hook()) 18238061Smsmith */ 18338061Smsmith 18478645Snsouch#define DECLARE_NIBBLE_INBYTE_SUBMICROSEQ \ 18578645Snsouchstruct ppb_microseq nibble_inbyte_submicroseq[] = { \ 18678645Snsouch/* loop: */ \ 18778645Snsouch MS_CASS( H_AUTO | H_SELIN | H_INIT | H_STROBE), \ 18878645Snsouch MS_DELAY(VP0_PULSE), \ 18978645Snsouch MS_RFETCH(MS_REG_STR, MS_FETCH_ALL, MS_UNKNOWN /* high nibble */),\ 19078645Snsouch MS_CASS(H_nAUTO | H_SELIN | H_INIT | H_STROBE), \ 19178645Snsouch MS_RFETCH(MS_REG_STR, MS_FETCH_ALL, MS_UNKNOWN /* low nibble */),\ 19278645Snsouch /* do a C call to format the received nibbles */ \ 19378645Snsouch MS_C_CALL(MS_UNKNOWN /* C hook */, MS_UNKNOWN /* param */),\ 19478645Snsouch MS_DBRA(-7 /* loop */), \ 19578645Snsouch MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE), \ 19678645Snsouch MS_RET(0) \ 19778645Snsouch} 19838061Smsmith 19938061Smsmith/* 20038061Smsmith * This is the sub-microseqence for MS_GET in PS2 mode 20138061Smsmith */ 20239134Snsouchstatic struct ppb_microseq ps2_inbyte_submicroseq[] = { 20338061Smsmith MS_CASS(PCD | H_AUTO | H_SELIN | H_INIT | H_nSTROBE), 20438061Smsmith 20538061Smsmith/* loop: */ 20638061Smsmith MS_RFETCH_P(1, MS_REG_DTR, MS_FETCH_ALL), 20738061Smsmith MS_CASS(PCD | H_nAUTO | H_SELIN | H_INIT | H_nSTROBE), 20838061Smsmith MS_CASS(PCD | H_AUTO | H_SELIN | H_INIT | H_nSTROBE), 20943433Snsouch MS_DBRA(-4 /* loop */), 21038061Smsmith 21138061Smsmith MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE), 21238061Smsmith MS_RET(0) 21338061Smsmith}; 21438061Smsmith 21538061Smsmith/* 21638061Smsmith * This is the sub-microsequence for MS_PUT in both NIBBLE and PS2 modes 21738061Smsmith */ 21839134Snsouchstatic struct ppb_microseq spp_outbyte_submicroseq[] = { 21938061Smsmith 22038061Smsmith/* loop: */ 221185003Sjhb MS_RASSERT_P(1, MS_REG_DTR), 22238061Smsmith MS_CASS(H_nAUTO | H_nSELIN | H_INIT | H_STROBE), 22338061Smsmith MS_CASS( H_AUTO | H_nSELIN | H_INIT | H_STROBE), 22438061Smsmith MS_DELAY(VP0_PULSE), 22543433Snsouch MS_DBRA(-5 /* loop */), 22638061Smsmith 22738061Smsmith /* return from the put call */ 22838061Smsmith MS_RET(0) 22938061Smsmith}; 23038061Smsmith 23138061Smsmith/* EPP 1.7 microsequences, ptr and len set at runtime */ 23239134Snsouchstatic struct ppb_microseq epp17_outstr_body[] = { 23338061Smsmith MS_CASS(H_AUTO | H_SELIN | H_INIT | H_STROBE), 23438061Smsmith 23538061Smsmith/* loop: */ 236185003Sjhb MS_RASSERT_P(1, MS_REG_EPP_D), 23743433Snsouch MS_BRSET(TIMEOUT, 3 /* error */), /* EPP timeout? */ 23843433Snsouch MS_DBRA(-3 /* loop */), 23938061Smsmith 24038061Smsmith MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE), 24138061Smsmith MS_RET(0), 24238061Smsmith/* error: */ 24338061Smsmith MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE), 24438061Smsmith MS_RET(1) 24538061Smsmith}; 24638061Smsmith 24739134Snsouchstatic struct ppb_microseq epp17_instr_body[] = { 24838061Smsmith MS_CASS(PCD | H_AUTO | H_SELIN | H_INIT | H_STROBE), 24938061Smsmith 25038061Smsmith/* loop: */ 251185003Sjhb MS_RFETCH_P(1, MS_REG_EPP_D, MS_FETCH_ALL), 25243433Snsouch MS_BRSET(TIMEOUT, 3 /* error */), /* EPP timeout? */ 25343433Snsouch MS_DBRA(-3 /* loop */), 25438061Smsmith 25538061Smsmith MS_CASS(PCD | H_AUTO | H_nSELIN | H_INIT | H_STROBE), 25638061Smsmith MS_RET(0), 25738061Smsmith/* error: */ 25838061Smsmith MS_CASS(PCD | H_AUTO | H_nSELIN | H_INIT | H_STROBE), 25938061Smsmith MS_RET(1) 26038061Smsmith}; 26138061Smsmith 26239134Snsouchstatic struct ppb_microseq in_disk_mode[] = { 26339134Snsouch MS_CASS( H_AUTO | H_nSELIN | H_INIT | H_STROBE), 26439134Snsouch MS_CASS(H_nAUTO | H_nSELIN | H_INIT | H_STROBE), 26539134Snsouch 26643433Snsouch MS_BRCLEAR(H_FLT, 3 /* error */), 26739134Snsouch MS_CASS( H_AUTO | H_nSELIN | H_INIT | H_STROBE), 26843433Snsouch MS_BRSET(H_FLT, 1 /* error */), 26939134Snsouch 27039520Snsouch MS_RET(1), 27139134Snsouch/* error: */ 27239520Snsouch MS_RET(0) 27339134Snsouch}; 27439134Snsouch 27538061Smsmithstatic int 27638061Smsmithvpoio_disconnect(struct vpoio_data *vpo) 27738061Smsmith{ 27855939Snsouch device_t ppbus = device_get_parent(vpo->vpo_dev); 27938061Smsmith int ret; 28038061Smsmith 28155939Snsouch ppb_MS_microseq(ppbus, vpo->vpo_dev, disconnect_microseq, &ret); 28255939Snsouch return (ppb_release_bus(ppbus, vpo->vpo_dev)); 28338061Smsmith} 28438061Smsmith 28538061Smsmith/* 28638061Smsmith * how : PPB_WAIT or PPB_DONTWAIT 28738061Smsmith */ 28838061Smsmithstatic int 28938061Smsmithvpoio_connect(struct vpoio_data *vpo, int how) 29038061Smsmith{ 29155939Snsouch device_t ppbus = device_get_parent(vpo->vpo_dev); 29238061Smsmith int error; 29338061Smsmith int ret; 29438061Smsmith 29555939Snsouch if ((error = ppb_request_bus(ppbus, vpo->vpo_dev, how))) { 29642475Snsouch 29742475Snsouch#ifdef VP0_DEBUG 29887599Sobrien printf("%s: can't request bus!\n", __func__); 29942475Snsouch#endif 300185003Sjhb return (error); 30142475Snsouch } 30238061Smsmith 30355939Snsouch if (PPB_IN_EPP_MODE(ppbus)) 30455939Snsouch ppb_MS_microseq(ppbus, vpo->vpo_dev, connect_epp_microseq, &ret); 30538061Smsmith else 30655939Snsouch ppb_MS_microseq(ppbus, vpo->vpo_dev, connect_spp_microseq, &ret); 30738061Smsmith 30838061Smsmith return (0); 30938061Smsmith} 31038061Smsmith 31138061Smsmith/* 31239134Snsouch * vpoio_reset() 31338061Smsmith * 31439134Snsouch * SCSI reset signal, the drive must be in disk mode 31538061Smsmith */ 31639134Snsouchstatic void 317185003Sjhbvpoio_reset(struct vpoio_data *vpo) 31838061Smsmith{ 31955939Snsouch device_t ppbus = device_get_parent(vpo->vpo_dev); 32039134Snsouch int ret; 32138061Smsmith 32239134Snsouch struct ppb_microseq reset_microseq[] = { 32338061Smsmith 32439134Snsouch #define INITIATOR MS_PARAM(0, 1, MS_TYP_INT) 32538061Smsmith 32639134Snsouch MS_DASS(MS_UNKNOWN), 32739134Snsouch MS_CASS(H_AUTO | H_nSELIN | H_nINIT | H_STROBE), 32839134Snsouch MS_DELAY(25), 32939134Snsouch MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE), 33039134Snsouch MS_RET(0) 33139134Snsouch }; 33238061Smsmith 33339134Snsouch ppb_MS_init_msq(reset_microseq, 1, INITIATOR, 1 << VP0_INITIATOR); 33455939Snsouch ppb_MS_microseq(ppbus, vpo->vpo_dev, reset_microseq, &ret); 33539134Snsouch 33639134Snsouch return; 33738061Smsmith} 33838061Smsmith 33938061Smsmith/* 34039134Snsouch * vpoio_in_disk_mode() 34138061Smsmith */ 34239134Snsouchstatic int 34339134Snsouchvpoio_in_disk_mode(struct vpoio_data *vpo) 34438061Smsmith{ 34555939Snsouch device_t ppbus = device_get_parent(vpo->vpo_dev); 34639134Snsouch int ret; 34738061Smsmith 34855939Snsouch ppb_MS_microseq(ppbus, vpo->vpo_dev, in_disk_mode, &ret); 34938061Smsmith 35039134Snsouch return (ret); 35138061Smsmith} 35238061Smsmith 35338061Smsmith/* 35438061Smsmith * vpoio_detect() 35538061Smsmith * 35638061Smsmith * Detect and initialise the VP0 adapter. 35738061Smsmith */ 35839134Snsouchstatic int 35938061Smsmithvpoio_detect(struct vpoio_data *vpo) 36038061Smsmith{ 36155939Snsouch device_t ppbus = device_get_parent(vpo->vpo_dev); 36239520Snsouch int error, ret; 36338061Smsmith 36439520Snsouch /* allocate the bus, then apply microsequences */ 36555939Snsouch if ((error = ppb_request_bus(ppbus, vpo->vpo_dev, PPB_DONTWAIT))) 366185003Sjhb return (error); 36739520Snsouch 36878645Snsouch /* Force disconnection */ 36955939Snsouch ppb_MS_microseq(ppbus, vpo->vpo_dev, disconnect_microseq, &ret); 37039520Snsouch 37170608Snsouch /* Try to enter EPP mode, then connect to the drive in EPP mode */ 37270608Snsouch if (ppb_set_mode(ppbus, PPB_EPP) != -1) { 37370608Snsouch /* call manually the microseq instead of using the appropriate function 37470608Snsouch * since we already requested the ppbus */ 37555939Snsouch ppb_MS_microseq(ppbus, vpo->vpo_dev, connect_epp_microseq, &ret); 37670608Snsouch } 37739520Snsouch 37870608Snsouch /* If EPP mode switch failed or ZIP connection in EPP mode failed, 37970608Snsouch * try to connect in NIBBLE mode */ 38070608Snsouch if (!vpoio_in_disk_mode(vpo)) { 38139520Snsouch 38270608Snsouch /* The interface must be at least PS/2 or NIBBLE capable. 38370608Snsouch * There is no way to know if the ZIP will work with 38470608Snsouch * PS/2 mode since PS/2 and SPP both use the same connect 38570608Snsouch * sequence. One must supress PS/2 with boot flags if 38670608Snsouch * PS/2 mode fails (see ppc(4)). 38739520Snsouch */ 38870608Snsouch if (ppb_set_mode(ppbus, PPB_PS2) != -1) { 38970608Snsouch vpo->vpo_mode_found = VP0_MODE_PS2; 39070608Snsouch } else { 39170608Snsouch if (ppb_set_mode(ppbus, PPB_NIBBLE) == -1) 39270608Snsouch goto error; 39370608Snsouch 39470608Snsouch vpo->vpo_mode_found = VP0_MODE_NIBBLE; 39570608Snsouch } 39670608Snsouch 39770608Snsouch /* Can't know if the interface is capable of PS/2 yet */ 39855939Snsouch ppb_MS_microseq(ppbus, vpo->vpo_dev, connect_spp_microseq, &ret); 39970608Snsouch if (!vpoio_in_disk_mode(vpo)) { 40070608Snsouch vpo->vpo_mode_found = VP0_MODE_UNDEFINED; 40139520Snsouch if (bootverbose) 402184130Sjhb device_printf(vpo->vpo_dev, 403184130Sjhb "can't connect to the drive\n"); 40439520Snsouch 40539520Snsouch /* disconnect and release the bus */ 40655939Snsouch ppb_MS_microseq(ppbus, vpo->vpo_dev, disconnect_microseq, 40739520Snsouch &ret); 40839520Snsouch goto error; 40939520Snsouch } 41070608Snsouch } else { 41170608Snsouch vpo->vpo_mode_found = VP0_MODE_EPP; 41238061Smsmith } 41338061Smsmith 41438061Smsmith /* send SCSI reset signal */ 41538061Smsmith vpoio_reset(vpo); 41638061Smsmith 41755939Snsouch ppb_MS_microseq(ppbus, vpo->vpo_dev, disconnect_microseq, &ret); 41838061Smsmith 419185003Sjhb /* ensure we are disconnected or daisy chained peripheral 42039134Snsouch * may cause serious problem to the disk */ 42170608Snsouch if (vpoio_in_disk_mode(vpo)) { 42239520Snsouch if (bootverbose) 423184130Sjhb device_printf(vpo->vpo_dev, 424184130Sjhb "can't disconnect from the drive\n"); 42539520Snsouch goto error; 42639520Snsouch } 42738061Smsmith 42855939Snsouch ppb_release_bus(ppbus, vpo->vpo_dev); 42938061Smsmith return (0); 43039520Snsouch 43139520Snsoucherror: 43255939Snsouch ppb_release_bus(ppbus, vpo->vpo_dev); 43339520Snsouch return (VP0_EINITFAILED); 43438061Smsmith} 43538061Smsmith 43638061Smsmith/* 43738061Smsmith * vpoio_outstr() 43838061Smsmith */ 43938061Smsmithstatic int 44038061Smsmithvpoio_outstr(struct vpoio_data *vpo, char *buffer, int size) 44138061Smsmith{ 44255939Snsouch device_t ppbus = device_get_parent(vpo->vpo_dev); 44338061Smsmith int error = 0; 44438061Smsmith 44555939Snsouch ppb_MS_exec(ppbus, vpo->vpo_dev, MS_OP_PUT, (union ppb_insarg)buffer, 44645342Speter (union ppb_insarg)size, (union ppb_insarg)MS_UNKNOWN, &error); 44738061Smsmith 44855939Snsouch ppb_ecp_sync(ppbus); 44938061Smsmith 45038061Smsmith return (error); 45138061Smsmith} 45238061Smsmith 45338061Smsmith/* 45438061Smsmith * vpoio_instr() 45538061Smsmith */ 45638061Smsmithstatic int 45738061Smsmithvpoio_instr(struct vpoio_data *vpo, char *buffer, int size) 45838061Smsmith{ 45955939Snsouch device_t ppbus = device_get_parent(vpo->vpo_dev); 46038061Smsmith int error = 0; 46138061Smsmith 46255939Snsouch ppb_MS_exec(ppbus, vpo->vpo_dev, MS_OP_GET, (union ppb_insarg)buffer, 46345342Speter (union ppb_insarg)size, (union ppb_insarg)MS_UNKNOWN, &error); 46438061Smsmith 46555939Snsouch ppb_ecp_sync(ppbus); 46638061Smsmith 46738061Smsmith return (error); 46838061Smsmith} 46938061Smsmith 47038061Smsmithstatic char 47138061Smsmithvpoio_select(struct vpoio_data *vpo, int initiator, int target) 47238061Smsmith{ 47355939Snsouch device_t ppbus = device_get_parent(vpo->vpo_dev); 47438061Smsmith int ret; 47538061Smsmith 47638061Smsmith struct ppb_microseq select_microseq[] = { 47738061Smsmith 47838061Smsmith /* parameter list 47938061Smsmith */ 48038061Smsmith #define SELECT_TARGET MS_PARAM(0, 1, MS_TYP_INT) 48138061Smsmith #define SELECT_INITIATOR MS_PARAM(3, 1, MS_TYP_INT) 48238061Smsmith 48338061Smsmith /* send the select command to the drive */ 48438061Smsmith MS_DASS(MS_UNKNOWN), 48538061Smsmith MS_CASS(H_nAUTO | H_nSELIN | H_INIT | H_STROBE), 48638061Smsmith MS_CASS( H_AUTO | H_nSELIN | H_INIT | H_STROBE), 48738061Smsmith MS_DASS(MS_UNKNOWN), 48838061Smsmith MS_CASS( H_AUTO | H_nSELIN | H_nINIT | H_STROBE), 48938061Smsmith 49038061Smsmith /* now, wait until the drive is ready */ 49138061Smsmith MS_SET(VP0_SELTMO), 49243433Snsouch/* loop: */ MS_BRSET(H_ACK, 2 /* ready */), 49343433Snsouch MS_DBRA(-2 /* loop */), 49438061Smsmith/* error: */ MS_RET(1), 49538061Smsmith/* ready: */ MS_RET(0) 49638061Smsmith }; 49738061Smsmith 49838061Smsmith /* initialize the select microsequence */ 49938061Smsmith ppb_MS_init_msq(select_microseq, 2, 50038061Smsmith SELECT_TARGET, 1 << target, 50138061Smsmith SELECT_INITIATOR, 1 << initiator); 502185003Sjhb 50355939Snsouch ppb_MS_microseq(ppbus, vpo->vpo_dev, select_microseq, &ret); 50438061Smsmith 50538061Smsmith if (ret) 50638061Smsmith return (VP0_ESELECT_TIMEOUT); 50738061Smsmith 50838061Smsmith return (0); 50938061Smsmith} 51038061Smsmith 51138061Smsmith/* 51238061Smsmith * vpoio_wait() 51338061Smsmith * 51438061Smsmith * H_SELIN must be low. 51538061Smsmith * 51638061Smsmith * XXX should be ported to microseq 51738061Smsmith */ 51838061Smsmithstatic char 51938061Smsmithvpoio_wait(struct vpoio_data *vpo, int tmo) 52038061Smsmith{ 52178645Snsouch DECLARE_WAIT_MICROSEQUENCE; 52278645Snsouch 52355939Snsouch device_t ppbus = device_get_parent(vpo->vpo_dev); 52478645Snsouch int ret, err; 52538061Smsmith 52638061Smsmith#if 0 /* broken */ 52755939Snsouch if (ppb_poll_device(ppbus, 150, nBUSY, nBUSY, PPB_INTR)) 52838061Smsmith return (0); 52938061Smsmith 53055939Snsouch return (ppb_rstr(ppbus) & 0xf0); 53138061Smsmith#endif 53238061Smsmith 53338061Smsmith /* 53438061Smsmith * Return some status information. 53538061Smsmith * Semantics : 0xc0 = ZIP wants more data 53638061Smsmith * 0xd0 = ZIP wants to send more data 53738061Smsmith * 0xe0 = ZIP wants command 53838061Smsmith * 0xf0 = end of transfer, ZIP is sending status 53938061Smsmith */ 54038061Smsmith 54178645Snsouch ppb_MS_init_msq(wait_microseq, 2, 54278645Snsouch WAIT_RET, (void *)&ret, 54378645Snsouch WAIT_TMO, tmo); 54478645Snsouch 54578645Snsouch ppb_MS_microseq(ppbus, vpo->vpo_dev, wait_microseq, &err); 54678645Snsouch 54778645Snsouch if (err) 548185003Sjhb return (0); /* command timed out */ 54978645Snsouch 55078645Snsouch return(ret); 55138061Smsmith} 55238061Smsmith 55338061Smsmith/* 55438061Smsmith * vpoio_probe() 55538061Smsmith * 55638061Smsmith * Low level probe of vpo device 55738061Smsmith * 55838061Smsmith */ 55955939Snsouchint 56055939Snsouchvpoio_probe(device_t dev, struct vpoio_data *vpo) 56138061Smsmith{ 56255939Snsouch int error; 56338061Smsmith 56438061Smsmith /* ppbus dependent initialisation */ 56555939Snsouch vpo->vpo_dev = dev; 56638061Smsmith 56739134Snsouch /* 56839134Snsouch * Initialize microsequence code 56939134Snsouch */ 57039134Snsouch INIT_TRIG_MICROSEQ; 57139134Snsouch 57238061Smsmith /* now, try to initialise the drive */ 57355939Snsouch if ((error = vpoio_detect(vpo))) { 57455939Snsouch return (error); 57538061Smsmith } 57638061Smsmith 57755939Snsouch return (0); 57838061Smsmith} 57938061Smsmith 58038061Smsmith/* 58138061Smsmith * vpoio_attach() 58238061Smsmith * 58338061Smsmith * Low level attachment of vpo device 58438061Smsmith * 58538061Smsmith */ 58638061Smsmithint 58738061Smsmithvpoio_attach(struct vpoio_data *vpo) 58838061Smsmith{ 589185003Sjhb DECLARE_NIBBLE_INBYTE_SUBMICROSEQ; 59055939Snsouch device_t ppbus = device_get_parent(vpo->vpo_dev); 59170608Snsouch int error = 0; 59238061Smsmith 59338061Smsmith vpo->vpo_nibble_inbyte_msq = (struct ppb_microseq *)malloc( 59438061Smsmith sizeof(nibble_inbyte_submicroseq), M_DEVBUF, M_NOWAIT); 59538061Smsmith 59638061Smsmith if (!vpo->vpo_nibble_inbyte_msq) 59755939Snsouch return (ENXIO); 59838061Smsmith 59938061Smsmith bcopy((void *)nibble_inbyte_submicroseq, 60038061Smsmith (void *)vpo->vpo_nibble_inbyte_msq, 60138061Smsmith sizeof(nibble_inbyte_submicroseq)); 60238061Smsmith 60378645Snsouch ppb_MS_init_msq(vpo->vpo_nibble_inbyte_msq, 4, 60478645Snsouch INB_NIBBLE_H, (void *)&(vpo)->vpo_nibble.h, 60578645Snsouch INB_NIBBLE_L, (void *)&(vpo)->vpo_nibble.l, 60678645Snsouch INB_NIBBLE_F, nibble_inbyte_hook, 607185003Sjhb INB_NIBBLE_P, (void *)&(vpo)->vpo_nibble); 60838061Smsmith 60938061Smsmith /* 61038061Smsmith * Initialize mode dependent in/out microsequences 61138061Smsmith */ 612187576Sjhb ppb_lock(ppbus); 61370608Snsouch if ((error = ppb_request_bus(ppbus, vpo->vpo_dev, PPB_WAIT))) 61470608Snsouch goto error; 61538061Smsmith 61670608Snsouch /* ppbus sets automatically the last mode entered during detection */ 61770608Snsouch switch (vpo->vpo_mode_found) { 61870608Snsouch case VP0_MODE_EPP: 61970608Snsouch ppb_MS_GET_init(ppbus, vpo->vpo_dev, epp17_instr_body); 62070608Snsouch ppb_MS_PUT_init(ppbus, vpo->vpo_dev, epp17_outstr_body); 621184130Sjhb device_printf(vpo->vpo_dev, "EPP mode\n"); 62270608Snsouch break; 62370608Snsouch case VP0_MODE_PS2: 62455939Snsouch ppb_MS_GET_init(ppbus, vpo->vpo_dev, ps2_inbyte_submicroseq); 62555939Snsouch ppb_MS_PUT_init(ppbus, vpo->vpo_dev, spp_outbyte_submicroseq); 626184130Sjhb device_printf(vpo->vpo_dev, "PS2 mode\n"); 62770608Snsouch break; 62870608Snsouch case VP0_MODE_NIBBLE: 62970608Snsouch ppb_MS_GET_init(ppbus, vpo->vpo_dev, vpo->vpo_nibble_inbyte_msq); 63070608Snsouch ppb_MS_PUT_init(ppbus, vpo->vpo_dev, spp_outbyte_submicroseq); 631184130Sjhb device_printf(vpo->vpo_dev, "NIBBLE mode\n"); 63270608Snsouch break; 63370608Snsouch default: 63470608Snsouch panic("vpo: unknown mode %d", vpo->vpo_mode_found); 63538061Smsmith } 63638061Smsmith 63755939Snsouch ppb_release_bus(ppbus, vpo->vpo_dev); 63838061Smsmith 63970608Snsoucherror: 640187576Sjhb ppb_unlock(ppbus); 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 65587599Sobrien printf("%s: not in disk mode!\n", __func__); 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 */ 677185003Sjhbint 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)) { 700185003Sjhb *ret = VP0_ECONNECT; 701185003Sjhb goto error; 70238061Smsmith } 70338061Smsmith 70438061Smsmith if ((*ret = vpoio_select(vpo,host,target))) 70538061Smsmith goto error; 70638061Smsmith 70738061Smsmith /* 70838061Smsmith * Send the command ... 70938061Smsmith * 71038061Smsmith * set H_SELIN low for vpoio_wait(). 71138061Smsmith */ 71255939Snsouch ppb_wctr(ppbus, H_AUTO | H_nSELIN | H_INIT | H_STROBE); 71338061Smsmith 71438061Smsmith for (k = 0; k < clen; k++) { 71538061Smsmith if (vpoio_wait(vpo, VP0_FAST_SPINTMO) != (char)0xe0) { 71638061Smsmith *ret = VP0_ECMD_TIMEOUT; 71738061Smsmith goto error; 71838061Smsmith } 71938061Smsmith if (vpoio_outstr(vpo, &command[k], 1)) { 72038061Smsmith *ret = VP0_EPPDATA_TIMEOUT; 72138061Smsmith goto error; 72238061Smsmith } 72338061Smsmith } 72438061Smsmith 725185003Sjhb /* 726185003Sjhb * Completion ... 72738061Smsmith */ 72838061Smsmith 72938061Smsmith *count = 0; 73038061Smsmith for (;;) { 73138061Smsmith 73238061Smsmith if (!(r = vpoio_wait(vpo, VP0_LOW_SPINTMO))) { 733185003Sjhb *ret = VP0_ESTATUS_TIMEOUT; 734185003Sjhb goto error; 73538061Smsmith } 73638061Smsmith 73738061Smsmith /* stop when the ZIP wants to send status */ 73838061Smsmith if (r == (char)0xf0) 73938061Smsmith break; 74038061Smsmith 74138061Smsmith if (*count >= blen) { 74238061Smsmith *ret = VP0_EDATA_OVERFLOW; 74338061Smsmith goto error; 74438061Smsmith } 74538061Smsmith 74639520Snsouch /* if in EPP mode or writing bytes, try to transfer a sector 74739520Snsouch * otherwise, just send one byte 74839520Snsouch */ 74955939Snsouch if (PPB_IN_EPP_MODE(ppbus) || r == (char)0xc0) 75039520Snsouch len = (((blen - *count) >= VP0_SECTOR_SIZE)) ? 75139520Snsouch VP0_SECTOR_SIZE : 1; 75239520Snsouch else 75339520Snsouch len = 1; 75439520Snsouch 75538061Smsmith /* ZIP wants to send data? */ 75638061Smsmith if (r == (char)0xc0) 75738061Smsmith error = vpoio_outstr(vpo, &buffer[*count], len); 75838061Smsmith else 75938061Smsmith error = vpoio_instr(vpo, &buffer[*count], len); 76038061Smsmith 76138061Smsmith if (error) { 76238061Smsmith *ret = error; 76338061Smsmith goto error; 76438061Smsmith } 76538061Smsmith 76638061Smsmith *count += len; 76738061Smsmith } 76838061Smsmith 76938061Smsmith if (vpoio_instr(vpo, &l, 1)) { 770185003Sjhb *ret = VP0_EOTHER; 771185003Sjhb goto error; 77238061Smsmith } 77338061Smsmith 77438061Smsmith /* check if the ZIP wants to send more status */ 77538061Smsmith if (vpoio_wait(vpo, VP0_FAST_SPINTMO) == (char)0xf0) 77638061Smsmith if (vpoio_instr(vpo, &h, 1)) { 777185003Sjhb *ret = VP0_EOTHER + 2; 778185003Sjhb goto error; 77938061Smsmith } 78038061Smsmith 78138061Smsmith *result = ((int) h << 8) | ((int) l & 0xff); 78238061Smsmith 78338061Smsmitherror: 78438061Smsmith /* return to printer state, release the ppbus */ 78538061Smsmith vpoio_disconnect(vpo); 78638061Smsmith return (0); 78738061Smsmith} 788