diff --git a/sys/dev/mca/dasd_mca.c b/sys/dev/mca/dasd_mca.c new file mode 100644 index 000000000000..560ed4fd0f9c --- /dev/null +++ b/sys/dev/mca/dasd_mca.c @@ -0,0 +1,810 @@ +/* $NetBSD: dasd_mca.c,v 1.1 2001/04/19 07:30:24 jdolecek Exp $ */ + +/* + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jaromir Dolecek. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. 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. + */ + +/* + * Driver for MCA ESDI controllers and disks conforming to IBM DASD + * spec. + * + * The driver was written with DASD Storage Interface Specification + * for MCA rev. 2.2 in hands, thanks to Scott Telford . + * + * TODO: + * - move the MCA DMA controller (dasd_setup_dma()) goo to device driver + * independant location + * - improve error recovery + * add any soft resets when anything gets stuck? + * - test with > 1 disk (this is supported by some controllers), eliminate + * any remaining devno=0 assumptions if there are any still + * - test with > 1 ESDI controller in machine; shared interrupts + * necessary for this to work should be supported - dasd_intr() specifically + * checks if the interrupt is for this controller + */ + +#include "rnd.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if NRND > 0 +#include +#endif + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#define DASD_MAXDEVS 7 +struct dasd_mca_softc { + struct device sc_dev; + + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + + bus_dma_tag_t sc_dmat; /* DMA tag as passed by parent */ + bus_space_handle_t sc_dmaextcmdh; + bus_space_handle_t sc_dmaexech; + + void *sc_ih; /* interrupt handle */ + int sc_drq; /* DRQ number */ + int sc_cmd_async; /* asynchronous cmd pending */ + + int sc_flags; +#define DASD_QUIET 0x01 /* don't dump cmd error info */ + struct ed_softc *sc_ed[DASD_MAXDEVS]; + + int sc_maxdevs; /* maximum # of devs supported by + * controller */ +}; + +int dasd_mca_probe __P((struct device *, struct cfdata *, void *)); +void dasd_mca_attach __P((struct device *, struct device *, void *)); + +struct cfattach dasd_mca_ca = { + sizeof(struct dasd_mca_softc), dasd_mca_probe, dasd_mca_attach +}; + +#define DMA_EXTCMD 0x18 +#define DMA_EXEC 0x1A + +static int dasd_intr __P((void *)); +static void dasd_attach_ed __P((struct device *)); +static void dasd_dump_status_block __P((struct dasd_mca_softc *, int, int, + int)); +static int dasd_setup_dma __P((struct dasd_mca_softc *, struct buf *, + bus_addr_t, bus_size_t)); +static int dasd_do_attn __P((struct dasd_mca_softc *, int, int, int)); +static int dasd_cmd_wait __P((struct dasd_mca_softc *, int, int, int)); + +int +dasd_mca_probe(parent, match, aux) + struct device *parent; + struct cfdata *match; + void *aux; +{ + struct mca_attach_args *ma = aux; + + switch (ma->ma_id) { + case MCA_PRODUCT_IBM_ESDIC: + case MCA_PRODUCT_IBM_ESDIC_IG: + return (1); + default: + return (0); + } +} + +void +dasd_mca_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct dasd_mca_softc *sc = (void *) self; + struct mca_attach_args *ma = aux; + int pos2, pos3, pos4; + int irq, drq, iobase; + const char *typestr; + + pos2 = mca_conf_read(ma->ma_mc, ma->ma_slot, 2); + pos3 = mca_conf_read(ma->ma_mc, ma->ma_slot, 3); + pos4 = mca_conf_read(ma->ma_mc, ma->ma_slot, 4); + + /* + * POS register 2: (adf pos0) + * + * 7 6 5 4 3 2 1 0 + * \ \____/ \ \__ enable: 0=adapter disabled, 1=adapter enabled + * \ \ \___ Primary/Alternate Port Adresses: + * \ \ 0=0x3510-3517 1=0x3518-0x351f + * \ \_____ DMA Arbitration Level: 0101=5 0110=6 0111=7 + * \ 0000=0 0001=1 0011=3 0100=4 + * \_________ Fairness On/Off: 1=On 0=Off + * + * POS register 3: (adf pos1) + * + * 7 6 5 4 3 2 1 0 + * 0 0 \_/ + * \__________ DMA Burst Pacing Interval: 10=24ms 11=31ms + * 01=16ms 00=Burst Disabled + * + * POS register 4: (adf pos2) + * + * 7 6 5 4 3 2 1 0 + * \_/ \__ DMA Pacing Control: 1=Disabled 0=Enabled + * \____ Time to Release: 1X=6ms 01=3ms 00=Immediate + * + * IRQ is fixed to 14 (0x0e). + */ + + switch (ma->ma_id) { + case MCA_PRODUCT_IBM_ESDIC: + typestr = "IBM ESDI Fixed Disk Controller"; + break; + case MCA_PRODUCT_IBM_ESDIC_IG: + typestr = "IBM Integ. ESDI Fixed Disk & Controller"; + break; + default: + /* never reached */ + } + + printf(" slot %d: %s\n", ma->ma_slot+1, typestr); + + irq = ESDIC_IRQ; + iobase = (pos2 & IO_IS_ALT) ? ESDIC_IOALT : ESDIC_IOPRM; + drq = (pos2 & DRQ_MASK) >> 2; + +#ifdef DIAGNOSTIC + /* + * It's not strictly necessary to check this, machine configuration + * utility uses only valid adresses. + */ + if (drq == 2 || drq >= 8) { + printf("%s: invalid DMA Arbitration Level %d\n", + sc->sc_dev.dv_xname, drq); + return; + } +#endif + + printf("%s: Fairness %s, Release %s, DRQ %d, ", + sc->sc_dev.dv_xname, + (pos2 & FAIRNESS_ENABLE) ? "On" : "Off", + (pos4 & RELEASE_1) ? "6ms" + : ((pos4 & RELEASE_2) ? "3ms" : "Immediate"), + drq); + if ((pos4 & PACING_CTRL_DISABLE) == 0) { + static const char * const pacint[] = + { "disabled", "16ms", "24ms", "31ms"}; + printf("DMA burst pacing interval %s\n", + pacint[(pos3 & PACING_INT_MASK) >> 4]); + } else + printf("DMA pacing control disabled\n"); + + sc->sc_iot = ma->ma_iot; + sc->sc_drq = drq; + + if (bus_space_map(sc->sc_iot, iobase, + ESDIC_REG_NPORTS, 0, &sc->sc_ioh)) { + printf("%s: couldn't map registers\n", + sc->sc_dev.dv_xname); + return; + } + + if (bus_space_map(sc->sc_iot, DMA_EXTCMD, 1, 0, &sc->sc_dmaextcmdh)) { + printf("%s: couldn't map registers\n", + sc->sc_dev.dv_xname); + return; + } + if (bus_space_map(sc->sc_iot, DMA_EXEC, 1, 0, &sc->sc_dmaexech)) { + printf("%s: couldn't map registers\n", + sc->sc_dev.dv_xname); + return; + } + + sc->sc_dmat = ma->ma_dmat; + + sc->sc_ih = mca_intr_establish(ma->ma_mc, irq, IPL_BIO, dasd_intr, sc); + if (sc->sc_ih == NULL) { + printf("%s: couldn't establish interrupt handler\n", + sc->sc_dev.dv_xname); + return; + } + printf("%s: interrupting at irq %d\n", sc->sc_dev.dv_xname, irq); + + /* + * Integrated ESDI controller supports only one disk, other + * controllers support two disks. + */ + if (ma->ma_id == MCA_PRODUCT_IBM_ESDIC_IG) + sc->sc_maxdevs = 1; + else + sc->sc_maxdevs = 2; + + /* Defer probe for individual disks until interrupts are enabled. */ + config_interrupts(self, dasd_attach_ed); +} + +/* + * Try to attach ed* at dasd? if any configured and installed now + * that interrupts are enabled. + */ +static void +dasd_attach_ed(self) + struct device *self; +{ + struct dasd_mca_softc *sc = (void *) self; + struct ed_softc *ed; + struct ed_attach_args eda; + int devno; + + /* Do a reset to ensure sane state after warm boot. */ + if (bus_space_read_1(sc->sc_iot, sc->sc_ioh, BSR) & BSR_BUSY) { + /* hard reset */ + printf("%s: controller busy, performing hardware reset ...\n", + sc->sc_dev.dv_xname); + bus_space_write_1(sc->sc_iot, sc->sc_ioh, BCR, + BCR_INT_ENABLE|BCR_RESET); + } else { + /* "SOFT" reset */ + dasd_do_attn(sc, ATN_RESET_ATTACHMENT, DASD_DEVNO_CONTROLLER,0); + } + + /* Wait until the reset completes. */ + while(bus_space_read_1(sc->sc_iot, sc->sc_ioh, BSR) & BSR_BUSY) + delay(1); + + /* + * Get dummy ed_softc to be used during probe. Once a disk is + * found, ed_mca_attach() calls dasd_add_disk() to insert the + * right pointer into sc->sc_ed[] array. + */ + MALLOC(ed, struct ed_softc *, sizeof(struct ed_softc), + M_TEMP, M_WAITOK); + + /* be quiet duting probes */ + sc->sc_flags |= DASD_QUIET; + + /* check for attached disks */ + for(devno=0; devno < sc->sc_maxdevs; devno++) { + eda.sc_devno = devno; + eda.sc_dmat = sc->sc_dmat; + sc->sc_ed[devno] = ed; + sc->sc_ed[devno] = + (void *) config_found_sm(self, &eda, NULL, NULL); + } + + /* enable full error dumps again */ + sc->sc_flags &= ~DASD_QUIET; + + /* cleanup */ + FREE(ed, M_TEMP); + + /* XXX disestablish interrupt if no disks found ? */ +} + +void +dasd_add_disk(sc, ed, devno) + struct dasd_mca_softc *sc; + struct ed_softc *ed; + int devno; +{ + sc->sc_ed[devno] = ed; +} + +static int +dasd_intr(arg) + void *arg; +{ + struct dasd_mca_softc *sc = arg; + u_int8_t isr, intr_id; + u_int16_t sifr; + int cmd, devno, bioerror; + struct ed_softc *ed=NULL; + + /* + * Check if the interrupt was for us. + */ + if ((bus_space_read_1(sc->sc_iot, sc->sc_ioh, BSR) & BSR_INTR) == 0) + return (0); + + /* + * Read ISR to find out interrupt type. This also clears the interrupt + * condition and BSR_INTR flag. Accordings to docs interrupt ID of 0, 2 + * and 4 are reserved and not used. + */ + isr = bus_space_read_1(sc->sc_iot, sc->sc_ioh, ISR); + intr_id = isr & ISR_INTR_ID_MASK; + +#ifdef DEBUG + if (intr_id == 0 || intr_id == 2 || intr_id == 4) { + printf("%s: bogus interrupt id %d\n", sc->sc_dev.dv_xname, + (int) intr_id); + return (0); + } +#endif + + /* Get number of device whose intr this was */ + devno = (isr & 0xe0) >> 5; + + /* + * Get Status block. Higher byte always says how long the status + * block is, rest is device number and command code. + * Check the status block length against our supported maximum length + * and fetch the data. + */ + if (bus_space_read_1(sc->sc_iot, sc->sc_ioh,BSR) & BSR_SIFR_FULL + && intr_id != ISR_RESET_COMPLETED) { + size_t len; + int i; + + sifr = le16toh(bus_space_read_2(sc->sc_iot, sc->sc_ioh, SIFR)); + len = (sifr & 0xff00) >> 8; + if (len > DASD_MAX_CMD_RES_LEN) { + printf("%s: maximum Status Length exceeded: %d > %d\n", + sc->sc_dev.dv_xname, + len, DASD_MAX_CMD_RES_LEN); + goto attn_eoi; + } + + /* Get command code */ + cmd = sifr & SIFR_CMD_MASK; + + /* Read whole status block */ + ed = sc->sc_ed[devno]; + ed->sc_status_block[0] = sifr; + for(i=1; i < len; i++) { + while((bus_space_read_1(sc->sc_iot, sc->sc_ioh, BSR) + & BSR_SIFR_FULL) == 0) + delay(1); + + ed->sc_status_block[i] = le16toh( + bus_space_read_2(sc->sc_iot, sc->sc_ioh, SIFR)); + } + } + + switch (intr_id) { + case ISR_DATA_TRANSFER_RDY: + /* + * Ready to do DMA, setup DMA controller and kick DASD + * controller to do the transfer. + */ + ed = sc->sc_ed[devno]; + if (!dasd_setup_dma(sc, ed->sc_bp, + ed->dmamap_xfer->dm_segs[0].ds_addr, + ed->dmamap_xfer->dm_segs[0].ds_len)) { + /* error XXX bail out? */ + bus_space_write_1(sc->sc_iot, sc->sc_ioh, BCR, + BCR_INT_ENABLE); + } else { + /* OK, proceed with DMA */ + bus_space_write_1(sc->sc_iot, sc->sc_ioh, BCR, + BCR_INT_ENABLE|BCR_DMA_ENABLE); + } + break; + case ISR_COMPLETED: + case ISR_COMPLETED_WITH_ECC: + case ISR_COMPLETED_RETRIES: + case ISR_COMPLETED_WARNING: + bioerror = 0; + break; + case ISR_RESET_COMPLETED: + case ISR_ABORT_COMPLETED: + /* nothing to do */ + break; + default: + if ((sc->sc_flags & DASD_QUIET) == 0) + dasd_dump_status_block(sc, devno, cmd, intr_id); + + bioerror = EIO; + break; + } + + attn_eoi: + /* + * Unless the interrupt is for Data Transfer Ready or + * Attention Error, finish by assertion EOI. This makes + * attachment aware the interrupt is processed and system + * is ready to accept another one. + */ + if (intr_id != ISR_DATA_TRANSFER_RDY && intr_id != ISR_ATTN_ERROR) + dasd_do_attn(sc, ATN_END_INT, devno, intr_id); + + /* If Read or Write Data, wakeup worker thread to finish it */ + if (intr_id != ISR_DATA_TRANSFER_RDY + && (cmd == CMD_READ_DATA || cmd == CMD_WRITE_DATA)) { + sc->sc_ed[devno]->sc_error = bioerror; + wakeup_one(&sc->sc_ed[devno]->dasd_softc); + } + + return (1); +} + +/* + * This follows the exact order for Attention Request as + * written in DASD Storage Interface Specification MC (Rev 2.2). + */ +static int +dasd_do_attn(sc, attn_type, devno, intr_id) + struct dasd_mca_softc *sc; + int attn_type, devno, intr_id; +{ + int tries; + + /* 1. Disable interrupts in BCR. */ + bus_space_write_1(sc->sc_iot, sc->sc_ioh, BCR, 0); + + /* 2. Assure NOT BUSY and NO INTERRUPT PENDING, unless acknowledging + * a RESET COMPLETED interrupt. */ + if (intr_id != ISR_RESET_COMPLETED) { + for(tries=0; tries < 1000; tries++) { + if ((bus_space_read_1(sc->sc_iot, sc->sc_ioh, BSR) + & (BSR_INT_PENDING|BSR_BUSY)) == 0) + break; + } + + if (tries == 1000) { + printf("%s: dasd_do_attn: timeout waiting for attachment to become available\n", + (devno == DASD_DEVNO_CONTROLLER) + ? sc->sc_dev.dv_xname + : sc->sc_ed[devno]->sc_dev.dv_xname); + return (EAGAIN); + } + } + + /* + * 3. Write proper DEVICE NUMBER and Attention number to ATN. + */ + bus_space_write_1(sc->sc_iot, sc->sc_ioh, ATN, + attn_type | (devno << 5)); + + /* + * 4. Enable interrupts via BCR. + */ + bus_space_write_1(sc->sc_iot, sc->sc_ioh, BCR, BCR_INT_ENABLE); + + return (0); +} + +/* + * Wait until command is processed, timeout after 'secs' seconds. + * We use mono_time, since we don't need actual RTC, just time + * interval. + */ +static int +dasd_cmd_wait(sc, devno, cmd, secs) + struct dasd_mca_softc *sc; + int devno, cmd, secs; +{ + struct timeval start, now; + int s; + + s = splclock(); + start = mono_time; + splx(s); + + while((bus_space_read_1(sc->sc_iot,sc->sc_ioh,BSR)&BSR_CMD_INPROGRESS)){ + s = splclock(); + now = mono_time; + splx(s); + if (now.tv_sec - start.tv_sec > secs) + break; + + delay(1); + } + + if (now.tv_sec - start.tv_sec >= secs && + bus_space_read_1(sc->sc_iot, sc->sc_ioh, BSR) & BSR_CMD_INPROGRESS){ + printf("%s: timed out waiting for previous cmd to finish, command %d not executed\n", + sc->sc_ed[devno]->sc_dev.dv_xname, cmd); + return (EAGAIN); + } + + return (0); +} + +int +dasd_run_cmd(sc, cmd, devno, cmd_args, cmd_len, async) + struct dasd_mca_softc *sc; + int cmd; + int devno; + u_int16_t cmd_args[]; + int cmd_len; + int async; +{ + int i, error; + u_int16_t cmd0; + + /* + * If there has been an asynchronous command executed, first wait for it + * to finish. + */ + if (sc->sc_cmd_async) { + /* Wait maximum 15s */ + if (dasd_cmd_wait(sc, devno, cmd, 15)) + return (EAGAIN); /* Busy */ + + sc->sc_cmd_async = 0; + } + + /* Do Attention Request for Command Request. */ + if ((error = dasd_do_attn(sc, ATN_CMD_REQ, devno, 0))) + return (error); + + /* + * Construct the command. The bits are like this: + * + * 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + * \_/ 0 0 1 0 \__/ \_____/ + * \ \__________/ \ \_ Command Code (see CMD_*) + * \ \ \__ Device: 0 common, 7 controller + * \ \__ Options: reserved, bit 10=cache bypass bit + * \_ Type: 00=2B, 01=4B, 10 and 11 reserved + * + * We always use device 0 or 1, so difference is made only by Command + * Code, Command Options and command length. + */ + cmd0 = ((cmd_len == 4) ? (CIFR_LONG_CMD) : 0) + | (devno << 5) + | (cmd_args[0] << 8) | cmd; + cmd_args[0] = cmd0; + + /* + * Write word of CMD to the CIFR. This sets "Command + * Interface Register Full (CMD IN)" in BSR. Once the attachment + * detects it, it reads the word and clears CMD IN. + */ + for(i=0; i < cmd_len; i++) { + bus_space_write_2(sc->sc_iot, sc->sc_ioh, CIFR, + htole16(cmd_args[i])); + + /* wait until CMD IN is cleared */ + while(bus_space_read_1(sc->sc_iot, sc->sc_ioh, BSR) & BSR_CIFR_FULL) + delay(1); + } + + /* + * Attachment is now executing the command. Unless we are executing + * command asynchronously, wait until it finishes. + */ + if (async) { + sc->sc_cmd_async = 1; + return (0); + } + + /* Wait for command to complete, but maximum 15 seconds. */ + if (dasd_cmd_wait(sc, devno, cmd, 15)) + return (EAGAIN); + + /* Check if the command completed successfully; if not, return error */ + switch(SB_GET_CMD_STATUS(sc->sc_ed[devno]->sc_status_block)) { + case ISR_COMPLETED: + case ISR_COMPLETED_WITH_ECC: + case ISR_COMPLETED_RETRIES: + case ISR_COMPLETED_WARNING: + return (0); + default: + return (EIO); + } +} + +static int +dasd_setup_dma(sc, bp, phys, cnt) + struct dasd_mca_softc *sc; + struct buf *bp; + bus_addr_t phys; + bus_size_t cnt; +{ + /* XXX magic constants, should be moved to device-independant location*/ + /* The exact sequence to setup MCA DMA controller is taken from Minix */ + + bus_space_write_1(sc->sc_iot, sc->sc_dmaextcmdh, 0, + 0x90 + sc->sc_drq); + /* Disable access to dma channel */ + bus_space_write_1(sc->sc_iot, sc->sc_dmaextcmdh, 0, + 0x20 + sc->sc_drq); + /* Clear the address byte pointer */ + bus_space_write_1(sc->sc_iot, sc->sc_dmaexech, 0, + (phys >> 0) & 0xff); /* address bits 0..7 */ + bus_space_write_1(sc->sc_iot, sc->sc_dmaexech, 0, + (phys >> 8) & 0xff); /* address bits 8..15 */ + bus_space_write_1(sc->sc_iot, sc->sc_dmaexech, 0, + (phys >> 16) & 0xff); /* address bits 16..23 */ + bus_space_write_1(sc->sc_iot, sc->sc_dmaextcmdh, 0, + 0x40 + sc->sc_drq); + /* Clear the count byte pointer */ + bus_space_write_1(sc->sc_iot, sc->sc_dmaexech, 0, + ((cnt - 1) >> 0) & 0xff); /* count bits 0..7 */ + bus_space_write_1(sc->sc_iot, sc->sc_dmaexech, 0, + ((cnt - 1) >> 8) & 0xff); /* count bits 8..15 */ + bus_space_write_1(sc->sc_iot, sc->sc_dmaextcmdh, 0, + 0x70 + sc->sc_drq); + /* Set the transfer mode */ + bus_space_write_1(sc->sc_iot, sc->sc_dmaexech, 0, + (bp->b_flags & B_READ) ? 0x4C : 0x44); + bus_space_write_1(sc->sc_iot, sc->sc_dmaextcmdh, 0, + 0xA0 + sc->sc_drq); + /* Enable access to dma channel */ + + return (1); +} + +static const char * const dasd_commands[] = { + "Invalid Command", + "Read Data", + "Write Data", + "Read Verify", + "Write with Verify", + "Seek", + "Park Head", + "Get Command Complete Status", + "Get Device Status", + "Get Device Configuration", + "Get POS Information", + "Translate RBA", + "Write Attachment Buffer", + "Read Attachment Buffer", + "Run Diagnostic Test", + "Get Diagnostic Status Block", + "Get MFG Header", + "Format Unit", + "Format Prepare", + "Set MAX RBA", + "Set Power Saving Mode", + "Power Conservation Command", +}; + +static const char * const dasd_cmd_status[256] = { + "Reserved", + "Command completed successfully", + "Reserved", + "Command completed successfully with ECC applied", + "Reserved", + "Command completed successfully with retries", + "Format Command partially completed", /* Status available */ + "Command completed successfully with ECC and retries", + "Command completed with Warning", /* Command Error is available */ + "Aborted", + "Reset completed", + "Data Transfer Ready", /* No Status Block available */ + "Command terminated with failure", /* Device Error is available */ + "DMA Error", /* Retry entire command as recovery */ + "Command Block Error", + "Attention Error (Illegal Attention Code)", + /* 0x14 - 0xff reserved */ +}; + +static const char * const dasd_cmd_error[256] = { + "No Error", + "Invalid parameter in the command block", + "Reserved", + "Command not supported", + "Command Aborted per request", + "Reserved", + "Command rejected", /* Attachment diagnostic failure */ + "Format Rejected", /* Prepare Format command is required */ + "Format Error (Primary Map is not readable)", + "Format Error (Secondary map is not readable)", + "Format Error (Diagnostic Failure)", + "Format Warning (Secondary Map Overflow)", + "Reserved" + "Format Error (Host Checksum Error)", + "Reserved", + "Format Warning (Push table overflow)", + "Format Warning (More pushes than allowed)", + "Reserved", + "Format Warning (Error during verifying)", + "Invalid device number for the command", + /* 0x14-0xff reserved */ +}; + +static const char * const dasd_dev_status[] = { + "Seek or Command complete", + "Track 0 Flag (emulated)", + "Write Fault (emulated)", + "Selected", + "Ready", + "Reserved (0)", + "STANDBY", + "Reserved (0)", +}; + +static const char * const dasd_dev_errors[] = { + "No Error", + "Seek Fault", /* Device report */ + "Interface Fault (Parity, Attn, or Cmd Complete Error)", + "Block not found (ID not found)", + "Block not found (AM not found)", + "Data ECC Error (hard error)", + "ID CRC Error", + "RBA Out of Range", + "Reserved", + "Defective Block", + "Reserved", + "Selection Error", + "Reserved", + "Write Fault", + "No index or sector pulse", + "Device Not Ready", + "Seek Error", /* Attachment report */ + "Bad Format", + "Volume Overflow", + "No Data AM Found", + "(Block not found) No ID AM or ID CRC error occured", + "Reserved", + "Reserved", + "No ID found on track (ID search)", + /* 0x19 - 0xff reserved */ +}; + +static void +dasd_dump_status_block(sc, devno, cmd, intr_id) + struct dasd_mca_softc *sc; + int devno, cmd, intr_id; +{ + struct ed_softc *ed = sc->sc_ed[devno]; + printf("%s: Command: %s, Status: %s\n", + ed->sc_dev.dv_xname, + dasd_commands[ed->sc_status_block[0] & 0x1f], + dasd_cmd_status[SB_GET_CMD_STATUS(ed->sc_status_block)] + ); + printf("%s: IntrId: %s\n", ed->sc_dev.dv_xname, + dasd_cmd_status[intr_id]); + + if (cmd == ISR_COMPLETED_WARNING) { + printf("%s: Command Error Code: %s\n", + ed->sc_dev.dv_xname, + dasd_cmd_error[ed->sc_status_block[1] & 0xff]); + } + + if (cmd == ISR_CMD_FAILED) { + printf("%s: Device: Status: %s, Error Code: %s\n", + ed->sc_dev.dv_xname, + dasd_dev_status[(ed->sc_status_block[2] & 0xff00) >> 8], + dasd_dev_errors[ed->sc_status_block[2] & 0xff]); + } +} diff --git a/sys/dev/mca/dasdreg.h b/sys/dev/mca/dasdreg.h new file mode 100644 index 000000000000..1e352daa83d8 --- /dev/null +++ b/sys/dev/mca/dasdreg.h @@ -0,0 +1,131 @@ +/* $NetBSD: dasdreg.h,v 1.1 2001/04/19 07:30:24 jdolecek Exp $ */ + +/* + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jaromir Dolecek. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. 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. + */ + +/* + * Driver for MCA ESDI controllers and disks. + */ + +#define ESDIC_IOPRM 0x3510 +#define ESDIC_IOALT 0x3518 +#define ESDIC_REG_NPORTS 8 +#define ESDIC_IRQ 14 /* this is fixed */ + +/* pos2 */ +#define IO_IS_ALT 0x02 +#define DRQ_MASK 0x3c +#define FAIRNESS_ENABLE 0x40 + +/* pos3 */ +#define PACING_INT_MASK 0x30 + +/* pos4 */ +#define PACING_CTRL_DISABLE 0x01 +#define RELEASE_2 0x02 /* lower bit of Time to Release */ +#define RELEASE_1 0x04 /* higher bit of Time to Release */ + +/* controller registers */ +#define SIFR 0 /* read Status Interface Register, + 2 bytes, little endian */ +#define SIFR_CMD_MASK 0x2f + +#define CIFR 0 /* write - Command Interface Reg, + 2 bytes, little endian */ +#define CIFR_LONG_CMD (1<<14) /* 4 word command */ + +/* Command Codes */ +#define CMD_READ_DATA 0x01 /* uses DMA */ +#define CMD_WRITE_DATA 0x02 /* uses DMA */ +#define CMD_READ_VERIFY 0x03 +#define CMD_WRITE_VERIFY 0x04 /* uses DMA */ +#define CMD_SEEK 0x05 +#define CMD_PARK_HEAD 0x06 +#define CMD_GET_CMD_COMP_STATUS 0x07 +#define CMD_GET_DEV_STATUS 0x08 +#define CMD_GET_DEV_CONF 0x09 +#define CMD_GET_POS_INFO 0x0A +#define CMD_TRANSLATE_RBA 0x0B +#define CMD_WRITE_ATTACH_BUFF 0x10 /* uses DMA */ +#define CMD_READ_ATTACH_BUFF 0x11 /* uses DMA */ +#define CMD_RUN_DIAG_TEST 0x12 +#define CMD_GET_DIAG_STAT_BLOCK 0x14 +#define CMD_GET_MFG_HEADER 0x15 /* uses DMA */ +#define CMD_FORMAT_UNIT 0x16 /* uses DMA */ +#define CMD_FORMAT_PREPARE 0x17 +#define CMD_SET_MAX_RBA 0x1A +#define CMD_SET_PWR_SAV_MODE 0x1B /* optional */ +#define CMD_POWER_CONS_CMD 0x1C /* optional */ + +#define BCR 2 /* write */ +#define BCR_INT_ENABLE 0x01 +#define BCR_DMA_ENABLE 0x02 +#define BCR_RESET 0x80 + +#define BSR 2 /* read */ +#define BSR_DMA_ENABLED 0x80 +#define BSR_INT_PENDING 0x40 +#define BSR_CMD_INPROGRESS 0x20 +#define BSR_BUSY 0x10 +#define BSR_SIFR_FULL 0x08 /* also called STATUS OUT */ +#define BSR_CIFR_FULL 0x04 +#define BSR_TRANSFER_REQ 0x02 +#define BSR_INTR 0x01 + +#define ISR 3 /* read, Interrupt Status Register */ +#define ISR_DEV_SELECT_MASK 0xE0 +#define ISR_ATTACH_ERR 0x10 +#define ISR_INTR_ID_MASK 0x0F +#define ISR_COMPLETED 0x01 +#define ISR_COMPLETED_WITH_ECC 0x03 +#define ISR_COMPLETED_RETRIES 0x05 +#define ISR_PARTIAL_FORMAT 0x06 /* Status available */ +#define ISR_COMPLETED_WARNING 0x08 +#define ISR_ABORT_COMPLETED 0x09 +#define ISR_RESET_COMPLETED 0x0A +#define ISR_DATA_TRANSFER_RDY 0x0B /* No Status Block */ +#define ISR_CMD_FAILED 0x0C +#define ISR_DMA_ERROR 0x0D +#define ISR_CMD_BLOCK_ERROR 0x0E +#define ISR_ATTN_ERROR 0x0F + +/* Macros to get info from command status block */ +#define SB_GET_CMD_STATUS(sb) (((sb)[1] & 0xff00) >> 8) + +#define ATN 3 /* write, Attention register */ +#define ATN_CMD_REQ 1 +#define ATN_END_INT 2 /* End of Interrupt (EOI) */ +#define ATN_ABORT_CMD 3 +#define ATN_RESET_ATTACHMENT 4 + +#define DASD_DEVNO_CONTROLLER 7 /* Device number for controller */ diff --git a/sys/dev/mca/dasdvar.h b/sys/dev/mca/dasdvar.h new file mode 100644 index 000000000000..199b38b9e969 --- /dev/null +++ b/sys/dev/mca/dasdvar.h @@ -0,0 +1,43 @@ +/* $NetBSD: dasdvar.h,v 1.1 2001/04/19 07:30:24 jdolecek Exp $ */ + +/* + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jaromir Dolecek. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. 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. + */ + +struct ed_attach_args { + int sc_devno; + bus_dma_tag_t sc_dmat; /* DMA tag as passed by parent */ +}; + +int dasd_run_cmd __P((struct dasd_mca_softc *, int, + int, u_int16_t [], int, int)); +void dasd_add_disk __P((struct dasd_mca_softc *, struct ed_softc *, int)); diff --git a/sys/dev/mca/ed_mca.c b/sys/dev/mca/ed_mca.c new file mode 100644 index 000000000000..2338f23ba633 --- /dev/null +++ b/sys/dev/mca/ed_mca.c @@ -0,0 +1,1185 @@ +/* $NetBSD: ed_mca.c,v 1.1 2001/04/19 07:30:24 jdolecek Exp $ */ + +/* + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jaromir Dolecek. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. 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. + */ + +/* + * Disk goo for MCA ESDI controller driver. + */ + +#include "rnd.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if NRND > 0 +#include +#endif + +#include +#include + +#include +#include +#include + +/* #define WDCDEBUG */ + +#ifdef WDCDEBUG +#define WDCDEBUG_PRINT(args, level) printf args +#else +#define WDCDEBUG_PRINT(args, level) +#endif + +#define EDLABELDEV(dev) (MAKEDISKDEV(major(dev), DISKUNIT(dev), RAW_PART)) + +#define ED_BB_SIZE 65536 + +/* XXX: these should go elsewhere */ +cdev_decl(edmca); +bdev_decl(edmca); + +static int ed_mca_probe __P((struct device *, struct cfdata *, void *)); +static void ed_mca_attach __P((struct device *, struct device *, void *)); + +struct cfattach ed_mca_ca = { + sizeof(struct ed_softc), ed_mca_probe, ed_mca_attach +}; + +extern struct cfdriver ed_cd; + +static int ed_get_params __P((struct ed_softc *)); +static int ed_lock __P((struct ed_softc *)); +static void ed_unlock __P((struct ed_softc *)); +static void edgetdisklabel __P((struct ed_softc *)); +static void edgetdefaultlabel __P((struct ed_softc *, struct disklabel *)); +static void ed_shutdown __P((void*)); +// static void edstart __P((void *)); +static void __edstart __P((struct ed_softc*, struct buf *)); +static void bad144intern __P((struct ed_softc *)); +static void edworker __P((void *)); +static void ed_spawn_worker __P((void *)); +static void edmcadone __P((struct ed_softc *)); + +static struct dkdriver eddkdriver = { edmcastrategy }; + +/* + * Just check if it's possible to identify the disk. + */ +static int +ed_mca_probe(parent, match, aux) + struct device *parent; + struct cfdata *match; + void *aux; +{ + u_int16_t cmd_args[2]; + struct dasd_mca_softc *sc = (void *) parent; + struct ed_attach_args *eda = (void *) aux; + + /* + * Get Device Configuration (09). + */ + cmd_args[0] = 6; /* Options: 00s110, s: 0=Physical 1=Pseudo */ + cmd_args[1] = 0; + if (dasd_run_cmd(sc, CMD_GET_DEV_CONF, eda->sc_devno, cmd_args, 2, 0)) + return (0); + + return (1); +} + +static void +ed_mca_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct ed_softc *ed = (void *) self; + struct dasd_mca_softc *sc = (void *) parent; + struct ed_attach_args *eda = (void *) aux; + char pbuf[8]; + int error, nsegs; + + ed->dasd_softc = sc; + ed->sc_dmat = eda->sc_dmat; + ed->sc_devno = eda->sc_devno; + dasd_add_disk(sc, ed, eda->sc_devno); + + BUFQ_INIT(&ed->sc_q); + spinlockinit(&ed->sc_q_lock, "edbqlock", 0); + + if (ed_get_params(ed)) { + printf(": IDENTIFY failed, no disk found\n"); + return; + } + + format_bytes(pbuf, sizeof(pbuf), + (u_int64_t) ed->sc_capacity * DEV_BSIZE); + printf(": %s, %u cyl, %u head, %u sec, 512 bytes/sect x %u sectors\n", + pbuf, + ed->cyl, ed->heads, ed->sectors, + ed->sc_capacity); + + printf("%s: %u spares/cyl, %s.%s.%s.%s.%s\n", + ed->sc_dev.dv_xname, ed->spares, + (ed->drv_flags & (1 << 0)) ? "NoRetries" : "Retries", + (ed->drv_flags & (1 << 1)) ? "Removable" : "Fixed", + (ed->drv_flags & (1 << 2)) ? "SkewedFormat" : "NoSkew", + (ed->drv_flags & (1 << 3)) ? "ZeroDefect" : "Defects", + (ed->drv_flags & (1 << 4)) ? "InvalidSecondary" : "SeconOK" + ); + + /* Create a DMA map for mapping individual transfer bufs */ + if ((error = bus_dmamap_create(ed->sc_dmat, 65536, 1, + 65536, 0, BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW, + &ed->dmamap_xfer)) != 0) { + printf("%s: unable to create xfer DMA map, error=%d\n", + ed->sc_dev.dv_xname, error); + return; + } + + /* + * Allocate DMA memory used in case where passed buf isn't + * physically contiguous. + */ + ed->sc_dmam_sz = ED_BB_SIZE; + if ((error = bus_dmamem_alloc(ed->sc_dmat, ed->sc_dmam_sz, + 65536, 65536, ed->sc_dmam, 1, &nsegs, + BUS_DMA_WAITOK|BUS_DMA_STREAMING)) != 0) { + printf("%s: unable to allocate DMA memory for xfer, errno=%d\n", + ed->sc_dev.dv_xname, error); + bus_dmamap_destroy(ed->sc_dmat, ed->dmamap_xfer); + return; + } + /* + * Map the memory. + */ + if ((error = bus_dmamem_map(ed->sc_dmat, ed->sc_dmam, 1, + ed->sc_dmam_sz, &ed->sc_dmamkva, BUS_DMA_WAITOK)) != 0) { + printf("%s: unable to map DMA memory, error=%d\n", + ed->sc_dev.dv_xname, error); + bus_dmamem_free(ed->sc_dmat, ed->sc_dmam, 1); + bus_dmamap_destroy(ed->sc_dmat, ed->dmamap_xfer); + return; + } + + + /* + * Initialize and attach the disk structure. + */ + ed->sc_dk.dk_driver = &eddkdriver; + ed->sc_dk.dk_name = ed->sc_dev.dv_xname; + disk_attach(&ed->sc_dk); +#if 0 + wd->sc_wdc_bio.lp = wd->sc_dk.dk_label; +#endif + ed->sc_sdhook = shutdownhook_establish(ed_shutdown, ed); + if (ed->sc_sdhook == NULL) + printf("%s: WARNING: unable to establish shutdown hook\n", + ed->sc_dev.dv_xname); +#if NRND > 0 + rnd_attach_source(&ed->rnd_source, ed->sc_dev.dv_xname, + RND_TYPE_DISK, 0); +#endif + + config_pending_incr(); + kthread_create(ed_spawn_worker, (void *) ed); +} + +void +ed_spawn_worker(arg) + void *arg; +{ + struct ed_softc *ed = (struct ed_softc *) arg; + int error; + + /* Now, everything is ready, start a kthread */ + if ((error = kthread_create1(edworker, ed, &ed->sc_worker, + "%s", ed->sc_dev.dv_xname))) { + printf("%s: cannot spawn worker thread: errno=%d\n", + ed->sc_dev.dv_xname, error); + panic("ed_spawn_worker"); + } +} + +/* + * Read/write routine for a buffer. Validates the arguments and schedules the + * transfer. Does not wait for the transfer to complete. + */ +void +edmcastrategy(bp) + struct buf *bp; +{ + struct ed_softc *wd = device_lookup(&ed_cd, DISKUNIT(bp->b_dev)); + struct disklabel *lp = wd->sc_dk.dk_label; + daddr_t blkno; + int s; + + WDCDEBUG_PRINT(("edmcastrategy (%s)\n", wd->sc_dev.dv_xname), + DEBUG_XFERS); + + /* Valid request? */ + if (bp->b_blkno < 0 || + (bp->b_bcount % lp->d_secsize) != 0 || + (bp->b_bcount / lp->d_secsize) >= (1 << NBBY)) { + bp->b_error = EINVAL; + goto bad; + } + + /* If device invalidated (e.g. media change, door open), error. */ + if ((wd->sc_flags & WDF_LOADED) == 0) { + bp->b_error = EIO; + goto bad; + } + + /* If it's a null transfer, return immediately. */ + if (bp->b_bcount == 0) + goto done; + + /* + * Do bounds checking, adjust transfer. if error, process. + * If end of partition, just return. + */ + if (DISKPART(bp->b_dev) != RAW_PART && + bounds_check_with_label(bp, wd->sc_dk.dk_label, + (wd->sc_flags & (WDF_WLABEL|WDF_LABELLING)) != 0) <= 0) + goto done; + + /* + * Now convert the block number to absolute and put it in + * terms of the device's logical block size. + */ + if (lp->d_secsize >= DEV_BSIZE) + blkno = bp->b_blkno / (lp->d_secsize / DEV_BSIZE); + else + blkno = bp->b_blkno * (DEV_BSIZE / lp->d_secsize); + + if (DISKPART(bp->b_dev) != RAW_PART) + blkno += lp->d_partitions[DISKPART(bp->b_dev)].p_offset; + + bp->b_rawblkno = blkno; + + /* Queue transfer on drive, activate drive and controller if idle. */ + s = splbio(); + simple_lock(&wd->sc_q_lock); + disksort_blkno(&wd->sc_q, bp); + simple_unlock(&wd->sc_q_lock); + + /* Ring the worker thread */ + wd->sc_flags |= EDF_PROCESS_QUEUE; + wakeup_one(&wd->sc_q); + + splx(s); + return; +bad: + bp->b_flags |= B_ERROR; +done: + /* Toss transfer; we're done early. */ + bp->b_resid = bp->b_bcount; + biodone(bp); +} + +#if 0 +/* + * Queue a drive for I/O. + */ +static void +edstart(arg) + void *arg; +{ + struct ed_softc *wd = arg; + struct buf *bp; + + WDCDEBUG_PRINT(("edstart %s\n", wd->sc_dev.dv_xname), + DEBUG_XFERS); + while (wd->openings > 0) { + + /* Is there a buf for us ? */ + if ((bp = BUFQ_FIRST(&wd->sc_q)) == NULL) + return; + BUFQ_REMOVE(&wd->sc_q, bp); + + /* + * Make the command. First lock the device + */ + wd->openings--; + + wd->retries = 0; + __edstart(wd, bp); + } +} +#endif + +static void +__edstart(ed, bp) + struct ed_softc *ed; + struct buf *bp; +{ + u_int16_t cmd_args[4]; + int error = 0; + u_int16_t track; + u_int16_t cyl; + u_int8_t head; + u_int8_t sector; + + WDCDEBUG_PRINT(("__edstart %s: %lu %lu %u\n", ed->sc_dev.dv_xname, + bp->b_bcount, bp->b_resid, bp->b_rawblkno), + DEBUG_XFERS); + + ed->sc_bp = bp; + + /* Get physical bus mapping for buf */ + if ((error = bus_dmamap_load(ed->sc_dmat, ed->dmamap_xfer, + bp->b_data, bp->b_bcount, NULL, + BUS_DMA_WAITOK|BUS_DMA_STREAMING)) != 0) { + +#ifdef DEBUG + if (bp->b_bcount > ed->sc_dmam_sz) + panic("%s: edstart: bp->b_bcount: %lu > %lu", + ed->sc_dev.dv_xname, + bp->b_bcount, ed->sc_dmam_sz + ); +#endif + /* + * Use our DMA safe memory to get data to/from device. + * For write, copy the data from buf to it now, too. + */ + if ((error = bus_dmamap_load(ed->sc_dmat, ed->dmamap_xfer, + ed->sc_dmamkva, bp->b_bcount, NULL, + BUS_DMA_WAITOK|BUS_DMA_STREAMING)) != 0) { + printf("%s: unable to load raw data for xfer, errno=%d\n", + ed->sc_dev.dv_xname, error); + goto out; + } + ed->sc_flags |= EDF_BOUNCEBUF; + + /* If data write, copy the data to our bounce buffer. */ + if ((bp->b_flags & B_READ) == 0) + memcpy(ed->sc_dmamkva, bp->b_data, bp->b_bcount); + } + ed->sc_flags |= EDF_DMAMAP_LOADED; + + track = bp->b_rawblkno / ed->sectors; + head = track % ed->heads; + cyl = track / ed->heads; + sector = bp->b_rawblkno % ed->sectors; + + WDCDEBUG_PRINT(("__edstart %s: map: %u %u %u\n", ed->sc_dev.dv_xname, + cyl, sector, head), + DEBUG_XFERS); + + /* Instrumentation. */ + disk_busy(&ed->sc_dk); + ed->sc_dk_busy++; + + /* Read or Write Data command */ + cmd_args[0] = 2; /* Options 0000010 */ + cmd_args[1] = bp->b_bcount / DEV_BSIZE; + cmd_args[2] = ((cyl & 0x1f) << 11) | (head << 5) | sector; + cmd_args[3] = ((cyl & 0x3E0) >> 5); + if (dasd_run_cmd(ed->dasd_softc, + (bp->b_flags & B_READ) ? CMD_READ_DATA : CMD_WRITE_DATA, + ed->sc_devno, cmd_args, 4, 1)) { + printf("%s: data i/o command failed\n", ed->sc_dev.dv_xname); + error = EIO; + } + + out: + if (error) + ed->sc_error = error; +} + + +static void +edmcadone(ed) + struct ed_softc *ed; +{ + struct buf *bp = ed->sc_bp; + + WDCDEBUG_PRINT(("eddone %s\n", ed->sc_dev.dv_xname), + DEBUG_XFERS); + + if (ed->sc_error) { + bp->b_error = ed->sc_error; + bp->b_flags |= B_ERROR; + } + + /* If no error and using bounce buffer, copy the data now. */ + if ((bp->b_flags & B_ERROR) == 0 && (ed->sc_flags & EDF_BOUNCEBUF) + && (bp->b_flags & B_READ)) { + memcpy(bp->b_data, ed->sc_dmamkva, bp->b_bcount); + ed->sc_flags &= ~EDF_BOUNCEBUF; + } + + /* Unload buf from DMA map */ + if (ed->sc_flags & EDF_DMAMAP_LOADED) { + bus_dmamap_unload(ed->sc_dmat, ed->dmamap_xfer); + ed->sc_flags &= ~EDF_DMAMAP_LOADED; + } + + /* If disk was busied, unbusy it now */ + if (ed->sc_dk_busy > 0) { + disk_unbusy(&ed->sc_dk, (bp->b_bcount - bp->b_resid)); + ed->sc_dk_busy--; + } + +#if NRND > 0 + rnd_add_uint32(&ed->rnd_source, bp->b_blkno); +#endif + biodone(bp); +} + +#if 0 +void +edmcarestart(v) + void *v; +{ + struct ed_softc *wd = v; + struct buf *bp = wd->sc_bp; + int s; + WDCDEBUG_PRINT(("wdrestart %s\n", wd->sc_dev.dv_xname), + DEBUG_XFERS); + + s = splbio(); + __edstart(v, bp); + splx(s); +} +#endif + +int +edmcaread(dev, uio, flags) + dev_t dev; + struct uio *uio; + int flags; +{ + WDCDEBUG_PRINT(("edread\n"), DEBUG_XFERS); + return (physio(edmcastrategy, NULL, dev, B_READ, minphys, uio)); +} + +int +edmcawrite(dev, uio, flags) + dev_t dev; + struct uio *uio; + int flags; +{ + WDCDEBUG_PRINT(("edwrite\n"), DEBUG_XFERS); + return (physio(edmcastrategy, NULL, dev, B_WRITE, minphys, uio)); +} + +/* + * Wait interruptibly for an exclusive lock. + * + * XXX + * Several drivers do this; it should be abstracted and made MP-safe. + */ +static int +ed_lock(wd) + struct ed_softc *wd; +{ + int error; + int s; + + WDCDEBUG_PRINT(("ed_lock\n"), DEBUG_FUNCS); + + s = splbio(); + + while ((wd->sc_flags & WDF_LOCKED) != 0) { + wd->sc_flags |= WDF_WANTED; + if ((error = tsleep(wd, PRIBIO | PCATCH, + "wdlck", 0)) != 0) { + splx(s); + return error; + } + } + wd->sc_flags |= WDF_LOCKED; + splx(s); + return 0; +} + +/* + * Unlock and wake up any waiters. + */ +static void +ed_unlock(wd) + struct ed_softc *wd; +{ + + WDCDEBUG_PRINT(("ed_unlock\n"), DEBUG_FUNCS); + + wd->sc_flags &= ~WDF_LOCKED; + if ((wd->sc_flags & WDF_WANTED) != 0) { + wd->sc_flags &= ~WDF_WANTED; + wakeup(wd); + } +} + +int +edmcaopen(dev, flag, fmt, p) + dev_t dev; + int flag, fmt; + struct proc *p; +{ + struct ed_softc *wd; + int part, error; + + WDCDEBUG_PRINT(("edopen\n"), DEBUG_FUNCS); + wd = device_lookup(&ed_cd, DISKUNIT(dev)); + if (wd == NULL) + return (ENXIO); + + if ((error = ed_lock(wd)) != 0) + goto bad4; + + if (wd->sc_dk.dk_openmask != 0) { + /* + * If any partition is open, but the disk has been invalidated, + * disallow further opens. + */ + if ((wd->sc_flags & WDF_LOADED) == 0) { + error = EIO; + goto bad3; + } + } else { + if ((wd->sc_flags & WDF_LOADED) == 0) { + wd->sc_flags |= WDF_LOADED; + + /* Load the physical device parameters. */ + ed_get_params(wd); + + /* Load the partition info if not already loaded. */ + edgetdisklabel(wd); + } + } + + part = DISKPART(dev); + + /* Check that the partition exists. */ + if (part != RAW_PART && + (part >= wd->sc_dk.dk_label->d_npartitions || + wd->sc_dk.dk_label->d_partitions[part].p_fstype == FS_UNUSED)) { + error = ENXIO; + goto bad; + } + + /* Insure only one open at a time. */ + switch (fmt) { + case S_IFCHR: + wd->sc_dk.dk_copenmask |= (1 << part); + break; + case S_IFBLK: + wd->sc_dk.dk_bopenmask |= (1 << part); + break; + } + wd->sc_dk.dk_openmask = + wd->sc_dk.dk_copenmask | wd->sc_dk.dk_bopenmask; + + ed_unlock(wd); + return 0; + +bad: + if (wd->sc_dk.dk_openmask == 0) { + } + +bad3: + ed_unlock(wd); +bad4: + return (error); +} + +int +edmcaclose(dev, flag, fmt, p) + dev_t dev; + int flag, fmt; + struct proc *p; +{ + struct ed_softc *wd = device_lookup(&ed_cd, DISKUNIT(dev)); + int part = DISKPART(dev); + int error; + + WDCDEBUG_PRINT(("edmcaclose\n"), DEBUG_FUNCS); + if ((error = ed_lock(wd)) != 0) + return error; + + switch (fmt) { + case S_IFCHR: + wd->sc_dk.dk_copenmask &= ~(1 << part); + break; + case S_IFBLK: + wd->sc_dk.dk_bopenmask &= ~(1 << part); + break; + } + wd->sc_dk.dk_openmask = + wd->sc_dk.dk_copenmask | wd->sc_dk.dk_bopenmask; + + if (wd->sc_dk.dk_openmask == 0) { +#if 0 + wd_flushcache(wd, AT_WAIT); +#endif + /* XXXX Must wait for I/O to complete! */ + + if (! (wd->sc_flags & WDF_KLABEL)) + wd->sc_flags &= ~WDF_LOADED; + } + + ed_unlock(wd); + + return 0; +} + +static void +edgetdefaultlabel(wd, lp) + struct ed_softc *wd; + struct disklabel *lp; +{ + WDCDEBUG_PRINT(("edgetdefaultlabel\n"), DEBUG_FUNCS); + memset(lp, 0, sizeof(struct disklabel)); + + lp->d_secsize = DEV_BSIZE; + lp->d_ntracks = wd->heads; + lp->d_nsectors = wd->sectors; + lp->d_ncylinders = wd->cyl; + lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors; + + lp->d_type = DTYPE_ESDI; + + strncpy(lp->d_typename, "ESDI", 16); + strncpy(lp->d_packname, "fictitious", 16); + lp->d_secperunit = wd->sc_capacity; + lp->d_rpm = 3600; + lp->d_interleave = 1; + lp->d_flags = 0; + + lp->d_partitions[RAW_PART].p_offset = 0; + lp->d_partitions[RAW_PART].p_size = + lp->d_secperunit * (lp->d_secsize / DEV_BSIZE); + lp->d_partitions[RAW_PART].p_fstype = FS_UNUSED; + lp->d_npartitions = RAW_PART + 1; + + lp->d_magic = DISKMAGIC; + lp->d_magic2 = DISKMAGIC; + lp->d_checksum = dkcksum(lp); +} + +/* + * Fabricate a default disk label, and try to read the correct one. + */ +static void +edgetdisklabel(wd) + struct ed_softc *wd; +{ + struct disklabel *lp = wd->sc_dk.dk_label; + char *errstring; + + WDCDEBUG_PRINT(("edgetdisklabel\n"), DEBUG_FUNCS); + + memset(wd->sc_dk.dk_cpulabel, 0, sizeof(struct cpu_disklabel)); + + edgetdefaultlabel(wd, lp); + +#if 0 + wd->sc_badsect[0] = -1; + + if (wd->drvp->state > RECAL) + wd->drvp->drive_flags |= DRIVE_RESET; +#endif + errstring = readdisklabel(MAKEDISKDEV(0, wd->sc_dev.dv_unit, RAW_PART), + edmcastrategy, lp, wd->sc_dk.dk_cpulabel); + if (errstring) { + /* + * This probably happened because the drive's default + * geometry doesn't match the DOS geometry. We + * assume the DOS geometry is now in the label and try + * again. XXX This is a kluge. + */ +#if 0 + if (wd->drvp->state > RECAL) + wd->drvp->drive_flags |= DRIVE_RESET; +#endif + errstring = readdisklabel(MAKEDISKDEV(0, wd->sc_dev.dv_unit, + RAW_PART), edmcastrategy, lp, wd->sc_dk.dk_cpulabel); + } + if (errstring) { + printf("%s: %s\n", wd->sc_dev.dv_xname, errstring); + return; + } + +#if 0 + if (wd->drvp->state > RECAL) + wd->drvp->drive_flags |= DRIVE_RESET; +#endif +#ifdef HAS_BAD144_HANDLING + if ((lp->d_flags & D_BADSECT) != 0) + bad144intern(wd); +#endif +} + +int +edmcaioctl(dev, xfer, addr, flag, p) + dev_t dev; + u_long xfer; + caddr_t addr; + int flag; + struct proc *p; +{ + struct ed_softc *wd = device_lookup(&ed_cd, DISKUNIT(dev)); + int error; +#ifdef __HAVE_OLD_DISKLABEL + struct disklabel newlabel; +#endif + + WDCDEBUG_PRINT(("wdioctl\n"), DEBUG_FUNCS); + + if ((wd->sc_flags & WDF_LOADED) == 0) + return EIO; + + switch (xfer) { +#ifdef HAS_BAD144_HANDLING + case DIOCSBAD: + if ((flag & FWRITE) == 0) + return EBADF; + wd->sc_dk.dk_cpulabel->bad = *(struct dkbad *)addr; + wd->sc_dk.dk_label->d_flags |= D_BADSECT; + bad144intern(wd); + return 0; +#endif + + case DIOCGDINFO: + *(struct disklabel *)addr = *(wd->sc_dk.dk_label); + return 0; +#ifdef __HAVE_OLD_DISKLABEL + case ODIOCGDINFO: + newlabel = *(wd->sc_dk.dk_label); + if (newlabel.d_npartitions > OLDMAXPARTITIONS) + return ENOTTY; + memcpy(addr, &newlabel, sizeof (struct olddisklabel)); + return 0; +#endif + + case DIOCGPART: + ((struct partinfo *)addr)->disklab = wd->sc_dk.dk_label; + ((struct partinfo *)addr)->part = + &wd->sc_dk.dk_label->d_partitions[DISKPART(dev)]; + return 0; + + case DIOCWDINFO: + case DIOCSDINFO: +#ifdef __HAVE_OLD_DISKLABEL + case ODIOCWDINFO: + case ODIOCSDINFO: +#endif + { + struct disklabel *lp; + +#ifdef __HAVE_OLD_DISKLABEL + if (xfer == ODIOCSDINFO || xfer == ODIOCWDINFO) { + memset(&newlabel, 0, sizeof newlabel); + memcpy(&newlabel, addr, sizeof (struct olddisklabel)); + lp = &newlabel; + } else +#endif + lp = (struct disklabel *)addr; + + if ((flag & FWRITE) == 0) + return EBADF; + + if ((error = ed_lock(wd)) != 0) + return error; + wd->sc_flags |= WDF_LABELLING; + + error = setdisklabel(wd->sc_dk.dk_label, + lp, /*wd->sc_dk.dk_openmask : */0, + wd->sc_dk.dk_cpulabel); + if (error == 0) { +#if 0 + if (wd->drvp->state > RECAL) + wd->drvp->drive_flags |= DRIVE_RESET; +#endif + if (xfer == DIOCWDINFO +#ifdef __HAVE_OLD_DISKLABEL + || xfer == ODIOCWDINFO +#endif + ) + error = writedisklabel(EDLABELDEV(dev), + edmcastrategy, wd->sc_dk.dk_label, + wd->sc_dk.dk_cpulabel); + } + + wd->sc_flags &= ~WDF_LABELLING; + ed_unlock(wd); + return error; + } + + case DIOCKLABEL: + if (*(int *)addr) + wd->sc_flags |= WDF_KLABEL; + else + wd->sc_flags &= ~WDF_KLABEL; + return 0; + + case DIOCWLABEL: + if ((flag & FWRITE) == 0) + return EBADF; + if (*(int *)addr) + wd->sc_flags |= WDF_WLABEL; + else + wd->sc_flags &= ~WDF_WLABEL; + return 0; + + case DIOCGDEFLABEL: + edgetdefaultlabel(wd, (struct disklabel *)addr); + return 0; +#ifdef __HAVE_OLD_DISKLABEL + case ODIOCGDEFLABEL: + edgetdefaultlabel(wd, &newlabel); + if (newlabel.d_npartitions > OLDMAXPARTITIONS) + return ENOTTY; + memcpy(addr, &newlabel, sizeof (struct olddisklabel)); + return 0; +#endif + +#ifdef notyet + case DIOCWFORMAT: + if ((flag & FWRITE) == 0) + return EBADF; + { + register struct format_op *fop; + struct iovec aiov; + struct uio auio; + + fop = (struct format_op *)addr; + aiov.iov_base = fop->df_buf; + aiov.iov_len = fop->df_count; + auio.uio_iov = &aiov; + auio.uio_iovcnt = 1; + auio.uio_resid = fop->df_count; + auio.uio_segflg = 0; + auio.uio_offset = + fop->df_startblk * wd->sc_dk.dk_label->d_secsize; + auio.uio_procp = p; + error = physio(wdformat, NULL, dev, B_WRITE, minphys, + &auio); + fop->df_count -= auio.uio_resid; + fop->df_reg[0] = wdc->sc_status; + fop->df_reg[1] = wdc->sc_error; + return error; + } +#endif + + default: + return ENOTTY; + } + +#ifdef DIAGNOSTIC + panic("wdioctl: impossible"); +#endif +} + +#if 0 +#ifdef B_FORMAT +int +edmcaformat(struct buf *bp) +{ + + bp->b_flags |= B_FORMAT; + return edmcastrategy(bp); +} +#endif +#endif + +int +edmcasize(dev) + dev_t dev; +{ + struct ed_softc *wd; + int part, omask; + int size; + + WDCDEBUG_PRINT(("edsize\n"), DEBUG_FUNCS); + + wd = device_lookup(&ed_cd, DISKUNIT(dev)); + if (wd == NULL) + return (-1); + + part = DISKPART(dev); + omask = wd->sc_dk.dk_openmask & (1 << part); + + if (omask == 0 && edmcaopen(dev, 0, S_IFBLK, NULL) != 0) + return (-1); + if (wd->sc_dk.dk_label->d_partitions[part].p_fstype != FS_SWAP) + size = -1; + else + size = wd->sc_dk.dk_label->d_partitions[part].p_size * + (wd->sc_dk.dk_label->d_secsize / DEV_BSIZE); + if (omask == 0 && edmcaclose(dev, 0, S_IFBLK, NULL) != 0) + return (-1); + return (size); +} + +/* #define WD_DUMP_NOT_TRUSTED if you just want to watch */ +static int wddoingadump = 0; +static int wddumprecalibrated = 0; + +/* + * Dump core after a system crash. + */ +int +edmcadump(dev, blkno, va, size) + dev_t dev; + daddr_t blkno; + caddr_t va; + size_t size; +{ + struct ed_softc *wd; /* disk unit to do the I/O */ + struct disklabel *lp; /* disk's disklabel */ + int part; // , err; + int nblks; /* total number of sectors left to write */ + + /* Check if recursive dump; if so, punt. */ + if (wddoingadump) + return EFAULT; + wddoingadump = 1; + + wd = device_lookup(&ed_cd, DISKUNIT(dev)); + if (wd == NULL) + return (ENXIO); + + part = DISKPART(dev); + +#if 0 + /* Make sure it was initialized. */ + if (wd->drvp->state < READY) + return ENXIO; +#endif + + /* Convert to disk sectors. Request must be a multiple of size. */ + lp = wd->sc_dk.dk_label; + if ((size % lp->d_secsize) != 0) + return EFAULT; + nblks = size / lp->d_secsize; + blkno = blkno / (lp->d_secsize / DEV_BSIZE); + + /* Check transfer bounds against partition size. */ + if ((blkno < 0) || ((blkno + nblks) > lp->d_partitions[part].p_size)) + return EINVAL; + + /* Offset block number to start of partition. */ + blkno += lp->d_partitions[part].p_offset; + + /* Recalibrate, if first dump transfer. */ + if (wddumprecalibrated == 0) { + wddumprecalibrated = 1; +#if 0 + wd->drvp->state = RESET; +#endif + } + + while (nblks > 0) { +#if 0 + wd->sc_wdc_bio.blkno = blkno; + wd->sc_wdc_bio.flags = ATA_POLL; + wd->sc_wdc_bio.bcount = lp->d_secsize; + wd->sc_wdc_bio.databuf = va; +#ifndef WD_DUMP_NOT_TRUSTED + switch (wdc_ata_bio(wd->drvp, &wd->sc_wdc_bio)) { + case WDC_TRY_AGAIN: + panic("wddump: try again"); + break; + case WDC_QUEUED: + panic("wddump: polled command has been queued"); + break; + case WDC_COMPLETE: + break; + } + if (err != 0) { + printf("\n"); + return err; + } +#else /* WD_DUMP_NOT_TRUSTED */ + /* Let's just talk about this first... */ + printf("ed%d: dump addr 0x%x, cylin %d, head %d, sector %d\n", + unit, va, cylin, head, sector); + delay(500 * 1000); /* half a second */ +#endif +#endif /* 0 */ + + /* update block count */ + nblks -= 1; + blkno += 1; + va += lp->d_secsize; + } + + wddoingadump = 0; + return (ESPIPE); +} + +#ifdef HAS_BAD144_HANDLING +/* + * Internalize the bad sector table. + */ +static void +bad144intern(wd) + struct ed_softc *wd; +{ + struct dkbad *bt = &wd->sc_dk.dk_cpulabel->bad; + struct disklabel *lp = wd->sc_dk.dk_label; + int i = 0; + + WDCDEBUG_PRINT(("bad144intern\n"), DEBUG_XFERS); + + for (; i < NBT_BAD; i++) { + if (bt->bt_bad[i].bt_cyl == 0xffff) + break; + wd->sc_badsect[i] = + bt->bt_bad[i].bt_cyl * lp->d_secpercyl + + (bt->bt_bad[i].bt_trksec >> 8) * lp->d_nsectors + + (bt->bt_bad[i].bt_trksec & 0xff); + } + for (; i < NBT_BAD+1; i++) + wd->sc_badsect[i] = -1; +} +#endif + +static int +ed_get_params(ed) + struct ed_softc *ed; +{ + u_int16_t cmd_args[2]; + + /* + * Get Device Configuration (09). + */ + cmd_args[0] = 6; /* Options: 00s110, s: 0=Physical 1=Pseudo */ + cmd_args[1] = 0; + if (dasd_run_cmd(ed->dasd_softc, CMD_GET_DEV_CONF, ed->sc_devno, cmd_args, 2, 0)) + return (1); + + ed->spares = ed->sc_status_block[1] >> 8; + ed->drv_flags = ed->sc_status_block[1] & 0x1f; + ed->rba = ed->sc_status_block[2] | + (ed->sc_status_block[3] << 16); + /* Instead of using: + ed->cyl = ed->sc_status_block[4]; + ed->heads = ed->sc_status_block[5] & 0xff; + ed->sectors = ed->sc_status_block[5] >> 8; + * we fabricate the numbers from RBA count, so that + * number of sectors is 32 and heads 64. This seems + * to be necessary for integrated ESDI controller. + */ + ed->sectors = 32; + ed->heads = 64; + ed->cyl = ed->rba / (ed->heads * ed->sectors); + ed->sc_capacity = ed->rba; + + return (0); +} + +/* + * Our shutdown hook. We attempt to park disk's head only. + */ +void +ed_shutdown(arg) + void *arg; +{ +#if 0 + struct ed_softc *ed = arg; + u_int16_t cmd_args[2]; + + /* Issue Park Head command */ + cmd_args[0] = 6; /* Options: 000110 */ + cmd_args[1] = 0; + (void) dasd_run_cmd(ed->dasd_softc, CMD_PARK_HEAD, ed->sc_devno, + cmd_args, 2, 0); +#endif +} + +/* + * Main worker thread function. + */ +void +edworker(arg) + void *arg; +{ + struct ed_softc *ed = (struct ed_softc *) arg; + struct buf *bp; + int s; + + config_pending_decr(); + + for(;;) { + /* Wait until awakened */ + (void) tsleep(&ed->sc_q, PRIBIO, "edidle", 0); + + if ((ed->sc_flags & EDF_PROCESS_QUEUE) == 0) + panic("edworker: expecting process queue"); + ed->sc_flags &= ~EDF_PROCESS_QUEUE; + + for(;;) { + /* Is there a buf for us ? */ + simple_lock(&ed->sc_q_lock); + if ((bp = BUFQ_FIRST(&ed->sc_q)) == NULL) { + simple_unlock(&ed->sc_q_lock); + break; + } + BUFQ_REMOVE(&ed->sc_q, bp); + simple_unlock(&ed->sc_q_lock); + + /* Schedule i/o operation */ + s = splbio(); + __edstart(ed, bp); + splx(s); + + /* + * Wait until the command executes; dasd_intr() wakes + * as up. + */ + (void) tsleep(&ed->dasd_softc, PRIBIO, "edwrk", 0); + + /* Handle i/o results */ + s = splbio(); + edmcadone(ed); + splx(s); + } + } +} diff --git a/sys/dev/mca/edvar.h b/sys/dev/mca/edvar.h new file mode 100644 index 000000000000..40bef4e245e4 --- /dev/null +++ b/sys/dev/mca/edvar.h @@ -0,0 +1,96 @@ +/* $NetBSD: edvar.h,v 1.1 2001/04/19 07:30:24 jdolecek Exp $ */ + +/* + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jaromir Dolecek. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. 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. + */ + +struct dasd_mca_softc; +#define DASD_MAX_CMD_RES_LEN 8 + +struct ed_softc { + struct device sc_dev; + + /* General disk infos */ + struct disk sc_dk; + struct buf_queue sc_q; + struct simplelock sc_q_lock; + struct callout sc_edstart; + + struct buf *sc_bp; /* buf being transfered */ + struct dasd_mca_softc *dasd_softc; /* pointer to our parent */ + + int sc_flags; +#define WDF_LOCKED 0x001 +#define WDF_WANTED 0x002 +#define WDF_WLABEL 0x004 /* label is writable */ +#define WDF_LABELLING 0x008 /* writing label */ +#define WDF_LOADED 0x010 /* parameters loaded */ +#define WDF_WAIT 0x020 /* waiting for resources */ +#define WDF_KLABEL 0x080 /* retain label after 'full' close */ +#define EDF_BOUNCEBUF 0x100 /* use bounce buffer */ +#define EDF_DMAMAP_LOADED 0x200 /* dmamap_xfer loaded */ +#define EDF_PROCESS_QUEUE 0x400 + int sc_capacity; + + /* actual drive parameters */ + int sc_devno; /* DASD device number */ + u_int16_t cyl; + u_int8_t heads; + u_int8_t sectors; + u_int8_t spares; /* spares per cylinder */ + u_int32_t rba; + u_int8_t drv_flags; + + u_int16_t sc_status_block[DASD_MAX_CMD_RES_LEN]; /* CMD status block */ + + int retries; /* number of xfer retry */ + int sc_dk_busy; + daddr_t sc_badsect[127]; /* 126 plus trailing -1 marker */ + + /* Info needed for DMA */ + bus_dma_tag_t sc_dmat; /* DMA tag as passed by parent */ + bus_dmamap_t dmamap_xfer; /* transfer dma map */ +#define ED_NSEGS 1 + bus_dma_segment_t sc_dmaseg[ED_NSEGS]; /* DMA segment array */ + bus_dma_segment_t sc_dmam[1]; /* Allocated DMA-safe memory */ + vsize_t sc_dmam_sz; /* Size of allocated DMA memory */ + caddr_t sc_dmamkva; /* Mapped kva of the memory */ + + void *sc_sdhook; /* our shutdown hook */ + +#if NRND > 0 + rndsource_element_t rnd_source; +#endif + + struct proc *sc_worker; /* Worker thread */ + int sc_error; +}; diff --git a/sys/dev/mca/files.mca b/sys/dev/mca/files.mca index f0f40cafce1f..6aa4faa8c98f 100644 --- a/sys/dev/mca/files.mca +++ b/sys/dev/mca/files.mca @@ -1,4 +1,4 @@ -# $NetBSD: files.mca,v 1.8 2001/03/31 00:43:05 jdolecek Exp $ +# $NetBSD: files.mca,v 1.9 2001/04/19 07:30:25 jdolecek Exp $ # # Config.new file and device description for machine-independent MCA code. # Included by ports that need it. @@ -39,3 +39,12 @@ file dev/mca/com_mca.c com_mca # device in sys/conf/files attach ate at mca with ate_mca file dev/mca/if_ate_mca.c ate_mca + +# ESDI controllers & disks +device dasd { } +attach dasd at mca with dasd_mca +file dev/mca/dasd_mca.c dasd_mca + +device ed: disk +attach ed at dasd with ed_mca +file dev/mca/ed_mca.c ed_mca needs-flag diff --git a/sys/dev/mca/mcadevs b/sys/dev/mca/mcadevs index 11848286c6bf..8e4dcd4da63e 100644 --- a/sys/dev/mca/mcadevs +++ b/sys/dev/mca/mcadevs @@ -1,4 +1,4 @@ -$NetBSD: mcadevs,v 1.7 2001/03/24 09:48:11 jdolecek Exp $ +$NetBSD: mcadevs,v 1.8 2001/04/19 07:30:25 jdolecek Exp $ /*- * Copyright (c) 2000 The NetBSD Foundation, Inc. @@ -342,10 +342,10 @@ product 0x8FD8 "IBM XGA/XGA-2 Display Adapter" [2] product 0x8FD9 "IBM XGA/XGA-2 Display Adapter" [2] product 0x8FDA "IBM XGA/XGA-2 Display Adapter" [2] product 0x8FDB "IBM XGA/XGA-2 Display Adapter" [2] -product 0xDDFF "IBM ESDI Fixed Disk Controller" [3] +product 0xDDFF IBM_ESDIC "IBM ESDI Fixed Disk Controller" [3] product 0xDEFF "IBM Multi-Protocol Communications Adapter" [3] product 0xDF7F "IBM 3363 Optical Disk Adapter" [5] -product 0xDF9F "IBM Integ. ESDI Fixed Disk & Controller" [3] +product 0xDF9F IBM_ESDIC_IG "IBM Integ. ESDI Fixed Disk & Controller" [3] product 0xDFBF "IBM 6157 Streaming Tape Adapter" [5] product 0xDFFA "IBM 5.25in Diskette Adapter/A" [5] product 0xDFFD "IBM ST506 Fixed Disk Adapter" [6]