📄 wdc.c
字号:
/* $OpenBSD: wdc.c,v 1.16 2000/04/10 07:06:14 csapuntz Exp $ *//* $NetBSD: wdc.c,v 1.68 1999/06/23 19:00:17 bouyer Exp $ *//* * Copyright (c) 1998 Manuel Bouyer. 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Manuel Bouyer. * 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. *//*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Charles M. Hannum, by Onno van der Linden and by Manuel Bouyer. * * 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. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``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 FOUNDATION OR CONTRIBUTORS * 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. *//* * CODE UNTESTED IN THE CURRENT REVISION: * */#include <sys/param.h>#include <sys/systm.h>#include <sys/kernel.h>#include <sys/conf.h>#include <sys/buf.h>#include <sys/device.h>#include <sys/malloc.h>#include <sys/syslog.h>#include <sys/proc.h>#include <vm/vm.h>#include <machine/intr.h>#include <machine/bus.h>#include <dev/ata/atavar.h>#include <dev/ata/atareg.h>#include <dev/ic/wdcreg.h>#include <dev/ic/wdcvar.h>#ifndef PMON#include "atapiscsi.h"#endif#define WDCDELAY 100 /* 100 microseconds */#define WDCNDELAY_RST (WDC_RESET_WAIT * 1000 / WDCDELAY)#if 0/* If you enable this, it will report any delays more than WDCDELAY * N long. */#define WDCNDELAY_DEBUG 50#endifLIST_HEAD(xfer_free_list, wdc_xfer) xfer_free_list;#ifndef PMONstatic void __wdcerror __P((struct channel_softc*, char *));#endifstatic int __wdcwait_reset __P((struct channel_softc *, int));void __wdccommand_done __P((struct channel_softc *, struct wdc_xfer *));void __wdccommand_start __P((struct channel_softc *, struct wdc_xfer *)); int __wdccommand_intr __P((struct channel_softc *, struct wdc_xfer *, int));int wdprint __P((void *, const char *));void wdc_kill_pending __P((struct channel_softc *));#define DEBUG_INTR 0x01#define DEBUG_XFERS 0x02#define DEBUG_STATUS 0x04#define DEBUG_FUNCS 0x08#define DEBUG_PROBE 0x10#define DEBUG_STATUSX 0x20#define DEBUG_SDRIVE 0x40#define DEBUG_DETACH 0x80#ifdef WDCDEBUGint wdcdebug_mask = 0;int wdc_nxfer = 0;#define WDCDEBUG_PRINT(args, level) if (wdcdebug_mask & (level)) printf args#else#define WDCDEBUG_PRINT(args, level)#endifint at_poll = AT_POLL;u_int8_t wdc_default_read_reg __P((struct channel_softc *, enum wdc_regs));void wdc_default_write_reg __P((struct channel_softc *, enum wdc_regs, u_int8_t));void wdc_default_read_raw_multi_2 __P((struct channel_softc *, void *, unsigned int));void wdc_default_write_raw_multi_2 __P((struct channel_softc *, void *, unsigned int));void wdc_default_read_raw_multi_4 __P((struct channel_softc *, void *, unsigned int));void wdc_default_write_raw_multi_4 __P((struct channel_softc *, void *, unsigned int));struct channel_softc_vtbl wdc_default_vtbl = { wdc_default_read_reg, wdc_default_write_reg, wdc_default_read_raw_multi_2, wdc_default_write_raw_multi_2, wdc_default_read_raw_multi_4, wdc_default_write_raw_multi_4};u_int8_twdc_default_read_reg(chp, reg) struct channel_softc *chp; enum wdc_regs reg;{ u_int8_t rv;#ifdef DIAGNOSTIC if (reg & _WDC_WRONLY) { printf ("wdc_default_read_reg: reading from a write-only register %d\n", reg); }#endif if (reg & _WDC_AUX) rv = bus_space_read_1(chp->ctl_iot, chp->ctl_ioh, reg & _WDC_REGMASK); else rv = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, reg & _WDC_REGMASK); return(rv);}voidwdc_default_write_reg(chp, reg, val) struct channel_softc *chp; enum wdc_regs reg; u_int8_t val;{#ifdef DIAGNOSTIC if (reg & _WDC_RDONLY) { printf ("wdc_default_write_reg: writing to a read-only register %d\n", reg); }#endif if (reg & _WDC_AUX) bus_space_write_1(chp->ctl_iot, chp->ctl_ioh, reg & _WDC_REGMASK, val); else bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, reg & _WDC_REGMASK, val);}voidwdc_default_read_raw_multi_2(chp, data, nbytes) struct channel_softc *chp; void *data; unsigned int nbytes;{ if (data == NULL) { int i; for (i = 0; i < nbytes; i += 2) { bus_space_read_2(chp->cmd_iot, chp->cmd_ioh, 0); } return; } bus_space_read_raw_multi_2(chp->cmd_iot, chp->cmd_ioh, 0, data, nbytes); return;}voidwdc_default_write_raw_multi_2(chp, data, nbytes) struct channel_softc *chp; void *data; unsigned int nbytes;{ if (data == NULL) { int i; for (i = 0; i < nbytes; i += 2) { bus_space_write_2(chp->cmd_iot, chp->cmd_ioh, 0, 0); } return; } bus_space_write_raw_multi_2(chp->cmd_iot, chp->cmd_ioh, 0, data, nbytes); return;}voidwdc_default_write_raw_multi_4(chp, data, nbytes) struct channel_softc *chp; void *data; unsigned int nbytes;{ if (data == NULL) { int i; for (i = 0; i < nbytes; i += 4) { bus_space_write_4(chp->cmd_iot, chp->cmd_ioh, 0, 0); } return; } bus_space_write_raw_multi_4(chp->cmd_iot, chp->cmd_ioh, 0, data, nbytes); return;}voidwdc_default_read_raw_multi_4(chp, data, nbytes) struct channel_softc *chp; void *data; unsigned int nbytes;{ if (data == NULL) { int i; for (i = 0; i < nbytes; i += 4) { bus_space_read_4(chp->cmd_iot, chp->cmd_ioh, 0); } return; } bus_space_read_raw_multi_4(chp->cmd_iot, chp->cmd_ioh, 0, data, nbytes); return;}intwdprint(aux, pnp) void *aux; const char *pnp;{ struct ata_atapi_attach *aa_link = aux; if (pnp) printf("drive at %s", pnp); printf(" channel %d drive %d", aa_link->aa_channel, aa_link->aa_drv_data->drive); return (UNCONF);}intatapi_print(aux, pnp) void *aux; const char *pnp;{ struct ata_atapi_attach *aa_link = aux; if (pnp) printf("atapibus at %s", pnp); printf(" channel %d", aa_link->aa_channel); return (UNCONF);}voidwdc_disable_intr(chp) struct channel_softc *chp;{ CHP_WRITE_REG(chp, wdr_ctlr, WDCTL_IDS);}voidwdc_enable_intr(chp) struct channel_softc *chp;{ CHP_WRITE_REG(chp, wdr_ctlr, WDCTL_4BIT);}intwdc_select_drive(chp, drive, howlong) struct channel_softc *chp; int drive; int howlong;{ CHP_WRITE_REG(chp, wdr_sdh, WDSD_IBM | (drive << 4)); delay(1); if (wdcwait(chp, WDCS_DRQ, 0, howlong)) { WDCDEBUG_PRINT(("wdc_select_drive %s:%d:%d waiting for %d" "after\n", chp->wdc->sc_dev.dv_xname, chp->channel, drive, howlong), DEBUG_SDRIVE); return -1; } return 0;}/* Test to see controller with at last one attached drive is there. * Returns a bit for each possible drive found (0x01 for drive 0, * 0x02 for drive 1). * Logic: * - If a status register is at 0xff, assume there is no drive here * (ISA has pull-up resistors). If no drive at all -> return. * - reset the controller, wait for it to complete (may take up to 31s !). * If timeout -> return. * - test ATA/ATAPI signatures. If at last one drive found -> return. * - try an ATA command on the master. */intwdcprobe(chp) struct channel_softc *chp;{ u_int8_t st0, st1, sc, sn, cl, ch; u_int8_t ret_value = 0x03; u_int8_t drive; if (!chp->_vtbl) chp->_vtbl = &wdc_default_vtbl; /* * Sanity check to see if the wdc channel responds at all. */ if (chp->wdc == NULL || (chp->wdc->cap & WDC_CAPABILITY_NO_EXTRA_RESETS) == 0) { CHP_WRITE_REG(chp, wdr_sdh, WDSD_IBM); delay(10); st0 = CHP_READ_REG(chp, wdr_status); CHP_WRITE_REG(chp, wdr_sdh, WDSD_IBM | 0x10); delay(10); st1 = CHP_READ_REG(chp, wdr_status); WDCDEBUG_PRINT(("%s:%d: before reset, st0=0x%x, st1=0x%x\n", chp->wdc ? chp->wdc->sc_dev.dv_xname : "wdcprobe", chp->channel, st0, st1), DEBUG_PROBE); if (st0 == 0xff) ret_value &= ~0x01; if (st1 == 0xff) ret_value &= ~0x02; if (ret_value == 0) return 0; } /* assert SRST, wait for reset to complete */ CHP_WRITE_REG(chp, wdr_sdh, WDSD_IBM); delay(10); CHP_WRITE_REG(chp,wdr_ctlr, WDCTL_RST | WDCTL_IDS); DELAY(1000); CHP_WRITE_REG(chp, wdr_ctlr, WDCTL_IDS); delay(1000); (void) CHP_READ_REG(chp, wdr_error); CHP_WRITE_REG(chp, wdr_ctlr, WDCTL_4BIT); delay(10); ret_value = __wdcwait_reset(chp, ret_value); WDCDEBUG_PRINT(("%s:%d: after reset, ret_value=0x%d\n", chp->wdc ? chp->wdc->sc_dev.dv_xname : "wdcprobe", chp->channel, ret_value), DEBUG_PROBE); /* if reset failed, there's nothing here */ if (ret_value == 0) return 0; /* * Test presence of drives. First test register signatures looking for * ATAPI devices. If it's not an ATAPI and reset said there may be * something here assume it's ATA or OLD. Ghost will be killed later in * attach routine. */ for (drive = 0; drive < 2; drive++) { if ((ret_value & (0x01 << drive)) == 0) continue; CHP_WRITE_REG(chp, wdr_sdh, WDSD_IBM | (drive << 4)); delay(10); /* Save registers contents */ sc = CHP_READ_REG(chp, wdr_seccnt); sn = CHP_READ_REG(chp, wdr_sector); cl = CHP_READ_REG(chp, wdr_cyl_lo); ch = CHP_READ_REG(chp, wdr_cyl_hi); WDCDEBUG_PRINT(("%s:%d:%d: after reset, sc=0x%x sn=0x%x " "cl=0x%x ch=0x%x\n", chp->wdc ? chp->wdc->sc_dev.dv_xname : "wdcprobe", chp->channel, drive, sc, sn, cl, ch), DEBUG_PROBE); /* * This is a simplification of the test in the ATAPI * spec since not all drives seem to set the other regs * correctly. */ if (cl == 0x14 && ch == 0xeb) { chp->ch_drive[drive].drive_flags |= DRIVE_ATAPI; } else { chp->ch_drive[drive].drive_flags |= DRIVE_ATA; if (chp->wdc == NULL || (chp->wdc->cap & WDC_CAPABILITY_PREATA) != 0) chp->ch_drive[drive].drive_flags |= DRIVE_OLD; } } return (ret_value); }#ifndef PMON/* * Call activate routine of underlying devices. */intwdcactivate(self, act) struct device *self; enum devact act;{ int error = 0; int s; s = splbio(); config_activate_children(self, act); splx(s); return (error);}#endifvoidwdcattach(chp) struct channel_softc *chp;{ int channel_flags, ctrl_flags, i;#ifndef __OpenBSD__ int error;#endif struct ata_atapi_attach aa_link; struct ataparams params; static int inited = 0;#ifndef PMON extern int cold; if (!cold) at_poll = AT_WAIT;#endif#ifndef __OpenBSD__ if ((error = wdc_addref(chp)) != 0) { printf("%s: unable to enable controller\n", chp->wdc->sc_dev.dv_xname); return; }#endif if (!chp->_vtbl) chp->_vtbl = &wdc_default_vtbl; if (wdcprobe(chp) == 0) { /* If no drives, abort attach here. */#ifndef __OpenBSD__ wdc_delref(chp);#endif return; } /* init list only once */ if (inited == 0) { LIST_INIT(&xfer_free_list); inited++; } TAILQ_INIT(&chp->ch_queue->sc_xfer); for (i = 0; i < 2; i++) { chp->ch_drive[i].chnl_softc = chp; chp->ch_drive[i].drive = i; /* If controller can't do 16bit flag the drives as 32bit */ if ((chp->wdc->cap & (WDC_CAPABILITY_DATA16 | WDC_CAPABILITY_DATA32)) == WDC_CAPABILITY_DATA32) chp->ch_drive[i].drive_flags |= DRIVE_CAP32; if ((chp->ch_drive[i].drive_flags & DRIVE) == 0) continue; if (i == 1 && ((chp->ch_drive[0].drive_flags & DRIVE) == 0)) chp->ch_flags |= WDCF_ONESLAVE; /* Issue a IDENTIFY command, to try to detect slave ghost */ if (ata_get_params(&chp->ch_drive[i], at_poll, ¶ms) == CMD_OK) { /* If IDENTIFY succeded, this is not an OLD ctrl */ chp->ch_drive[0].drive_flags &= ~DRIVE_OLD; chp->ch_drive[1].drive_flags &= ~DRIVE_OLD; } else { chp->ch_drive[i].drive_flags &= ~(DRIVE_ATA | DRIVE_ATAPI); WDCDEBUG_PRINT(("%s:%d:%d: IDENTIFY failed\n", chp->wdc->sc_dev.dv_xname, chp->channel, i), DEBUG_PROBE); if ((chp->ch_drive[i].drive_flags & DRIVE_OLD) == 0) continue; /* * Pre-ATA drive ? * Test registers writability (Error register not * writable, but cyllo is), then try an ATA command. */ CHP_WRITE_REG(chp, wdr_sdh, WDSD_IBM | (i << 4)); delay(10); CHP_WRITE_REG(chp, wdr_features, 0x58); CHP_WRITE_REG(chp, wdr_cyl_lo, 0xa5); if ((CHP_READ_REG(chp, wdr_error) == 0x58) || (CHP_READ_REG(chp, wdr_cyl_lo) != 0xa5)) { WDCDEBUG_PRINT(("%s:%d:%d: register " "writability failed\n", chp->wdc->sc_dev.dv_xname, chp->channel, i), DEBUG_PROBE); chp->ch_drive[i].drive_flags &= ~DRIVE_OLD; } CHP_WRITE_REG(chp, wdr_sdh, WDSD_IBM | (i << 4)); delay(100); if (wait_for_ready(chp, 10000) != 0) { WDCDEBUG_PRINT(("%s:%d:%d: not ready\n", chp->wdc->sc_dev.dv_xname, chp->channel, i), DEBUG_PROBE); chp->ch_drive[i].drive_flags &= ~DRIVE_OLD; continue; } CHP_WRITE_REG(chp, wdr_command, WDCC_RECAL); if (wait_for_ready(chp, 10000) != 0) { WDCDEBUG_PRINT(("%s:%d:%d: WDCC_RECAL failed\n", chp->wdc->sc_dev.dv_xname, chp->channel, i), DEBUG_PROBE); chp->ch_drive[i].drive_flags &= ~DRIVE_OLD; } } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -