1/* $NetBSD: wdc.c,v 1.12 2010/01/10 16:20:45 tsutsui 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 <lib/libkern/libkern.h> 38#include <machine/param.h> 39 40#include "boot.h" 41#include "wdvar.h" 42 43#define WDCDELAY 100 44#define WDCNDELAY_RST 31000 * 10 45 46static int wdcprobe(struct wdc_channel *chp); 47static int wdc_wait_for_ready(struct wdc_channel *chp); 48static int wdc_read_block(struct wd_softc *sc, struct wdc_command *wd_c); 49static int __wdcwait_reset(struct wdc_channel *chp, int drv_mask); 50 51/* 52 * Reset the controller. 53 */ 54static 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 */ 112static int 113wdcprobe(struct wdc_channel *chp) 114{ 115 uint8_t st0, st1; 116 uint8_t ret_value = 0x03; 117 uint8_t drive; 118 int found; 119 120 /* 121 * Sanity check to see if the wdc channel responds at all. 122 */ 123 WDC_WRITE_REG(chp, wd_sdh, WDSD_IBM); 124 delay(10); 125 st0 = WDC_READ_REG(chp, wd_status); 126 WDC_WRITE_REG(chp, wd_sdh, WDSD_IBM | 0x10); 127 delay(10); 128 st1 = WDC_READ_REG(chp, wd_status); 129 130 if (st0 == 0xff || st0 == WDSD_IBM) 131 ret_value &= ~0x01; 132 if (st1 == 0xff || st1 == (WDSD_IBM | 0x10)) 133 ret_value &= ~0x02; 134 if (ret_value == 0) 135 return ENXIO; 136 137 /* assert SRST, wait for reset to complete */ 138 WDC_WRITE_REG(chp, wd_sdh, WDSD_IBM); 139 delay(10); 140 WDC_WRITE_CTLREG(chp, wd_aux_ctlr, WDCTL_RST | WDCTL_IDS); 141 delay(1000); 142 WDC_WRITE_CTLREG(chp, wd_aux_ctlr, WDCTL_IDS); 143 delay(1000); 144 (void) WDC_READ_REG(chp, wd_error); 145 WDC_WRITE_CTLREG(chp, wd_aux_ctlr, WDCTL_4BIT); 146 delay(10); 147 148 ret_value = __wdcwait_reset(chp, ret_value); 149 150 /* if reset failed, there's nothing here */ 151 if (ret_value == 0) 152 return ENXIO; 153 154 /* 155 * Test presence of drives. First test register signatures looking for 156 * ATAPI devices. If it's not an ATAPI and reset said there may be 157 * something here assume it's ATA or OLD. Ghost will be killed later in 158 * attach routine. 159 */ 160 found = 0; 161 for (drive = 0; drive < 2; drive++) { 162 if ((ret_value & (0x01 << drive)) == 0) 163 continue; 164 return 0; 165 } 166 return ENXIO; 167} 168 169/* 170 * Initialize the device. 171 */ 172int 173wdc_init(struct wd_softc *sc, u_int *unit) 174{ 175 176 if (pciide_init(&sc->sc_channel, unit) != 0) 177 return ENXIO; 178 if (wdcprobe(&sc->sc_channel) != 0) 179 return ENXIO; 180 return 0; 181} 182 183/* 184 * Wait until the device is ready. 185 */ 186int 187wdc_wait_for_ready(struct wdc_channel *chp) 188{ 189 u_int timeout; 190 191 for (timeout = WDC_TIMEOUT; timeout > 0; --timeout) { 192 if ((WDC_READ_REG(chp, wd_status) & (WDCS_BSY | WDCS_DRDY)) 193 == WDCS_DRDY) 194 return 0; 195 } 196 return ENXIO; 197} 198 199/* 200 * Read one block off the device. 201 */ 202int 203wdc_read_block(struct wd_softc *sc, struct wdc_command *wd_c) 204{ 205 int i; 206 struct wdc_channel *chp = &sc->sc_channel; 207 uint16_t *ptr = (uint16_t *)wd_c->data; 208 209 if (ptr == NULL) 210 return 0; 211 212 for (i = wd_c->bcount; i > 0; i -= sizeof(uint16_t)) 213 *ptr++ = WDC_READ_DATA(chp); 214 215 return 0; 216} 217 218/* 219 * Send a command to the device (CHS and LBA addressing). 220 */ 221int 222wdccommand(struct wd_softc *sc, struct wdc_command *wd_c) 223{ 224 struct wdc_channel *chp = &sc->sc_channel; 225 226#if 0 227 DPRINTF(("wdccommand(%d, %d, %d, %d, %d, %d, %d)\n", 228 wd_c->drive, wd_c->r_command, wd_c->r_cyl, 229 wd_c->r_head, wd_c->r_sector, wd_c->bcount, 230 wd_c->r_precomp)); 231#endif 232 233 WDC_WRITE_REG(chp, wd_features, wd_c->r_features); 234 WDC_WRITE_REG(chp, wd_seccnt, wd_c->r_count); 235 WDC_WRITE_REG(chp, wd_sector, wd_c->r_sector); 236 WDC_WRITE_REG(chp, wd_cyl_lo, wd_c->r_cyl); 237 WDC_WRITE_REG(chp, wd_cyl_hi, wd_c->r_cyl >> 8); 238 WDC_WRITE_REG(chp, wd_sdh, 239 WDSD_IBM | (wd_c->drive << 4) | wd_c->r_head); 240 WDC_WRITE_REG(chp, wd_command, wd_c->r_command); 241 242 if (wdc_wait_for_ready(chp) != 0) 243 return ENXIO; 244 245 if (WDC_READ_REG(chp, wd_status) & WDCS_ERR) { 246 printf("wd%d: error %x\n", chp->compatchan, 247 WDC_READ_REG(chp, wd_error)); 248 return ENXIO; 249 } 250 251 return 0; 252} 253 254/* 255 * Send a command to the device (LBA48 addressing). 256 */ 257int 258wdccommandext(struct wd_softc *wd, struct wdc_command *wd_c) 259{ 260 struct wdc_channel *chp = &wd->sc_channel; 261 262#if 0 263 DPRINTF(("%s(%d, %x, %" PRId64 ", %d)\n", __func__, 264 wd_c->drive, wd_c->r_command, 265 wd_c->r_blkno, wd_c->r_count)); 266#endif 267 268 /* Select drive, head, and addressing mode. */ 269 WDC_WRITE_REG(chp, wd_sdh, (wd_c->drive << 4) | WDSD_LBA); 270 271 /* previous */ 272 WDC_WRITE_REG(chp, wd_features, 0); 273 WDC_WRITE_REG(chp, wd_seccnt, wd_c->r_count >> 8); 274 WDC_WRITE_REG(chp, wd_lba_hi, wd_c->r_blkno >> 40); 275 WDC_WRITE_REG(chp, wd_lba_mi, wd_c->r_blkno >> 32); 276 WDC_WRITE_REG(chp, wd_lba_lo, wd_c->r_blkno >> 24); 277 278 /* current */ 279 WDC_WRITE_REG(chp, wd_features, 0); 280 WDC_WRITE_REG(chp, wd_seccnt, wd_c->r_count); 281 WDC_WRITE_REG(chp, wd_lba_hi, wd_c->r_blkno >> 16); 282 WDC_WRITE_REG(chp, wd_lba_mi, wd_c->r_blkno >> 8); 283 WDC_WRITE_REG(chp, wd_lba_lo, wd_c->r_blkno); 284 285 /* Send command. */ 286 WDC_WRITE_REG(chp, wd_command, wd_c->r_command); 287 288 if (wdc_wait_for_ready(chp) != 0) 289 return ENXIO; 290 291 if (WDC_READ_REG(chp, wd_status) & WDCS_ERR) { 292 printf("wd%d: error %x\n", chp->compatchan, 293 WDC_READ_REG(chp, wd_error)); 294 return ENXIO; 295 } 296 297 return 0; 298} 299 300/* 301 * Issue 'device identify' command. 302 */ 303int 304wdc_exec_identify(struct wd_softc *wd, void *data) 305{ 306 int error; 307 struct wdc_command wd_c; 308 309 memset(&wd_c, 0, sizeof(wd_c)); 310 311 wd_c.drive = wd->sc_unit; 312 wd_c.r_command = WDCC_IDENTIFY; 313 wd_c.bcount = DEV_BSIZE; 314 wd_c.data = data; 315 316 if ((error = wdccommand(wd, &wd_c)) != 0) 317 return error; 318 319 return wdc_read_block(wd, &wd_c); 320} 321 322/* 323 * Issue 'read' command. 324 */ 325int 326wdc_exec_read(struct wd_softc *wd, uint8_t cmd, daddr_t blkno, void *data) 327{ 328 int error; 329 struct wdc_command wd_c; 330 bool lba, lba48; 331 332 memset(&wd_c, 0, sizeof(wd_c)); 333 lba = false; 334 lba48 = false; 335 336 wd_c.data = data; 337 wd_c.r_count = 1; 338 wd_c.r_features = 0; 339 wd_c.drive = wd->sc_unit; 340 wd_c.bcount = wd->sc_label.d_secsize; 341 342 if ((wd->sc_flags & WDF_LBA48) != 0 && blkno > wd->sc_capacity28) 343 lba48 = true; 344 else if ((wd->sc_flags & WDF_LBA) != 0) 345 lba = true; 346 347 if (lba48) { 348 /* LBA48 */ 349 wd_c.r_command = atacmd_to48(cmd); 350 wd_c.r_blkno = blkno; 351 } else if (lba) { 352 /* LBA */ 353 wd_c.r_command = cmd; 354 wd_c.r_sector = (blkno >> 0) & 0xff; 355 wd_c.r_cyl = (blkno >> 8) & 0xffff; 356 wd_c.r_head = (blkno >> 24) & 0x0f; 357 wd_c.r_head |= WDSD_LBA; 358 } else { 359 /* CHS */ 360 wd_c.r_command = cmd; 361 wd_c.r_sector = blkno % wd->sc_label.d_nsectors; 362 wd_c.r_sector++; /* Sectors begin with 1, not 0. */ 363 blkno /= wd->sc_label.d_nsectors; 364 wd_c.r_head = blkno % wd->sc_label.d_ntracks; 365 blkno /= wd->sc_label.d_ntracks; 366 wd_c.r_cyl = blkno; 367 wd_c.r_head |= WDSD_CHS; 368 } 369 370 if (lba48) 371 error = wdccommandext(wd, &wd_c); 372 else 373 error = wdccommand(wd, &wd_c); 374 375 if (error != 0) 376 return error; 377 378 return wdc_read_block(wd, &wd_c); 379} 380