1*2e7ffe50Skrw /* $OpenBSD: wd.c,v 1.111 2013/09/15 13:44:53 krw Exp $ */ 27481efa2Scsapuntz /* $NetBSD: wd.c,v 1.193 1999/02/28 17:15:27 explorer Exp $ */ 37481efa2Scsapuntz 47481efa2Scsapuntz /* 59a5cc0e7Sgrange * Copyright (c) 1998, 2001 Manuel Bouyer. All rights reserved. 67481efa2Scsapuntz * 77481efa2Scsapuntz * Redistribution and use in source and binary forms, with or without 87481efa2Scsapuntz * modification, are permitted provided that the following conditions 97481efa2Scsapuntz * are met: 107481efa2Scsapuntz * 1. Redistributions of source code must retain the above copyright 117481efa2Scsapuntz * notice, this list of conditions and the following disclaimer. 127481efa2Scsapuntz * 2. Redistributions in binary form must reproduce the above copyright 137481efa2Scsapuntz * notice, this list of conditions and the following disclaimer in the 147481efa2Scsapuntz * documentation and/or other materials provided with the distribution. 157481efa2Scsapuntz * 167481efa2Scsapuntz * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 177481efa2Scsapuntz * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 187481efa2Scsapuntz * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 197481efa2Scsapuntz * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 207481efa2Scsapuntz * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 217481efa2Scsapuntz * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 227481efa2Scsapuntz * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 237481efa2Scsapuntz * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 247481efa2Scsapuntz * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 257481efa2Scsapuntz * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 267481efa2Scsapuntz */ 277481efa2Scsapuntz 287481efa2Scsapuntz /*- 297481efa2Scsapuntz * Copyright (c) 1998 The NetBSD Foundation, Inc. 307481efa2Scsapuntz * All rights reserved. 317481efa2Scsapuntz * 327481efa2Scsapuntz * This code is derived from software contributed to The NetBSD Foundation 337481efa2Scsapuntz * by Charles M. Hannum and by Onno van der Linden. 347481efa2Scsapuntz * 357481efa2Scsapuntz * Redistribution and use in source and binary forms, with or without 367481efa2Scsapuntz * modification, are permitted provided that the following conditions 377481efa2Scsapuntz * are met: 387481efa2Scsapuntz * 1. Redistributions of source code must retain the above copyright 397481efa2Scsapuntz * notice, this list of conditions and the following disclaimer. 407481efa2Scsapuntz * 2. Redistributions in binary form must reproduce the above copyright 417481efa2Scsapuntz * notice, this list of conditions and the following disclaimer in the 427481efa2Scsapuntz * documentation and/or other materials provided with the distribution. 437481efa2Scsapuntz * 447481efa2Scsapuntz * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 457481efa2Scsapuntz * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 467481efa2Scsapuntz * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 477481efa2Scsapuntz * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 487481efa2Scsapuntz * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 497481efa2Scsapuntz * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 507481efa2Scsapuntz * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 517481efa2Scsapuntz * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 527481efa2Scsapuntz * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 537481efa2Scsapuntz * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 547481efa2Scsapuntz * POSSIBILITY OF SUCH DAMAGE. 557481efa2Scsapuntz */ 567481efa2Scsapuntz 577481efa2Scsapuntz #if 0 587481efa2Scsapuntz #include "rnd.h" 597481efa2Scsapuntz #endif 607481efa2Scsapuntz 617481efa2Scsapuntz #include <sys/param.h> 627481efa2Scsapuntz #include <sys/systm.h> 637481efa2Scsapuntz #include <sys/kernel.h> 647481efa2Scsapuntz #include <sys/conf.h> 657481efa2Scsapuntz #include <sys/file.h> 667481efa2Scsapuntz #include <sys/stat.h> 677481efa2Scsapuntz #include <sys/ioctl.h> 68fd114b83Sthib #include <sys/mutex.h> 697481efa2Scsapuntz #include <sys/buf.h> 707481efa2Scsapuntz #include <sys/uio.h> 717481efa2Scsapuntz #include <sys/malloc.h> 727481efa2Scsapuntz #include <sys/device.h> 737481efa2Scsapuntz #include <sys/disklabel.h> 747481efa2Scsapuntz #include <sys/disk.h> 757481efa2Scsapuntz #include <sys/syslog.h> 767481efa2Scsapuntz #include <sys/proc.h> 77f97ca3e4Scsapuntz #include <sys/vnode.h> 7891f4f7d8Sdlg #include <sys/dkio.h> 7914cbac95Skettenis #include <sys/reboot.h> 807481efa2Scsapuntz 81489e49f9Smiod #include <uvm/uvm_extern.h> 827481efa2Scsapuntz 837481efa2Scsapuntz #include <machine/intr.h> 847481efa2Scsapuntz #include <machine/bus.h> 857481efa2Scsapuntz 867481efa2Scsapuntz #include <dev/ata/atareg.h> 877481efa2Scsapuntz #include <dev/ata/atavar.h> 887481efa2Scsapuntz #include <dev/ata/wdvar.h> 897481efa2Scsapuntz #include <dev/ic/wdcreg.h> 90f5423f8cScsapuntz #include <dev/ic/wdcvar.h> 917481efa2Scsapuntz #if 0 927481efa2Scsapuntz #include "locators.h" 937481efa2Scsapuntz #endif 947481efa2Scsapuntz 9581a81281Saaron #define LBA48_THRESHOLD (0xfffffff) /* 128GB / DEV_BSIZE */ 9681a81281Saaron 977481efa2Scsapuntz #define WDIORETRIES_SINGLE 4 /* number of retries before single-sector */ 987481efa2Scsapuntz #define WDIORETRIES 5 /* number of retries before giving up */ 997481efa2Scsapuntz #define RECOVERYTIME hz/2 /* time to wait before retrying a cmd */ 1007481efa2Scsapuntz 1017481efa2Scsapuntz #define DEBUG_INTR 0x01 1027481efa2Scsapuntz #define DEBUG_XFERS 0x02 1037481efa2Scsapuntz #define DEBUG_STATUS 0x04 1047481efa2Scsapuntz #define DEBUG_FUNCS 0x08 1057481efa2Scsapuntz #define DEBUG_PROBE 0x10 1067481efa2Scsapuntz #ifdef WDCDEBUG 1077481efa2Scsapuntz extern int wdcdebug_wd_mask; /* init'ed in ata_wdc.c */ 108d6986e4fSgrange #define WDCDEBUG_PRINT(args, level) do { \ 109d6986e4fSgrange if ((wdcdebug_wd_mask & (level)) != 0) \ 110d6986e4fSgrange printf args; \ 111d6986e4fSgrange } while (0) 1127481efa2Scsapuntz #else 1137481efa2Scsapuntz #define WDCDEBUG_PRINT(args, level) 1147481efa2Scsapuntz #endif 1157481efa2Scsapuntz 1167481efa2Scsapuntz 1177481efa2Scsapuntz #define sc_drive sc_wdc_bio.drive 1187481efa2Scsapuntz #define sc_mode sc_wdc_bio.mode 1197481efa2Scsapuntz #define sc_multi sc_wdc_bio.multi 1207481efa2Scsapuntz 121c4071fd1Smillert int wdprobe(struct device *, void *, void *); 122c4071fd1Smillert void wdattach(struct device *, struct device *, void *); 123c4071fd1Smillert int wddetach(struct device *, int); 124e78728c7Spirofti int wdactivate(struct device *, int); 125c4071fd1Smillert int wdprint(void *, char *); 1267481efa2Scsapuntz 1277481efa2Scsapuntz struct cfattach wd_ca = { 128f97ca3e4Scsapuntz sizeof(struct wd_softc), wdprobe, wdattach, 129a23d39c4Smiod wddetach, wdactivate 1307481efa2Scsapuntz }; 1317481efa2Scsapuntz 1327481efa2Scsapuntz struct cfdriver wd_cd = { 1337481efa2Scsapuntz NULL, "wd", DV_DISK 1347481efa2Scsapuntz }; 1357481efa2Scsapuntz 136c4071fd1Smillert void wdgetdefaultlabel(struct wd_softc *, struct disklabel *); 137df591ed6Sderaadt int wdgetdisklabel(dev_t dev, struct wd_softc *, struct disklabel *, int); 138c4071fd1Smillert void wdstrategy(struct buf *); 139c4071fd1Smillert void wdstart(void *); 140c4071fd1Smillert void __wdstart(struct wd_softc*, struct buf *); 141c4071fd1Smillert void wdrestart(void *); 142c4071fd1Smillert int wd_get_params(struct wd_softc *, u_int8_t, struct ataparams *); 143c4071fd1Smillert void wd_flushcache(struct wd_softc *, int); 144f8fc1e98Skettenis void wd_standby(struct wd_softc *, int); 1457481efa2Scsapuntz 1467481efa2Scsapuntz /* XXX: these should go elsewhere */ 1477481efa2Scsapuntz cdev_decl(wd); 1487481efa2Scsapuntz bdev_decl(wd); 1497481efa2Scsapuntz 15031efee77Sjsing #define wdlookup(unit) (struct wd_softc *)disk_lookup(&wd_cd, (unit)) 151f97ca3e4Scsapuntz 1527481efa2Scsapuntz 1537481efa2Scsapuntz int 15406ad70d2Sgrange wdprobe(struct device *parent, void *match_, void *aux) 1557481efa2Scsapuntz { 1567481efa2Scsapuntz struct ata_atapi_attach *aa_link = aux; 1577481efa2Scsapuntz struct cfdata *match = match_; 1587481efa2Scsapuntz 1597481efa2Scsapuntz if (aa_link == NULL) 1607481efa2Scsapuntz return 0; 1617481efa2Scsapuntz if (aa_link->aa_type != T_ATA) 1627481efa2Scsapuntz return 0; 1637481efa2Scsapuntz 1647481efa2Scsapuntz if (match->cf_loc[0] != -1 && 1657481efa2Scsapuntz match->cf_loc[0] != aa_link->aa_channel) 1667481efa2Scsapuntz return 0; 1677481efa2Scsapuntz 1687481efa2Scsapuntz if (match->cf_loc[1] != -1 && 1697481efa2Scsapuntz match->cf_loc[1] != aa_link->aa_drv_data->drive) 1707481efa2Scsapuntz return 0; 1717481efa2Scsapuntz 1727481efa2Scsapuntz return 1; 1737481efa2Scsapuntz } 1747481efa2Scsapuntz 1757481efa2Scsapuntz void 17606ad70d2Sgrange wdattach(struct device *parent, struct device *self, void *aux) 1777481efa2Scsapuntz { 1787481efa2Scsapuntz struct wd_softc *wd = (void *)self; 1797481efa2Scsapuntz struct ata_atapi_attach *aa_link= aux; 18042a17170Sjsg struct wdc_command wdc_c; 1817481efa2Scsapuntz int i, blank; 1827481efa2Scsapuntz char buf[41], c, *p, *q; 1837481efa2Scsapuntz WDCDEBUG_PRINT(("wdattach\n"), DEBUG_FUNCS | DEBUG_PROBE); 1847481efa2Scsapuntz 1857481efa2Scsapuntz wd->openings = aa_link->aa_openings; 186f97ca3e4Scsapuntz wd->drvp = aa_link->aa_drv_data; 187f97ca3e4Scsapuntz 18802acd7cbSray strlcpy(wd->drvp->drive_name, wd->sc_dev.dv_xname, 18902acd7cbSray sizeof(wd->drvp->drive_name)); 190f97ca3e4Scsapuntz wd->drvp->cf_flags = wd->sc_dev.dv_cfdata->cf_flags; 1917481efa2Scsapuntz 192ec8802a4Scsapuntz if ((NERRS_MAX - 2) > 0) 193ec8802a4Scsapuntz wd->drvp->n_dmaerrs = NERRS_MAX - 2; 194ec8802a4Scsapuntz else 195ec8802a4Scsapuntz wd->drvp->n_dmaerrs = 0; 196ec8802a4Scsapuntz 1977481efa2Scsapuntz /* read our drive info */ 198f97ca3e4Scsapuntz if (wd_get_params(wd, at_poll, &wd->sc_params) != 0) { 1997481efa2Scsapuntz printf("%s: IDENTIFY failed\n", wd->sc_dev.dv_xname); 2007481efa2Scsapuntz return; 2017481efa2Scsapuntz } 2027481efa2Scsapuntz 2037481efa2Scsapuntz for (blank = 0, p = wd->sc_params.atap_model, q = buf, i = 0; 2047481efa2Scsapuntz i < sizeof(wd->sc_params.atap_model); i++) { 2057481efa2Scsapuntz c = *p++; 2067481efa2Scsapuntz if (c == '\0') 2077481efa2Scsapuntz break; 2087481efa2Scsapuntz if (c != ' ') { 2097481efa2Scsapuntz if (blank) { 2107481efa2Scsapuntz *q++ = ' '; 2117481efa2Scsapuntz blank = 0; 2127481efa2Scsapuntz } 2137481efa2Scsapuntz *q++ = c; 2147481efa2Scsapuntz } else 2157481efa2Scsapuntz blank = 1; 2167481efa2Scsapuntz } 2177481efa2Scsapuntz *q++ = '\0'; 2187481efa2Scsapuntz 2197481efa2Scsapuntz printf(": <%s>\n", buf); 2207481efa2Scsapuntz 22112dc5567Scsapuntz wdc_probe_caps(wd->drvp, &wd->sc_params); 22212dc5567Scsapuntz wdc_print_caps(wd->drvp); 22312dc5567Scsapuntz 2247481efa2Scsapuntz if ((wd->sc_params.atap_multi & 0xff) > 1) { 2257481efa2Scsapuntz wd->sc_multi = wd->sc_params.atap_multi & 0xff; 2267481efa2Scsapuntz } else { 2277481efa2Scsapuntz wd->sc_multi = 1; 2287481efa2Scsapuntz } 2297481efa2Scsapuntz 230c3a9df52Sderaadt printf("%s: %d-sector PIO,", wd->sc_dev.dv_xname, wd->sc_multi); 2317481efa2Scsapuntz 2324b4c6f4eSgluk /* use 48-bit LBA if enabled */ 2334b4c6f4eSgluk /* XXX: shall we use it if drive capacity < 137Gb? */ 2344b4c6f4eSgluk if ((wd->sc_params.atap_cmd2_en & ATAPI_CMD2_48AD) != 0) 2354b4c6f4eSgluk wd->sc_flags |= WDF_LBA48; 2364b4c6f4eSgluk 2377481efa2Scsapuntz /* Prior to ATA-4, LBA was optional. */ 2387481efa2Scsapuntz if ((wd->sc_params.atap_capabilities1 & WDC_CAP_LBA) != 0) 2397481efa2Scsapuntz wd->sc_flags |= WDF_LBA; 2407481efa2Scsapuntz #if 0 2417481efa2Scsapuntz /* ATA-4 requires LBA. */ 2427481efa2Scsapuntz if (wd->sc_params.atap_ataversion != 0xffff && 2437481efa2Scsapuntz wd->sc_params.atap_ataversion >= WDC_VER_ATA4) 2447481efa2Scsapuntz wd->sc_flags |= WDF_LBA; 2457481efa2Scsapuntz #endif 2467481efa2Scsapuntz 2474b4c6f4eSgluk if ((wd->sc_flags & WDF_LBA48) != 0) { 2484b4c6f4eSgluk wd->sc_capacity = 2494b4c6f4eSgluk (((u_int64_t)wd->sc_params.atap_max_lba[3] << 48) | 2504b4c6f4eSgluk ((u_int64_t)wd->sc_params.atap_max_lba[2] << 32) | 2514b4c6f4eSgluk ((u_int64_t)wd->sc_params.atap_max_lba[1] << 16) | 2524b4c6f4eSgluk (u_int64_t)wd->sc_params.atap_max_lba[0]); 2532a13a35bSgrange printf(" LBA48, %lluMB, %llu sectors\n", 2544b4c6f4eSgluk wd->sc_capacity / (1048576 / DEV_BSIZE), 2554b4c6f4eSgluk wd->sc_capacity); 2564b4c6f4eSgluk } else if ((wd->sc_flags & WDF_LBA) != 0) { 2577481efa2Scsapuntz wd->sc_capacity = 2587481efa2Scsapuntz (wd->sc_params.atap_capacity[1] << 16) | 2597481efa2Scsapuntz wd->sc_params.atap_capacity[0]; 2602a13a35bSgrange printf(" LBA, %lluMB, %llu sectors\n", 2617481efa2Scsapuntz wd->sc_capacity / (1048576 / DEV_BSIZE), 2627481efa2Scsapuntz wd->sc_capacity); 2637481efa2Scsapuntz } else { 2647481efa2Scsapuntz wd->sc_capacity = 2657481efa2Scsapuntz wd->sc_params.atap_cylinders * 2667481efa2Scsapuntz wd->sc_params.atap_heads * 2677481efa2Scsapuntz wd->sc_params.atap_sectors; 2684b4c6f4eSgluk printf(" CHS, %lluMB, %d cyl, %d head, %d sec, %llu sectors\n", 2697481efa2Scsapuntz wd->sc_capacity / (1048576 / DEV_BSIZE), 2707481efa2Scsapuntz wd->sc_params.atap_cylinders, 2717481efa2Scsapuntz wd->sc_params.atap_heads, 2727481efa2Scsapuntz wd->sc_params.atap_sectors, 2737481efa2Scsapuntz wd->sc_capacity); 2747481efa2Scsapuntz } 2757481efa2Scsapuntz WDCDEBUG_PRINT(("%s: atap_dmatiming_mimi=%d, atap_dmatiming_recom=%d\n", 2767481efa2Scsapuntz self->dv_xname, wd->sc_params.atap_dmatiming_mimi, 2777481efa2Scsapuntz wd->sc_params.atap_dmatiming_recom), DEBUG_PROBE); 27842a17170Sjsg 27944323affSjsg /* use read look ahead if supported */ 280119641fdSjsg if (wd->sc_params.atap_cmd_set1 & WDC_CMD1_AHEAD) { 281119641fdSjsg bzero(&wdc_c, sizeof(struct wdc_command)); 282119641fdSjsg wdc_c.r_command = SET_FEATURES; 283119641fdSjsg wdc_c.r_precomp = WDSF_READAHEAD_EN; 284119641fdSjsg wdc_c.timeout = 1000; 285119641fdSjsg wdc_c.flags = at_poll; 286119641fdSjsg 287119641fdSjsg if (wdc_exec_command(wd->drvp, &wdc_c) != WDC_COMPLETE) { 288119641fdSjsg printf("%s: enable look ahead command didn't " 289119641fdSjsg "complete\n", wd->sc_dev.dv_xname); 290119641fdSjsg } 291119641fdSjsg } 292119641fdSjsg 29344323affSjsg /* use write cache if supported */ 294119641fdSjsg if (wd->sc_params.atap_cmd_set1 & WDC_CMD1_CACHE) { 295119641fdSjsg bzero(&wdc_c, sizeof(struct wdc_command)); 296119641fdSjsg wdc_c.r_command = SET_FEATURES; 297119641fdSjsg wdc_c.r_precomp = WDSF_EN_WR_CACHE; 298119641fdSjsg wdc_c.timeout = 1000; 299119641fdSjsg wdc_c.flags = at_poll; 300119641fdSjsg 301119641fdSjsg if (wdc_exec_command(wd->drvp, &wdc_c) != WDC_COMPLETE) { 302119641fdSjsg printf("%s: enable write cache command didn't " 303119641fdSjsg "complete\n", wd->sc_dev.dv_xname); 304119641fdSjsg } 305119641fdSjsg } 30644323affSjsg 30742a17170Sjsg /* 30842a17170Sjsg * FREEZE LOCK the drive so malicous users can't lock it on us. 30942a17170Sjsg * As there is no harm in issuing this to drives that don't 31042a17170Sjsg * support the security feature set we just send it, and don't 31142a17170Sjsg * bother checking if the drive sends a command abort to tell us it 31242a17170Sjsg * doesn't support it. 31342a17170Sjsg */ 31442a17170Sjsg bzero(&wdc_c, sizeof(struct wdc_command)); 31542a17170Sjsg 31642a17170Sjsg wdc_c.r_command = WDCC_SEC_FREEZE_LOCK; 31742a17170Sjsg wdc_c.timeout = 1000; 31842a17170Sjsg wdc_c.flags = at_poll; 31942a17170Sjsg if (wdc_exec_command(wd->drvp, &wdc_c) != WDC_COMPLETE) { 32042a17170Sjsg printf("%s: freeze lock command didn't complete\n", 32142a17170Sjsg wd->sc_dev.dv_xname); 32242a17170Sjsg } 32342a17170Sjsg 3247481efa2Scsapuntz /* 32573b308c6Sjsing * Initialize disk structures. 3267481efa2Scsapuntz */ 3277481efa2Scsapuntz wd->sc_dk.dk_name = wd->sc_dev.dv_xname; 328c2235760Sdlg bufq_init(&wd->sc_bufq, BUFQ_DEFAULT); 3297c4d2af8Sart timeout_set(&wd->sc_restart_timeout, wdrestart, wd); 33073b308c6Sjsing 33173b308c6Sjsing /* Attach disk. */ 3322690bc4bSjsing disk_attach(&wd->sc_dev, &wd->sc_dk); 33373b308c6Sjsing wd->sc_wdc_bio.lp = wd->sc_dk.dk_label; 3347481efa2Scsapuntz } 3357481efa2Scsapuntz 336f97ca3e4Scsapuntz int 337e78728c7Spirofti wdactivate(struct device *self, int act) 338f97ca3e4Scsapuntz { 3396753d76aSkettenis struct wd_softc *wd = (void *)self; 340f97ca3e4Scsapuntz int rv = 0; 341f97ca3e4Scsapuntz 342f97ca3e4Scsapuntz switch (act) { 3436753d76aSkettenis case DVACT_SUSPEND: 3441e4b376cSderaadt break; 3451e4b376cSderaadt case DVACT_POWERDOWN: 34645326f51Skettenis wd_flushcache(wd, AT_POLL); 3471e4b376cSderaadt if (boothowto & RB_POWERDOWN) 348f8fc1e98Skettenis wd_standby(wd, AT_POLL); 3496753d76aSkettenis break; 350dff40dcaSmlarkin case DVACT_RESUME: 351dff40dcaSmlarkin /* 352dff40dcaSmlarkin * Do two resets separated by a small delay. The 353dff40dcaSmlarkin * first wakes the controller, the second resets 354ba8cbb4aSmiod * the channel. 355dff40dcaSmlarkin */ 356dff40dcaSmlarkin wdc_disable_intr(wd->drvp->chnl_softc); 357ba8cbb4aSmiod wdc_reset_channel(wd->drvp, 1); 358dff40dcaSmlarkin delay(10000); 359ba8cbb4aSmiod wdc_reset_channel(wd->drvp, 0); 360dff40dcaSmlarkin wdc_enable_intr(wd->drvp->chnl_softc); 361ba8cbb4aSmiod wd_get_params(wd, at_poll, &wd->sc_params); 362dff40dcaSmlarkin break; 363f97ca3e4Scsapuntz } 364f97ca3e4Scsapuntz return (rv); 365f97ca3e4Scsapuntz } 366f97ca3e4Scsapuntz 367f97ca3e4Scsapuntz int 36806ad70d2Sgrange wddetach(struct device *self, int flags) 369f97ca3e4Scsapuntz { 370f97ca3e4Scsapuntz struct wd_softc *sc = (struct wd_softc *)self; 371f97ca3e4Scsapuntz 3724072b0baSderaadt timeout_del(&sc->sc_restart_timeout); 3734072b0baSderaadt 37499b6117dSmatthew bufq_drain(&sc->sc_bufq); 375f97ca3e4Scsapuntz 376e1d64023Smatthew disk_gone(wdopen, self->dv_unit); 377f97ca3e4Scsapuntz 378a23d39c4Smiod /* Detach disk. */ 379c2235760Sdlg bufq_destroy(&sc->sc_bufq); 380a23d39c4Smiod disk_detach(&sc->sc_dk); 381a23d39c4Smiod 382f97ca3e4Scsapuntz return (0); 383f97ca3e4Scsapuntz } 384f97ca3e4Scsapuntz 3857481efa2Scsapuntz /* 3867481efa2Scsapuntz * Read/write routine for a buffer. Validates the arguments and schedules the 3877481efa2Scsapuntz * transfer. Does not wait for the transfer to complete. 3887481efa2Scsapuntz */ 3897481efa2Scsapuntz void 39006ad70d2Sgrange wdstrategy(struct buf *bp) 3917481efa2Scsapuntz { 392f97ca3e4Scsapuntz struct wd_softc *wd; 3937481efa2Scsapuntz int s; 394f97ca3e4Scsapuntz 395a83b1495Skrw wd = wdlookup(DISKUNIT(bp->b_dev)); 396f97ca3e4Scsapuntz if (wd == NULL) { 397f97ca3e4Scsapuntz bp->b_error = ENXIO; 398f97ca3e4Scsapuntz goto bad; 399f97ca3e4Scsapuntz } 400f97ca3e4Scsapuntz 4017481efa2Scsapuntz WDCDEBUG_PRINT(("wdstrategy (%s)\n", wd->sc_dev.dv_xname), 4027481efa2Scsapuntz DEBUG_XFERS); 4037481efa2Scsapuntz 4047481efa2Scsapuntz /* If device invalidated (e.g. media change, door open), error. */ 4057481efa2Scsapuntz if ((wd->sc_flags & WDF_LOADED) == 0) { 4067481efa2Scsapuntz bp->b_error = EIO; 4077481efa2Scsapuntz goto bad; 4087481efa2Scsapuntz } 4097481efa2Scsapuntz 410d1e5b14cSmatthew /* Validate the request. */ 411d1e5b14cSmatthew if (bounds_check_with_label(bp, wd->sc_dk.dk_label) == -1) 4127481efa2Scsapuntz goto done; 4137481efa2Scsapuntz 414d1e5b14cSmatthew /* Check that the number of sectors can fit in a byte. */ 415d1e5b14cSmatthew if ((bp->b_bcount / wd->sc_dk.dk_label->d_secsize) >= (1 << NBBY)) { 416d1e5b14cSmatthew bp->b_error = EINVAL; 417d1e5b14cSmatthew goto bad; 418d1e5b14cSmatthew } 419d1e5b14cSmatthew 4207481efa2Scsapuntz /* Queue transfer on drive, activate drive and controller if idle. */ 421c2235760Sdlg bufq_queue(&wd->sc_bufq, bp); 4227481efa2Scsapuntz s = splbio(); 4237481efa2Scsapuntz wdstart(wd); 4247481efa2Scsapuntz splx(s); 425f97ca3e4Scsapuntz device_unref(&wd->sc_dev); 4267481efa2Scsapuntz return; 427d1e5b14cSmatthew 4287481efa2Scsapuntz bad: 4297481efa2Scsapuntz bp->b_flags |= B_ERROR; 4307481efa2Scsapuntz bp->b_resid = bp->b_bcount; 431d1e5b14cSmatthew done: 432343aa71cSart s = splbio(); 4337481efa2Scsapuntz biodone(bp); 434343aa71cSart splx(s); 435f97ca3e4Scsapuntz if (wd != NULL) 436f97ca3e4Scsapuntz device_unref(&wd->sc_dev); 4377481efa2Scsapuntz } 4387481efa2Scsapuntz 4397481efa2Scsapuntz /* 4407481efa2Scsapuntz * Queue a drive for I/O. 4417481efa2Scsapuntz */ 4427481efa2Scsapuntz void 44306ad70d2Sgrange wdstart(void *arg) 4447481efa2Scsapuntz { 4457481efa2Scsapuntz struct wd_softc *wd = arg; 446fd114b83Sthib struct buf *bp = NULL; 4477481efa2Scsapuntz 4487481efa2Scsapuntz WDCDEBUG_PRINT(("wdstart %s\n", wd->sc_dev.dv_xname), 4497481efa2Scsapuntz DEBUG_XFERS); 4507481efa2Scsapuntz while (wd->openings > 0) { 4517481efa2Scsapuntz 4527481efa2Scsapuntz /* Is there a buf for us ? */ 453c2235760Sdlg if ((bp = bufq_dequeue(&wd->sc_bufq)) == NULL) 4547481efa2Scsapuntz return; 4557481efa2Scsapuntz /* 4567481efa2Scsapuntz * Make the command. First lock the device 4577481efa2Scsapuntz */ 4587481efa2Scsapuntz wd->openings--; 4597481efa2Scsapuntz 4607481efa2Scsapuntz wd->retries = 0; 4617481efa2Scsapuntz __wdstart(wd, bp); 4627481efa2Scsapuntz } 4637481efa2Scsapuntz } 4647481efa2Scsapuntz 4657481efa2Scsapuntz void 46606ad70d2Sgrange __wdstart(struct wd_softc *wd, struct buf *bp) 4677481efa2Scsapuntz { 468*2e7ffe50Skrw struct disklabel *lp; 469*2e7ffe50Skrw u_int64_t nsecs; 470cec793cfSgrange 471*2e7ffe50Skrw lp = wd->sc_dk.dk_label; 472*2e7ffe50Skrw wd->sc_wdc_bio.blkno = DL_BLKTOSEC(lp, bp->b_blkno + DL_SECTOBLK(lp, 473*2e7ffe50Skrw DL_GETPOFFSET(&lp->d_partitions[DISKPART(bp->b_dev)]))); 4747481efa2Scsapuntz wd->sc_wdc_bio.blkdone =0; 4757481efa2Scsapuntz wd->sc_bp = bp; 4767481efa2Scsapuntz /* 4777481efa2Scsapuntz * If we're retrying, retry in single-sector mode. This will give us 4787481efa2Scsapuntz * the sector number of the problem, and will eventually allow the 4797481efa2Scsapuntz * transfer to succeed. 4807481efa2Scsapuntz */ 481a9a2d616Ssthen if (wd->retries >= WDIORETRIES_SINGLE) 4827481efa2Scsapuntz wd->sc_wdc_bio.flags = ATA_SINGLE; 4837481efa2Scsapuntz else 4847481efa2Scsapuntz wd->sc_wdc_bio.flags = 0; 485*2e7ffe50Skrw nsecs = howmany(bp->b_bcount, lp->d_secsize); 486cec793cfSgrange if ((wd->sc_flags & WDF_LBA48) && 487cec793cfSgrange /* use LBA48 only if really need */ 488*2e7ffe50Skrw ((wd->sc_wdc_bio.blkno + nsecs - 1 >= LBA48_THRESHOLD) || 489*2e7ffe50Skrw (nsecs > 0xff))) 4904b4c6f4eSgluk wd->sc_wdc_bio.flags |= ATA_LBA48; 4917481efa2Scsapuntz if (wd->sc_flags & WDF_LBA) 4927481efa2Scsapuntz wd->sc_wdc_bio.flags |= ATA_LBA; 4937481efa2Scsapuntz if (bp->b_flags & B_READ) 4947481efa2Scsapuntz wd->sc_wdc_bio.flags |= ATA_READ; 4957481efa2Scsapuntz wd->sc_wdc_bio.bcount = bp->b_bcount; 4967481efa2Scsapuntz wd->sc_wdc_bio.databuf = bp->b_data; 497f97ca3e4Scsapuntz wd->sc_wdc_bio.wd = wd; 4987481efa2Scsapuntz /* Instrumentation. */ 4997481efa2Scsapuntz disk_busy(&wd->sc_dk); 5007481efa2Scsapuntz switch (wdc_ata_bio(wd->drvp, &wd->sc_wdc_bio)) { 5017481efa2Scsapuntz case WDC_TRY_AGAIN: 502e15e1c8cSblambert timeout_add_sec(&wd->sc_restart_timeout, 1); 5037481efa2Scsapuntz break; 5047481efa2Scsapuntz case WDC_QUEUED: 5057481efa2Scsapuntz break; 5067481efa2Scsapuntz case WDC_COMPLETE: 50713456e59Scsapuntz /* 50813456e59Scsapuntz * This code is never executed because we never set 50913456e59Scsapuntz * the ATA_POLL flag above 51013456e59Scsapuntz */ 51113456e59Scsapuntz #if 0 51213456e59Scsapuntz if (wd->sc_wdc_bio.flags & ATA_POLL) 5137481efa2Scsapuntz wddone(wd); 51413456e59Scsapuntz #endif 5157481efa2Scsapuntz break; 5167481efa2Scsapuntz default: 5177481efa2Scsapuntz panic("__wdstart: bad return code from wdc_ata_bio()"); 5187481efa2Scsapuntz } 5197481efa2Scsapuntz } 5207481efa2Scsapuntz 5217481efa2Scsapuntz void 52206ad70d2Sgrange wddone(void *v) 5237481efa2Scsapuntz { 5247481efa2Scsapuntz struct wd_softc *wd = v; 5257481efa2Scsapuntz struct buf *bp = wd->sc_bp; 5267481efa2Scsapuntz char buf[256], *errbuf = buf; 5277481efa2Scsapuntz WDCDEBUG_PRINT(("wddone %s\n", wd->sc_dev.dv_xname), 5287481efa2Scsapuntz DEBUG_XFERS); 5297481efa2Scsapuntz 5307481efa2Scsapuntz bp->b_resid = wd->sc_wdc_bio.bcount; 5317481efa2Scsapuntz errbuf[0] = '\0'; 5327481efa2Scsapuntz switch (wd->sc_wdc_bio.error) { 533f97ca3e4Scsapuntz case ERR_NODEV: 534f97ca3e4Scsapuntz bp->b_flags |= B_ERROR; 535f97ca3e4Scsapuntz bp->b_error = ENXIO; 536f97ca3e4Scsapuntz break; 5377481efa2Scsapuntz case ERR_DMA: 5387481efa2Scsapuntz errbuf = "DMA error"; 5397481efa2Scsapuntz goto retry; 5407481efa2Scsapuntz case ERR_DF: 5417481efa2Scsapuntz errbuf = "device fault"; 5427481efa2Scsapuntz goto retry; 5437481efa2Scsapuntz case TIMEOUT: 5447481efa2Scsapuntz errbuf = "device timeout"; 5457481efa2Scsapuntz goto retry; 5467481efa2Scsapuntz case ERROR: 5477481efa2Scsapuntz /* Don't care about media change bits */ 5487481efa2Scsapuntz if (wd->sc_wdc_bio.r_error != 0 && 5497481efa2Scsapuntz (wd->sc_wdc_bio.r_error & ~(WDCE_MC | WDCE_MCR)) == 0) 5507481efa2Scsapuntz goto noerror; 551b6136c85Sho ata_perror(wd->drvp, wd->sc_wdc_bio.r_error, errbuf, 552b6136c85Sho sizeof buf); 553f97ca3e4Scsapuntz retry: 554f97ca3e4Scsapuntz /* Just reset and retry. Can we do more ? */ 555ba8cbb4aSmiod wdc_reset_channel(wd->drvp, 0); 5567481efa2Scsapuntz diskerr(bp, "wd", errbuf, LOG_PRINTF, 5577481efa2Scsapuntz wd->sc_wdc_bio.blkdone, wd->sc_dk.dk_label); 5587481efa2Scsapuntz if (wd->retries++ < WDIORETRIES) { 5597481efa2Scsapuntz printf(", retrying\n"); 5607c4d2af8Sart timeout_add(&wd->sc_restart_timeout, RECOVERYTIME); 5617481efa2Scsapuntz return; 5627481efa2Scsapuntz } 5637481efa2Scsapuntz printf("\n"); 5647481efa2Scsapuntz bp->b_flags |= B_ERROR; 5657481efa2Scsapuntz bp->b_error = EIO; 5667481efa2Scsapuntz break; 5677481efa2Scsapuntz case NOERROR: 5687481efa2Scsapuntz noerror: if ((wd->sc_wdc_bio.flags & ATA_CORR) || wd->retries > 0) 5697481efa2Scsapuntz printf("%s: soft error (corrected)\n", 5707481efa2Scsapuntz wd->sc_dev.dv_xname); 5717481efa2Scsapuntz } 57235fa4c39Stedu disk_unbusy(&wd->sc_dk, (bp->b_bcount - bp->b_resid), 57335fa4c39Stedu (bp->b_flags & B_READ)); 5747481efa2Scsapuntz biodone(bp); 5757481efa2Scsapuntz wd->openings++; 5767481efa2Scsapuntz wdstart(wd); 5777481efa2Scsapuntz } 5787481efa2Scsapuntz 5797481efa2Scsapuntz void 58006ad70d2Sgrange wdrestart(void *v) 5817481efa2Scsapuntz { 5827481efa2Scsapuntz struct wd_softc *wd = v; 5837481efa2Scsapuntz struct buf *bp = wd->sc_bp; 5844072b0baSderaadt struct channel_softc *chnl; 5857481efa2Scsapuntz int s; 5867481efa2Scsapuntz WDCDEBUG_PRINT(("wdrestart %s\n", wd->sc_dev.dv_xname), 5877481efa2Scsapuntz DEBUG_XFERS); 5887481efa2Scsapuntz 5894072b0baSderaadt chnl = (struct channel_softc *)(wd->drvp->chnl_softc); 5904072b0baSderaadt if (chnl->dying) 5914072b0baSderaadt return; 5924072b0baSderaadt 5937481efa2Scsapuntz s = splbio(); 59469052e72Sderaadt disk_unbusy(&wd->sc_dk, 0, (bp->b_flags & B_READ)); 5957481efa2Scsapuntz __wdstart(v, bp); 5967481efa2Scsapuntz splx(s); 5977481efa2Scsapuntz } 5987481efa2Scsapuntz 5997481efa2Scsapuntz int 60006ad70d2Sgrange wdread(dev_t dev, struct uio *uio, int flags) 6017481efa2Scsapuntz { 6027481efa2Scsapuntz 6037481efa2Scsapuntz WDCDEBUG_PRINT(("wdread\n"), DEBUG_XFERS); 60490f6ca67Smatthew return (physio(wdstrategy, dev, B_READ, minphys, uio)); 6057481efa2Scsapuntz } 6067481efa2Scsapuntz 6077481efa2Scsapuntz int 60806ad70d2Sgrange wdwrite(dev_t dev, struct uio *uio, int flags) 6097481efa2Scsapuntz { 6107481efa2Scsapuntz 6117481efa2Scsapuntz WDCDEBUG_PRINT(("wdwrite\n"), DEBUG_XFERS); 61290f6ca67Smatthew return (physio(wdstrategy, dev, B_WRITE, minphys, uio)); 6137481efa2Scsapuntz } 6147481efa2Scsapuntz 6157481efa2Scsapuntz int 61606ad70d2Sgrange wdopen(dev_t dev, int flag, int fmt, struct proc *p) 6177481efa2Scsapuntz { 6187481efa2Scsapuntz struct wd_softc *wd; 6194072b0baSderaadt struct channel_softc *chnl; 6207481efa2Scsapuntz int unit, part; 6217481efa2Scsapuntz int error; 6227481efa2Scsapuntz 6237481efa2Scsapuntz WDCDEBUG_PRINT(("wdopen\n"), DEBUG_FUNCS); 624f97ca3e4Scsapuntz 625a83b1495Skrw unit = DISKUNIT(dev); 626f97ca3e4Scsapuntz wd = wdlookup(unit); 6277481efa2Scsapuntz if (wd == NULL) 6287481efa2Scsapuntz return ENXIO; 6294072b0baSderaadt chnl = (struct channel_softc *)(wd->drvp->chnl_softc); 6304072b0baSderaadt if (chnl->dying) 6314072b0baSderaadt return (ENXIO); 6327481efa2Scsapuntz 6337481efa2Scsapuntz /* 6347481efa2Scsapuntz * If this is the first open of this device, add a reference 6357481efa2Scsapuntz * to the adapter. 6367481efa2Scsapuntz */ 63785bbddc9Sderaadt if ((error = disk_lock(&wd->sc_dk)) != 0) 6387481efa2Scsapuntz goto bad4; 6397481efa2Scsapuntz 6407481efa2Scsapuntz if (wd->sc_dk.dk_openmask != 0) { 6417481efa2Scsapuntz /* 6427481efa2Scsapuntz * If any partition is open, but the disk has been invalidated, 6437481efa2Scsapuntz * disallow further opens. 6447481efa2Scsapuntz */ 6457481efa2Scsapuntz if ((wd->sc_flags & WDF_LOADED) == 0) { 6467481efa2Scsapuntz error = EIO; 6477481efa2Scsapuntz goto bad3; 6487481efa2Scsapuntz } 6497481efa2Scsapuntz } else { 6507481efa2Scsapuntz if ((wd->sc_flags & WDF_LOADED) == 0) { 6517481efa2Scsapuntz wd->sc_flags |= WDF_LOADED; 6527481efa2Scsapuntz 6537481efa2Scsapuntz /* Load the physical device parameters. */ 654e1a5e72aScsapuntz wd_get_params(wd, AT_WAIT, &wd->sc_params); 6557481efa2Scsapuntz 6567481efa2Scsapuntz /* Load the partition info if not already loaded. */ 657df591ed6Sderaadt if (wdgetdisklabel(dev, wd, 658df591ed6Sderaadt wd->sc_dk.dk_label, 0) == EIO) { 659df591ed6Sderaadt error = EIO; 660df591ed6Sderaadt goto bad; 661df591ed6Sderaadt } 6627481efa2Scsapuntz } 6637481efa2Scsapuntz } 6647481efa2Scsapuntz 665a83b1495Skrw part = DISKPART(dev); 6667481efa2Scsapuntz 667e1d64023Smatthew if ((error = disk_openpart(&wd->sc_dk, part, fmt, 1)) != 0) 6687481efa2Scsapuntz goto bad; 6697481efa2Scsapuntz 67085bbddc9Sderaadt disk_unlock(&wd->sc_dk); 671f97ca3e4Scsapuntz device_unref(&wd->sc_dev); 6727481efa2Scsapuntz return 0; 6737481efa2Scsapuntz 6747481efa2Scsapuntz bad: 6757481efa2Scsapuntz if (wd->sc_dk.dk_openmask == 0) { 6767481efa2Scsapuntz } 6777481efa2Scsapuntz 6787481efa2Scsapuntz bad3: 67985bbddc9Sderaadt disk_unlock(&wd->sc_dk); 6807481efa2Scsapuntz bad4: 681f97ca3e4Scsapuntz device_unref(&wd->sc_dev); 6827481efa2Scsapuntz return error; 6837481efa2Scsapuntz } 6847481efa2Scsapuntz 6857481efa2Scsapuntz int 68606ad70d2Sgrange wdclose(dev_t dev, int flag, int fmt, struct proc *p) 6877481efa2Scsapuntz { 688f97ca3e4Scsapuntz struct wd_softc *wd; 689a83b1495Skrw int part = DISKPART(dev); 690f97ca3e4Scsapuntz 691a83b1495Skrw wd = wdlookup(DISKUNIT(dev)); 692f97ca3e4Scsapuntz if (wd == NULL) 693f97ca3e4Scsapuntz return ENXIO; 6947481efa2Scsapuntz 6957481efa2Scsapuntz WDCDEBUG_PRINT(("wdclose\n"), DEBUG_FUNCS); 6965f30bcfcSderaadt 6975f30bcfcSderaadt disk_lock_nointr(&wd->sc_dk); 6987481efa2Scsapuntz 699e1d64023Smatthew disk_closepart(&wd->sc_dk, part, fmt); 7007481efa2Scsapuntz 7017481efa2Scsapuntz if (wd->sc_dk.dk_openmask == 0) { 7027481efa2Scsapuntz wd_flushcache(wd, 0); 7037481efa2Scsapuntz /* XXXX Must wait for I/O to complete! */ 7047481efa2Scsapuntz } 7057481efa2Scsapuntz 70685bbddc9Sderaadt disk_unlock(&wd->sc_dk); 707f97ca3e4Scsapuntz 708f97ca3e4Scsapuntz device_unref(&wd->sc_dev); 7095f30bcfcSderaadt return (0); 7107481efa2Scsapuntz } 7117481efa2Scsapuntz 7127481efa2Scsapuntz void 71306ad70d2Sgrange wdgetdefaultlabel(struct wd_softc *wd, struct disklabel *lp) 7147481efa2Scsapuntz { 7157481efa2Scsapuntz WDCDEBUG_PRINT(("wdgetdefaultlabel\n"), DEBUG_FUNCS); 716afaefa16Sniklas bzero(lp, sizeof(struct disklabel)); 7177481efa2Scsapuntz 7187481efa2Scsapuntz lp->d_secsize = DEV_BSIZE; 719e16633b4Sderaadt DL_SETDSIZE(lp, wd->sc_capacity); 7207481efa2Scsapuntz lp->d_ntracks = wd->sc_params.atap_heads; 7217481efa2Scsapuntz lp->d_nsectors = wd->sc_params.atap_sectors; 7227481efa2Scsapuntz lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors; 723e16633b4Sderaadt lp->d_ncylinders = DL_GETDSIZE(lp) / lp->d_secpercyl; 724a2c80ea8Smillert if (wd->drvp->ata_vers == -1) { 7257481efa2Scsapuntz lp->d_type = DTYPE_ST506; 726bf3a6870Skrw strncpy(lp->d_typename, "ST506/MFM/RLL", sizeof lp->d_typename); 7277481efa2Scsapuntz } else { 7287481efa2Scsapuntz lp->d_type = DTYPE_ESDI; 729bf3a6870Skrw strncpy(lp->d_typename, "ESDI/IDE disk", sizeof lp->d_typename); 7307481efa2Scsapuntz } 731a2c80ea8Smillert /* XXX - user viscopy() like sd.c */ 732bf3a6870Skrw strncpy(lp->d_packname, wd->sc_params.atap_model, sizeof lp->d_packname); 7337481efa2Scsapuntz lp->d_flags = 0; 73422230921Sderaadt lp->d_version = 1; 7357481efa2Scsapuntz 7367481efa2Scsapuntz lp->d_magic = DISKMAGIC; 7377481efa2Scsapuntz lp->d_magic2 = DISKMAGIC; 7387481efa2Scsapuntz lp->d_checksum = dkcksum(lp); 7397481efa2Scsapuntz } 7407481efa2Scsapuntz 7417481efa2Scsapuntz /* 7427481efa2Scsapuntz * Fabricate a default disk label, and try to read the correct one. 7437481efa2Scsapuntz */ 744df591ed6Sderaadt int 74506ad70d2Sgrange wdgetdisklabel(dev_t dev, struct wd_softc *wd, struct disklabel *lp, 746c326f117Sderaadt int spoofonly) 7477481efa2Scsapuntz { 748df591ed6Sderaadt int error; 7497481efa2Scsapuntz 7507481efa2Scsapuntz WDCDEBUG_PRINT(("wdgetdisklabel\n"), DEBUG_FUNCS); 7517481efa2Scsapuntz 7527481efa2Scsapuntz wdgetdefaultlabel(wd, lp); 7537481efa2Scsapuntz 7547481efa2Scsapuntz if (wd->drvp->state > RECAL) 7557481efa2Scsapuntz wd->drvp->drive_flags |= DRIVE_RESET; 756df591ed6Sderaadt error = readdisklabel(DISKLABELDEV(dev), wdstrategy, lp, 757657ac4baSkrw spoofonly); 7587481efa2Scsapuntz if (wd->drvp->state > RECAL) 7597481efa2Scsapuntz wd->drvp->drive_flags |= DRIVE_RESET; 760df591ed6Sderaadt return (error); 7617481efa2Scsapuntz } 7627481efa2Scsapuntz 7637481efa2Scsapuntz int 76406ad70d2Sgrange wdioctl(dev_t dev, u_long xfer, caddr_t addr, int flag, struct proc *p) 7657481efa2Scsapuntz { 766f97ca3e4Scsapuntz struct wd_softc *wd; 767ce578d69Sderaadt struct disklabel *lp; 768f97ca3e4Scsapuntz int error = 0; 7697481efa2Scsapuntz 7707481efa2Scsapuntz WDCDEBUG_PRINT(("wdioctl\n"), DEBUG_FUNCS); 7717481efa2Scsapuntz 772a83b1495Skrw wd = wdlookup(DISKUNIT(dev)); 773f97ca3e4Scsapuntz if (wd == NULL) 774f97ca3e4Scsapuntz return ENXIO; 775f97ca3e4Scsapuntz 776f97ca3e4Scsapuntz if ((wd->sc_flags & WDF_LOADED) == 0) { 777f97ca3e4Scsapuntz error = EIO; 778f97ca3e4Scsapuntz goto exit; 779f97ca3e4Scsapuntz } 7807481efa2Scsapuntz 7817481efa2Scsapuntz switch (xfer) { 7821dfe2d86Sderaadt case DIOCRLDINFO: 783ce578d69Sderaadt lp = malloc(sizeof(*lp), M_TEMP, M_WAITOK); 784c326f117Sderaadt wdgetdisklabel(dev, wd, lp, 0); 785ce578d69Sderaadt bcopy(lp, wd->sc_dk.dk_label, sizeof(*lp)); 786ce578d69Sderaadt free(lp, M_TEMP); 787f97ca3e4Scsapuntz goto exit; 788ce578d69Sderaadt 789c326f117Sderaadt case DIOCGPDINFO: 790c326f117Sderaadt wdgetdisklabel(dev, wd, (struct disklabel *)addr, 1); 791f97ca3e4Scsapuntz goto exit; 792e1a5e72aScsapuntz 7937481efa2Scsapuntz case DIOCGDINFO: 7947481efa2Scsapuntz *(struct disklabel *)addr = *(wd->sc_dk.dk_label); 795f97ca3e4Scsapuntz goto exit; 7967481efa2Scsapuntz 7977481efa2Scsapuntz case DIOCGPART: 7987481efa2Scsapuntz ((struct partinfo *)addr)->disklab = wd->sc_dk.dk_label; 7997481efa2Scsapuntz ((struct partinfo *)addr)->part = 800a83b1495Skrw &wd->sc_dk.dk_label->d_partitions[DISKPART(dev)]; 801f97ca3e4Scsapuntz goto exit; 8027481efa2Scsapuntz 8037481efa2Scsapuntz case DIOCWDINFO: 8047481efa2Scsapuntz case DIOCSDINFO: 805f97ca3e4Scsapuntz if ((flag & FWRITE) == 0) { 806f97ca3e4Scsapuntz error = EBADF; 807f97ca3e4Scsapuntz goto exit; 808f97ca3e4Scsapuntz } 8097481efa2Scsapuntz 81085bbddc9Sderaadt if ((error = disk_lock(&wd->sc_dk)) != 0) 811f97ca3e4Scsapuntz goto exit; 8127481efa2Scsapuntz 8137481efa2Scsapuntz error = setdisklabel(wd->sc_dk.dk_label, 814a2f17f2eSmatthew (struct disklabel *)addr, wd->sc_dk.dk_openmask); 8157481efa2Scsapuntz if (error == 0) { 8167481efa2Scsapuntz if (wd->drvp->state > RECAL) 8177481efa2Scsapuntz wd->drvp->drive_flags |= DRIVE_RESET; 8187481efa2Scsapuntz if (xfer == DIOCWDINFO) 819a83b1495Skrw error = writedisklabel(DISKLABELDEV(dev), 820c326f117Sderaadt wdstrategy, wd->sc_dk.dk_label); 8217481efa2Scsapuntz } 8227481efa2Scsapuntz 82385bbddc9Sderaadt disk_unlock(&wd->sc_dk); 824f97ca3e4Scsapuntz goto exit; 8257481efa2Scsapuntz 8267481efa2Scsapuntz #ifdef notyet 8277481efa2Scsapuntz case DIOCWFORMAT: 8287481efa2Scsapuntz if ((flag & FWRITE) == 0) 8297481efa2Scsapuntz return EBADF; 8307481efa2Scsapuntz { 83171f8714bSkrw struct format_op *fop; 8327481efa2Scsapuntz struct iovec aiov; 8337481efa2Scsapuntz struct uio auio; 8347481efa2Scsapuntz 8357481efa2Scsapuntz fop = (struct format_op *)addr; 8367481efa2Scsapuntz aiov.iov_base = fop->df_buf; 8377481efa2Scsapuntz aiov.iov_len = fop->df_count; 8387481efa2Scsapuntz auio.uio_iov = &aiov; 8397481efa2Scsapuntz auio.uio_iovcnt = 1; 8407481efa2Scsapuntz auio.uio_resid = fop->df_count; 8417481efa2Scsapuntz auio.uio_segflg = 0; 8427481efa2Scsapuntz auio.uio_offset = 8437481efa2Scsapuntz fop->df_startblk * wd->sc_dk.dk_label->d_secsize; 8447481efa2Scsapuntz auio.uio_procp = p; 84590f6ca67Smatthew error = physio(wdformat, dev, B_WRITE, minphys, &auio); 8467481efa2Scsapuntz fop->df_count -= auio.uio_resid; 8477481efa2Scsapuntz fop->df_reg[0] = wdc->sc_status; 8487481efa2Scsapuntz fop->df_reg[1] = wdc->sc_error; 849f97ca3e4Scsapuntz goto exit; 8507481efa2Scsapuntz } 8517481efa2Scsapuntz #endif 8527481efa2Scsapuntz 8537481efa2Scsapuntz default: 85418885d83Smickey error = wdc_ioctl(wd->drvp, xfer, addr, flag, p); 855f97ca3e4Scsapuntz goto exit; 8567481efa2Scsapuntz } 8577481efa2Scsapuntz 8587481efa2Scsapuntz #ifdef DIAGNOSTIC 8597481efa2Scsapuntz panic("wdioctl: impossible"); 8607481efa2Scsapuntz #endif 861f97ca3e4Scsapuntz 862f97ca3e4Scsapuntz exit: 863f97ca3e4Scsapuntz device_unref(&wd->sc_dev); 864f97ca3e4Scsapuntz return (error); 8657481efa2Scsapuntz } 8667481efa2Scsapuntz 8677481efa2Scsapuntz #ifdef B_FORMAT 8687481efa2Scsapuntz int 8697481efa2Scsapuntz wdformat(struct buf *bp) 8707481efa2Scsapuntz { 8717481efa2Scsapuntz 8727481efa2Scsapuntz bp->b_flags |= B_FORMAT; 8737481efa2Scsapuntz return wdstrategy(bp); 8747481efa2Scsapuntz } 8757481efa2Scsapuntz #endif 8767481efa2Scsapuntz 8771abdbfdeSderaadt daddr_t 87806ad70d2Sgrange wdsize(dev_t dev) 8797481efa2Scsapuntz { 8807481efa2Scsapuntz struct wd_softc *wd; 881f97ca3e4Scsapuntz int part, omask; 88215aab03cSderaadt int64_t size; 8837481efa2Scsapuntz 8847481efa2Scsapuntz WDCDEBUG_PRINT(("wdsize\n"), DEBUG_FUNCS); 8857481efa2Scsapuntz 886a83b1495Skrw wd = wdlookup(DISKUNIT(dev)); 8877481efa2Scsapuntz if (wd == NULL) 8887481efa2Scsapuntz return (-1); 8897481efa2Scsapuntz 890a83b1495Skrw part = DISKPART(dev); 8917481efa2Scsapuntz omask = wd->sc_dk.dk_openmask & (1 << part); 8927481efa2Scsapuntz 893f97ca3e4Scsapuntz if (omask == 0 && wdopen(dev, 0, S_IFBLK, NULL) != 0) { 894f97ca3e4Scsapuntz size = -1; 895f97ca3e4Scsapuntz goto exit; 896f97ca3e4Scsapuntz } 897f97ca3e4Scsapuntz 898e16633b4Sderaadt size = DL_GETPSIZE(&wd->sc_dk.dk_label->d_partitions[part]) * 8997481efa2Scsapuntz (wd->sc_dk.dk_label->d_secsize / DEV_BSIZE); 9007481efa2Scsapuntz if (omask == 0 && wdclose(dev, 0, S_IFBLK, NULL) != 0) 901f97ca3e4Scsapuntz size = -1; 902f97ca3e4Scsapuntz 903f97ca3e4Scsapuntz exit: 904f97ca3e4Scsapuntz device_unref(&wd->sc_dev); 9057481efa2Scsapuntz return (size); 9067481efa2Scsapuntz } 9077481efa2Scsapuntz 9087481efa2Scsapuntz /* #define WD_DUMP_NOT_TRUSTED if you just want to watch */ 9097481efa2Scsapuntz static int wddoingadump = 0; 9107481efa2Scsapuntz static int wddumprecalibrated = 0; 9117481efa2Scsapuntz static int wddumpmulti = 1; 9127481efa2Scsapuntz 9137481efa2Scsapuntz /* 9147481efa2Scsapuntz * Dump core after a system crash. 9157481efa2Scsapuntz */ 9167481efa2Scsapuntz int 9171abdbfdeSderaadt wddump(dev_t dev, daddr_t blkno, caddr_t va, size_t size) 9187481efa2Scsapuntz { 9197481efa2Scsapuntz struct wd_softc *wd; /* disk unit to do the I/O */ 9207481efa2Scsapuntz struct disklabel *lp; /* disk's disklabel */ 9217481efa2Scsapuntz int unit, part; 9227481efa2Scsapuntz int nblks; /* total number of sectors left to write */ 9237481efa2Scsapuntz int err; 9247481efa2Scsapuntz char errbuf[256]; 9257481efa2Scsapuntz 9267481efa2Scsapuntz /* Check if recursive dump; if so, punt. */ 9277481efa2Scsapuntz if (wddoingadump) 9287481efa2Scsapuntz return EFAULT; 9297481efa2Scsapuntz wddoingadump = 1; 9307481efa2Scsapuntz 931a83b1495Skrw unit = DISKUNIT(dev); 932f97ca3e4Scsapuntz wd = wdlookup(unit); 933f97ca3e4Scsapuntz if (wd == NULL) 9347481efa2Scsapuntz return ENXIO; 9357481efa2Scsapuntz 936a83b1495Skrw part = DISKPART(dev); 9377481efa2Scsapuntz 9387481efa2Scsapuntz /* Make sure it was initialized. */ 9397481efa2Scsapuntz if (wd->drvp->state < READY) 9407481efa2Scsapuntz return ENXIO; 9417481efa2Scsapuntz 9427481efa2Scsapuntz /* Convert to disk sectors. Request must be a multiple of size. */ 9437481efa2Scsapuntz lp = wd->sc_dk.dk_label; 9447481efa2Scsapuntz if ((size % lp->d_secsize) != 0) 9457481efa2Scsapuntz return EFAULT; 9467481efa2Scsapuntz nblks = size / lp->d_secsize; 9477481efa2Scsapuntz blkno = blkno / (lp->d_secsize / DEV_BSIZE); 9487481efa2Scsapuntz 9497481efa2Scsapuntz /* Check transfer bounds against partition size. */ 950e16633b4Sderaadt if ((blkno < 0) || ((blkno + nblks) > DL_GETPSIZE(&lp->d_partitions[part]))) 9517481efa2Scsapuntz return EINVAL; 9527481efa2Scsapuntz 9537481efa2Scsapuntz /* Offset block number to start of partition. */ 954e16633b4Sderaadt blkno += DL_GETPOFFSET(&lp->d_partitions[part]); 9557481efa2Scsapuntz 9567481efa2Scsapuntz /* Recalibrate, if first dump transfer. */ 9577481efa2Scsapuntz if (wddumprecalibrated == 0) { 9587481efa2Scsapuntz wddumpmulti = wd->sc_multi; 9597481efa2Scsapuntz wddumprecalibrated = 1; 9607481efa2Scsapuntz wd->drvp->state = RECAL; 9617481efa2Scsapuntz } 9627481efa2Scsapuntz 9637481efa2Scsapuntz while (nblks > 0) { 9647481efa2Scsapuntz wd->sc_wdc_bio.blkno = blkno; 9657481efa2Scsapuntz wd->sc_wdc_bio.flags = ATA_POLL; 9664b4c6f4eSgluk if (wd->sc_flags & WDF_LBA48) 9674b4c6f4eSgluk wd->sc_wdc_bio.flags |= ATA_LBA48; 9687481efa2Scsapuntz if (wd->sc_flags & WDF_LBA) 9697481efa2Scsapuntz wd->sc_wdc_bio.flags |= ATA_LBA; 9707481efa2Scsapuntz wd->sc_wdc_bio.bcount = 9717481efa2Scsapuntz min(nblks, wddumpmulti) * lp->d_secsize; 9727481efa2Scsapuntz wd->sc_wdc_bio.databuf = va; 973f97ca3e4Scsapuntz wd->sc_wdc_bio.wd = wd; 9747481efa2Scsapuntz #ifndef WD_DUMP_NOT_TRUSTED 9757481efa2Scsapuntz switch (wdc_ata_bio(wd->drvp, &wd->sc_wdc_bio)) { 9767481efa2Scsapuntz case WDC_TRY_AGAIN: 9777481efa2Scsapuntz panic("wddump: try again"); 9787481efa2Scsapuntz break; 9797481efa2Scsapuntz case WDC_QUEUED: 9807481efa2Scsapuntz panic("wddump: polled command has been queued"); 9817481efa2Scsapuntz break; 9827481efa2Scsapuntz case WDC_COMPLETE: 9837481efa2Scsapuntz break; 9847481efa2Scsapuntz } 9857481efa2Scsapuntz switch(wd->sc_wdc_bio.error) { 9867481efa2Scsapuntz case TIMEOUT: 9877481efa2Scsapuntz printf("wddump: device timed out"); 9887481efa2Scsapuntz err = EIO; 9897481efa2Scsapuntz break; 9907481efa2Scsapuntz case ERR_DF: 9917481efa2Scsapuntz printf("wddump: drive fault"); 9927481efa2Scsapuntz err = EIO; 9937481efa2Scsapuntz break; 9947481efa2Scsapuntz case ERR_DMA: 9957481efa2Scsapuntz printf("wddump: DMA error"); 9967481efa2Scsapuntz err = EIO; 9977481efa2Scsapuntz break; 9987481efa2Scsapuntz case ERROR: 9997481efa2Scsapuntz errbuf[0] = '\0'; 1000b6136c85Sho ata_perror(wd->drvp, wd->sc_wdc_bio.r_error, errbuf, 1001b6136c85Sho sizeof errbuf); 10027481efa2Scsapuntz printf("wddump: %s", errbuf); 10037481efa2Scsapuntz err = EIO; 10047481efa2Scsapuntz break; 10057481efa2Scsapuntz case NOERROR: 10067481efa2Scsapuntz err = 0; 10077481efa2Scsapuntz break; 10087481efa2Scsapuntz default: 10097481efa2Scsapuntz panic("wddump: unknown error type"); 10107481efa2Scsapuntz } 10117481efa2Scsapuntz if (err != 0) { 10127481efa2Scsapuntz printf("\n"); 10137481efa2Scsapuntz return err; 10147481efa2Scsapuntz } 10157481efa2Scsapuntz #else /* WD_DUMP_NOT_TRUSTED */ 10167481efa2Scsapuntz /* Let's just talk about this first... */ 10177481efa2Scsapuntz printf("wd%d: dump addr 0x%x, cylin %d, head %d, sector %d\n", 10187481efa2Scsapuntz unit, va, cylin, head, sector); 10197481efa2Scsapuntz delay(500 * 1000); /* half a second */ 10207481efa2Scsapuntz #endif 10217481efa2Scsapuntz 10227481efa2Scsapuntz /* update block count */ 10237481efa2Scsapuntz nblks -= min(nblks, wddumpmulti); 10247481efa2Scsapuntz blkno += min(nblks, wddumpmulti); 10257481efa2Scsapuntz va += min(nblks, wddumpmulti) * lp->d_secsize; 10267481efa2Scsapuntz } 10277481efa2Scsapuntz 10287481efa2Scsapuntz wddoingadump = 0; 10297481efa2Scsapuntz return 0; 10307481efa2Scsapuntz } 10317481efa2Scsapuntz 10327481efa2Scsapuntz int 103306ad70d2Sgrange wd_get_params(struct wd_softc *wd, u_int8_t flags, struct ataparams *params) 10347481efa2Scsapuntz { 10357481efa2Scsapuntz switch (ata_get_params(wd->drvp, flags, params)) { 10367481efa2Scsapuntz case CMD_AGAIN: 10377481efa2Scsapuntz return 1; 10387481efa2Scsapuntz case CMD_ERR: 1039e9207eccSuwe /* If we already have drive parameters, reuse them. */ 1040e880da88Suwe if (wd->sc_params.atap_cylinders != 0) { 1041e9207eccSuwe if (params != &wd->sc_params) 1042e9207eccSuwe bcopy(&wd->sc_params, params, 1043e9207eccSuwe sizeof(struct ataparams)); 1044e880da88Suwe return 0; 1045e880da88Suwe } 10467481efa2Scsapuntz /* 10477481efa2Scsapuntz * We `know' there's a drive here; just assume it's old. 10487481efa2Scsapuntz * This geometry is only used to read the MBR and print a 10497481efa2Scsapuntz * (false) attach message. 10507481efa2Scsapuntz */ 1051e9207eccSuwe bzero(params, sizeof(struct ataparams)); 10527481efa2Scsapuntz strncpy(params->atap_model, "ST506", 10537481efa2Scsapuntz sizeof params->atap_model); 10547481efa2Scsapuntz params->atap_config = ATA_CFG_FIXED; 10557481efa2Scsapuntz params->atap_cylinders = 1024; 10567481efa2Scsapuntz params->atap_heads = 8; 10577481efa2Scsapuntz params->atap_sectors = 17; 10587481efa2Scsapuntz params->atap_multi = 1; 10597481efa2Scsapuntz params->atap_capabilities1 = params->atap_capabilities2 = 0; 10607481efa2Scsapuntz wd->drvp->ata_vers = -1; /* Mark it as pre-ATA */ 10617481efa2Scsapuntz return 0; 10627481efa2Scsapuntz case CMD_OK: 10637481efa2Scsapuntz return 0; 10647481efa2Scsapuntz default: 10657481efa2Scsapuntz panic("wd_get_params: bad return code from ata_get_params"); 10667481efa2Scsapuntz /* NOTREACHED */ 10677481efa2Scsapuntz } 10687481efa2Scsapuntz } 10697481efa2Scsapuntz 10707481efa2Scsapuntz void 107106ad70d2Sgrange wd_flushcache(struct wd_softc *wd, int flags) 10727481efa2Scsapuntz { 10737481efa2Scsapuntz struct wdc_command wdc_c; 10747481efa2Scsapuntz 10757481efa2Scsapuntz if (wd->drvp->ata_vers < 4) /* WDCC_FLUSHCACHE is here since ATA-4 */ 10767481efa2Scsapuntz return; 1077afaefa16Sniklas bzero(&wdc_c, sizeof(struct wdc_command)); 10785a1200c8Sgrange wdc_c.r_command = (wd->sc_flags & WDF_LBA48 ? WDCC_FLUSHCACHE_EXT : 10795a1200c8Sgrange WDCC_FLUSHCACHE); 10807481efa2Scsapuntz wdc_c.r_st_bmask = WDCS_DRDY; 10817481efa2Scsapuntz wdc_c.r_st_pmask = WDCS_DRDY; 10823e0c401eScsapuntz if (flags != 0) { 10833e0c401eScsapuntz wdc_c.flags = AT_POLL; 10843e0c401eScsapuntz } else { 10853e0c401eScsapuntz wdc_c.flags = AT_WAIT; 10863e0c401eScsapuntz } 10877481efa2Scsapuntz wdc_c.timeout = 30000; /* 30s timeout */ 10887481efa2Scsapuntz if (wdc_exec_command(wd->drvp, &wdc_c) != WDC_COMPLETE) { 10897481efa2Scsapuntz printf("%s: flush cache command didn't complete\n", 10907481efa2Scsapuntz wd->sc_dev.dv_xname); 10917481efa2Scsapuntz } 10927481efa2Scsapuntz if (wdc_c.flags & AT_TIMEOU) { 10937481efa2Scsapuntz printf("%s: flush cache command timeout\n", 10947481efa2Scsapuntz wd->sc_dev.dv_xname); 10957481efa2Scsapuntz } 10967481efa2Scsapuntz if (wdc_c.flags & AT_DF) { 10977481efa2Scsapuntz printf("%s: flush cache command: drive fault\n", 10987481efa2Scsapuntz wd->sc_dev.dv_xname); 10997481efa2Scsapuntz } 11007481efa2Scsapuntz /* 11017481efa2Scsapuntz * Ignore error register, it shouldn't report anything else 11027481efa2Scsapuntz * than COMMAND ABORTED, which means the device doesn't support 11037481efa2Scsapuntz * flush cache 11047481efa2Scsapuntz */ 11057481efa2Scsapuntz } 11067481efa2Scsapuntz 11077481efa2Scsapuntz void 1108f8fc1e98Skettenis wd_standby(struct wd_softc *wd, int flags) 1109f8fc1e98Skettenis { 1110f8fc1e98Skettenis struct wdc_command wdc_c; 1111f8fc1e98Skettenis 1112f8fc1e98Skettenis bzero(&wdc_c, sizeof(struct wdc_command)); 1113f8fc1e98Skettenis wdc_c.r_command = WDCC_STANDBY_IMMED; 1114f8fc1e98Skettenis wdc_c.r_st_bmask = WDCS_DRDY; 1115f8fc1e98Skettenis wdc_c.r_st_pmask = WDCS_DRDY; 1116f8fc1e98Skettenis if (flags != 0) { 1117f8fc1e98Skettenis wdc_c.flags = AT_POLL; 1118f8fc1e98Skettenis } else { 1119f8fc1e98Skettenis wdc_c.flags = AT_WAIT; 1120f8fc1e98Skettenis } 11216693db94Skettenis wdc_c.timeout = 30000; /* 30s timeout */ 1122f8fc1e98Skettenis if (wdc_exec_command(wd->drvp, &wdc_c) != WDC_COMPLETE) { 1123f8fc1e98Skettenis printf("%s: standby command didn't complete\n", 1124f8fc1e98Skettenis wd->sc_dev.dv_xname); 1125f8fc1e98Skettenis } 1126f8fc1e98Skettenis if (wdc_c.flags & AT_TIMEOU) { 1127f8fc1e98Skettenis printf("%s: standby command timeout\n", 1128f8fc1e98Skettenis wd->sc_dev.dv_xname); 1129f8fc1e98Skettenis } 1130f8fc1e98Skettenis if (wdc_c.flags & AT_DF) { 1131f8fc1e98Skettenis printf("%s: standby command: drive fault\n", 1132f8fc1e98Skettenis wd->sc_dev.dv_xname); 1133f8fc1e98Skettenis } 1134f8fc1e98Skettenis /* 1135f8fc1e98Skettenis * Ignore error register, it shouldn't report anything else 1136f8fc1e98Skettenis * than COMMAND ABORTED, which means the device doesn't support 1137f8fc1e98Skettenis * standby 1138f8fc1e98Skettenis */ 1139f8fc1e98Skettenis } 1140