📄 wdc.c
字号:
ctrl_flags = chp->wdc->sc_dev.dv_cfdata->cf_flags; channel_flags = (ctrl_flags >> (NBBY * chp->channel)) & 0xff; WDCDEBUG_PRINT(("wdcattach: ch_drive_flags 0x%x 0x%x\n", chp->ch_drive[0].drive_flags, chp->ch_drive[1].drive_flags), DEBUG_PROBE); /* If no drives, abort here */ if ((chp->ch_drive[0].drive_flags & DRIVE) == 0 && (chp->ch_drive[1].drive_flags & DRIVE) == 0) return; /* * Attach an ATAPI bus, if needed. */ if ((chp->ch_drive[0].drive_flags & DRIVE_ATAPI) || (chp->ch_drive[1].drive_flags & DRIVE_ATAPI)) {#if NATAPISCSI > 0 wdc_atapibus_attach(chp);#else /* * Fills in a fake aa_link and call config_found, so that * the config machinery will print * "atapibus at xxx not configured" */ bzero(&aa_link, sizeof(struct ata_atapi_attach)); aa_link.aa_type = T_ATAPI; aa_link.aa_channel = chp->channel; aa_link.aa_openings = 1; aa_link.aa_drv_data = 0; aa_link.aa_bus_private = NULL; (void)config_found(&chp->wdc->sc_dev, (void *)&aa_link, atapi_print);#endif } for (i = 0; i < 2; i++) { if ((chp->ch_drive[i].drive_flags & (DRIVE_ATA | DRIVE_OLD)) == 0) { continue; } bzero(&aa_link, sizeof(struct ata_atapi_attach)); aa_link.aa_type = T_ATA; aa_link.aa_channel = chp->channel; aa_link.aa_openings = 1; aa_link.aa_drv_data = &chp->ch_drive[i]; config_found(&chp->wdc->sc_dev, (void *)&aa_link, wdprint); } /* * reset drive_flags for unnatached devices, reset state for attached * ones */ for (i = 0; i < 2; i++) { if (chp->ch_drive[i].drive_name[0] == 0) chp->ch_drive[i].drive_flags = 0; else chp->ch_drive[i].state = 0; } /* * Reset channel. The probe, with some combinations of ATA/ATAPI * devices keep it in a mostly working, but strange state (with busy * led on) */ if ((chp->wdc->cap & WDC_CAPABILITY_NO_EXTRA_RESETS) == 0) { wdcreset(chp, VERBOSE); /* * Read status registers to avoid spurious interrupts. */ for (i = 1; i >= 0; i--) { if (chp->ch_drive[i].drive_flags & DRIVE) { CHP_WRITE_REG(chp, wdr_sdh, WDSD_IBM | (i << 4)); if (wait_for_unbusy(chp, 10000) < 0) printf("%s:%d:%d: device busy\n", chp->wdc->sc_dev.dv_xname, chp->channel, i); } } }#ifndef __OpenBSD__ wdc_delref(chp);#endif}/* * Start I/O on a controller, for the given channel. * The first xfer may be not for our channel if the channel queues * are shared. */voidwdcstart(chp) struct channel_softc *chp;{ struct wdc_xfer *xfer;#ifdef WDC_DIAGNOSTIC int spl1, spl2; spl1 = splbio(); spl2 = splbio(); if (spl2 != spl1) { printf("wdcstart: not at splbio()\n"); panic("wdcstart"); } splx(spl2); splx(spl1);#endif /* WDC_DIAGNOSTIC */ /* is there a xfer ? */ if ((xfer = chp->ch_queue->sc_xfer.tqh_first) == NULL) { return; } /* adjust chp, in case we have a shared queue */ chp = xfer->chp; if ((chp->ch_flags & WDCF_ACTIVE) != 0 ) { return; /* channel already active */ }#ifdef DIAGNOSTIC if ((chp->ch_flags & WDCF_IRQ_WAIT) != 0) panic("wdcstart: channel waiting for irq\n");#endif if (chp->wdc->cap & WDC_CAPABILITY_HWLOCK) if (!(chp->wdc->claim_hw)(chp, 0)) return; WDCDEBUG_PRINT(("wdcstart: xfer %p channel %d drive %d\n", xfer, chp->channel, xfer->drive), DEBUG_XFERS); chp->ch_flags |= WDCF_ACTIVE; if (chp->ch_drive[xfer->drive].drive_flags & DRIVE_RESET) { chp->ch_drive[xfer->drive].drive_flags &= ~DRIVE_RESET; chp->ch_drive[xfer->drive].state = 0; } xfer->c_start(chp, xfer);}#ifndef PMONintwdcdetach(chp, flags) struct channel_softc *chp; int flags;{ int s, rv; s = splbio(); wdc_kill_pending(chp); rv = config_detach_children((struct device *)chp->wdc, flags); splx(s); return (rv);}#endif/* restart an interrupted I/O */voidwdcrestart(v) void *v;{ struct channel_softc *chp = v; int s; s = splbio(); wdcstart(chp); splx(s);} /* * Interrupt routine for the controller. Acknowledge the interrupt, check for * errors on the current operation, mark it done if necessary, and start the * next request. Also check for a partially done transfer, and continue with * the next chunk if so. */intwdcintr(arg) void *arg;{#ifndef PMON struct channel_softc *chp = arg; struct wdc_xfer *xfer; int ret; if ((chp->ch_flags & WDCF_IRQ_WAIT) == 0) { WDCDEBUG_PRINT(("wdcintr: inactive controller\n"), DEBUG_INTR); return 0; } WDCDEBUG_PRINT(("wdcintr\n"), DEBUG_INTR); untimeout(wdctimeout, chp); chp->ch_flags &= ~WDCF_IRQ_WAIT; xfer = chp->ch_queue->sc_xfer.tqh_first; ret = xfer->c_intr(chp, xfer, 1);#if notyet if (ret == 0) chp->ch_flags |= WDCF_IRQ_WAIT;#endif return (ret);#else return 0;#endif}/* Put all disk in RESET state */void wdc_reset_channel(drvp) struct ata_drive_datas *drvp;{ struct channel_softc *chp = drvp->chnl_softc; int drive; WDCDEBUG_PRINT(("ata_reset_channel %s:%d for drive %d\n", chp->wdc->sc_dev.dv_xname, chp->channel, drvp->drive), DEBUG_FUNCS); (void) wdcreset(chp, VERBOSE); for (drive = 0; drive < 2; drive++) { chp->ch_drive[drive].state = 0; }}intwdcreset(chp, verb) struct channel_softc *chp; int verb;{ int drv_mask1, drv_mask2; if (!chp->_vtbl) chp->_vtbl = &wdc_default_vtbl; CHP_WRITE_REG(chp, wdr_sdh, WDSD_IBM); /* master */ CHP_WRITE_REG(chp, wdr_ctlr, WDCTL_RST | WDCTL_IDS); delay(1000); CHP_WRITE_REG(chp, wdr_ctlr, WDCTL_IDS); delay(2000); (void) CHP_READ_REG(chp,wdr_error); CHP_WRITE_REG(chp, wdr_ctlr, WDCTL_4BIT); drv_mask1 = (chp->ch_drive[0].drive_flags & DRIVE) ? 0x01:0x00; drv_mask1 |= (chp->ch_drive[1].drive_flags & DRIVE) ? 0x02:0x00; drv_mask2 = __wdcwait_reset(chp, drv_mask1); if (verb && drv_mask2 != drv_mask1) { printf("%s channel %d: reset failed for", chp->wdc->sc_dev.dv_xname, chp->channel); if ((drv_mask1 & 0x01) != 0 && (drv_mask2 & 0x01) == 0) printf(" drive 0"); if ((drv_mask1 & 0x02) != 0 && (drv_mask2 & 0x02) == 0) printf(" drive 1"); printf("\n"); } return (drv_mask1 != drv_mask2) ? 1 : 0;}static int__wdcwait_reset(chp, drv_mask) struct channel_softc *chp; int drv_mask;{ int timeout; u_int8_t st0, st1; /* Wait 50ms for drive firmware to settle */ delay(50000); /* wait for BSY to deassert */ for (timeout = 0; timeout < WDCNDELAY_RST;timeout++) { CHP_WRITE_REG(chp, wdr_sdh, WDSD_IBM); /* master */ delay(10); st0 = CHP_READ_REG(chp, wdr_status); CHP_WRITE_REG(chp, wdr_sdh, WDSD_IBM | 0x10); /* slave */ delay(10); st1 = CHP_READ_REG(chp, wdr_status); if ((drv_mask & 0x01) == 0) { /* no master */ if ((drv_mask & 0x02) != 0 && (st1 & WDCS_BSY) == 0) { /* No master, slave is ready, it's done */ goto end; } } else if ((drv_mask & 0x02) == 0) { /* no slave */ if ((drv_mask & 0x01) != 0 && (st0 & WDCS_BSY) == 0) { /* No slave, master is ready, it's done */ goto end; } } else { /* Wait for both master and slave to be ready */ if ((st0 & WDCS_BSY) == 0 && (st1 & WDCS_BSY) == 0) { goto end; } } delay(WDCDELAY); } /* Reset timed out. Maybe it's because drv_mask was not rigth */ if (st0 & WDCS_BSY) drv_mask &= ~0x01; if (st1 & WDCS_BSY) drv_mask &= ~0x02;end: WDCDEBUG_PRINT(("%s:%d: wdcwait_reset() end, st0=0x%x, st1=0x%x\n", chp->wdc ? chp->wdc->sc_dev.dv_xname : "wdcprobe", chp->channel, st0, st1), DEBUG_PROBE); return drv_mask;}/* * Wait for a drive to be !BSY, and have mask in its status register. * return -1 for a timeout after "timeout" ms. */intwdcwait(chp, mask, bits, timeout) struct channel_softc *chp; int mask, bits, timeout;{ u_char status; int time = 0;#ifndef PMON#ifdef WDCNDELAY_DEBUG extern int cold;#endif#endif WDCDEBUG_PRINT(("wdcwait %s:%d\n", chp->wdc ?chp->wdc->sc_dev.dv_xname :"none", chp->channel), DEBUG_STATUS); chp->ch_error = 0; timeout = timeout * 1000 / WDCDELAY; /* delay uses microseconds */ for (;;) {#ifdef TEST_ALTSTS chp->ch_status = status = CHP_READ_REG(chp, wdr_altsts);#else chp->ch_status = status = CHP_READ_REG(chp, wdr_status);#endif if (status == 0xff && (chp->ch_flags & WDCF_ONESLAVE)) { CHP_WRITE_REG(chp, wdr_sdh, WDSD_IBM | 0x10);#ifdef TEST_ALTSTS chp->ch_status = status = CHP_READ_REG(chp, wdr_altsts);#else chp->ch_status = status = CHP_READ_REG(chp, wdr_status);#endif } if ((status & WDCS_BSY) == 0 && (status & mask) == bits) break; if (++time > timeout) { WDCDEBUG_PRINT(("wdcwait: timeout, status %x " "error %x\n", status, CHP_READ_REG(chp, wdr_error)), DEBUG_STATUSX | DEBUG_STATUS); return -1; } delay(WDCDELAY); }#ifdef TEST_ALTSTS /* Acknowledge any pending interrupts */ CHP_READ_REG(chp, wdr_status);#endif if (status & WDCS_ERR) { chp->ch_error = CHP_READ_REG(chp, wdr_error); WDCDEBUG_PRINT(("wdcwait: error %x\n", chp->ch_error), DEBUG_STATUSX | DEBUG_STATUS); }#ifndef PMON#ifdef WDCNDELAY_DEBUG /* After autoconfig, there should be no long delays. */ if (!cold && time > WDCNDELAY_DEBUG) { struct wdc_xfer *xfer = chp->ch_queue->sc_xfer.tqh_first; if (xfer == NULL) printf("%s channel %d: warning: busy-wait took %dus\n", chp->wdc->sc_dev.dv_xname, chp->channel, WDCDELAY * time); else printf("%s:%d:%d: warning: busy-wait took %dus\n", chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive, WDCDELAY * time); }#endif#endif return 0;}#ifndef PMONvoidwdctimeout(arg) void *arg;{ struct channel_softc *chp = (struct channel_softc *)arg; struct wdc_xfer *xfer = chp->ch_queue->sc_xfer.tqh_first; int s; WDCDEBUG_PRINT(("wdctimeout\n"), DEBUG_FUNCS); s = splbio(); if ((chp->ch_flags & WDCF_IRQ_WAIT) != 0) { __wdcerror(chp, "timeout"); printf("\ttype: %s\n", (xfer->c_flags & C_ATAPI) ? "atapi":"ata"); printf("\tc_bcount: %d\n", xfer->c_bcount); printf("\tc_skip: %d\n", xfer->c_skip); /* * Call the interrupt routine. If we just missed and interrupt, * it will do what's needed. Else, it will take the needed * action (reset the device). */ xfer->c_flags |= C_TIMEOU; chp->ch_flags &= ~WDCF_IRQ_WAIT; xfer->c_intr(chp, xfer, 1); } else __wdcerror(chp, "missing untimeout"); splx(s);}#endif /*PMON*//* * Probe drive's capabilites, for use by the controller later * Assumes drvp points to an existing drive. * XXX this should be a controller-indep function */voidwdc_probe_caps(drvp, params) struct ata_drive_datas *drvp; struct ataparams *params;{ struct channel_softc *chp = drvp->chnl_softc; struct wdc_softc *wdc = chp->wdc; int i, printed; int cf_flags = drvp->cf_flags; if ((wdc->cap & (WDC_CAPABILITY_DATA16 | WDC_CAPABILITY_DATA32)) == (WDC_CAPABILITY_DATA16 | WDC_CAPABILITY_DATA32)) { struct ataparams params2; /* * Controller claims 16 and 32 bit transfers. * Re-do an IDENTIFY with 32-bit transfers, * and compare results. */ drvp->drive_flags |= DRIVE_CAP32; ata_get_params(drvp, at_poll, ¶ms2); if (bcmp(params, ¶ms2, sizeof(struct ataparams)) != 0) { /* Not good. fall back to 16bits */ drvp->drive_flags &= ~DRIVE_CAP32; } }#if 0 /* Some ultra-DMA drives claims to only support ATA-3. sigh */ if (params->atap_ata_major > 0x01 && params->atap_ata_major != 0xffff) { for (i = 14; i > 0; i--) { if (params->atap_ata_major & (1 << i)) { printf("%sATA version %d\n", sep, i); drvp->ata_vers = i; break; } } } else #endif /* An ATAPI device is at last PIO mode 3 */ if (drvp->drive_flags & DRIVE_ATAPI) drvp->PIO_mode = 3; /* * It's not in the specs, but it seems that some drive * returns 0xffff in atap_extensions when this field is invalid */ if (params->atap_extensions != 0xffff && (params->atap_extensions & WDC_EXT_MODES)) { printed = 0; /* * XXX some drives report something wrong here (they claim to * support PIO mode 8 !). As mode is coded on 3 bits in * SET FEATURE, limit it to 7 (so limit i to 4). * If higther mode than 7 is found, abort. */ for (i = 7; i >= 0; i--) { if ((params->atap_piomode_supp & (1 << i)) == 0) continue; if (i > 4) { return; } /* * See if mode is accepted. * If the controller can't set its PIO mode, * assume the defaults are good, so don't try * to set it */ if ((wdc->cap & WDC_CAPABILITY_MODE) != 0) if (ata_set_mode(drvp, 0x08 | (i + 3), at_poll) != CMD_OK) continue; if (!printed) { printed = 1; } /* * If controller's driver can't set its PIO mode, * get the highter one for the drive. */ if ((wdc->cap & WDC_CAPABILITY_MODE) == 0 || wdc->PIO_cap >= i + 3) { drvp->PIO_mode = i + 3; drvp->PIO_cap = i + 3; break; } } if (!printed) { /* * We didn't find a valid PIO mode. * Assume the values returned for DMA are buggy too */ return; } drvp->drive_flags |= DRIVE_MODE; printed = 0; for (i = 7; i >= 0; i--) { if ((params->atap_dmamode_supp & (1 << i)) == 0) continue; if ((wdc->cap & WDC_CAPABILITY_DMA) && (wdc->cap & WDC_CAPABILITY_MODE)) if (ata_set_mode(drvp, 0x20 | i, at_poll) != CMD_OK) continue; if (!printed) { printed = 1; } if (wdc->cap & WDC_CAPABILITY_DMA) { if ((wdc->cap & WDC_CAPABILITY_MODE) && wdc->DMA_cap < i) continue; drvp->DMA_mode = i; drvp->DMA_cap = i; drvp->drive_flags |= DRIVE_DMA; } break; } if (params->atap_extensions & WDC_EXT_UDMA_MODES) { for (i = 7; i >= 0; i--) { if ((params->atap_udmamode_supp & (1 << i)) == 0) continue; if ((wdc->cap & WDC_CAPABILITY_MODE) && (wdc->cap & WDC_CAPABILITY_UDMA)) if (ata_set_mode(drvp, 0x40 | i, at_poll) != CMD_OK) continue; if (wdc->cap & WDC_CAPABILITY_UDMA) { if ((wdc->cap & WDC_CAPABILITY_MODE) && wdc->UDMA_cap < i) continue; drvp->UDMA_mode = i; drvp->UDMA_cap = i; drvp->drive_flags |= DRIVE_UDMA; } break; } } } /* Try to guess ATA version here, if it didn't get reported */ if (drvp->ata_vers == 0) { if (drvp->drive_flags & DRIVE_UDMA) drvp->ata_vers = 4; /* should be at last ATA-4 */ else if (drvp->PIO_cap > 2) drvp->ata_vers = 2; /* should be at last ATA-2 */ } if (cf_flags & ATA_CONFIG_PIO_SET) { drvp->PIO_mode = (cf_flags & ATA_CONFIG_PIO_MODES) >> ATA_CONFIG_PIO_OFF; drvp->drive_flags |= DRIVE_MODE; } if ((wdc->cap & WDC_CAPABILITY_DMA) == 0) { /* don't care about DMA modes */ return; } if (cf_flags & ATA_CONFIG_DMA_SET) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -