1 /* $NetBSD: wdc.c,v 1.16 2019/01/08 19:15:54 christos Exp $ */ 2 3 /*- 4 * Copyright (c) 2003 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Manuel Bouyer. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/param.h> 33 #include <sys/types.h> 34 #include <sys/disklabel.h> 35 #include <sys/bootblock.h> 36 37 #include <lib/libsa/stand.h> 38 #include <lib/libkern/libkern.h> 39 40 #include "boot.h" 41 #include "wdvar.h" 42 43 #define WDCDELAY 100 44 #define WDCNDELAY_RST 31000 * 10 45 46 static int wdcprobe(struct wdc_channel *chp); 47 static int wdc_wait_for_ready(struct wdc_channel *chp); 48 static int wdc_read_block(struct wd_softc *sc, struct wdc_command *wd_c); 49 static int __wdcwait_reset(struct wdc_channel *chp, int drv_mask); 50 51 /* 52 * Reset the controller. 53 */ 54 static int 55 __wdcwait_reset(struct wdc_channel *chp, int drv_mask) 56 { 57 int timeout; 58 uint8_t st0, st1; 59 60 /* wait for BSY to deassert */ 61 for (timeout = 0; timeout < WDCNDELAY_RST; timeout++) { 62 WDC_WRITE_REG(chp, wd_sdh, WDSD_IBM); /* master */ 63 delay(10); 64 st0 = WDC_READ_REG(chp, wd_status); 65 WDC_WRITE_REG(chp, wd_sdh, WDSD_IBM | 0x10); /* slave */ 66 delay(10); 67 st1 = WDC_READ_REG(chp, wd_status); 68 69 if ((drv_mask & 0x01) == 0) { 70 /* no master */ 71 if ((drv_mask & 0x02) != 0 && (st1 & WDCS_BSY) == 0) { 72 /* No master, slave is ready, it's done */ 73 goto end; 74 } 75 } else if ((drv_mask & 0x02) == 0) { 76 /* no slave */ 77 if ((drv_mask & 0x01) != 0 && (st0 & WDCS_BSY) == 0) { 78 /* No slave, master is ready, it's done */ 79 goto end; 80 } 81 } else { 82 /* Wait for both master and slave to be ready */ 83 if ((st0 & WDCS_BSY) == 0 && (st1 & WDCS_BSY) == 0) { 84 goto end; 85 } 86 } 87 88 delay(WDCDELAY); 89 } 90 91 /* Reset timed out. Maybe it's because drv_mask was not right */ 92 if (st0 & WDCS_BSY) 93 drv_mask &= ~0x01; 94 if (st1 & WDCS_BSY) 95 drv_mask &= ~0x02; 96 97 end: 98 return drv_mask; 99 } 100 101 /* Test to see controller with at last one attached drive is there. 102 * Returns a bit for each possible drive found (0x01 for drive 0, 103 * 0x02 for drive 1). 104 * Logic: 105 * - If a status register is at 0xff, assume there is no drive here 106 * (ISA has pull-up resistors). Similarly if the status register has 107 * the value we last wrote to the bus (for IDE interfaces without pullups). 108 * If no drive at all -> return. 109 * - reset the controller, wait for it to complete (may take up to 31s !). 110 * If timeout -> return. 111 */ 112 static int 113 wdcprobe(struct wdc_channel *chp) 114 { 115 uint8_t st0, st1; 116 uint8_t ret_value = 0x03; 117 uint8_t drive; 118 119 /* 120 * Sanity check to see if the wdc channel responds at all. 121 */ 122 WDC_WRITE_REG(chp, wd_sdh, WDSD_IBM); 123 delay(10); 124 st0 = WDC_READ_REG(chp, wd_status); 125 WDC_WRITE_REG(chp, wd_sdh, WDSD_IBM | 0x10); 126 delay(10); 127 st1 = WDC_READ_REG(chp, wd_status); 128 129 if (st0 == 0xff || st0 == WDSD_IBM) 130 ret_value &= ~0x01; 131 if (st1 == 0xff || st1 == (WDSD_IBM | 0x10)) 132 ret_value &= ~0x02; 133 if (ret_value == 0) 134 return ENXIO; 135 136 /* assert SRST, wait for reset to complete */ 137 WDC_WRITE_REG(chp, wd_sdh, WDSD_IBM); 138 delay(10); 139 WDC_WRITE_CTLREG(chp, wd_aux_ctlr, WDCTL_RST | WDCTL_IDS); 140 delay(1000); 141 WDC_WRITE_CTLREG(chp, wd_aux_ctlr, WDCTL_IDS); 142 delay(1000); 143 (void) WDC_READ_REG(chp, wd_error); 144 WDC_WRITE_CTLREG(chp, wd_aux_ctlr, WDCTL_4BIT); 145 delay(10); 146 147 ret_value = __wdcwait_reset(chp, ret_value); 148 149 /* if reset failed, there's nothing here */ 150 if (ret_value == 0) 151 return ENXIO; 152 153 /* 154 * Test presence of drives. First test register signatures looking for 155 * ATAPI devices. If it's not an ATAPI and reset said there may be 156 * something here assume it's ATA or OLD. Ghost will be killed later in 157 * attach routine. 158 */ 159 for (drive = 0; drive < 2; drive++) { 160 if ((ret_value & (0x01 << drive)) == 0) 161 continue; 162 return 0; 163 } 164 return ENXIO; 165 } 166 167 /* 168 * Initialize the device. 169 */ 170 int 171 wdc_init(struct wd_softc *sc, u_int *unit) 172 { 173 174 if (pciide_init(&sc->sc_channel, unit) != 0) 175 return ENXIO; 176 if (wdcprobe(&sc->sc_channel) != 0) 177 return ENXIO; 178 return 0; 179 } 180 181 /* 182 * Wait until the device is ready. 183 */ 184 int 185 wdc_wait_for_ready(struct wdc_channel *chp) 186 { 187 u_int timeout; 188 189 for (timeout = WDC_TIMEOUT; timeout > 0; --timeout) { 190 if ((WDC_READ_REG(chp, wd_status) & (WDCS_BSY | WDCS_DRDY)) 191 == WDCS_DRDY) 192 return 0; 193 } 194 return ENXIO; 195 } 196 197 /* 198 * Read one block off the device. 199 */ 200 int 201 wdc_read_block(struct wd_softc *sc, struct wdc_command *wd_c) 202 { 203 int i; 204 struct wdc_channel *chp = &sc->sc_channel; 205 uint16_t *ptr = (uint16_t *)wd_c->data; 206 207 if (ptr == NULL) 208 return 0; 209 210 for (i = wd_c->bcount; i > 0; i -= sizeof(uint16_t)) 211 *ptr++ = WDC_READ_DATA(chp); 212 213 return 0; 214 } 215 216 /* 217 * Send a command to the device (CHS and LBA addressing). 218 */ 219 int 220 wdccommand(struct wd_softc *sc, struct wdc_command *wd_c) 221 { 222 struct wdc_channel *chp = &sc->sc_channel; 223 224 #if 0 225 DPRINTF(("wdccommand(%d, %d, %d, %d, %d, %d, %d)\n", 226 wd_c->drive, wd_c->r_command, wd_c->r_cyl, 227 wd_c->r_head, wd_c->r_sector, wd_c->bcount, 228 wd_c->r_precomp)); 229 #endif 230 231 WDC_WRITE_REG(chp, wd_features, wd_c->r_features); 232 WDC_WRITE_REG(chp, wd_seccnt, wd_c->r_count); 233 WDC_WRITE_REG(chp, wd_sector, wd_c->r_sector); 234 WDC_WRITE_REG(chp, wd_cyl_lo, wd_c->r_cyl); 235 WDC_WRITE_REG(chp, wd_cyl_hi, wd_c->r_cyl >> 8); 236 WDC_WRITE_REG(chp, wd_sdh, 237 WDSD_IBM | (wd_c->drive << 4) | wd_c->r_head); 238 WDC_WRITE_REG(chp, wd_command, wd_c->r_command); 239 240 if (wdc_wait_for_ready(chp) != 0) 241 return ENXIO; 242 243 if (WDC_READ_REG(chp, wd_status) & WDCS_ERR) { 244 printf("wd%d: error %x\n", chp->compatchan, 245 WDC_READ_REG(chp, wd_error)); 246 return ENXIO; 247 } 248 249 return 0; 250 } 251 252 /* 253 * Send a command to the device (LBA48 addressing). 254 */ 255 int 256 wdccommandext(struct wd_softc *wd, struct wdc_command *wd_c) 257 { 258 struct wdc_channel *chp = &wd->sc_channel; 259 260 #if 0 261 DPRINTF(("%s(%d, %x, %" PRId64 ", %d)\n", __func__, 262 wd_c->drive, wd_c->r_command, 263 wd_c->r_blkno, wd_c->r_count)); 264 #endif 265 266 /* Select drive, head, and addressing mode. */ 267 WDC_WRITE_REG(chp, wd_sdh, (wd_c->drive << 4) | WDSD_LBA); 268 269 /* previous */ 270 WDC_WRITE_REG(chp, wd_features, 0); 271 WDC_WRITE_REG(chp, wd_seccnt, wd_c->r_count >> 8); 272 WDC_WRITE_REG(chp, wd_lba_hi, wd_c->r_blkno >> 40); 273 WDC_WRITE_REG(chp, wd_lba_mi, wd_c->r_blkno >> 32); 274 WDC_WRITE_REG(chp, wd_lba_lo, wd_c->r_blkno >> 24); 275 276 /* current */ 277 WDC_WRITE_REG(chp, wd_features, 0); 278 WDC_WRITE_REG(chp, wd_seccnt, wd_c->r_count); 279 WDC_WRITE_REG(chp, wd_lba_hi, wd_c->r_blkno >> 16); 280 WDC_WRITE_REG(chp, wd_lba_mi, wd_c->r_blkno >> 8); 281 WDC_WRITE_REG(chp, wd_lba_lo, wd_c->r_blkno); 282 283 /* Send command. */ 284 WDC_WRITE_REG(chp, wd_command, wd_c->r_command); 285 286 if (wdc_wait_for_ready(chp) != 0) 287 return ENXIO; 288 289 if (WDC_READ_REG(chp, wd_status) & WDCS_ERR) { 290 printf("wd%d: error %x\n", chp->compatchan, 291 WDC_READ_REG(chp, wd_error)); 292 return ENXIO; 293 } 294 295 return 0; 296 } 297 298 /* 299 * Issue 'device identify' command. 300 */ 301 int 302 wdc_exec_identify(struct wd_softc *wd, void *data) 303 { 304 int error; 305 struct wdc_command wd_c; 306 307 memset(&wd_c, 0, sizeof(wd_c)); 308 309 wd_c.drive = wd->sc_unit; 310 wd_c.r_command = WDCC_IDENTIFY; 311 wd_c.bcount = DEV_BSIZE; 312 wd_c.data = data; 313 314 if ((error = wdccommand(wd, &wd_c)) != 0) 315 return error; 316 317 return wdc_read_block(wd, &wd_c); 318 } 319 320 /* 321 * Issue 'read' command. 322 */ 323 int 324 wdc_exec_read(struct wd_softc *wd, uint8_t cmd, daddr_t blkno, void *data) 325 { 326 int error; 327 struct wdc_command wd_c; 328 bool lba, lba48; 329 330 memset(&wd_c, 0, sizeof(wd_c)); 331 lba = false; 332 lba48 = false; 333 334 wd_c.data = data; 335 wd_c.r_count = 1; 336 wd_c.r_features = 0; 337 wd_c.drive = wd->sc_unit; 338 wd_c.bcount = wd->sc_label.d_secsize; 339 340 if ((wd->sc_flags & WDF_LBA48) != 0 && blkno > wd->sc_capacity28) 341 lba48 = true; 342 else if ((wd->sc_flags & WDF_LBA) != 0) 343 lba = true; 344 345 if (lba48) { 346 /* LBA48 */ 347 wd_c.r_command = atacmd_to48(cmd); 348 wd_c.r_blkno = blkno; 349 } else if (lba) { 350 /* LBA */ 351 wd_c.r_command = cmd; 352 wd_c.r_sector = (blkno >> 0) & 0xff; 353 wd_c.r_cyl = (blkno >> 8) & 0xffff; 354 wd_c.r_head = (blkno >> 24) & 0x0f; 355 wd_c.r_head |= WDSD_LBA; 356 } else { 357 /* CHS */ 358 wd_c.r_command = cmd; 359 wd_c.r_sector = blkno % wd->sc_label.d_nsectors; 360 wd_c.r_sector++; /* Sectors begin with 1, not 0. */ 361 blkno /= wd->sc_label.d_nsectors; 362 wd_c.r_head = blkno % wd->sc_label.d_ntracks; 363 blkno /= wd->sc_label.d_ntracks; 364 wd_c.r_cyl = blkno; 365 wd_c.r_head |= WDSD_CHS; 366 } 367 368 if (lba48) 369 error = wdccommandext(wd, &wd_c); 370 else 371 error = wdccommand(wd, &wd_c); 372 373 if (error != 0) 374 return error; 375 376 return wdc_read_block(wd, &wd_c); 377 } 378