NetBSD/x68k hdc(4) ドライバの残骸。 isaki@NetBSD.org 2011/10/09 これは何? NetBSD/x68k 用の hdc(4) ドライバです。初代/ACE/PRO/EXPERT の SASI ポートを SCSI として使うためのドライバです。 Human68k では SxSI がありますがそれと似たことを NetBSD/x68k で やってみようとしたものです。 当時 SCSI ハードディスクを認識してマウントするところまでは 動いていました。ls するとパニックとかしてたので、たぶん何かが 踏み外してるとかそういうことで、あと一歩なんじゃないかと思いますが、 何分、SASI も SCSI も SxSI も scsipi(9) も理解していないので限界です。 パッチは 2003/01/13 時点 (たぶん NetBSD 1.6L に対して) のものです。 たぶん今となってはまずパッチがまともにあたらないはずです。現在の -current に字面上だけでも追従すると余計に動かなくなる可能性のほうが 高いと思いますので、当時のパッチのままにしておきます。 使い方 このパッチをなんとかして当てて、このパッチの manpage にあるように カーネルに hdc を追加すればよいはずです。 SCSI デバイス側がパリティを必要とするデバイスの場合、別途、 パリティ発生回路が必要になると思います。 詳細は SxSI のドキュメントを参照してください。 連絡先 isaki@NetBSD.org --- sys/arch/x68k/conf/files.x68k 1 Dec 2002 12:01:59 -0000 1.1.1.10 +++ sys/arch/x68k/conf/files.x68k 1 Dec 2002 13:27:08 -0000 1.16 @@ -166,6 +166,13 @@ attach mha at scsirom file arch/x68k/dev/mha.c mha needs-flag +defparam opt_hdc.h HDC_DEBUG +defparam opt_hdc.h HDC_BUSDMA +defparam opt_hdc.h HDC_TARGET_NUM +device hdc: scsi +attach hdc at intio +file arch/x68k/dev/hdc.c hdc needs-flag + defpseudo bell file arch/x68k/dev/opmbell.c bell needs-flag --- sys/arch/x68k/dev/dmacvar.h 2 Sep 2001 13:00:48 -0000 1.1.1.2 +++ sys/arch/x68k/dev/dmacvar.h 2 Sep 2001 13:03:03 -0000 1.2 @@ -122,6 +122,7 @@ int dmac_start_xfer_offset __P((struct device*, struct dmac_dma_xfer*, u_int, u_int)); int dmac_abort_xfer __P((struct device*, struct dmac_dma_xfer*)); +int dmac_sab_xfer __P((struct device*, struct dmac_dma_xfer*)); /* Compatibility function: alloc, fill defaults, load */ struct dmac_dma_xfer *dmac_prepare_xfer __P((struct dmac_channel_stat*, bus_dma_tag_t, --- sys/arch/x68k/dev/intio_dmac.c 24 Nov 2002 13:13:42 -0000 1.1.1.5 +++ sys/arch/x68k/dev/intio_dmac.c 24 Nov 2002 13:15:16 -0000 1.6 @@ -599,6 +599,23 @@ return 0; } +int +dmac_sab_xfer(self, xf) + struct device *self; + struct dmac_dma_xfer *xf; +{ + struct dmac_softc *sc = (void*) self; + struct dmac_channel_stat *chan = xf->dx_channel; + //struct x68k_bus_dmamap *dmamap = xf->dx_dmamap; + + bus_space_write_1(sc->sc_bst, chan->ch_bht, DMAC_REG_CCR, + DMAC_CCR_INT | DMAC_CCR_SAB); + bus_space_write_1(sc->sc_bst, chan->ch_bht, DMAC_REG_CSR, 0xff); + xf->dx_nextoff = xf->dx_nextsize = -1; + + return 0; +} + #ifdef DMAC_DEBUG static int dmac_dump_regs(void) --- /dev/null 2011-10-09 03:24:27.000000000 +0900 +++ sys/arch/x68k/dev/hdc.c 2003-01-13 17:19:47.000000000 +0900 @@ -0,0 +1,1102 @@ +/* $Id: hdc.c,v 1.169 2003/01/13 08:19:47 isaki Exp $ */ + +/* + * Copyright (c) 2001 Tetsuya Isaki. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * hdc - SASI compatible Hard Disk Controller as SCSI controller. + * It's known as 'SxSI' in x68k world. + */ + +#include "hdc.h" +#include "opt_hdc.h" +#if NHDC > 0 + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include + + +#ifdef HDC_DEBUG +#define DPRINTF(z, x) do { \ + if ((hdc_debug & (z)) != 0) \ + printf x; \ +} while (0) +#else +#define DPRINTF(z, x) do { } while (0) +#endif + +extern struct cfdriver hdc_cd; + +static int hdc_dummy; + +#ifdef HDC_DEBUG +#define D_TRACE (0x0001) /* function trace */ +#define D_MSG (0x0002) /* debug messages */ +#define D_XFER (0x0004) /* xfer messages (noisy!) */ +#define D_ACB (0x0008) /* acb queue status */ +int hdc_debug = D_TRACE | D_MSG; + +static char phasestr[][8] = { + "FREE", + "SEL", + "CMD", + "DIN", + "DOUT", + "MIN", + "MOUT", + "STAT" +}; +#endif /* HDC_DEBUG */ + +static int hdc_match(struct device *, struct cfdata *, void *); +static void hdc_attach(struct device *, struct device *, void *); + +static int hdc_intr(void *); +static int hdc_dmaintr(void *); +static int hdc_dmaerrintr(void *); + +static inline void hdc_phase(struct hdc_softc *, int); +static struct hdc_acb *hdc_get_acb(struct hdc_softc *); +static void hdc_free_acb(struct hdc_softc *, struct hdc_acb *); +static void hdc_dequeue(struct hdc_softc *, struct hdc_acb *); +static void hdc_scsipi_request(struct scsipi_channel *, + scsipi_adapter_req_t, void *); +static void hdc_sched(struct hdc_softc *); +static void hdc_select(struct hdc_softc *, struct hdc_acb *); +static int hdc_wait_for_ready(struct hdc_softc *, int); +static int hdc_write_data_1(struct hdc_softc *, unsigned char); +static int hdc_read_data_1(struct hdc_softc *); +static int hdc_dataio(struct hdc_softc *, int); +static int hdc_dataio_intr(struct hdc_softc *, int); +static int hdc_dataio_poll(struct hdc_softc *, int); +//static void hdc_timeout(struct hdc_acb *); +#ifdef HDC_BUSDMA +/*static*/ int hdc_allocmem(struct hdc_softc *, struct hdc_acb *); +#endif +static void hdc_done(struct hdc_softc *, struct hdc_acb *); +static int hdc_ioctl(struct scsipi_channel *, u_long, caddr_t, int, + struct proc *); +static void hdc_bus_reset(struct hdc_softc *); + +#ifdef HDC_DEBUG +static void print_xfer(struct scsipi_xfer *); +static void print_acb(struct hdc_softc *); +static void print_acb1(struct hdc_softc *, struct hdc_acb *); +#else +#define print_xfer(X) +#define print_acb(X) +#define print_acb1(X, Y) +#endif + +CFATTACH_DECL(hdc, sizeof(struct hdc_softc), + hdc_match, hdc_attach, NULL, NULL); + +/* + * return non-zero value if a controller is found. + */ +static int +hdc_match(struct device *parent, struct cfdata *cf, void *aux) +{ + struct intio_attach_args *ia = aux; + + if (strcmp(ia->ia_name, "hdc") || cf->cf_unit > 0) + return 0; + + if (ia->ia_addr == INTIOCF_ADDR_DEFAULT) + ia->ia_addr = HDC_ADDR; + if (ia->ia_dma == INTIOCF_DMA_DEFAULT) + ia->ia_dma = HDC_DMA; + if (ia->ia_dmaintr == INTIOCF_DMAINTR_DEFAULT) + ia->ia_dmaintr = HDC_DMAINTR; + + /* fixed parameters */ + if (ia->ia_addr != HDC_ADDR) + return 0; + if (ia->ia_dma != HDC_DMA) + return 0; + if (ia->ia_dmaintr != HDC_DMAINTR) + return 0; + + /* Simple check */ + if (badbaddr((caddr_t)INTIO_ADDR(ia->ia_addr + HDC_STATUS * 2))) + return 0; + return 1; +} + +static void +hdc_attach(struct device *parent, struct device *self, void *aux) +{ + struct hdc_softc *sc = (struct hdc_softc *)self; + bus_space_tag_t iot; + bus_space_handle_t ioh; + struct intio_attach_args *ia = aux; +#ifndef HDC_BUSDMA + volatile struct dmac *dmac = &IODEVbase->io_dma[HDC_DMA]; +#endif + int i; + + printf("\n"); + + /* Re-map the I/O space */ + iot = ia->ia_bst; + bus_space_map(iot, ia->ia_addr, HDC_PORTSIZE * 2, + BUS_SPACE_MAP_SHIFTED, &ioh); + + /* Initialize sc */ + sc->sc_iot = iot; + sc->sc_ioh = ioh; +#ifdef HDC_BUSDMA + sc->sc_addr = (caddr_t) ia->ia_addr; +#endif + sc->sc_id = 7; /* fixed */ + + TAILQ_INIT(&sc->ready_list); + TAILQ_INIT(&sc->free_list); + sc->sc_nexus = NULL; + memset(sc->sc_acb, 0, sizeof(sc->sc_acb)); + for (i = 0; i < sizeof(sc->sc_acb)/sizeof(sc->sc_acb[0]); i++) + TAILQ_INSERT_TAIL(&sc->free_list, &sc->sc_acb[i], acb_chain); + + memset(sc->sc_tinfo, 0, sizeof(sc->sc_tinfo)); + + sc->sc_state = HDC_IDLE; + sc->sc_phase = sc->sc_prevphase = PH_BUSFREE; + + /* Fill in the adapter */ + sc->sc_adapter.adapt_dev = &sc->sc_dev; + sc->sc_adapter.adapt_nchannels = 1; + sc->sc_adapter.adapt_openings = 7; + sc->sc_adapter.adapt_max_periph = 1; + sc->sc_adapter.adapt_request = hdc_scsipi_request; + sc->sc_adapter.adapt_minphys = minphys; + sc->sc_adapter.adapt_ioctl = hdc_ioctl; + /* sc->sc_adapter.adapt_enable = NULL; */ + /* sc->sc_adapter.adapt_getgeom = NULL; */ + /* sc->sc_adapter.adapt_accesschk = NULL; */ + + sc->sc_channel.chan_adapter = &sc->sc_adapter; + sc->sc_channel.chan_bustype = &scsi_bustype; + sc->sc_channel.chan_channel = 0; +#ifndef HDC_TARGET_NUM +#define HDC_TARGET_NUM 8 +#endif + sc->sc_channel.chan_ntargets = HDC_TARGET_NUM; + sc->sc_channel.chan_nluns = HDC_TARGET_NUM; + sc->sc_channel.chan_id = sc->sc_id; + /* sc->sc_channel.chan_flags = 0; */ /* XXX ? */ + /* sc->sc_channel.chan_openings = 0; */ /* XXX ? */ + /* sc->sc_channel.chan_max_periph = 0; */ /* XXX ? */ + /* sc->sc_channel.chan_defquirks = 0; */ + + /* Initialize DMAC */ +#ifdef HDC_BUSDMA + sc->sc_dmat = ia->ia_dmat; + sc->sc_dma_ch = dmac_alloc_channel(parent, ia->ia_dma, "hdc", + ia->ia_dmaintr, hdc_dmaintr, sc, + ia->ia_dmaintr+1, hdc_dmaerrintr, sc); + if (bus_dmamap_create(sc->sc_dmat, DMAC_MAXSEGSZ, 1, DMAC_MAXSEGSZ, + 0, BUS_DMA_NOWAIT, &sc->sc_dmamap)) { + printf("%s: cannot create dmamap\n", sc->sc_dev.dv_xname); + return; + } +#else + /* dmac->csr = 0xff; */ + dmac->dcr = DMAC_DCR_XRM_CSWOH + | DMAC_DCR_OTYP_EASYNC + | DMAC_DCR_OPS_8BIT + | DMAC_DCR_PCL_STATUS; + dmac->ocr = DMAC_OCR_SIZE_BYTE_NOPACK + | DMAC_OCR_CHAIN_DISABLED + | DMAC_OCR_REQG_AUTO_START; + dmac->scr = DMAC_SCR_MAC_COUNT_UP | DMAC_SCR_DAC_NO_COUNT; + dmac->mfc = DMAC_FC_KERNEL_DATA; + dmac->dfc = DMAC_FC_KERNEL_DATA; + dmac->bfc = DMAC_FC_KERNEL_DATA; + dmac->dar = (unsigned long)kvtop((u_char*)(IODEVbase->spcpad1+1)); + dmac->niv = ia->ia_dmaintr; + dmac->eiv = ia->ia_dmaintr + 1; + + /* Establish interrupts */ + if (intio_intr_establish(ia->ia_dmaintr, "hdcdmaintr", + hdc_dmaintr, sc)) + return; + if (intio_intr_establish(ia->ia_dmaintr+1, "hdcdmaintr", + hdc_dmaerrintr, sc)) + return; +#endif /* HDC_BUSDMA */ + + printf("%s: SASI compatible Hard Disk Controller as SCSI Host Adapter\n", + sc->sc_dev.dv_xname); + +#if 1 /* Now, Debugging !! */ + printf("%s: %d target(s), use %s mode\n", + sc->sc_dev.dv_xname, HDC_TARGET_NUM, +#ifdef HDC_BUSDMA + "bus_dma" +#else + "direct DMA" +#endif + ); +#endif /* 1 */ + + config_found(self, &sc->sc_channel, scsiprint); +} + +static inline void +hdc_phase(struct hdc_softc *sc, int phase) +{ + sc->sc_prevphase = sc->sc_phase; + sc->sc_phase = phase; + if (sc->sc_prevphase != sc->sc_phase) + DPRINTF(D_MSG, ("@%s ", phasestr[sc->sc_phase])); +} + +static int +hdc_dmaintr(void *arg) +{ + volatile struct dmac *dmac = &IODEVbase->io_dma[HDC_DMA]; + struct hdc_softc *sc = arg; + int s; + + DPRINTF(D_TRACE, ("hdc_dmaintr()")); + + /* Disable interrupt and wake up */ + dmac->csr = 0xff; + dmac->ccr &= ~DMAC_CCR_INT; + s = splbio(); + sc->sc_wchan = 0; + splx(s); + + return 0; +} + +static int +hdc_dmaerrintr(void *arg) +{ + struct hdc_softc *sc = arg; + volatile struct dmac *dmac = &IODEVbase->io_dma[HDC_DMA]; + + /* If sc_abort is ON, treat DMA transfer terminating normally */ + if (sc->sc_abort) { + sc->sc_abort = 0; + } else { + printf("%s: dmaerrintr: CSR=%02x, CER=%0x2\n", + sc->sc_dev.dv_xname, dmac->csr, dmac->cer); + } + + return hdc_dmaintr(arg); +} + +static struct hdc_acb * +hdc_get_acb(struct hdc_softc *sc) +{ + int s; + struct hdc_acb *acb; + + DPRINTF(D_ACB, ("hdc_get_acb()")); + + s = splbio(); + acb = TAILQ_FIRST(&sc->free_list); + if (acb) { + TAILQ_REMOVE(&sc->free_list, acb, acb_chain); + acb->acb_flag |= ACB_ALLOC; + } + splx(s); + + return acb; +} + +static void +hdc_dequeue(struct hdc_softc *sc, struct hdc_acb *acb) +{ + /* DPRINTF(D_TRACE, ("hdc_dequeue()")); */ + + if (acb->acb_flag & ACB_NEXUS) { + sc->sc_nexus = NULL; + }else{ + TAILQ_REMOVE(&sc->ready_list, acb, acb_chain); + } +} + +static void +hdc_free_acb(struct hdc_softc *sc, struct hdc_acb *acb) +{ + int s; + + /* DPRINTF(D_TRACE, ("free_acb()")); */ + + s = splbio(); + memset(acb, 0, sizeof(*acb)); + TAILQ_INSERT_HEAD(&sc->free_list, acb, acb_chain); + splx(s); + print_acb(sc); +} + +/* + * Start a SCSI command. + * This function is called by the higher level SCSI driver to + * queue/run SCSI command. + */ +static void +hdc_scsipi_request(struct scsipi_channel *chan, + scsipi_adapter_req_t req, + void *arg) +{ + struct scsipi_adapter *adapter; + struct hdc_softc *sc; + struct scsipi_xfer *xs; + struct scsipi_periph *periph; + struct hdc_acb *acb; + int s, flags; + + /* DPRINTF(D_TRACE, ("hdc_scsipi_request()")); */ + + adapter = chan->chan_adapter; + sc = (struct hdc_softc *)adapter->adapt_dev; + + switch (req) { + case ADAPTER_REQ_RUN_XFER: + xs = arg; + periph = xs->xs_periph; + + DPRINTF(D_MSG, ("[0x%x,%d,%d]->%d ", (int)xs->cmd->opcode, + xs->cmdlen, xs->datalen, periph->periph_target)); + print_xfer(xs); + + flags = xs->xs_control; + + /* Get a hdc command block */ + acb = hdc_get_acb(sc); + if (acb == NULL) { + xs->error = XS_RESOURCE_SHORTAGE; + scsipi_done(xs); + return; + } + + /* Initialize acb */ + acb->acb_xs = xs; + memcpy(&acb->acb_cmd, xs->cmd, xs->cmdlen); + acb->acb_cmdlen = xs->cmdlen; + acb->acb_data = xs->data; + acb->acb_datalen = xs->datalen; + acb->acb_dp = acb->acb_data; + acb->acb_dleft = acb->acb_datalen; + acb->acb_timeout = xs->timeout; + + s = splbio(); + TAILQ_INSERT_TAIL(&sc->ready_list, acb, acb_chain); + print_acb(sc); + + /* + * Start scheduling unless a queue process is in progress. + */ + if (sc->sc_state == HDC_IDLE) + hdc_sched(sc); + splx(s); + + if (sc->sc_state == HDC_IDLE) + return; + hdc_intr(sc); + if ((xs->xs_status & XS_STS_DONE)) + hdc_done(sc, acb); + break; + case ADAPTER_REQ_GROW_RESOURCES: + DPRINTF(D_MSG, ("grow_resources")); + break; + case ADAPTER_REQ_SET_XFER_MODE: + DPRINTF(D_MSG, ("set_xfer_mode")); + break; + } + return; +} + +static void +hdc_sched(struct hdc_softc *sc) +{ + struct hdc_acb *acb; + struct scsipi_periph *periph; + struct hdc_tinfo *ti; + + DPRINTF(D_TRACE, ("hdc_sched()")); + + /* + * Find first acb in ready queue that is for a target/lun pair + * that is not busy. + */ + TAILQ_FOREACH(acb, &sc->ready_list, acb_chain) { + periph = acb->acb_xs->xs_periph; + ti = &sc->sc_tinfo[periph->periph_target]; + if ((ti->ti_lubusy & (1 << periph->periph_lun)) == 0) { + TAILQ_REMOVE(&sc->ready_list, acb, acb_chain); + sc->sc_nexus = acb; + acb->acb_flag |= ACB_NEXUS; + print_acb(sc); + hdc_select(sc, acb); + return; + }else{ + scsipi_printaddr(periph); + printf("busy\n"); + } + } +} + +/* + * Start a selection. + * This is used by hdc_sched() to select an idle target. + */ +static void +hdc_select(struct hdc_softc *sc, struct hdc_acb *acb) +{ + struct scsipi_xfer *xs = acb->acb_xs; + struct scsipi_periph *periph = xs->xs_periph; + int data; + int count; + int r; + + DPRINTF(D_TRACE, ("hdc_select()")); + + data = (1 << periph->periph_target) | (1 << sc->sc_id); + bus_space_write_1(sc->sc_iot, sc->sc_ioh, HDC_SELON, data); + hdc_phase(sc, PH_SELECT); + + /* + * Spec. of SCSI says selection timeout delay is 200usec. + * cf. SxSI seems to wait 11msec. + */ + /* XXX polling... */ + count = 1000; + while (count) { + r = bus_space_read_1(sc->sc_iot, sc->sc_ioh, HDC_STATUS); + if ((r & HDC_STATUS_BSY)) { + sc->sc_state = HDC_CONNECTED; + hdc_phase(sc, PH_COMMAND); + break; + } + delay(10); + count--; + } + bus_space_write_1(sc->sc_iot, sc->sc_ioh, HDC_SELOFF, 0); + if (count == 0) { + hdc_phase(sc, PH_BUSFREE); +#ifdef HDC_DEBUG + printf("selection failed\n"); +#else + if ((xs->xs_control & XS_CTL_DISCOVERY) == 0) { + scsipi_printaddr(periph); + printf("selection failed\n"); + } +#endif + delay(10000); /* XXX */ + xs->error = XS_SELTIMEOUT; + hdc_done(sc, acb); + } + return; +} + +static int +hdc_wait_for_ready(struct hdc_softc *sc, int req) +{ + int count; + + count = 2000; /* XXX ? */ + while (count) { + int status; + status = bus_space_read_1(sc->sc_iot, sc->sc_ioh, HDC_STATUS); + /* DPRINTF(D_MSG, ("status reg=0x%x\n", status)); */ + if ((status & req))break; + delay(10); + count--; + } + + if (count == 0) { + DPRINTF(D_XFER, ("timeout\n")); + return -1; + } + return 0; +} + +static int +hdc_write_data_1(struct hdc_softc *sc, unsigned char data) +{ + if (hdc_wait_for_ready(sc, HDC_STATUS_REQ)) + return -1; + bus_space_write_1(sc->sc_iot, sc->sc_ioh, HDC_DATA, data); + /* + * XXX: Here, it seems to need some wait. But why? + * o printf("%x", data); + * x no-wait + * x delay(1000); + * o delay(10000); + * o hdc_wait_for_ready(sc, HDC_STATUS_REQ); + * o ltsleep(&hdc_dummy, 255, "poll", hz/100, NULL); + * x bus_space_read_1(sc->sc_iot, sc->sc_ioh, HDC_STATUS); + */ + hdc_wait_for_ready(sc, HDC_STATUS_REQ); + DPRINTF(D_XFER, ("send(%x)", data)); + return 0; +} + +static int +hdc_read_data_1(struct hdc_softc *sc) +{ + int data; + + if (hdc_wait_for_ready(sc, HDC_STATUS_REQ)) + return -1; + data = bus_space_read_1(sc->sc_iot, sc->sc_ioh, HDC_DATA); + /* + * XXX: Here, it seems to need some wait. + * o printf("%x", data); + * x no-wait + * x delay(1000); + * o delay(10000); + * o hdc_wait_for_ready(sc, HDC_STATUS_REQ); + * o ltsleep(&hdc_dummy, 255, "poll", hz/100, NULL); + */ + hdc_wait_for_ready(sc, HDC_STATUS_REQ); + DPRINTF(D_XFER, ("recv(%x)", data)); + return data; +} + +/* + * interrupt handler. + * 戻り値は意味を持たないので常に 0。状態は xs->error とかで知らせる。 + */ +static int +hdc_intr(void *arg) +{ + struct hdc_softc *sc = arg; + struct hdc_acb *acb = sc->sc_nexus; + struct scsipi_xfer *xs; + struct scsipi_periph *periph; + int i, r; + int data; + + DPRINTF(D_TRACE, ("hdc_intr()")); + + if (acb == NULL) { + DPRINTF(D_MSG, ("acb==NULL ")); + return 0; + } + xs = acb->acb_xs; + periph = xs->xs_periph; + + loop: + switch (sc->sc_phase) { + case PH_COMMAND: + for (i = 0; i < acb->acb_cmdlen; i++) { + if (hdc_write_data_1(sc, ((u_char*)(&acb->acb_cmd))[i]) + < 0) { + xs->error = XS_TIMEOUT; + return 0; + } + } + break; + + case PH_DATAIN: + if (acb->acb_dleft == 0) { + /* + * ドライバ的には全部読み込んだつもりなのにまだ DATAIN + * フェーズのままだとどうすべか。 + * コントローラの気が済むまで読み込めばいいのか? + */ + scsipi_printaddr(periph); + printf("DATAIN but dleft==0\n"); + xs->error = XS_DRIVER_STUFFUP; + return 0; + } + if (hdc_dataio(sc, DMAC_OCR_DIR_DTM)) + return -1; /* XXX */ + break; + + case PH_DATAOUT: + if (acb->acb_dleft == 0) { + /* + * もう全部送信したつもりなのにまだ DATAOUT フェーズ + * のまま。 + */ + scsipi_printaddr(periph); + printf("DATAOUT but dleft==0\n"); + xs->error = XS_DRIVER_STUFFUP; + return 0; + } + if (hdc_dataio(sc, DMAC_OCR_DIR_MTD)) + return -1; /* XXX */ + break; + + case PH_STATUS: + if ((data = hdc_read_data_1(sc)) < 0) { + xs->error = XS_TIMEOUT; + return 0; + } + /* DPRINTF(D_MSG, ("status phase=%d\n", data)); */ + break; + + case PH_MSGIN: + if ((data = hdc_read_data_1(sc)) < 0) { + xs->error = XS_TIMEOUT; + return 0; + } + /* DPRINTF(D_MSG, ("msgin phase=%d\n", data)); */ + break; + + case PH_BUSFREE: + if (sc->sc_prevphase == PH_MSGIN) { + xs->xs_status |= XS_STS_DONE; + } else if (sc->sc_prevphase == PH_SELECT) { + /* nothing to do */ + } else { + scsipi_printaddr(periph); + printf("unexpected busfree phase: %d\n", + sc->sc_prevphase); + goto reset; + } + sc->sc_state = HDC_IDLE; + return 0; + + default: + printf("unsupported phase %d\n", sc->sc_phase); + goto reset; + } + + /* Phase transition */ + /* + * The hdc controller (not device driver!) does not + * have an arbitration phase. And it also cannot detect + * a (re)selection phase. + */ + //phasecheck: + r = bus_space_read_1(sc->sc_iot, sc->sc_ioh, HDC_STATUS); + switch (r & HDC_PHASE_MASK) { + case 0: + hdc_phase(sc, PH_BUSFREE); + break; + case HDC_PHASE_COMMAND: + hdc_phase(sc, PH_COMMAND); + break; + case HDC_PHASE_STATUS: + hdc_phase(sc, PH_STATUS); + break; + case HDC_PHASE_DATAOUT: + hdc_phase(sc, PH_DATAOUT); + break; + case HDC_PHASE_DATAIN: + hdc_phase(sc, PH_DATAIN); + break; + case HDC_PHASE_MSGOUT: + hdc_phase(sc, PH_MSGOUT); + break; + case HDC_PHASE_MSGIN: + hdc_phase(sc, PH_MSGIN); + break; + default: + scsipi_printaddr(periph); + printf("unknown phase: %d\n", sc->sc_phase); + goto reset; + } + DPRINTF(D_TRACE, ("loop_hdc_intr()")); + goto loop; + + reset: + hdc_bus_reset(sc); + return 0; +} + +/* + * Do Data transfer phase. + */ +static int +hdc_dataio(struct hdc_softc *sc, int dir) +{ + volatile struct dmac *dmac = &IODEVbase->io_dma[HDC_DMA]; + struct hdc_acb *acb = sc->sc_acb; + struct scsipi_xfer *xs = acb->acb_xs; + int error; + int dleft; /* 1回分の転送量 */ + int n; + + DPRINTF(D_TRACE, ("hdc_dataio")); + +#ifdef HDC_BUSDMA +#else /* HDC_BUSDMA */ + /* Set DMAC */ + dmac->csr = 0xff; + dmac->ocr &= ~DMAC_OCR_DIR_MASK; + dmac->ocr |= dir; + dleft = MIN(acb->acb_dleft, DMAC_MAXSEGSZ); + dmac->mtc = dleft; + dmac->mar = (u_long)kvtop(acb->acb_dp); +#endif /* HDC_BUSDMA */ + + if (hdc_wait_for_ready(sc, HDC_STATUS_REQ)) + return -1; /* XXX */ + if ((xs->xs_control & XS_CTL_POLL)) + error = hdc_dataio_poll(sc, dir); + else + error = hdc_dataio_intr(sc, dir); + + if (error) { + xs->error = XS_TIMEOUT; + xs->status |= XS_STS_DONE; +printf("!"); + } else { + n = dleft - dmac->mtc; + acb->acb_dleft -= n; + acb->acb_dp += n; + } + return error; +} + +/* + * Do Data transfer phase using DMA with interrupts. + * return value: + * 0 : goto next (= phase check). + * -1 : timeout, return + */ +static int +hdc_dataio_intr(struct hdc_softc *sc, int dir) +{ +#ifdef HDC_BUSDMA + struct hdc_acb *acb = sc->sc_acb; + struct dmac_channel_stat *chan = sc->sc_dma_ch; + struct dmac_dma_xfer *xf; +#else + volatile struct dmac *dmac = &IODEVbase->io_dma[HDC_DMA]; +#endif + int count; + int phase; + int s; + int r; + + DPRINTF(D_TRACE, ("_intr()")); + + phase = bus_space_read_1(sc->sc_iot, sc->sc_ioh, HDC_STATUS); + phase &= HDC_PHASE_MASK; + +#ifdef HDC_BUSDMA + if (bus_dmamap_load(sc->sc_dmat, sc->sc_dmamap, acb->acb_addr, + acb->acb_datalen, NULL, BUS_DMA_WAITOK)) { + printf("bus_dmamap_load failed\n"); + return -1; + } + xf = dmac_alloc_xfer(chan, sc->sc_dmat, sc->sc_dmamap); + chan->ch_dcr = DMAC_DCR_XRM_CSWOH + | DMAC_DCR_OTYP_EASYNC + | DMAC_DCR_OPS_8BIT; + chan->ch_ocr = DMAC_OCR_REQG_EXTERNAL; + xf->dx_ocr = dir; + xf->dx_scr = DMAC_SCR_MAC_COUNT_UP | DMAC_SCR_DAC_NO_COUNT; + xf->dx_device = sc->sc_addr + HDC_DATA*2 + 1; + + sc->sc_wchan = 1; + dmac_load_xfer(chan->ch_softc, xf); + dmac_start_xfer_offset(chan->ch_softc, xf, 0, acb->acb_dleft); +#else /* HDC_BUSDMA */ + /* Initialize */ + sc->sc_wchan = 1; + dmac->ccr = DMAC_CCR_STR | DMAC_CCR_INT; +#endif /* HDC_BUSDMA */ + + /* Loop for waiting dmaintr() */ + for (count = 1000; count; count--) { + s = splbio(); + if (sc->sc_wchan == 0) { + splx(s); + return 0; + } + splx(s); + /* + * Check phase transition. + * Thanks to GONTA. + */ + r = bus_space_read_1(sc->sc_iot, sc->sc_ioh, HDC_STATUS); + if ((r & HDC_PHASE_MASK) != phase) { + sc->sc_abort = 1; +#ifdef HDC_BUSDMA + dmac_sab_xfer(chan->ch_softc, xf); +#else + DPRINTF(D_MSG, ("SAB=%d", dmac->mtc)); + dmac->ccr |= DMAC_CCR_SAB; +#endif + } + ltsleep(&hdc_dummy, PZERO | PCATCH, "dmaintr", hz/100, NULL); + } + printf("dataio_intr timeout"); + return -1; /* XXX: phase ? */ +} + +/* + * Do Data transfer phase using DMA without interrupts. + */ +static int +hdc_dataio_poll(struct hdc_softc *sc, int dir) +{ + volatile struct dmac *dmac = &IODEVbase->io_dma[HDC_DMA]; + int count; + int phase; + int r; + + DPRINTF(D_TRACE, ("_poll()")); + + phase = bus_space_read_1(sc->sc_iot, sc->sc_ioh, HDC_STATUS); + phase &= HDC_PHASE_MASK; + + dmac->ccr = DMAC_CCR_STR; + for (count = 1000; count; count--) { + if ((dmac->csr & 0x90)) + return 0; + /* Check phase transition */ + r = bus_space_read_1(sc->sc_iot, sc->sc_ioh, HDC_STATUS); + if ((r & HDC_PHASE_MASK) != phase) { + DPRINTF(D_MSG, ("SAB")); + dmac->ccr |= DMAC_CCR_SAB; + } + delay(100); + } + printf("dataio_poll timeout"); + return -1; /* XXX: phase ? */ +} + +#ifdef HDC_BUSDMA +int +hdc_allocmem(struct hdc_softc *sc, struct hdc_acb *acb) +{ + int error; + int wait; + + DPRINTF(D_TRACE, ("hdc_allocmem(")); + + wait = BUS_DMA_NOWAIT; + + error = bus_dmamem_alloc(sc->sc_dmat, acb->acb_datalen, 32, 0, + acb->acb_segs, + sizeof(acb->acb_segs) / sizeof(acb->acb_segs[0]), + &acb->acb_nsegs, wait); + if (error) + goto out; + + error = bus_dmamem_map(sc->sc_dmat, acb->acb_segs, acb->acb_nsegs, + acb->acb_datalen, &acb->acb_addr, wait | BUS_DMA_COHERENT); + if (error) + goto free; + + error = bus_dmamap_create(sc->sc_dmat, acb->acb_datalen, + sizeof(acb->acb_segs) / sizeof(acb->acb_segs[0]), + DMAC_MAXSEGSZ, 0, wait, &acb->acb_map); + if (error) + goto unmap; + + error = bus_dmamap_load(sc->sc_dmat, acb->acb_map, + acb->acb_addr, acb->acb_datalen, NULL, wait); + if (error) + goto destroy; + + DPRINTF(D_TRACE, (")")); + return 0; + + destroy: + DPRINTF(D_TRACE, ("D")); + bus_dmamap_destroy(sc->sc_dmat, acb->acb_map); + unmap: + DPRINTF(D_TRACE, ("U")); + bus_dmamem_unmap(sc->sc_dmat, acb->acb_addr, acb->acb_datalen); + free: + DPRINTF(D_TRACE, ("F")); + bus_dmamem_free(sc->sc_dmat, acb->acb_segs, acb->acb_nsegs); + out: + return error; +} +#endif /* HDC_BUSDMA */ + +#if 0 +static void +hdc_timeout(struct hdc_acb *acb) +{ + struct scsipi_xfer *xs = acb->acb_xs; + struct scsipi_periph *periph = xs->xs_periph; + //struct hdc_softc *sc = + // (void *) periph->periph_channel->chan_adapter->adapt_dev; + int s; + + DPRINTF(D_TRACE, ("hdc_timeout()")); + + s = splbio(); + scsipi_printaddr(periph); + printf("timeout"); + + if (acb->acb_flag & ACB_ABORT) { + /* abort timed out */ + printf(" again\n"); + /* XXX must reset! */ + } else { + /* Abort the operation that has timed out */ + printf("\n"); + xs->error = XS_TIMEOUT; + xs->xs_status |= XS_STS_DONE; + } + splx(s); +} +#endif + +/* + * Wrapper for scsipi_done(). + */ +static void +hdc_done(struct hdc_softc *sc, struct hdc_acb *acb) +{ + struct scsipi_xfer *xs = acb->acb_xs; + + DPRINTF(D_TRACE, ("hdc_done(")); + + if (xs->error == XS_NOERROR) { + xs->resid = acb->acb_dleft; + } + hdc_dequeue(sc, acb); + hdc_free_acb(sc, acb); + scsipi_done(xs); + DPRINTF(D_TRACE, (")\n")); +} + +/* + * ioctl for SCBUSIORESET. + */ +static int +hdc_ioctl(struct scsipi_channel *chan, u_long cmd, caddr_t arg, int flag, + struct proc *p) +{ + struct hdc_softc *sc = (struct hdc_softc *)chan->chan_adapter->adapt_dev; + + switch (cmd) { + case SCBUSIORESET: + hdc_bus_reset(sc); + return 0; + default: + DPRINTF(D_MSG, ("hdc_ioctl = %lx\n", cmd)); + break; + } + return ENOTTY; +} + +/* + * bus reset. + */ +static void +hdc_bus_reset(struct hdc_softc *sc) +{ + bus_space_write_1(sc->sc_iot, sc->sc_ioh, HDC_RESET, 1); +} + + +#ifdef HDC_DEBUG +static void +print_xfer(struct scsipi_xfer *xs) +{ + int i; + char buf[256]; + + if ((hdc_debug & D_MSG) == 0) + return; + + bitmask_snprintf(xs->xs_control, "\20" + "\x1NOSLEEP" "\x2POLL" "\x3""DISC" "\x4""ASYNC" + "\x5USERCMD" "\x6SILENT" "\x7IGNRDY" "\x8IGNCHG" + "\x9IGNREQ" "\xaSLTDEV" "\xbRESET" "\xc""DATAUIO" + "\xd""DATAIN" "\xe""DATAOUT" "\xfTARGET" "\x10""ESCAPE" + "\x11URGENT" "\x12SIMPLETAG" "\x13ORDERTAG" "\x14HEADTAG" + "\x15THAWPRH" "\x16""FRZPRH" "\x17""DATONSTK" "\x18REQSNS", + buf, sizeof(buf)); + printf("0x%s", buf); + + /* verbose */ + printf(", cmdlen=%d, cmd", xs->cmdlen); + for (i = 0; i < xs->cmdlen; i++) + printf("%c%02x", (i)? ' ' : '=', ((u_char *)xs->cmd)[i]); + + printf(", datalen=%d, resid=%d, error=%d\n", + xs->datalen, xs->resid, xs->error + ); +} + +static void +print_acb(struct hdc_softc *sc) +{ + struct hdc_acb *acb; + + if ((hdc_debug & D_ACB) == 0) + return; + + printf("acb[n="); + if (sc->sc_nexus) + print_acb1(sc, sc->sc_nexus); + printf(",r="); + TAILQ_FOREACH(acb, &sc->ready_list, acb_chain) + print_acb1(sc, acb); + printf(",f="); + TAILQ_FOREACH(acb, &sc->free_list, acb_chain) + print_acb1(sc, acb); + printf("] "); +} + +static void +print_acb1(struct hdc_softc *sc, struct hdc_acb *acb) +{ + int i; + + if ((hdc_debug & D_ACB) == 0) + return; + + for(i = 0; i < sizeof(sc->sc_acb)/sizeof(sc->sc_acb[0]); i++) { + struct hdc_acb *a = &sc->sc_acb[i]; + if (a == acb) { + printf("%d", i); + return; + } + } + printf("!"); +} +#endif /* HDC_DEBUG */ + +#endif /* NHDC > 0 */ --- /dev/null 2011-10-09 03:24:27.000000000 +0900 +++ sys/arch/x68k/dev/hdcvar.h 2002-12-01 22:58:07.000000000 +0900 @@ -0,0 +1,132 @@ +/* $Id: hdcvar.h,v 1.36 2002/12/01 13:58:07 isaki Exp $ */ + +/* + * Copyright (c) 2001 Tetsuya Isaki. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * hdc - SASI compatible Hard Disk Controller as SCSI controller. + * It's known as 'SxSI' in x68k world. + */ + +#define HDC_ADDR (0xe96000) +#define HDC_DMA (1) +#define HDC_DMAINTR (0x66) + +#define HDC_DATA (0) +#define HDC_STATUS (1) +#define HDC_SELOFF HDC_STATUS +#define HDC_RESET (2) +#define HDC_SELON (3) +#define HDC_PORTSIZE (4) + +#define HDC_STATUS_REQ (0x01) +#define HDC_STATUS_BSY (0x02) +#define HDC_STATUS_IO (0x04) +#define HDC_STATUS_CD (0x08) +#define HDC_STATUS_MSG (0x10) + +#define HDC_PHASE_MASK (0x1e) +#define HDC_PHASE_COMMAND (HDC_STATUS_BSY | HDC_STATUS_CD) +#define HDC_PHASE_DATAIN (HDC_STATUS_BSY | HDC_STATUS_IO) +#define HDC_PHASE_DATAOUT (HDC_STATUS_BSY) +#define HDC_PHASE_MSGIN (HDC_STATUS_BSY | HDC_STATUS_IO \ + | HDC_STATUS_CD | HDC_STATUS_MSG) +#define HDC_PHASE_MSGOUT (HDC_STATUS_BSY | HDC_STATUS_CD | HDC_STATUS_MSG) +#define HDC_PHASE_STATUS (HDC_STATUS_BSY | HDC_STATUS_CD | HDC_STATUS_IO) + +#define PH_BUSFREE (0) +#define PH_SELECT (1) +#define PH_COMMAND (2) +#define PH_DATAIN (3) +#define PH_DATAOUT (4) +#define PH_MSGIN (5) +#define PH_MSGOUT (6) +#define PH_STATUS (7) + +struct hdc_acb { + TAILQ_ENTRY(hdc_acb) acb_chain; + struct scsipi_xfer *acb_xs; /* SCSI xfer ctrl block from scsipi(9) */ + int acb_flag; +#define ACB_ALLOC (0x01) +#define ACB_NEXUS (0x02) +#define ACB_ABORT (0x04) + struct scsi_generic acb_cmd; /* SCSI command block */ + int acb_cmdlen; + u_char *acb_data; + int acb_datalen; + u_char *acb_dp; + int acb_dleft; + int acb_timeout; + +#ifdef HDC_BUSDMA + /* DMA buffer */ + caddr_t acb_addr; + bus_dmamap_t acb_map; + bus_dma_segment_t acb_segs[1]; + int acb_nsegs; +#endif /* HDC_BUSDMA */ +}; + +struct hdc_tinfo { + unsigned short ti_lubusy; /* What local units/subr. are busy ? */ +}; + +struct hdc_softc { + struct device sc_dev; + + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + + struct scsipi_adapter sc_adapter; + struct scsipi_channel sc_channel; + + TAILQ_HEAD(, hdc_acb) free_list, ready_list; + struct hdc_acb *sc_nexus; /* current command */ + struct hdc_acb sc_acb[8]; /* one per target */ + struct hdc_tinfo sc_tinfo[8]; + + /* Adapter state */ + int sc_phase; /* Copy of what bus phase we are in */ + int sc_prevphase; /* Copy of what bus phase we were in */ + int sc_state; /* State applicable to the adapter */ +#define HDC_IDLE (0) +#define HDC_CONNECTED (1) + int sc_abort; /* SAB flag */ + + /* Hardware stuff */ + int sc_id; /* SCSI ID is fixed to 7 */ + int sc_wchan; /* wait status: 0=go, 1=waiting */ + +#ifdef HDC_BUSDMA + u_int8_t *sc_addr; /* For DMAC->DAC */ + + /* DMA buffer */ + bus_dma_tag_t sc_dmat; + bus_dmamap_t sc_dmamap; + struct dmac_channel_stat *sc_dma_ch; +#endif +}; --- /dev/null 2011-10-09 03:24:27.000000000 +0900 +++ share/man/man4/man4.x68k/hdc.4 2002-08-03 17:58:38.000000000 +0900 @@ -0,0 +1,50 @@ +.\" $Id: hdc.4,v 1.4 2002/08/03 08:58:38 isaki Exp $ +.\" +.\" Copyright (c) 2001 Tetsuya Isaki. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. The name of the author may not be used to endorse or promote products +.\" derived from this software without specific prior written permission +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +.\" AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +.\" OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd Aug 14, 2001 +.Os +.Dt HDC 4 x68k +.Sh NAME +.Nm hdc +.Nd Old SASI compatible hard disk controller as SCSI host adapter +.Sh SYNOPSIS +.Cd "hdc0 at intio0 addr 0xe96000 dma 1 dmaintr 102" +.Cd scsibus* at hdc? +.Sh DESCRIPTION +.Nm +drives built-in hard disk controller in X68000/ACE/PRO/EXPERT. +It is implemented as an +.Xr scsi 4 +device. +.Pp +.Sh BUGS +The controller cannot generate a parity-bit on the bus. +.Pp +The controller cannot generate atention line. +.Sh SEE ALSO +.Xr intio 4 , +.Xr scsi 4