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