Module Name: src Committed By: kiyohara Date: Thu Oct 14 06:50:44 UTC 2010
Modified Files: src/sys/arch/bebox/stand/boot: Makefile boot.c conf.c filesystem.c version Added Files: src/sys/arch/bebox/stand/boot: wd.c wdc.c wdvar.h Log Message: Support kernel load from IDE HDD with onboard wdc. like cobalt, sandpoint. To generate a diff of this commit: cvs rdiff -u -r1.27 -r1.28 src/sys/arch/bebox/stand/boot/Makefile cvs rdiff -u -r1.21 -r1.22 src/sys/arch/bebox/stand/boot/boot.c cvs rdiff -u -r1.7 -r1.8 src/sys/arch/bebox/stand/boot/conf.c \ src/sys/arch/bebox/stand/boot/filesystem.c cvs rdiff -u -r1.8 -r1.9 src/sys/arch/bebox/stand/boot/version cvs rdiff -u -r0 -r1.1 src/sys/arch/bebox/stand/boot/wd.c \ src/sys/arch/bebox/stand/boot/wdc.c src/sys/arch/bebox/stand/boot/wdvar.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/bebox/stand/boot/Makefile diff -u src/sys/arch/bebox/stand/boot/Makefile:1.27 src/sys/arch/bebox/stand/boot/Makefile:1.28 --- src/sys/arch/bebox/stand/boot/Makefile:1.27 Thu Oct 14 06:17:29 2010 +++ src/sys/arch/bebox/stand/boot/Makefile Thu Oct 14 06:50:43 2010 @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.27 2010/10/14 06:17:29 kiyohara Exp $ +# $NetBSD: Makefile,v 1.28 2010/10/14 06:50:43 kiyohara Exp $ NOMAN= # defined @@ -23,7 +23,7 @@ SRCS= srt0.s SRCS+= boot.c clock.c com.c conf.c cons.c cpu.c devopen.c SRCS+= fd.c filesystem.c inkernel.c io.c kbd.c monitor.c ns16550.c -SRCS+= pci.c prf.c tgets.c vers.c vga.c video.c vreset.c +SRCS+= pci.c prf.c tgets.c vers.c vga.c video.c vreset.c wdc.c wd.c SRCS+= setjmp.S CFLAGS= -Wno-main -ffreestanding Index: src/sys/arch/bebox/stand/boot/boot.c diff -u src/sys/arch/bebox/stand/boot/boot.c:1.21 src/sys/arch/bebox/stand/boot/boot.c:1.22 --- src/sys/arch/bebox/stand/boot/boot.c:1.21 Thu Oct 14 06:39:52 2010 +++ src/sys/arch/bebox/stand/boot/boot.c Thu Oct 14 06:50:44 2010 @@ -1,4 +1,4 @@ -/* $NetBSD: boot.c,v 1.21 2010/10/14 06:39:52 kiyohara Exp $ */ +/* $NetBSD: boot.c,v 1.22 2010/10/14 06:50:44 kiyohara Exp $ */ /* * Copyright (C) 1995, 1996 Wolfgang Solfrank. @@ -40,9 +40,12 @@ #include <machine/cpu.h> #include "boot.h" +#include "wdvar.h" char *names[] = { + "/dev/disk/ide/0/master/0_0:/netbsd", "/dev/disk/floppy:netbsd", "/dev/disk/floppy:netbsd.gz", + "/dev/disk/ide/0/master/0_0:/onetbsd", "/dev/disk/floppy:onetbsd", "/dev/disk/floppy:onetbsd.gz" "in", }; @@ -115,11 +118,6 @@ p += sizeof (btinfo_console); memcpy(p, (void *)&btinfo_clock, sizeof (btinfo_clock)); - /* - * attached kernel check - */ - init_in(); - runCPU1((void *)start_CPU1); wait_for(&CPU1_alive); @@ -127,6 +125,16 @@ printf(">> (%s, %s)\n", bootprog_maker, bootprog_date); printf(">> Memory: %d k\n", btinfo_memory.memsize / 1024); + /* + * attached kernel check and copy. + */ + init_in(); + + printf("\n"); + + /* Initialize w...@isa port 0x1f0 */ + wdc_init(0x1f0); + for (;;) { name = names[n++]; if (n >= NUMNAMES) Index: src/sys/arch/bebox/stand/boot/conf.c diff -u src/sys/arch/bebox/stand/boot/conf.c:1.7 src/sys/arch/bebox/stand/boot/conf.c:1.8 --- src/sys/arch/bebox/stand/boot/conf.c:1.7 Thu Oct 14 06:39:52 2010 +++ src/sys/arch/bebox/stand/boot/conf.c Thu Oct 14 06:50:44 2010 @@ -1,4 +1,4 @@ -/* $NetBSD: conf.c,v 1.7 2010/10/14 06:39:52 kiyohara Exp $ */ +/* $NetBSD: conf.c,v 1.8 2010/10/14 06:50:44 kiyohara Exp $ */ /* * Copyright (c) 1982, 1986, 1990, 1993 @@ -42,8 +42,13 @@ extern int inopen(struct open_file *, ...); extern int inclose(struct open_file *); +extern int wdstrategy(void *, int, daddr_t, size_t, void *, size_t *); +extern int wdopen(struct open_file *, ...); +extern int wdclose(struct open_file *); + struct devsw devsw[] = { { "fd", fdstrategy, fdopen, fdclose, noioctl }, + { "wd", wdstrategy, wdopen, wdclose, noioctl }, { NULL, NULL, NULL, NULL, NULL }, }; Index: src/sys/arch/bebox/stand/boot/filesystem.c diff -u src/sys/arch/bebox/stand/boot/filesystem.c:1.7 src/sys/arch/bebox/stand/boot/filesystem.c:1.8 --- src/sys/arch/bebox/stand/boot/filesystem.c:1.7 Mon May 26 16:28:39 2008 +++ src/sys/arch/bebox/stand/boot/filesystem.c Thu Oct 14 06:50:44 2010 @@ -1,4 +1,4 @@ -/* $NetBSD: filesystem.c,v 1.7 2008/05/26 16:28:39 kiyohara Exp $ */ +/* $NetBSD: filesystem.c,v 1.8 2010/10/14 06:50:44 kiyohara Exp $ */ /*- * Copyright (c) 1996, 1997 The NetBSD Foundation, Inc. @@ -30,8 +30,11 @@ */ #include <lib/libsa/stand.h> +#include <ufs.h> struct fs_ops file_system[] = { + FS_OPS(ffsv1), + FS_OPS(ffsv2), FS_OPS(null), }; Index: src/sys/arch/bebox/stand/boot/version diff -u src/sys/arch/bebox/stand/boot/version:1.8 src/sys/arch/bebox/stand/boot/version:1.9 --- src/sys/arch/bebox/stand/boot/version:1.8 Thu Oct 14 06:23:27 2010 +++ src/sys/arch/bebox/stand/boot/version Thu Oct 14 06:50:44 2010 @@ -1,4 +1,4 @@ -$NetBSD: version,v 1.8 2010/10/14 06:23:27 kiyohara Exp $ +$NetBSD: version,v 1.9 2010/10/14 06:50:44 kiyohara Exp $ 1.1: Boot program for BeBox; initial revision 1.2: check BUS FREQ, add clock information @@ -8,3 +8,4 @@ Headers. 1.6: Support framebuffer and vga. Split boot{,_com0,_vga}. +1.7: Support kernel load from IDE HDD with onboard wdc. Added files: Index: src/sys/arch/bebox/stand/boot/wd.c diff -u /dev/null src/sys/arch/bebox/stand/boot/wd.c:1.1 --- /dev/null Thu Oct 14 06:50:44 2010 +++ src/sys/arch/bebox/stand/boot/wd.c Thu Oct 14 06:50:43 2010 @@ -0,0 +1,320 @@ +/* $NetBSD: wd.c,v 1.1 2010/10/14 06:50:43 kiyohara Exp $ */ + +/*- + * Copyright (c) 2003 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Manuel Bouyer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/stdint.h> + +#include <lib/libsa/stand.h> +#include <lib/libkern/libkern.h> + +#include <machine/param.h> +#include <machine/stdarg.h> +#include <dev/raidframe/raidframevar.h> /* For RF_PROTECTED_SECTORS */ + +#include "boot.h" +#include "wdvar.h" + +#ifdef DEBUG +#define DPRINTF(x) printf x +#else +#define DPRINTF(x) +#endif + +static int wd_get_params(struct wd_softc *wd); +static int wdgetdisklabel(struct wd_softc *wd); +static void wdgetdefaultlabel(struct wd_softc *wd, struct disklabel *lp); + +int wdopen(struct open_file *, ...); +int wdclose(struct open_file *); +int wdstrategy(void *, int, daddr_t, size_t, void *, size_t *); + +/* + * Get drive parameters through 'device identify' command. + */ +int +wd_get_params(struct wd_softc *wd) +{ + int error; + uint8_t buf[DEV_BSIZE]; + + if ((error = wdc_exec_identify(wd, buf)) != 0) + return error; + + wd->sc_params = *(struct ataparams *)buf; + + /* 48-bit LBA addressing */ + if ((wd->sc_params.atap_cmd2_en & ATA_CMD2_LBA48) != 0) + wd->sc_flags |= WDF_LBA48; + + /* Prior to ATA-4, LBA was optional. */ + if ((wd->sc_params.atap_capabilities1 & WDC_CAP_LBA) != 0) + wd->sc_flags |= WDF_LBA; + + if ((wd->sc_flags & WDF_LBA48) != 0) { + DPRINTF(("Drive supports LBA48.\n")); + wd->sc_capacity = + ((uint64_t)wd->sc_params.atap_max_lba[3] << 48) | + ((uint64_t)wd->sc_params.atap_max_lba[2] << 32) | + ((uint64_t)wd->sc_params.atap_max_lba[1] << 16) | + ((uint64_t)wd->sc_params.atap_max_lba[0] << 0); + DPRINTF(("atap_max_lba = (0x%x, 0x%x, 0x%x, 0x%x)\n", + wd->sc_params.atap_max_lba[3], + wd->sc_params.atap_max_lba[2], + wd->sc_params.atap_max_lba[1], + wd->sc_params.atap_max_lba[0])); + wd->sc_capacity28 = + ((uint32_t)wd->sc_params.atap_capacity[1] << 16) | + ((uint32_t)wd->sc_params.atap_capacity[0] << 0); + DPRINTF(("atap_capacity = (0x%x, 0x%x)\n", + wd->sc_params.atap_capacity[1], + wd->sc_params.atap_capacity[0])); + } else if ((wd->sc_flags & WDF_LBA) != 0) { + DPRINTF(("Drive supports LBA.\n")); + wd->sc_capacity = + ((uint32_t)wd->sc_params.atap_capacity[1] << 16) | + ((uint32_t)wd->sc_params.atap_capacity[0] << 0); + wd->sc_capacity28 = + ((uint32_t)wd->sc_params.atap_capacity[1] << 16) | + ((uint32_t)wd->sc_params.atap_capacity[0] << 0); + } else { + DPRINTF(("Drive doesn't support LBA; using CHS.\n")); + wd->sc_capacity = wd->sc_capacity28 = + wd->sc_params.atap_cylinders * + wd->sc_params.atap_heads * + wd->sc_params.atap_sectors; + } + DPRINTF(("wd->sc_capacity = %" PRId64 ", wd->sc_capacity28 = %d.\n", + wd->sc_capacity, wd->sc_capacity28)); + + return 0; +} + +/* + * Initialize disk label to the default value. + */ +void +wdgetdefaultlabel(struct wd_softc *wd, struct disklabel *lp) +{ + + memset(lp, 0, sizeof(struct disklabel)); + + lp->d_secsize = DEV_BSIZE; + lp->d_ntracks = wd->sc_params.atap_heads; + lp->d_nsectors = wd->sc_params.atap_sectors; + lp->d_ncylinders = wd->sc_params.atap_cylinders; + lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors; + + if (strcmp((const char *)wd->sc_params.atap_model, "ST506") == 0) + lp->d_type = DTYPE_ST506; + else + lp->d_type = DTYPE_ESDI; + + strncpy(lp->d_typename, (const char *)wd->sc_params.atap_model, 16); + strncpy(lp->d_packname, "fictitious", 16); + if (wd->sc_capacity > UINT32_MAX) + lp->d_secperunit = UINT32_MAX; + else + lp->d_secperunit = wd->sc_capacity; + lp->d_rpm = 3600; + lp->d_interleave = 1; + lp->d_flags = 0; + + lp->d_partitions[RAW_PART].p_offset = 0; + lp->d_partitions[RAW_PART].p_size = + lp->d_secperunit * (lp->d_secsize / DEV_BSIZE); + lp->d_partitions[RAW_PART].p_fstype = FS_UNUSED; + lp->d_npartitions = MAXPARTITIONS; /* RAW_PART + 1 ??? */ + + lp->d_magic = DISKMAGIC; + lp->d_magic2 = DISKMAGIC; + lp->d_checksum = dkcksum(lp); +} + +/* + * Read disk label from the device. + */ +int +wdgetdisklabel(struct wd_softc *wd) +{ + struct mbr_sector *mbr; + struct mbr_partition *mp; + struct disklabel *lp; + size_t rsize; + int sector, i; + char *msg; + uint8_t buf[DEV_BSIZE]; + + wdgetdefaultlabel(wd, &wd->sc_label); + + /* + * Find NetBSD Partition in DOS partition table. + */ + sector = 0; + if (wdstrategy(wd, F_READ, MBR_BBSECTOR, DEV_BSIZE, buf, &rsize)) + return EOFFSET; + + mbr = (struct mbr_sector *)buf; + if (mbr->mbr_magic == htole16(MBR_MAGIC)) { + /* + * Lookup NetBSD slice. If there is none, go ahead + * and try to read the disklabel off sector #0. + */ + mp = mbr->mbr_parts; + for (i = 0; i < MBR_PART_COUNT; i++) { + if (mp[i].mbrp_type == MBR_PTYPE_NETBSD) { + sector = le32toh(mp[i].mbrp_start); + break; + } + } + } + + if (wdstrategy(wd, F_READ, sector + LABELSECTOR, DEV_BSIZE, + buf, &rsize)) + return EOFFSET; + + msg = getdisklabel((const char *)buf + LABELOFFSET, &wd->sc_label); + if (msg) + printf("ide/0/%s/0: getdisklabel: %s\n", + (wd->sc_unit == 0) ? "master" : "slave", msg); + + lp = &wd->sc_label; + + /* check partition */ + if ((wd->sc_part >= lp->d_npartitions) || + (lp->d_partitions[wd->sc_part].p_fstype == FS_UNUSED)) { + DPRINTF(("illegal partition\n")); + return EPART; + } + + DPRINTF(("label info: d_secsize %d, d_nsectors %d, d_ncylinders %d," + " d_ntracks %d, d_secpercyl %d\n", + wd->sc_label.d_secsize, + wd->sc_label.d_nsectors, + wd->sc_label.d_ncylinders, + wd->sc_label.d_ntracks, + wd->sc_label.d_secpercyl)); + + return 0; +} + +/* + * Open device (read drive parameters and disklabel) + */ +int +wdopen(struct open_file *f, ...) +{ + int error; + va_list ap; + u_int ctlr, unit, lunit, part; + struct wd_softc *wd; + + va_start(ap, f); + ctlr = va_arg(ap, u_int); + unit = va_arg(ap, u_int); + lunit = va_arg(ap, u_int); + part = va_arg(ap, u_int); + va_end(ap); + + DPRINTF(("wdopen: ide/%d/%s/%d_%d\n", + ctlr, (unit == 0) ? "master" : "slave", lunit, part)); + if (lunit != 0) + return ENOENT; + + wd = alloc(sizeof(struct wd_softc)); + if (wd == NULL) + return ENOMEM; + + memset(wd, 0, sizeof(struct wd_softc)); + + wd->sc_part = part; + wd->sc_unit = unit; + wd->sc_ctlr = ctlr; + + if ((error = wd_get_params(wd)) != 0) + return error; + + if ((error = wdgetdisklabel(wd)) != 0) + return error; + + f->f_devdata = wd; + return 0; +} + +/* + * Close device. + */ +int +wdclose(struct open_file *f) +{ + + return 0; +} + +/* + * Read some data. + */ +int +wdstrategy(void *f, int rw, daddr_t dblk, size_t size, void *p, size_t *rsize) +{ + int i, nsect; + daddr_t blkno; + struct wd_softc *wd; + struct partition *pp; + uint8_t *buf; + + if (size == 0) + return 0; + + if (rw != F_READ) + return EOPNOTSUPP; + + buf = p; + wd = f; + pp = &wd->sc_label.d_partitions[wd->sc_part]; + + nsect = howmany(size, wd->sc_label.d_secsize); + blkno = dblk + pp->p_offset; + if (pp->p_fstype == FS_RAID) + blkno += RF_PROTECTED_SECTORS; + + for (i = 0; i < nsect; i++, blkno++) { + int error; + + if ((error = wdc_exec_read(wd, WDCC_READ, blkno, buf)) != 0) + return error; + + buf += wd->sc_label.d_secsize; + } + + *rsize = size; + return 0; +} Index: src/sys/arch/bebox/stand/boot/wdc.c diff -u /dev/null src/sys/arch/bebox/stand/boot/wdc.c:1.1 --- /dev/null Thu Oct 14 06:50:44 2010 +++ src/sys/arch/bebox/stand/boot/wdc.c Thu Oct 14 06:50:43 2010 @@ -0,0 +1,479 @@ +/* $NetBSD: wdc.c,v 1.1 2010/10/14 06:50:43 kiyohara Exp $ */ + +/*- + * Copyright (c) 2003 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Manuel Bouyer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/types.h> +#include <sys/disklabel.h> +#include <sys/bootblock.h> + +#include <lib/libsa/stand.h> +#include <lib/libkern/libkern.h> +#include <machine/param.h> + +#include "boot.h" +#include "wdvar.h" + +#define WDCDELAY 100 +#define WDCNDELAY_RST 31000 * 10 + +static int __wdcwait_reset(struct wdc_channel *, int); +static char *mkident(uint8_t *, int); +static int wdcprobe(struct wdc_channel *); +static int wdc_wait_for_ready(struct wdc_channel *); +static int wdc_read_block(struct wdc_channel *, struct wdc_command *); +static int wdccommand(struct wdc_channel *, struct wdc_command *); +static int wdccommandext(struct wdc_channel *, struct wdc_command *); +static int _wdc_exec_identify(struct wdc_channel *, int, void *); + +static struct wdc_channel ch; + +/* + * Reset the controller. + */ +static int +__wdcwait_reset(struct wdc_channel *chp, int drv_mask) +{ + int timeout; + uint8_t st0, st1; + + /* wait for BSY to deassert */ + for (timeout = 0; timeout < WDCNDELAY_RST; timeout++) { + WDC_WRITE_REG(chp, wd_sdh, WDSD_IBM); /* master */ + delay(10); + st0 = WDC_READ_REG(chp, wd_status); + WDC_WRITE_REG(chp, wd_sdh, WDSD_IBM | 0x10); /* slave */ + delay(10); + st1 = WDC_READ_REG(chp, wd_status); + + if ((drv_mask & 0x01) == 0) { + /* no master */ + if ((drv_mask & 0x02) != 0 && (st1 & WDCS_BSY) == 0) { + /* No master, slave is ready, it's done */ + goto end; + } + } else if ((drv_mask & 0x02) == 0) { + /* no slave */ + if ((drv_mask & 0x01) != 0 && (st0 & WDCS_BSY) == 0) { + /* No slave, master is ready, it's done */ + goto end; + } + } else { + /* Wait for both master and slave to be ready */ + if ((st0 & WDCS_BSY) == 0 && (st1 & WDCS_BSY) == 0) { + goto end; + } + } + + delay(WDCDELAY); + } + + /* Reset timed out. Maybe it's because drv_mask was not right */ + if (st0 & WDCS_BSY) + drv_mask &= ~0x01; + if (st1 & WDCS_BSY) + drv_mask &= ~0x02; + + end: + return drv_mask; +} + +static char * +mkident(uint8_t *src, int len) +{ + static char local[40]; + uint8_t *end; + char *dst, *last; + + if (len > sizeof(local)) + len = sizeof(local); + dst = last = local; + end = src + len - 1; + + /* reserve space for '\0' */ + if (len < 2) + goto out; + /* skip leading white space */ + while (*src != '\0' && src < end && *src == ' ') + ++src; + /* copy string, omitting trailing white space */ + while (*src != '\0' && src < end) { + *dst++ = *src; + if (*src++ != ' ') + last = dst; + } + out: + *last = '\0'; + return local; +} + +/* Test to see controller with at last one attached drive is there. + * Returns a bit for each possible drive found (0x01 for drive 0, + * 0x02 for drive 1). + * Logic: + * - If a status register is at 0xff, assume there is no drive here + * (ISA has pull-up resistors). Similarly if the status register has + * the value we last wrote to the bus (for IDE interfaces without pullups). + * If no drive at all -> return. + * - reset the controller, wait for it to complete (may take up to 31s !). + * If timeout -> return. + */ +static int +wdcprobe(struct wdc_channel *chp) +{ + uint8_t st0, st1; + uint8_t drives = 0x03; + uint8_t drive, cl, ch; + uint8_t ident[DEV_BSIZE]; + + /* + * Sanity check to see if the wdc channel responds at all. + */ + WDC_WRITE_REG(chp, wd_sdh, WDSD_IBM); + delay(10); + st0 = WDC_READ_REG(chp, wd_status); + WDC_WRITE_REG(chp, wd_sdh, WDSD_IBM | 0x10); + delay(10); + st1 = WDC_READ_REG(chp, wd_status); + + if (st0 == 0xff || st0 == WDSD_IBM) + drives &= ~0x01; + if (st1 == 0xff || st1 == (WDSD_IBM | 0x10)) + drives &= ~0x02; + if (drives == 0) + return 0; + + if (!(st0 & WDCS_DRDY)) + drives &= ~0x01; + if (!(st1 & WDCS_DRDY)) + drives &= ~0x02; + if (drives == 0) + return 0; + + /* assert SRST, wait for reset to complete */ + WDC_WRITE_REG(chp, wd_sdh, WDSD_IBM); + delay(10); + WDC_WRITE_CTLREG(chp, wd_aux_ctlr, WDCTL_RST | WDCTL_IDS); + delay(1000); + WDC_WRITE_CTLREG(chp, wd_aux_ctlr, WDCTL_IDS); + delay(1000); + (void) WDC_READ_REG(chp, wd_error); + WDC_WRITE_CTLREG(chp, wd_aux_ctlr, WDCTL_4BIT); + delay(10); + + drives = __wdcwait_reset(chp, drives); + + /* if reset failed, there's nothing here */ + if (drives == 0) + return 0; + + /* + * Test presence of drives. First test register signatures looking for + * ATAPI devices. If it's not an ATAPI and reset said there may be + * something here assume it's ATA or OLD. Ghost will be killed later in + * attach routine. + */ + for (drive = 0; drive < 2; drive++) { + if ((drives & (1 << drive)) == 0) + continue; + + /* + * ATAPI device not support... + */ + WDC_WRITE_REG(chp, wd_sdh, WDSD_IBM | (drive << 4)); + cl = WDC_READ_REG(chp, wd_cyl_lo); + ch = WDC_READ_REG(chp, wd_cyl_hi); + if (cl == 0x14 && ch == 0xeb) { + drives &= ~(1 << drive); + continue; + } + + if (_wdc_exec_identify(chp, drive, ident) == 0) { + struct ataparams *prms = (struct ataparams *)ident; + char *model; + + model = + mkident(prms->atap_model, sizeof(prms->atap_model)); + printf("/dev/disk/ide/0/%s/0: <%s>\n", + (drive == 0) ? "master" : "slave", model); + } else + printf("/dev/disk/ide/0/%s/0: identify failed\n", + (drive == 0) ? "master" : "slave"); + } + return drives; +} + +/* + * Wait until the device is ready. + */ +int +wdc_wait_for_ready(struct wdc_channel *chp) +{ + u_int timeout; + + for (timeout = WDC_TIMEOUT; timeout > 0; --timeout) { + if ((WDC_READ_REG(chp, wd_status) & (WDCS_BSY | WDCS_DRDY)) + == WDCS_DRDY) + return 0; + } + return ENXIO; +} + +/* + * Read one block off the device. + */ +int +wdc_read_block(struct wdc_channel *chp, struct wdc_command *wd_c) +{ + int i; + uint16_t *ptr = (uint16_t *)wd_c->data; + + if (ptr == NULL) + return EIO; + + if (wd_c->r_command == WDCC_IDENTIFY) + for (i = wd_c->bcount; i > 0; i -= sizeof(uint16_t)) + *ptr++ = WDC_READ_DATA(chp); + else + for (i = wd_c->bcount; i > 0; i -= sizeof(uint16_t)) + *ptr++ = WDC_READ_DATA_STREAM(chp); + + return 0; +} + +/* + * Send a command to the device (CHS and LBA addressing). + */ +int +wdccommand(struct wdc_channel *chp, struct wdc_command *wd_c) +{ + +#if 0 + DPRINTF(("wdccommand(%d, %d, %d, %d, %d, %d)\n", + wd_c->drive, wd_c->r_command, wd_c->r_cyl, + wd_c->r_head, wd_c->r_sector, wd_c->bcount)); +#endif + + WDC_WRITE_REG(chp, wd_features, wd_c->r_features); + WDC_WRITE_REG(chp, wd_seccnt, wd_c->r_count); + WDC_WRITE_REG(chp, wd_sector, wd_c->r_sector); + WDC_WRITE_REG(chp, wd_cyl_lo, wd_c->r_cyl); + WDC_WRITE_REG(chp, wd_cyl_hi, wd_c->r_cyl >> 8); + WDC_WRITE_REG(chp, wd_sdh, + WDSD_IBM | (wd_c->drive << 4) | wd_c->r_head); + WDC_WRITE_REG(chp, wd_command, wd_c->r_command); + + if (wdc_wait_for_ready(chp) != 0) + return ENXIO; + + if (WDC_READ_REG(chp, wd_status) & WDCS_ERR) { + printf("/dev/disk/ide/0/%s/0: error %x\n", + (wd_c->drive == 0) ? "master" : "slave", + WDC_READ_REG(chp, wd_error)); + return ENXIO; + } + + return 0; +} + +/* + * Send a command to the device (LBA48 addressing). + */ +int +wdccommandext(struct wdc_channel *chp, struct wdc_command *wd_c) +{ + +#if 0 + DPRINTF(("%s(%d, %x, %" PRId64 ", %d)\n", __func__, + wd_c->drive, wd_c->r_command, + wd_c->r_blkno, wd_c->r_count)); +#endif + + /* Select drive, head, and addressing mode. */ + WDC_WRITE_REG(chp, wd_sdh, (wd_c->drive << 4) | WDSD_LBA); + + /* previous */ + WDC_WRITE_REG(chp, wd_features, 0); + WDC_WRITE_REG(chp, wd_seccnt, wd_c->r_count >> 8); + WDC_WRITE_REG(chp, wd_lba_hi, wd_c->r_blkno >> 40); + WDC_WRITE_REG(chp, wd_lba_mi, wd_c->r_blkno >> 32); + WDC_WRITE_REG(chp, wd_lba_lo, wd_c->r_blkno >> 24); + + /* current */ + WDC_WRITE_REG(chp, wd_features, 0); + WDC_WRITE_REG(chp, wd_seccnt, wd_c->r_count); + WDC_WRITE_REG(chp, wd_lba_hi, wd_c->r_blkno >> 16); + WDC_WRITE_REG(chp, wd_lba_mi, wd_c->r_blkno >> 8); + WDC_WRITE_REG(chp, wd_lba_lo, wd_c->r_blkno); + + /* Send command. */ + WDC_WRITE_REG(chp, wd_command, wd_c->r_command); + + if (wdc_wait_for_ready(chp) != 0) + return ENXIO; + + if (WDC_READ_REG(chp, wd_status) & WDCS_ERR) { + printf("/dev/disk/ide/0/%s/0: error %x\n", + (wd_c->drive == 0) ? "master" : "slave", + WDC_READ_REG(chp, wd_error)); + return ENXIO; + } + + return 0; +} + +static int +_wdc_exec_identify(struct wdc_channel *chp, int drive, void *data) +{ + struct wdc_command wd_c; + int error; + + memset(&wd_c, 0, sizeof(wd_c)); + + wd_c.drive = drive; + wd_c.r_command = WDCC_IDENTIFY; + wd_c.bcount = DEV_BSIZE; + wd_c.data = data; + + if ((error = wdccommand(chp, &wd_c)) != 0) + return error; + + return wdc_read_block(chp, &wd_c); +} + +/* + * Initialize the device. + */ +int +wdc_init(int addr) +{ + struct wdc_channel tmp; + int i; + + memset(&ch, 0, sizeof(ch)); + + /* set up cmd/ctl regsiters */ + tmp.c_cmdbase = addr; +#define WDC_ISA_AUXREG_OFFSET 0x206 + tmp.c_ctlbase = addr + WDC_ISA_AUXREG_OFFSET; + tmp.c_data = addr + wd_data; + for (i = 0; i < WDC_NPORTS; i++) + tmp.c_cmdreg[i] = tmp.c_cmdbase + i; + /* set up shadow registers */ + tmp.c_cmdreg[wd_status] = tmp.c_cmdreg[wd_command]; + tmp.c_cmdreg[wd_features] = tmp.c_cmdreg[wd_precomp]; + + if (wdcprobe(&tmp) == 0) + return ENXIO; + ch = tmp; + return 0; +} + +/* + * Issue 'device identify' command. + */ +int +wdc_exec_identify(struct wd_softc *wd, void *data) +{ + struct wdc_channel *chp; + + if (wd->sc_ctlr != 0) + return ENOTSUP; + if (ch.c_cmdbase == 0) + return ENOENT; + chp = &ch; + + return _wdc_exec_identify(chp, wd->sc_unit, data); +} + +/* + * Issue 'read' command. + */ +int +wdc_exec_read(struct wd_softc *wd, uint8_t cmd, daddr_t blkno, void *data) +{ + struct wdc_command wd_c; + struct wdc_channel *chp; + int error; + bool lba, lba48; + + if (wd->sc_ctlr != 0) + return ENOTSUP; + if (ch.c_cmdbase == 0) + return ENOENT; + chp = &ch; + + memset(&wd_c, 0, sizeof(wd_c)); + lba = false; + lba48 = false; + + wd_c.data = data; + wd_c.r_count = 1; + wd_c.r_features = 0; + wd_c.drive = wd->sc_unit; + wd_c.bcount = wd->sc_label.d_secsize; + + if ((wd->sc_flags & WDF_LBA48) != 0 && blkno > wd->sc_capacity28) + lba48 = true; + else if ((wd->sc_flags & WDF_LBA) != 0) + lba = true; + + if (lba48) { + /* LBA48 */ + wd_c.r_command = atacmd_to48(cmd); + wd_c.r_blkno = blkno; + } else if (lba) { + /* LBA */ + wd_c.r_command = cmd; + wd_c.r_sector = (blkno >> 0) & 0xff; + wd_c.r_cyl = (blkno >> 8) & 0xffff; + wd_c.r_head = (blkno >> 24) & 0x0f; + wd_c.r_head |= WDSD_LBA; + } else { + /* CHS */ + wd_c.r_command = cmd; + wd_c.r_sector = blkno % wd->sc_label.d_nsectors; + wd_c.r_sector++; /* Sectors begin with 1, not 0. */ + blkno /= wd->sc_label.d_nsectors; + wd_c.r_head = blkno % wd->sc_label.d_ntracks; + blkno /= wd->sc_label.d_ntracks; + wd_c.r_cyl = blkno; + wd_c.r_head |= WDSD_CHS; + } + + if (lba48) + error = wdccommandext(chp, &wd_c); + else + error = wdccommand(chp, &wd_c); + + if (error != 0) + return error; + + return wdc_read_block(chp, &wd_c); +} Index: src/sys/arch/bebox/stand/boot/wdvar.h diff -u /dev/null src/sys/arch/bebox/stand/boot/wdvar.h:1.1 --- /dev/null Thu Oct 14 06:50:44 2010 +++ src/sys/arch/bebox/stand/boot/wdvar.h Thu Oct 14 06:50:43 2010 @@ -0,0 +1,96 @@ +/* $NetBSD: wdvar.h,v 1.1 2010/10/14 06:50:43 kiyohara Exp $ */ + +/*- + * Copyright (c) 2003 The NetBSD Foundation, Inc. + * Copyright (c) 2001 Dynarc AB, Sweden. All rights reserved. + * + * This code is derived from software written by Anders Magnusson, + * ra...@ludd.luth.se + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _STAND_WDVAR_H +#define _STAND_WDVAR_H + +#include <dev/ic/wdcreg.h> +#include <dev/ata/atareg.h> + +#include <sys/disklabel.h> +#include <sys/bootblock.h> + +#define WDC_TIMEOUT 2000000 +#define WDC_NPORTS 8 /* XXX */ +#define WDC_NSHADOWREG 2 /* XXX */ + +struct wdc_channel { + int c_cmdbase; + int c_ctlbase; + int c_data; + int c_cmdreg[WDC_NPORTS + WDC_NSHADOWREG]; +}; + +#define WDC_READ_REG(chp, reg) inb((chp)->c_cmdreg[(reg)]) +#define WDC_WRITE_REG(chp, reg, val) outb((chp)->c_cmdreg[(reg)], (val)) +#define WDC_READ_CTLREG(chp, reg) inb((chp)->c_ctlbase) +#define WDC_WRITE_CTLREG(chp, reg, val) outb((chp)->c_ctlbase, (val)) +#define WDC_READ_DATA_STREAM(chp) inw((chp)->c_data) +#define WDC_READ_DATA(chp) inwrb((chp)->c_data) + +struct wd_softc { +#define WDF_LBA 0x0001 +#define WDF_LBA48 0x0002 + uint16_t sc_flags; + + u_int sc_part; + u_int sc_unit; + u_int sc_ctlr; + + uint64_t sc_capacity; + uint32_t sc_capacity28; + + struct ataparams sc_params; + struct disklabel sc_label; +}; + +struct wdc_command { + uint8_t drive; /* drive id */ + + uint8_t r_command; /* Parameters to upload to registers */ + uint8_t r_head; + uint16_t r_cyl; + uint8_t r_sector; + uint8_t r_count; + uint8_t r_features; + + uint16_t bcount; + void *data; + + uint64_t r_blkno; +}; + +int wdc_init(int); +int wdc_exec_identify(struct wd_softc *, void *); +int wdc_exec_read(struct wd_softc *, uint8_t, daddr_t, void *); + +#endif /* _STAND_WDVAR_H */