1 /* $NetBSD: wdc.c,v 1.11 2008/04/28 20:23:16 martin 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/types.h> 33 #include <sys/disklabel.h> 34 #include <sys/bootblock.h> 35 36 #include <lib/libsa/stand.h> 37 #include <machine/param.h> 38 39 #include "boot.h" 40 #include "wdvar.h" 41 42 #define WDCDELAY 100 43 #define WDCNDELAY_RST 31000 * 10 44 45 static int wdcprobe(struct wdc_channel *chp); 46 static int wdc_wait_for_ready(struct wdc_channel *chp); 47 static int wdc_read_block(struct wd_softc *sc, struct wdc_command *wd_c); 48 static int __wdcwait_reset(struct wdc_channel *chp, int drv_mask); 49 50 /* 51 * Reset the controller. 52 */ 53 static int 54 __wdcwait_reset(struct wdc_channel *chp, int drv_mask) 55 { 56 int timeout; 57 uint8_t st0, st1; 58 59 /* wait for BSY to deassert */ 60 for (timeout = 0; timeout < WDCNDELAY_RST; timeout++) { 61 WDC_WRITE_REG(chp, wd_sdh, WDSD_IBM); /* master */ 62 delay(10); 63 st0 = WDC_READ_REG(chp, wd_status); 64 WDC_WRITE_REG(chp, wd_sdh, WDSD_IBM | 0x10); /* slave */ 65 delay(10); 66 st1 = WDC_READ_REG(chp, wd_status); 67 68 if ((drv_mask & 0x01) == 0) { 69 /* no master */ 70 if ((drv_mask & 0x02) != 0 && (st1 & WDCS_BSY) == 0) { 71 /* No master, slave is ready, it's done */ 72 goto end; 73 } 74 } else if ((drv_mask & 0x02) == 0) { 75 /* no slave */ 76 if ((drv_mask & 0x01) != 0 && (st0 & WDCS_BSY) == 0) { 77 /* No slave, master is ready, it's done */ 78 goto end; 79 } 80 } else { 81 /* Wait for both master and slave to be ready */ 82 if ((st0 & WDCS_BSY) == 0 && (st1 & WDCS_BSY) == 0) { 83 goto end; 84 } 85 } 86 87 delay(WDCDELAY); 88 } 89 90 /* Reset timed out. Maybe it's because drv_mask was not right */ 91 if (st0 & WDCS_BSY) 92 drv_mask &= ~0x01; 93 if (st1 & WDCS_BSY) 94 drv_mask &= ~0x02; 95 96 end: 97 return drv_mask; 98 } 99 100 /* Test to see controller with at last one attached drive is there. 101 * Returns a bit for each possible drive found (0x01 for drive 0, 102 * 0x02 for drive 1). 103 * Logic: 104 * - If a status register is at 0xff, assume there is no drive here 105 * (ISA has pull-up resistors). Similarly if the status register has 106 * the value we last wrote to the bus (for IDE interfaces without pullups). 107 * If no drive at all -> return. 108 * - reset the controller, wait for it to complete (may take up to 31s !). 109 * If timeout -> return. 110 */ 111 static int 112 wdcprobe(struct wdc_channel *chp) 113 { 114 uint8_t st0, st1; 115 uint8_t ret_value = 0x03; 116 uint8_t drive; 117 int found; 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 found = 0; 160 for (drive = 0; drive < 2; drive++) { 161 if ((ret_value & (0x01 << drive)) == 0) 162 continue; 163 return 0; 164 } 165 return ENXIO; 166 } 167 168 /* 169 * Initialize the device. 170 */ 171 int 172 wdc_init(struct wd_softc *sc, u_int *unit) 173 { 174 175 if (pciide_init(&sc->sc_channel, unit) != 0) 176 return ENXIO; 177 if (wdcprobe(&sc->sc_channel) != 0) 178 return ENXIO; 179 return 0; 180 } 181 182 /* 183 * Wait until the device is ready. 184 */ 185 int 186 wdc_wait_for_ready(struct wdc_channel *chp) 187 { 188 u_int timeout; 189 190 for (timeout = WDC_TIMEOUT; timeout > 0; --timeout) { 191 if ((WDC_READ_REG(chp, wd_status) & (WDCS_BSY | WDCS_DRDY)) 192 == WDCS_DRDY) 193 return 0; 194 } 195 return ENXIO; 196 } 197 198 /* 199 * Read one block off the device. 200 */ 201 int 202 wdc_read_block(struct wd_softc *sc, struct wdc_command *wd_c) 203 { 204 int i; 205 struct wdc_channel *chp = &sc->sc_channel; 206 uint16_t *ptr = (uint16_t *)wd_c->data; 207 208 if (ptr == NULL) 209 return 0; 210 211 for (i = wd_c->bcount; i > 0; i -= sizeof(uint16_t)) 212 *ptr++ = WDC_READ_DATA(chp); 213 214 return 0; 215 } 216 217 /* 218 * Send a command to the device (CHS and LBA addressing). 219 */ 220 int 221 wdccommand(struct wd_softc *sc, struct wdc_command *wd_c) 222 { 223 struct wdc_channel *chp = &sc->sc_channel; 224 225 #if 0 226 DPRINTF(("wdccommand(%d, %d, %d, %d, %d, %d, %d)\n", 227 wd_c->drive, wd_c->r_command, wd_c->r_cyl, 228 wd_c->r_head, wd_c->r_sector, wd_c->bcount, 229 wd_c->r_precomp)); 230 #endif 231 232 WDC_WRITE_REG(chp, wd_precomp, wd_c->r_precomp); 233 WDC_WRITE_REG(chp, wd_seccnt, wd_c->r_count); 234 WDC_WRITE_REG(chp, wd_sector, wd_c->r_sector); 235 WDC_WRITE_REG(chp, wd_cyl_lo, wd_c->r_cyl); 236 WDC_WRITE_REG(chp, wd_cyl_hi, wd_c->r_cyl >> 8); 237 WDC_WRITE_REG(chp, wd_sdh, 238 WDSD_IBM | (wd_c->drive << 4) | wd_c->r_head); 239 WDC_WRITE_REG(chp, wd_command, wd_c->r_command); 240 241 if (wdc_wait_for_ready(chp) != 0) 242 return ENXIO; 243 244 if (WDC_READ_REG(chp, wd_status) & WDCS_ERR) { 245 printf("wd%d: error %x\n", chp->compatchan, 246 WDC_READ_REG(chp, wd_error)); 247 return ENXIO; 248 } 249 250 return 0; 251 } 252 253 /* 254 * Send a command to the device (LBA48 addressing). 255 */ 256 int 257 wdccommandext(struct wd_softc *wd, struct wdc_command *wd_c) 258 { 259 struct wdc_channel *chp = &wd->sc_channel; 260 261 /* Select drive, head, and addressing mode. */ 262 WDC_WRITE_REG(chp, wd_sdh, (wd_c->drive << 4) | WDSD_LBA); 263 264 /* previous */ 265 WDC_WRITE_REG(chp, wd_features, 0); 266 WDC_WRITE_REG(chp, wd_seccnt, wd_c->r_count >> 8); 267 WDC_WRITE_REG(chp, wd_lba_hi, wd_c->r_blkno >> 40); 268 WDC_WRITE_REG(chp, wd_lba_mi, wd_c->r_blkno >> 32); 269 WDC_WRITE_REG(chp, wd_lba_lo, wd_c->r_blkno >> 24); 270 271 /* current */ 272 WDC_WRITE_REG(chp, wd_features, 0); 273 WDC_WRITE_REG(chp, wd_seccnt, wd_c->r_count); 274 WDC_WRITE_REG(chp, wd_lba_hi, wd_c->r_blkno >> 16); 275 WDC_WRITE_REG(chp, wd_lba_mi, wd_c->r_blkno >> 8); 276 WDC_WRITE_REG(chp, wd_lba_lo, wd_c->r_blkno); 277 278 /* Send command. */ 279 WDC_WRITE_REG(chp, wd_command, wd_c->r_command); 280 281 if (wdc_wait_for_ready(chp) != 0) 282 return ENXIO; 283 284 if (WDC_READ_REG(chp, wd_status) & WDCS_ERR) { 285 printf("wd%d: error %x\n", chp->compatchan, 286 WDC_READ_REG(chp, wd_error)); 287 return ENXIO; 288 } 289 290 return 0; 291 } 292 293 /* 294 * Issue 'device identify' command. 295 */ 296 int 297 wdc_exec_identify(struct wd_softc *wd, void *data) 298 { 299 int error; 300 struct wdc_command wd_c; 301 302 memset(&wd_c, 0, sizeof(wd_c)); 303 304 wd_c.drive = wd->sc_unit; 305 wd_c.r_command = WDCC_IDENTIFY; 306 wd_c.bcount = DEV_BSIZE; 307 wd_c.data = data; 308 309 if ((error = wdccommand(wd, &wd_c)) != 0) 310 return error; 311 312 return wdc_read_block(wd, &wd_c); 313 } 314 315 /* 316 * Issue 'read' command. 317 */ 318 int 319 wdc_exec_read(struct wd_softc *wd, uint8_t cmd, daddr_t blkno, void *data) 320 { 321 int error; 322 struct wdc_command wd_c; 323 324 memset(&wd_c, 0, sizeof(wd_c)); 325 326 if (wd->sc_flags & WDF_LBA48) { 327 /* LBA48 */ 328 wd_c.r_blkno = blkno; 329 } else if (wd->sc_flags & WDF_LBA) { 330 /* LBA */ 331 wd_c.r_sector = (blkno >> 0) & 0xff; 332 wd_c.r_cyl = (blkno >> 8) & 0xffff; 333 wd_c.r_head = (blkno >> 24) & 0x0f; 334 wd_c.r_head |= WDSD_LBA; 335 } else { 336 /* LHS */ 337 wd_c.r_sector = blkno % wd->sc_label.d_nsectors; 338 wd_c.r_sector++; /* Sectors begin with 1, not 0. */ 339 blkno /= wd->sc_label.d_nsectors; 340 wd_c.r_head = blkno % wd->sc_label.d_ntracks; 341 blkno /= wd->sc_label.d_ntracks; 342 wd_c.r_cyl = blkno; 343 wd_c.r_head |= WDSD_CHS; 344 } 345 346 wd_c.data = data; 347 wd_c.r_count = 1; 348 wd_c.drive = wd->sc_unit; 349 wd_c.r_command = cmd; 350 wd_c.bcount = wd->sc_label.d_secsize; 351 352 if (wd->sc_flags & WDF_LBA48) 353 error = wdccommandext(wd, &wd_c); 354 else 355 error = wdccommand(wd, &wd_c); 356 357 if (error != 0) 358 return error; 359 360 return wdc_read_block(wd, &wd_c); 361 } 362