diff options
Diffstat (limited to 'sys')
31 files changed, 9336 insertions, 22 deletions
diff --git a/sys/arch/i386/conf/NEWATA b/sys/arch/i386/conf/NEWATA new file mode 100644 index 00000000000..6be490c7fac --- /dev/null +++ b/sys/arch/i386/conf/NEWATA @@ -0,0 +1,297 @@ +# $OpenBSD: NEWATA,v 1.1 1999/07/18 21:25:21 csapuntz Exp $ +# +# NEWATA - Generic 07/17/99 with new ATA stuff +# + +machine i386 # architecture, used by config; REQUIRED + +include "../../../conf/GENERIC" + +option I386_CPU # CPU classes; at least one is REQUIRED +option I486_CPU +option I586_CPU +option I686_CPU +option GPL_MATH_EMULATE # floating point emulation + +#option VM86 # Virtual 8086 emulation +#option USER_LDT # user-settable LDT; used by WINE +option XSERVER # diddle with console driver +option APERTURE # in-kernel aperture driver for XFree86 + +# Some BIOSes don't get the size of extended memory right. If you +# have a broken BIOS, uncomment the following and set the value +# properly for your system. +#option EXTMEM_SIZE=... # size of extended memory +#option DUMMY_NOPS # speed hack; recommended + +option COMPAT_SVR4 # binary compatibility with SVR4 +option COMPAT_IBCS2 # binary compatibility with SCO and ISC +option COMPAT_LINUX # binary compatibility with Linux +option COMPAT_FREEBSD # binary compatibility with FreeBSD +option COMPAT_BSDOS # binary compatibility with BSD/OS + +maxusers 32 # estimated number of users + +config bsd swap generic + +mainbus0 at root + +bios0 at mainbus0 +apm0 at bios0 + +isa0 at mainbus0 +eisa0 at mainbus0 +pci* at mainbus0 bus ? + +option PCIVERBOSE +option EISAVERBOSE +option PCMCIAVERBOSE + +pchb* at pci? dev ? function ? # PCI-Host bridges +ppb* at pci? dev ? function ? # PCI-PCI bridges +pci* at ppb? bus ? +pci* at pchb? bus ? +pcib* at pci? dev ? function ? # PCI-ISA bridges (do nothing) + +# ISA PCMCIA controllers +pcic0 at isa? port 0x3e0 iomem 0xd0000 iosiz 0x4000 +pcic1 at isa? port 0x3e2 iomem 0xd4000 iosiz 0x4000 + +# PCMCIA bus support +pcmcia* at pcic? controller ? socket ? + +npx0 at isa? port 0xf0 irq 13 # math coprocessor +isadma0 at isa? +isapnp0 at isa? + +#pc0 at isa? port 0x60 irq 1 # generic PC console device +vt0 at isa? port 0x60 irq 1 + +pcppi0 at isa? +sysbeep0 at pcppi? + +pccom0 at isa? port 0x3f8 irq 4 # standard PC serial ports +pccom1 at isa? port 0x2f8 irq 3 +pccom2 at isa? port 0x3e8 irq 5 +#pccom3 at isa? port 0x2e8 irq 9 # (conflicts with some video cards) +ast0 at isa? port 0x1a0 irq 5 # AST 4-port serial card +#ast1 at isa? port 0x2a0 irq 6 # 2nd AST 4-port serial card +pccom* at ast? slave ? +#boca0 at isa? port 0x100 irq 5 # BOCA 8-port serial cards +#pccom* at boca? slave ? +#rtfps0 at isa? port 0x1230 irq 10 # RT 4-port serial cards +#pccom* at rtfps? slave ? +pccom* at pcmcia? function ? # PCMCIA modems/serial ports +pccom* at isapnp? + +# option for using CD1400 handshaking for incoming data +# requires a special cable that exchanges the RTS and DTR lines +# options CY_HW_RTS +cy0 at isa? iomem 0xd4000 irq 12 # ISA cyclom serial card +#cy* at pci? # PCI cyclom serial card + +lpt0 at isa? port 0x378 irq 7 # standard PC parallel ports +lpt1 at isa? port 0x278 +lpt2 at isa? port 0x3bc + +lms0 at isa? port 0x23c irq 5 # Logitech bus mouse +lms1 at isa? port 0x238 irq 5 +mms0 at isa? port 0x23c irq 5 # Microsoft InPort mouse +mms1 at isa? port 0x238 irq 5 +pms0 at pckbd? irq 12 # PS/2 auxiliary port mouse + +bt0 at isa? port 0x330 irq ? drq ? # BusLogic [57]4X SCSI controllers +bt1 at isa? port 0x334 irq ? drq ? # BusLogic [57]4X SCSI controllers +bt2 at isa? port ? irq ? +scsibus* at bt? +aha0 at isa? port 0x330 irq ? drq ? # Adaptec 154[02] SCSI controllers +aha1 at isa? port 0x334 irq ? drq ? # Adaptec 154[02] SCSI controllers +aha* at isapnp? +scsibus* at aha? +ahb* at eisa? slot ? # Adaptec 174[024] SCSI controllers +scsibus* at ahb? +ahc0 at isa? port ? irq ? # Adaptec 284x SCSI controllers +ahc* at eisa? slot ? # Adaptec 274x, aic7770 SCSI controllers +ahc* at pci? dev ? function ? # Adaptec 2940 SCSI controllers +scsibus* at ahc? +isp* at pci? dev ? function ? # Qlogic ISP [12]0x0 SCSI/FibreChannel +scsibus* at isp? +aic0 at isa? port 0x340 irq 11 # Adaptec 152[02] SCSI controllers +aic* at pcmcia? function ? # PCMCIA based aic SCSI controllers +scsibus* at aic? +ncr* at pci? dev ? function ? # NCR 538XX SCSI controllers +scsibus* at ncr? +adv* at pci? dev ? function ? # AdvanSys 1200A/B and ULTRA SCSI +scsibus* at adv? +adw* at pci? dev ? function ? # AdvanSys ULTRA WIDE SCSI +scsibus* at adw? +sea0 at isa? iomem 0xc8000 irq 5 # Seagate ST0[12] SCSI controllers +scsibus* at sea? +uha0 at isa? port 0x330 irq ? drq ? # UltraStor [13]4f SCSI controllers +uha1 at isa? port 0x334 irq ? drq ? # UltraStor [13]4f SCSI controllers +uha* at eisa? slot ? # UltraStor 24f SCSI controllers +scsibus* at uha? +wds0 at isa? port 0x350 irq 15 drq 6 # WD7000 and TMC-7000 controllers +#wds1 at isa? port 0x358 irq 11 drq 5 +scsibus* at wds? + +sd* at scsibus? target ? lun ? # SCSI disk drives +st* at scsibus? target ? lun ? # SCSI tape drives +cd* at scsibus? target ? lun ? # SCSI CD-ROM drives +ch* at scsibus? target ? lun ? # SCSI autochangers +ss* at scsibus? target ? lun ? # SCSI scanners +uk* at scsibus? target ? lun ? # unknown SCSI + +fdc0 at isa? port 0x3f0 irq 6 drq 2 # standard PC floppy controllers +#fdc1 at isa? port 0x370 irq ? drq ? +fd* at fdc? drive ? + +#mcd0 at isa? port 0x300 irq 10 # Mitsumi CD-ROM drives + +# IDE and related devices +# PCI IDE controllers - see pciide(4) for supported hardware. +# The 0x0001 flag force the driver to use DMA, even if the driver doesn't know +# how to set up DMA modes for this chip. This may work, or may cause +# a machine hang with some controllers. +pciide* at pci ? dev ? function ? flags 0x0000 + +# ISA ST506, ESDI, and IDE controllers +# Use flags 0x01 if you want to try to use 32bits data I/O (the driver will +# fall back to 16bits I/O if 32bits I/O are not functionnal). +# Some controllers pass the initial 32bit test, but will fail later. +wdc0 at isa? port 0x1f0 irq 14 flags 0x00 +wdc1 at isa? port 0x170 irq 15 flags 0x00 + +# IDE drives +# Flags are used only with controllers that support DMA operations +# and mode settings (e.g. some pciide controllers) +# The lowest order four bits (rightmost digit) of the flags define the PIO +# mode to use, the next set of four bits the DMA mode and the third set the +# UltraDMA mode. For each set of four bits, the 3 lower bits define the mode +# to use, and the last bit must be 1 for this setting to be used. +# For DMA and UDMA, 0xf (1111) means 'disable'. +# 0x0fac means 'use PIO mode 4, DMA mode 2, disable UltraDMA'. +# (0xc=1100, 0xa=1010, 0xf=1111) +# 0x0000 means "use whatever the drive claims to support". +wd* at wdc? channel ? drive ? flags 0x0000 +wd* at pciide? channel ? drive ? flags 0x0000 + +atapiscsi* at wdc? channel ? +atapiscsi* at pciide? channel ? +scsibus* at atapiscsi? + +#wdc0 at isa? port 0x1f0 irq 14 # ST506, ESDI, and IDE controllers +#wdc1 at isa? port 0x170 irq 15 +#wd* at wdc? drive ? + +#atapibus* at wdc? +#acd* at atapibus? drive? + +wt0 at isa? port 0x308 irq 5 drq 1 # Archive and Wangtek QIC tape drives + +we0 at isa? port 0x280 iomem 0xd0000 irq 9 # WD/SMC 80x3 ethernet +we1 at isa? port 0x300 iomem 0xcc000 irq 10 # +we* at isapnp? +ec0 at isa? port 0x250 iomem 0xd8000 irq 9 # 3C503 ethernet +ne0 at isa? port 0x240 irq 9 # NE[12]000 ethernet +ne1 at isa? port 0x300 irq 10 # NE[12]000 ethernet +ne2 at isa? port 0x280 irq 9 # NE[12]000 ethernet +ne* at isapnp? # NE[12]000 PnP ethernet +eg0 at isa? port 0x310 irq 5 # 3C505/Etherlink+ ethernet +el0 at isa? port 0x300 irq 9 # 3C501 ethernet +ep0 at isa? port ? irq ? # 3C509 ethernet +ep* at isapnp? # 3C509 PnP ethernet +ep* at isa? port ? irq ? # 3C509 ethernet +ie0 at isa? port 0x360 iomem 0xd0000 irq 7 # StarLAN and 3C507 +ie1 at isa? port 0x300 irq 10 # EtherExpress +le0 at isa? port 0x360 irq 15 drq 6 # IsoLan, NE2100, and DEPCA +ex0 at isa? port 0x320 irq 5 # Intel EtherExpress PRO/10 +ep0 at eisa? slot ? +ep* at eisa? slot ? # 3C579 ethernet +fea* at eisa? slot ? # DEC DEFEA FDDI +lmc* at pci? dev ? function ? # Lan Media Corp SSI/T3/HSSI +le* at pci? dev ? function ? # PCnet-PCI based ethernet +le* at isapnp? +de* at pci? dev ? function ? # DC21X4X-based ethernet +fxp* at pci? dev ? function ? # EtherExpress 10/100B ethernet +ne* at pci? dev ? function ? # NE2000-compat ethernet +ep0 at pci? dev ? function ? # 3C59x ethernet +ep* at pci? dev ? function ? # 3C59x ethernet +ne* at pcmcia? function ? # PCMCIA based NE2000 ethernet +ep* at pcmcia? function ? # PCMCIA based 3C5xx ethernet +sm* at pcmcia? function ? # PCMCIA based sm ethernet +xe* at pcmcia? function ? # Xircom ethernet +wi* at pcmcia? function ? # WaveLAN IEEE 802.11 +fpa* at pci? dev ? function ? # DEC DEFPA FDDI +xl* at pci? dev ? function ? # 3c9xx ethernet +rl* at pci? dev ? function ? # RealTek 81[23]9 ethernet +tx* at pci? dev ? function ? # SMC 83C170 EPIC ethernet +tl* at pci? dev ? function ? # Compaq Thunderlan ethernet +mx* at pci? dev ? function ? # Macronix PMAC ethernet +vr* at pci? dev ? function ? # VIA Rhine ethernet +pn* at pci? dev ? function ? # Lite-On PNIC ethernet +wb* at pci? dev ? function ? # Winbond W89C840F ethernet + +# Media Independent Interface (mii) drivers +#exphy* at mii? phy ? # 3Com internal PHYs +inphy* at mii? phy ? # Intel 82555 PHYs +icsphy* at mii? phy ? # ICS 1890 PHYs +#lxtphy* at mii? phy ? # Level1 LXT970 PHYs +nsphy* at mii? phy ? # NS and compatible PHYs +#qsphy* at mii? phy ? # Quality Semi QS6612 PHYs +#sqphy* at mii? phy ? # Seeq 8x220 PHYs +rlphy* at mii? phy ? # RealTek 8139 internal PHYs +#mtdphy* at mii? phy ? # Myson MTD972 PHYs +ukphy* at mii? phy ? # "unknown" PHYs + +pss0 at isa? port 0x220 irq 7 drq 6 # Personal Sound System +sp0 at pss0 port 0x530 irq 10 drq 0 # sound port driver + +eap* at pci? dev ? function ? # Ensoniq AudioPCI S5016 +sv* at pci? dev ? function ? # S3 SonicVibes (S3 617) +sb0 at isa? port 0x220 irq 7 drq 1 # SoundBlaster +sb* at isapnp? +wss0 at isa? port 0x530 irq 10 drq 0 # Windows Sound System +wss* at isapnp? +pas0 at isa? port 0x220 irq 7 drq 1 # ProAudio Spectrum +gus0 at isa? port 0x220 irq 7 drq 1 drq2 6 # Gravis UltraSound (drq2 is record drq) +ym* at isapnp? + +# OPL[23] FM syntheziers +#opl0 at isa? port 0x388 # use only if not attached to sound card +opl* at sb? + +# MIDI support +midi* at pcppi? # MIDI interface to the PC speaker +midi* at sb? # SB MPU401 port +midi* at opl? # OPL FM synth + +# The spkr driver provides a simple tone interface to the built in speaker. +#spkr0 at pcppi? # PC speaker + +#Audio Support +audio* at sb? +audio* at gus? +audio* at pas? +audio* at sp? +audio* at wss? +audio* at ym? +audio* at eap? +audio* at sv? + +bktr0 at pci? dev ? function ? + +# Joystick driver. Probe is a little strange; add only if you have one. +#joy0 at isa? port 0x201 +joy* at isapnp? + +#wdt0 at pci? dev ? function ? # Ind Computer Source PCI-WDT50x driver + +# crypto support +#aeon* at pci? dev ? function ? # Aeon crypto card + +pseudo-device pctr 1 +pseudo-device sequencer 1 +#pseudo-device raid 4 # RAIDframe disk driver + +#option DEBUG_ISAPNP diff --git a/sys/arch/i386/conf/files.i386 b/sys/arch/i386/conf/files.i386 index 07009ea332a..bafd242ad6f 100644 --- a/sys/arch/i386/conf/files.i386 +++ b/sys/arch/i386/conf/files.i386 @@ -1,4 +1,4 @@ -# $OpenBSD: files.i386,v 1.51 1999/01/21 08:26:48 niklas Exp $ +# $OpenBSD: files.i386,v 1.52 1999/07/18 21:25:21 csapuntz Exp $ # $NetBSD: files.i386,v 1.73 1996/05/07 00:58:36 thorpej Exp $ # # new style config file for i386 architecture @@ -62,8 +62,13 @@ major {rd = 17} # Machine-independent ATAPI drivers # +#Comment out this include below if you're using the new ATA stuff include "../../../dev/atapi/files.atapi" +#Start Uncomment for new ATA stuff +#include "../../../dev/atapiscsi/files.atapiscsi" +#include "../../../dev/ata/files.ata" +#End Uncomment for new ATA stuff # # System bus types @@ -84,6 +89,9 @@ file arch/i386/i386/mainbus.c mainbus include "../../../dev/pci/files.pci" file arch/i386/pci/pci_machdep.c pci +#Start Uncomment for new ATA stuff +#file arch/i386/pci/pciide_machdep.c pciide +#End Uncomment for new ATA stuff file arch/i386/pci/pci_compat.c pci # XXX compatibility # PCI-Host bridge chipsets diff --git a/sys/arch/i386/pci/pciide_machdep.c b/sys/arch/i386/pci/pciide_machdep.c new file mode 100644 index 00000000000..f65a038c0a9 --- /dev/null +++ b/sys/arch/i386/pci/pciide_machdep.c @@ -0,0 +1,75 @@ +/* $OpenBSD: pciide_machdep.c,v 1.1 1999/07/18 21:25:21 csapuntz Exp $ */ +/* $NetBSD: pciide_machdep.c,v 1.2 1999/02/19 18:01:27 mycroft Exp $ */ + +/* + * Copyright (c) 1998 Christopher G. Demetriou. All rights reserved. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Christopher G. Demetriou + * for the NetBSD Project. + * 4. 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. + */ + +/* + * PCI IDE controller driver (i386 machine-dependent portion). + * + * Author: Christopher G. Demetriou, March 2, 1998 (derived from NetBSD + * sys/dev/pci/ppb.c, revision 1.16). + * + * See "PCI IDE Controller Specification, Revision 1.0 3/4/94" from the + * PCI SIG. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> + +#include <dev/pci/pcireg.h> +#include <dev/pci/pcivar.h> +#include <dev/pci/pciidereg.h> +#include <dev/pci/pciidevar.h> + +#include <dev/isa/isavar.h> + +#define PCIIDE_CHANNEL_NAME(chan) ((chan) == 0 ? "primary" : "secondary") + +void * +pciide_machdep_compat_intr_establish(dev, pa, chan, func, arg) + struct device *dev; + struct pci_attach_args *pa; + int chan; + int (*func) __P((void *)); + void *arg; +{ + int irq; + void *cookie; + + irq = PCIIDE_COMPAT_IRQ(chan); + cookie = isa_intr_establish(NULL, irq, IST_EDGE, IPL_BIO, func, arg, dev->dv_xname); + if (cookie == NULL) + return (NULL); + printf("%s: %s channel interrupting at irq %d\n", dev->dv_xname, + PCIIDE_CHANNEL_NAME(chan), irq); + return (cookie); +} diff --git a/sys/conf/files b/sys/conf/files index 7b994ae2f62..aefbe5bde73 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1,4 +1,4 @@ -# $OpenBSD: files,v 1.120 1999/07/06 20:17:52 cmetz Exp $ +# $OpenBSD: files,v 1.121 1999/07/18 21:25:20 csapuntz Exp $ # $NetBSD: files,v 1.87 1996/05/19 17:17:50 jonathan Exp $ # @(#)files.newconf 7.5 (Berkeley) 5/10/93 @@ -48,6 +48,15 @@ define rtl80x9 # RealTek 8019/8029 NE2000-compatible # common file (e.g. vga) definitions. define wsconsdev {[console = -1]} +#Start uncommenting for new ATA stuff +#define wdc_base +#define ata {[channel = -1], [drive = -1]} +#define atapi {[channel = -1]} +## Common code for ESDI/IDE/etc. controllers +#device wdc: ata, atapi, wdc_base +#file dev/ic/wdc.c wdc_base +#End uncommenting for new ATA stuff + # "Chipset" drivers. These are the bus-independent routines which # contain the cfdrivers. Attachments are provided by files.<bus> diff --git a/sys/dev/ata/ata.c b/sys/dev/ata/ata.c new file mode 100644 index 00000000000..08edbc41aa7 --- /dev/null +++ b/sys/dev/ata/ata.c @@ -0,0 +1,199 @@ +/* $OpenBSD: ata.c,v 1.1 1999/07/18 21:25:17 csapuntz Exp $ */ +/* $NetBSD: ata.c,v 1.9 1999/04/15 09:41:09 bouyer Exp $ */ +/* + * Copyright (c) 1998 Manuel Bouyer. All rights reserved. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Manuel Bouyer. + * 4. 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 WDCDEBUG +#define WDCDEBUG +#endif /* WDCDEBUG */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <sys/malloc.h> +#include <sys/device.h> +#include <sys/syslog.h> + +#include <dev/ic/wdcreg.h> +#include <dev/ata/atareg.h> +#include <dev/ata/atavar.h> + +#define DEBUG_FUNCS 0x08 +#define DEBUG_PROBE 0x10 +#ifdef WDCDEBUG +extern int wdcdebug_mask; /* init'ed in wdc.c */ +#define WDCDEBUG_PRINT(args, level) \ + if (wdcdebug_mask & (level)) \ + printf args +#else +#define WDCDEBUG_PRINT(args, level) +#endif + +/* Get the disk's parameters */ +int +ata_get_params(drvp, flags, prms) + struct ata_drive_datas *drvp; + u_int8_t flags; + struct ataparams *prms; +{ + char tb[DEV_BSIZE]; + struct wdc_command wdc_c; + +#if BYTE_ORDER == LITTLE_ENDIAN + int i; + u_int16_t *p; +#endif + + WDCDEBUG_PRINT(("wdc_ata_get_parms\n"), DEBUG_FUNCS); + + memset(tb, 0, DEV_BSIZE); + memset(prms, 0, sizeof(struct ataparams)); + memset(&wdc_c, 0, sizeof(struct wdc_command)); + + if (drvp->drive_flags & DRIVE_ATA) { + wdc_c.r_command = WDCC_IDENTIFY; + wdc_c.r_st_bmask = WDCS_DRDY; + wdc_c.r_st_pmask = WDCS_DRQ; + wdc_c.timeout = 1000; /* 1s */ + } else if (drvp->drive_flags & DRIVE_ATAPI) { + wdc_c.r_command = ATAPI_IDENTIFY_DEVICE; + wdc_c.r_st_bmask = 0; + wdc_c.r_st_pmask = WDCS_DRQ; + wdc_c.timeout = 10000; /* 10s */ + } else { + return CMD_ERR; + } + wdc_c.flags = AT_READ | flags; + wdc_c.data = tb; + wdc_c.bcount = DEV_BSIZE; + + { + int ret; + if ((ret = wdc_exec_command(drvp, &wdc_c)) != WDC_COMPLETE) { + printf ("WDC_EXEC_COMMAND: %d\n"); + return CMD_AGAIN; + } + } + + if (wdc_c.flags & (AT_ERROR | AT_TIMEOU | AT_DF)) { + return CMD_ERR; + } else { + /* Read in parameter block. */ + memcpy(prms, tb, sizeof(struct ataparams)); +#if BYTE_ORDER == LITTLE_ENDIAN + /* + * Shuffle string byte order. + * ATAPI Mitsumi and NEC drives don't need this. + */ + if ((prms->atap_config & WDC_CFG_ATAPI_MASK) == + WDC_CFG_ATAPI && + ((prms->atap_model[0] == 'N' && + prms->atap_model[1] == 'E') || + (prms->atap_model[0] == 'F' && + prms->atap_model[1] == 'X'))) + return 0; + for (i = 0; i < sizeof(prms->atap_model); i += 2) { + p = (u_short *)(prms->atap_model + i); + *p = ntohs(*p); + } + for (i = 0; i < sizeof(prms->atap_serial); i += 2) { + p = (u_short *)(prms->atap_serial + i); + *p = ntohs(*p); + } + for (i = 0; i < sizeof(prms->atap_revision); i += 2) { + p = (u_short *)(prms->atap_revision + i); + *p = ntohs(*p); + } +#endif + return CMD_OK; + } +} + +int +ata_set_mode(drvp, mode, flags) + struct ata_drive_datas *drvp; + u_int8_t mode; + u_int8_t flags; +{ + struct wdc_command wdc_c; + + WDCDEBUG_PRINT(("wdc_ata_set_mode=0x%x\n", mode), DEBUG_FUNCS); + memset(&wdc_c, 0, sizeof(struct wdc_command)); + + wdc_c.r_command = SET_FEATURES; + wdc_c.r_st_bmask = 0; + wdc_c.r_st_pmask = 0; + wdc_c.r_precomp = WDSF_SET_MODE; + wdc_c.r_count = mode; + wdc_c.flags = AT_READ | flags; + wdc_c.timeout = 1000; /* 1s */ + if (wdc_exec_command(drvp, &wdc_c) != WDC_COMPLETE) + return CMD_AGAIN; + if (wdc_c.flags & (AT_ERROR | AT_TIMEOU | AT_DF)) { + return CMD_ERR; + } + return CMD_OK; +} + +void +ata_perror(drvp, errno, buf) + struct ata_drive_datas *drvp; + int errno; + char *buf; +{ + static char *errstr0_3[] = {"address mark not found", + "track 0 not found", "aborted command", "media change requested", + "id not found", "media changed", "uncorrectable data error", + "bad block detected"}; + static char *errstr4_5[] = {"", + "no media/write protected", "aborted command", + "media change requested", "id not found", "media changed", + "uncorrectable data error", "interface CRC error"}; + char **errstr; + int i; + char *sep = ""; + + if (drvp->ata_vers >= 4) + errstr = errstr4_5; + else + errstr = errstr0_3; + + if (errno == 0) { + sprintf(buf, "error not notified"); + } + + for (i = 0; i < 8; i++) { + if (errno & (1 << i)) { + buf += sprintf(buf, "%s %s", sep, errstr[i]); + sep = ","; + } + } +} diff --git a/sys/dev/ata/ata_wdc.c b/sys/dev/ata/ata_wdc.c new file mode 100644 index 00000000000..412a04ef898 --- /dev/null +++ b/sys/dev/ata/ata_wdc.c @@ -0,0 +1,802 @@ +/* $OpenBSD: ata_wdc.c,v 1.1 1999/07/18 21:25:17 csapuntz Exp $ */ +/* $NetBSD: ata_wdc.c,v 1.19 1999/04/01 21:46:28 bouyer Exp $ */ + +/* + * Copyright (c) 1998 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + * + */ + +/*- + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Charles M. Hannum, by Onno van der Linden and 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 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. + */ + +#ifndef WDCDEBUG +#define WDCDEBUG +#endif /* WDCDEBUG */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <sys/buf.h> +#include <sys/malloc.h> +#include <sys/device.h> +#include <sys/disklabel.h> +#include <sys/syslog.h> +#include <sys/proc.h> + +#include <machine/intr.h> +#include <machine/bus.h> +#ifndef __BUS_SPACE_HAS_STREAM_METHODS +#define bus_space_write_multi_stream_2 bus_space_write_multi_2 +#define bus_space_write_multi_stream_4 bus_space_write_multi_4 +#define bus_space_read_multi_stream_2 bus_space_read_multi_2 +#define bus_space_read_multi_stream_4 bus_space_read_multi_4 +#endif /* __BUS_SPACE_HAS_STREAM_METHODS */ + +#include <dev/ata/atareg.h> +#include <dev/ata/atavar.h> +#include <dev/ic/wdcreg.h> +#include <dev/ic/wdcvar.h> +#include <dev/ata/wdvar.h> + +#define DEBUG_INTR 0x01 +#define DEBUG_XFERS 0x02 +#define DEBUG_STATUS 0x04 +#define DEBUG_FUNCS 0x08 +#define DEBUG_PROBE 0x10 +#ifdef WDCDEBUG +int wdcdebug_wd_mask = 0; +#define WDCDEBUG_PRINT(args, level) \ + if (wdcdebug_wd_mask & (level)) \ + printf args +#else +#define WDCDEBUG_PRINT(args, level) +#endif + +#define ATA_DELAY 10000 /* 10s for a drive I/O */ + +#ifdef __OpenBSD__ +struct cfdriver wdc_cd = { + NULL, "wdc", DV_DULL +}; +#endif + +void wdc_ata_bio_start __P((struct channel_softc *,struct wdc_xfer *)); +int wdc_ata_bio_intr __P((struct channel_softc *, struct wdc_xfer *, int)); +void wdc_ata_bio_done __P((struct channel_softc *, struct wdc_xfer *)); +int wdc_ata_ctrl_intr __P((struct channel_softc *, struct wdc_xfer *, int)); +int wdc_ata_err __P((struct ata_drive_datas *, struct ata_bio *)); +#define WDC_ATA_NOERR 0x00 /* Drive doesn't report an error */ +#define WDC_ATA_RECOV 0x01 /* There was a recovered error */ +#define WDC_ATA_ERR 0x02 /* Drive reports an error */ + +/* + * Handle block I/O operation. Return WDC_COMPLETE, WDC_QUEUED, or + * WDC_TRY_AGAIN. Must be called at splio(). + */ +int +wdc_ata_bio(drvp, ata_bio) + struct ata_drive_datas *drvp; + struct ata_bio *ata_bio; +{ + struct wdc_xfer *xfer; + struct channel_softc *chp = drvp->chnl_softc; + + xfer = wdc_get_xfer(WDC_NOSLEEP); + if (xfer == NULL) + return WDC_TRY_AGAIN; + if (ata_bio->flags & ATA_POLL) + xfer->c_flags |= C_POLL; + if ((drvp->drive_flags & (DRIVE_DMA | DRIVE_UDMA)) && + (ata_bio->flags & ATA_SINGLE) == 0) + xfer->c_flags |= C_DMA; + xfer->drive = drvp->drive; + xfer->cmd = ata_bio; + xfer->databuf = ata_bio->databuf; + xfer->c_bcount = ata_bio->bcount; + xfer->c_start = wdc_ata_bio_start; + xfer->c_intr = wdc_ata_bio_intr; + wdc_exec_xfer(chp, xfer); + return (ata_bio->flags & ATA_ITSDONE) ? WDC_COMPLETE : WDC_QUEUED; +} + +void +wdc_ata_bio_start(chp, xfer) + struct channel_softc *chp; + struct wdc_xfer *xfer; +{ + struct ata_bio *ata_bio = xfer->cmd; + struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive]; + u_int16_t cyl; + u_int8_t head, sect, cmd = 0; + int nblks; + int ata_delay; + int dma_flags = 0; + + WDCDEBUG_PRINT(("wdc_ata_bio_start %s:%d:%d\n", + chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive), + DEBUG_XFERS); + + /* Do control operations specially. */ + if (drvp->state < READY) { + /* + * Actually, we want to be careful not to mess with the control + * state if the device is currently busy, but we can assume + * that we never get to this point if that's the case. + */ + /* at this point, we should only be in RECAL state */ + if (drvp->state != RECAL) { + printf("%s:%d:%d: bad state %d in wdc_ata_bio_start\n", + chp->wdc->sc_dev.dv_xname, chp->channel, + xfer->drive, drvp->state); + panic("wdc_ata_bio_start: bad state"); + } + xfer->c_intr = wdc_ata_ctrl_intr; + bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sdh, + WDSD_IBM | (xfer->drive << 4)); + if (wdcwait(chp, WDCS_DRDY, WDCS_DRDY, ATA_DELAY) != 0) + goto timeout; + wdccommandshort(chp, xfer->drive, WDCC_RECAL); + drvp->state = RECAL_WAIT; + if ((ata_bio->flags & ATA_POLL) == 0) { + chp->ch_flags |= WDCF_IRQ_WAIT; + timeout(wdctimeout, chp, + ATA_DELAY / 1000 * hz); + } else { + /* Wait for at last 400ns for status bit to be valid */ + DELAY(1); + wdc_ata_ctrl_intr(chp, xfer, 0); + } + return; + } + + if (xfer->c_flags & C_DMA) { + dma_flags = (ata_bio->flags & ATA_READ) ? WDC_DMA_READ : 0; + dma_flags |= (ata_bio->flags & ATA_POLL) ? WDC_DMA_POLL : 0; + } + if (ata_bio->flags & ATA_SINGLE) + ata_delay = ATA_DELAY; + else + ata_delay = ATA_DELAY; +again: + /* + * + * When starting a multi-sector transfer, or doing single-sector + * transfers... + */ + if (xfer->c_skip == 0 || (ata_bio->flags & ATA_SINGLE) != 0) { + if (ata_bio->flags & ATA_SINGLE) + nblks = 1; + else + nblks = xfer->c_bcount / ata_bio->lp->d_secsize; + /* Check for bad sectors and adjust transfer, if necessary. */ + if ((ata_bio->lp->d_flags & D_BADSECT) != 0) { + long blkdiff; + int i; + for (i = 0; (blkdiff = ata_bio->badsect[i]) != -1; + i++) { + blkdiff -= ata_bio->blkno; + if (blkdiff < 0) + continue; + if (blkdiff == 0) { + /* Replace current block of transfer. */ + ata_bio->blkno = + ata_bio->lp->d_secperunit - + ata_bio->lp->d_nsectors - i - 1; + } + if (blkdiff < nblks) { + /* Bad block inside transfer. */ + ata_bio->flags |= ATA_SINGLE; + nblks = 1; + } + break; + } + /* Transfer is okay now. */ + } + if (ata_bio->flags & ATA_LBA) { + sect = (ata_bio->blkno >> 0) & 0xff; + cyl = (ata_bio->blkno >> 8) & 0xffff; + head = (ata_bio->blkno >> 24) & 0x0f; + head |= WDSD_LBA; + } else { + int blkno = ata_bio->blkno; + sect = blkno % ata_bio->lp->d_nsectors; + sect++; /* Sectors begin with 1, not 0. */ + blkno /= ata_bio->lp->d_nsectors; + head = blkno % ata_bio->lp->d_ntracks; + blkno /= ata_bio->lp->d_ntracks; + cyl = blkno; + head |= WDSD_CHS; + } + if (xfer->c_flags & C_DMA) { + ata_bio->nblks = nblks; + ata_bio->nbytes = xfer->c_bcount; + cmd = (ata_bio->flags & ATA_READ) ? + WDCC_READDMA : WDCC_WRITEDMA; + nblks = ata_bio->nblks; + /* Init the DMA channel. */ + if ((*chp->wdc->dma_init)(chp->wdc->dma_arg, + chp->channel, xfer->drive, + (char *)xfer->databuf + xfer->c_skip, + ata_bio->nbytes, dma_flags) != 0) { + ata_bio->error = ERR_DMA; + ata_bio->r_error = 0; + wdc_ata_bio_done(chp, xfer); + return; + } + /* Initiate command */ + bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sdh, + WDSD_IBM | (xfer->drive << 4)); + if (wait_for_ready(chp, ata_delay) < 0) + goto timeout; + wdccommand(chp, xfer->drive, cmd, cyl, + head, sect, nblks, 0); + /* start the DMA channel */ + (*chp->wdc->dma_start)(chp->wdc->dma_arg, + chp->channel, xfer->drive, dma_flags); + /* wait for irq */ + goto intr; + } /* else not DMA */ + ata_bio->nblks = min(nblks, ata_bio->multi); + ata_bio->nbytes = ata_bio->nblks * ata_bio->lp->d_secsize; + if (ata_bio->nblks > 1 && (ata_bio->flags & ATA_SINGLE) == 0) { + cmd = (ata_bio->flags & ATA_READ) ? + WDCC_READMULTI : WDCC_WRITEMULTI; + } else { + cmd = (ata_bio->flags & ATA_READ) ? + WDCC_READ : WDCC_WRITE; + } + /* Initiate command! */ + bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sdh, + WDSD_IBM | (xfer->drive << 4)); + if (wait_for_ready(chp, ata_delay) < 0) + goto timeout; + wdccommand(chp, xfer->drive, cmd, cyl, + head, sect, nblks, + (ata_bio->lp->d_type == DTYPE_ST506) ? + ata_bio->lp->d_precompcyl / 4 : 0); + } else if (ata_bio->nblks > 1) { + /* The number of blocks in the last stretch may be smaller. */ + nblks = xfer->c_bcount / ata_bio->lp->d_secsize; + if (ata_bio->nblks > nblks) { + ata_bio->nblks = nblks; + ata_bio->nbytes = xfer->c_bcount; + } + } + /* If this was a write and not using DMA, push the data. */ + if ((ata_bio->flags & ATA_READ) == 0) { + if (wait_for_drq(chp, ata_delay) != 0) { + printf("%s:%d:%d: timeout waiting for DRQ, " + "st=0x%02x, err=0x%02x\n", + chp->wdc->sc_dev.dv_xname, chp->channel, + xfer->drive, chp->ch_status, chp->ch_error); + if (wdc_ata_err(drvp, ata_bio) != WDC_ATA_ERR) + ata_bio->error = TIMEOUT; + wdc_ata_bio_done(chp, xfer); + return; + } + if (wdc_ata_err(drvp, ata_bio) == WDC_ATA_ERR) { + wdc_ata_bio_done(chp, xfer); + return; + } + if ((chp->wdc->cap & WDC_CAPABILITY_ATA_NOSTREAM)) { + if (drvp->drive_flags & DRIVE_CAP32) { + bus_space_write_multi_4(chp->data32iot, + chp->data32ioh, 0, + (u_int32_t *)((char *)xfer->databuf + + xfer->c_skip), + ata_bio->nbytes >> 2); + } else { + bus_space_write_multi_2(chp->cmd_iot, + chp->cmd_ioh, wd_data, + (u_int16_t *)((char *)xfer->databuf + + xfer->c_skip), + ata_bio->nbytes >> 1); + } + } else { + if (drvp->drive_flags & DRIVE_CAP32) { + bus_space_write_multi_stream_4(chp->data32iot, + chp->data32ioh, 0, + (u_int32_t *)((char *)xfer->databuf + + xfer->c_skip), + ata_bio->nbytes >> 2); + } else { + bus_space_write_multi_stream_2(chp->cmd_iot, + chp->cmd_ioh, wd_data, + (u_int16_t *)((char *)xfer->databuf + + xfer->c_skip), + ata_bio->nbytes >> 1); + } + } + } + +intr: /* Wait for IRQ (either real or polled) */ + if ((ata_bio->flags & ATA_POLL) == 0) { + chp->ch_flags |= WDCF_IRQ_WAIT; + timeout(wdctimeout, chp, ata_delay / 1000 * hz); + } else { + /* Wait for at last 400ns for status bit to be valid */ + delay(1); + wdc_ata_bio_intr(chp, xfer, 0); + if ((ata_bio->flags & ATA_ITSDONE) == 0) + goto again; + } + return; +timeout: + printf("%s:%d:%d: not ready, st=0x%02x, err=0x%02x\n", + chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive, + chp->ch_status, chp->ch_error); + if (wdc_ata_err(drvp, ata_bio) != WDC_ATA_ERR) + ata_bio->error = TIMEOUT; + wdc_ata_bio_done(chp, xfer); + return; +} + +int +wdc_ata_bio_intr(chp, xfer, irq) + struct channel_softc *chp; + struct wdc_xfer *xfer; + int irq; +{ + struct ata_bio *ata_bio = xfer->cmd; + struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive]; + int drv_err; + int dma_flags = 0; + + WDCDEBUG_PRINT(("wdc_ata_bio_intr %s:%d:%d\n", + chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive), + DEBUG_INTR | DEBUG_XFERS); + + + /* Is it not a transfer, but a control operation? */ + if (drvp->state < READY) { + printf("%s:%d:%d: bad state %d in wdc_ata_bio_intr\n", + chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive, + drvp->state); + panic("wdc_ata_bio_intr: bad state\n"); + } + + if (xfer->c_flags & C_DMA) { + dma_flags = (ata_bio->flags & ATA_READ) ? WDC_DMA_READ : 0; + dma_flags |= (ata_bio->flags & ATA_POLL) ? WDC_DMA_POLL : 0; + } + + /* Ack interrupt done by wait_for_unbusy */ + if (wait_for_unbusy(chp, + (irq == 0) ? ATA_DELAY : 0) < 0) { + if (irq && (xfer->c_flags & C_TIMEOU) == 0) + return 0; /* IRQ was not for us */ + printf("%s:%d:%d: device timeout, c_bcount=%d, c_skip%d\n", + chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive, + xfer->c_bcount, xfer->c_skip); + /* if we were using DMA, turn off DMA channel */ + if (xfer->c_flags & C_DMA) { + (*chp->wdc->dma_finish)(chp->wdc->dma_arg, + chp->channel, xfer->drive, dma_flags); + drvp->n_dmaerrs++; + } + ata_bio->error = TIMEOUT; + wdc_ata_bio_done(chp, xfer); + return 1; + } + + drv_err = wdc_ata_err(drvp, ata_bio); + + /* If we were using DMA, Turn off the DMA channel and check for error */ + if (xfer->c_flags & C_DMA) { + if (ata_bio->flags & ATA_POLL) { + /* + * IDE drives deassert WDCS_BSY before transfer is + * complete when using DMA. Polling for DRQ to deassert + * is not enouth DRQ is not required to be + * asserted for DMA transfers, so poll for DRDY. + */ + if (wdcwait(chp, WDCS_DRDY | WDCS_DRQ, WDCS_DRDY, + ATA_DELAY) < 0) { + printf("%s:%d:%d: polled transfer timed out " + "(st=0x%x)\n", chp->wdc->sc_dev.dv_xname, + chp->channel, xfer->drive, chp->ch_status); + ata_bio->error = TIMEOUT; + drv_err = WDC_ATA_ERR; + } + } + if ((*chp->wdc->dma_finish)(chp->wdc->dma_arg, + chp->channel, xfer->drive, dma_flags) != 0) { + if (drv_err != WDC_ATA_ERR) { + ata_bio->error = ERR_DMA; + drv_err = WDC_ATA_ERR; + } + } + if (chp->ch_status & WDCS_DRQ) { + if (drv_err != WDC_ATA_ERR) { + printf("%s:%d:%d: intr with DRQ (st=0x%x)\n", + chp->wdc->sc_dev.dv_xname, chp->channel, + xfer->drive, chp->ch_status); + ata_bio->error = TIMEOUT; + drv_err = WDC_ATA_ERR; + } + } + if (drv_err != WDC_ATA_ERR) + goto end; + drvp->n_dmaerrs++; + } + + /* if we had an error, end */ + if (drv_err == WDC_ATA_ERR) { + wdc_ata_bio_done(chp, xfer); + return 1; + } + + /* If this was a read and not using DMA, fetch the data. */ + if ((ata_bio->flags & ATA_READ) != 0) { + if ((chp->ch_status & WDCS_DRQ) != WDCS_DRQ) { + printf("%s:%d:%d: read intr before drq\n", + chp->wdc->sc_dev.dv_xname, chp->channel, + xfer->drive); + ata_bio->error = TIMEOUT; + wdc_ata_bio_done(chp, xfer); + return 1; + } + if ((chp->wdc->cap & WDC_CAPABILITY_ATA_NOSTREAM)) { + if (drvp->drive_flags & DRIVE_CAP32) { + bus_space_read_multi_4(chp->data32iot, + chp->data32ioh, 0, + (u_int32_t *)((char *)xfer->databuf + + xfer->c_skip), + ata_bio->nbytes >> 2); + } else { + bus_space_read_multi_2(chp->cmd_iot, + chp->cmd_ioh, wd_data, + (u_int16_t *)((char *)xfer->databuf + + xfer->c_skip), + ata_bio->nbytes >> 1); + } + } else { + if (drvp->drive_flags & DRIVE_CAP32) { + bus_space_read_multi_stream_4(chp->data32iot, + chp->data32ioh, 0, + (u_int32_t *)((char *)xfer->databuf + + xfer->c_skip), + ata_bio->nbytes >> 2); + } else { + bus_space_read_multi_stream_2(chp->cmd_iot, + chp->cmd_ioh, wd_data, + (u_int16_t *)((char *)xfer->databuf + + xfer->c_skip), + ata_bio->nbytes >> 1); + } + } + } + +end: + ata_bio->blkno += ata_bio->nblks; + ata_bio->blkdone += ata_bio->nblks; + xfer->c_skip += ata_bio->nbytes; + xfer->c_bcount -= ata_bio->nbytes; + /* See if this transfer is complete. */ + if (xfer->c_bcount > 0) { + if ((ata_bio->flags & ATA_POLL) == 0) { + /* Start the next operation */ + wdc_ata_bio_start(chp, xfer); + } else { + /* Let wdc_ata_bio_start do the loop */ + return 1; + } + } else { /* Done with this transfer */ + ata_bio->error = NOERROR; + wdc_ata_bio_done(chp, xfer); + } + return 1; +} + +void +wdc_ata_bio_done(chp, xfer) + struct channel_softc *chp; + struct wdc_xfer *xfer; +{ + struct ata_bio *ata_bio = xfer->cmd; + int need_done = xfer->c_flags & C_NEEDDONE; + int drive = xfer->drive; + struct ata_drive_datas *drvp = &chp->ch_drive[drive]; + + WDCDEBUG_PRINT(("wdc_ata_bio_done %s:%d:%d: flags 0x%x\n", + chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive, + (u_int)xfer->c_flags), + DEBUG_XFERS); + + if (ata_bio->error == NOERROR) + drvp->n_dmaerrs = 0; + else if (drvp->n_dmaerrs >= NERRS_MAX) { + wdc_downgrade_mode(drvp); + } + + /* feed back residual bcount to our caller */ + ata_bio->bcount = xfer->c_bcount; + + /* remove this command from xfer queue */ + wdc_free_xfer(chp, xfer); + + ata_bio->flags |= ATA_ITSDONE; + if (need_done) { + WDCDEBUG_PRINT(("wdc_ata_done: wddone\n"), DEBUG_XFERS); + wddone(chp->ch_drive[drive].drv_softc); + } + WDCDEBUG_PRINT(("wdcstart from wdc_ata_done, flags 0x%x\n", + chp->ch_flags), DEBUG_XFERS); + wdcstart(chp); +} + +/* + * Implement operations needed before read/write. + */ +int +wdc_ata_ctrl_intr(chp, xfer, irq) + struct channel_softc *chp; + struct wdc_xfer *xfer; + int irq; +{ + struct ata_bio *ata_bio = xfer->cmd; + struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive]; + char *errstring = NULL; + int delay = (irq == 0) ? ATA_DELAY : 0; + + WDCDEBUG_PRINT(("wdc_ata_ctrl_intr: state %d\n", drvp->state), + DEBUG_FUNCS); + +again: + switch (drvp->state) { + case RECAL: /* Should not be in this state here */ + panic("wdc_ata_ctrl_intr: state==RECAL"); + break; + + case RECAL_WAIT: + errstring = "recal"; + if (wdcwait(chp, WDCS_DRDY, WDCS_DRDY, delay)) + goto timeout; + if (chp->ch_status & (WDCS_ERR | WDCS_DWF)) + goto error; + /* fall through */ + + case PIOMODE: + /* Don't try to set modes if controller can't be adjusted */ + if ((chp->wdc->cap & WDC_CAPABILITY_MODE) == 0) + goto geometry; + /* Also don't try if the drive didn't report its mode */ + if ((drvp->drive_flags & DRIVE_MODE) == 0) + goto geometry; + wdccommand(chp, drvp->drive, SET_FEATURES, 0, 0, 0, + 0x08 | drvp->PIO_mode, WDSF_SET_MODE); + drvp->state = PIOMODE_WAIT; + break; + + case PIOMODE_WAIT: + errstring = "piomode"; + if (wdcwait(chp, WDCS_DRDY, WDCS_DRDY, delay)) + goto timeout; + if (chp->ch_status & (WDCS_ERR | WDCS_DWF)) + goto error; + /* fall through */ + + case DMAMODE: + if (drvp->drive_flags & DRIVE_UDMA) { + wdccommand(chp, drvp->drive, SET_FEATURES, 0, 0, 0, + 0x40 | drvp->UDMA_mode, WDSF_SET_MODE); + } else if (drvp->drive_flags & DRIVE_DMA) { + wdccommand(chp, drvp->drive, SET_FEATURES, 0, 0, 0, + 0x20 | drvp->DMA_mode, WDSF_SET_MODE); + } else { + goto geometry; + } + drvp->state = DMAMODE_WAIT; + break; + case DMAMODE_WAIT: + errstring = "dmamode"; + if (wdcwait(chp, WDCS_DRDY, WDCS_DRDY, delay)) + goto timeout; + if (chp->ch_status & (WDCS_ERR | WDCS_DWF)) + goto error; + /* fall through */ + + case GEOMETRY: + geometry: + if (ata_bio->flags & ATA_LBA) + goto multimode; + wdccommand(chp, xfer->drive, WDCC_IDP, + ata_bio->lp->d_ncylinders, + ata_bio->lp->d_ntracks - 1, 0, ata_bio->lp->d_nsectors, + (ata_bio->lp->d_type == DTYPE_ST506) ? + ata_bio->lp->d_precompcyl / 4 : 0); + drvp->state = GEOMETRY_WAIT; + break; + + case GEOMETRY_WAIT: + errstring = "geometry"; + if (wdcwait(chp, WDCS_DRDY, WDCS_DRDY, delay)) + goto timeout; + if (chp->ch_status & (WDCS_ERR | WDCS_DWF)) + goto error; + /* fall through */ + + case MULTIMODE: + multimode: + if (ata_bio->multi == 1) + goto ready; + wdccommand(chp, xfer->drive, WDCC_SETMULTI, 0, 0, 0, + ata_bio->multi, 0); + drvp->state = MULTIMODE_WAIT; + break; + + case MULTIMODE_WAIT: + errstring = "setmulti"; + if (wdcwait(chp, WDCS_DRDY, WDCS_DRDY, delay)) + goto timeout; + if (chp->ch_status & (WDCS_ERR | WDCS_DWF)) + goto error; + /* fall through */ + + case READY: + ready: + drvp->state = READY; + /* + * The drive is usable now + */ + xfer->c_intr = wdc_ata_bio_intr; + wdc_ata_bio_start(chp, xfer); + return 1; + } + + if ((ata_bio->flags & ATA_POLL) == 0) { + chp->ch_flags |= WDCF_IRQ_WAIT; + timeout(wdctimeout, chp, ATA_DELAY / 1000 * hz); + } else { + goto again; + } + return 1; + +timeout: + if (irq && (xfer->c_flags & C_TIMEOU) == 0) { + return 0; /* IRQ was not for us */ + } + printf("%s:%d:%d: %s timed out\n", + chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive, errstring); + ata_bio->error = TIMEOUT; + drvp->state = 0; + wdc_ata_bio_done(chp, xfer); + return 0; +error: + printf("%s:%d:%d: %s ", + chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive, + errstring); + if (chp->ch_status & WDCS_DWF) { + printf("drive fault\n"); + ata_bio->error = ERR_DF; + } else { + printf("error (%x)\n", chp->ch_error); + ata_bio->r_error = chp->ch_error; + ata_bio->error = ERROR; + } + drvp->state = 0; + wdc_ata_bio_done(chp, xfer); + return 1; +} + +int +wdc_ata_err(drvp, ata_bio) + struct ata_drive_datas *drvp; + struct ata_bio *ata_bio; +{ + struct channel_softc *chp = drvp->chnl_softc; + ata_bio->error = 0; + if (chp->ch_status & WDCS_BSY) { + ata_bio->error = TIMEOUT; + return WDC_ATA_ERR; + } + + if (chp->ch_status & WDCS_DWF) { + ata_bio->error = ERR_DF; + return WDC_ATA_ERR; + } + + if (chp->ch_status & WDCS_ERR) { + ata_bio->error = ERROR; + ata_bio->r_error = chp->ch_error; + if (drvp->drive_flags & DRIVE_UDMA && + (ata_bio->r_error & WDCE_CRC)) { + /* + * Record the CRC error, to avoid downgrading to + * multiword DMA + */ + drvp->drive_flags |= DRIVE_DMAERR; + } + if (ata_bio->r_error & (WDCE_BBK | WDCE_UNC | WDCE_IDNF | + WDCE_ABRT | WDCE_TK0NF | WDCE_AMNF)) + return WDC_ATA_ERR; + return WDC_ATA_NOERR; + } + + if (chp->ch_status & WDCS_CORR) + ata_bio->flags |= ATA_CORR; + return WDC_ATA_NOERR; +} + +#if 0 +int +wdc_ata_addref(drvp) + struct ata_drive_datas *drvp; +{ + struct channel_softc *chp = drvp->chnl_softc; + + return (wdc_addref(chp)); +} + +void +wdc_ata_delref(drvp) + struct ata_drive_datas *drvp; +{ + struct channel_softc *chp = drvp->chnl_softc; + + wdc_delref(chp); +} +#endif diff --git a/sys/dev/ata/atareg.h b/sys/dev/ata/atareg.h new file mode 100644 index 00000000000..18fbff35cc6 --- /dev/null +++ b/sys/dev/ata/atareg.h @@ -0,0 +1,158 @@ +/* $OpenBSD: atareg.h,v 1.1 1999/07/18 21:25:17 csapuntz Exp $ */ +/* $NetBSD: atareg.h,v 1.5 1999/01/18 20:06:24 bouyer Exp $ */ + +/* + * Drive parameter structure for ATA/ATAPI. + * Bit fields: WDC_* : common to ATA/ATAPI + * ATA_* : ATA only + * ATAPI_* : ATAPI only. + */ +struct ataparams { + /* drive info */ + u_int16_t atap_config; /* 0: general configuration */ +#define WDC_CFG_ATAPI_MASK 0xc000 +#define WDC_CFG_ATAPI 0x8000 +#define ATA_CFG_REMOVABLE 0x0080 +#define ATA_CFG_FIXED 0x0040 +#define ATAPI_CFG_TYPE_MASK 0x1f00 +#define ATAPI_CFG_TYPE(x) (((x) & ATAPI_CFG_TYPE_MASK) >> 8) +#define ATAPI_CFG_REMOV 0x0080 +#define ATAPI_CFG_DRQ_MASK 0x0060 +#define ATAPI_CFG_STD_DRQ 0x0000 +#define ATAPI_CFG_IRQ_DRQ 0x0020 +#define ATAPI_CFG_ACCEL_DRQ 0x0040 +#define ATAPI_CFG_CMD_MASK 0x0003 +#define ATAPI_CFG_CMD_12 0x0000 +#define ATAPI_CFG_CMD_16 0x0001 +/* words 1-9 are ATA only */ + u_int16_t atap_cylinders; /* 1: # of non-removable cylinders */ + u_int16_t __reserved1; + u_int16_t atap_heads; /* 3: # of heads */ + u_int16_t __retired1[2]; /* 4-5: # of unform. bytes/track */ + u_int16_t atap_sectors; /* 6: # of sectors */ + u_int16_t __retired2[3]; + + u_int8_t atap_serial[20]; /* 10-19: serial number */ + u_int16_t __retired3[2]; + u_int16_t __obsolete1; + u_int8_t atap_revision[8]; /* 23-26: firmware revision */ + u_int8_t atap_model[40]; /* 27-46: model number */ + u_int16_t atap_multi; /* 47: maximum sectors per irq (ATA) */ + u_int16_t __reserved2; + u_int16_t atap_capabilities1; /* 49: capability flags */ +#define WDC_CAP_IORDY 0x0800 +#define WDC_CAP_IORDY_DSBL 0x0400 +#define WDC_CAP_LBA 0x0200 +#define WDC_CAP_DMA 0x0100 +#define ATA_CAP_STBY 0x2000 +#define ATAPI_CAP_INTERL_DMA 0x8000 +#define ATAPI_CAP_CMD_QUEUE 0x4000 +#define ATAPI_CAP_OVERLP 0X2000 +#define ATAPI_CAP_ATA_RST 0x1000 + u_int16_t atap_capabilities2; /* 50: capability flags (ATA) */ +#if BYTE_ORDER == LITTLE_ENDIAN + u_int8_t __junk2; + u_int8_t atap_oldpiotiming; /* 51: old PIO timing mode */ + u_int8_t __junk3; + u_int8_t atap_olddmatiming; /* 52: old DMA timing mode (ATA) */ +#else + u_int8_t atap_oldpiotiming; /* 51: old PIO timing mode */ + u_int8_t __junk2; + u_int8_t atap_olddmatiming; /* 52: old DMA timing mode (ATA) */ + u_int8_t __junk3; +#endif + u_int16_t atap_extensions; /* 53: extentions supported */ +#define WDC_EXT_UDMA_MODES 0x0004 +#define WDC_EXT_MODES 0x0002 +#define WDC_EXT_GEOM 0x0001 +/* words 54-62 are ATA only */ + u_int16_t atap_curcylinders; /* 54: current logical cyliners */ + u_int16_t atap_curheads; /* 55: current logical heads */ + u_int16_t atap_cursectors; /* 56: current logical sectors/tracks */ + u_int16_t atap_curcapacity[2]; /* 57-58: current capacity */ + u_int16_t atap_curmulti; /* 59: current multi-sector setting */ +#define WDC_MULTI_VALID 0x0100 +#define WDC_MULTI_MASK 0x00ff + u_int16_t atap_capacity[2]; /* 60-61: total capacity (LBA only) */ + u_int16_t __retired4; +#if BYTE_ORDER == LITTLE_ENDIAN + u_int8_t atap_dmamode_supp; /* 63: multiword DMA mode supported */ + u_int8_t atap_dmamode_act; /* multiword DMA mode active */ + u_int8_t atap_piomode_supp; /* 64: PIO mode supported */ + u_int8_t __junk4; +#else + u_int8_t atap_dmamode_act; /* multiword DMA mode active */ + u_int8_t atap_dmamode_supp; /* 63: multiword DMA mode supported */ + u_int8_t __junk4; + u_int8_t atap_piomode_supp; /* 64: PIO mode supported */ +#endif + u_int16_t atap_dmatiming_mimi; /* 65: minimum DMA cycle time */ + u_int16_t atap_dmatiming_recom; /* 66: recomended DMA cycle time */ + u_int16_t atap_piotiming; /* 67: mini PIO cycle time without FC */ + u_int16_t atap_piotiming_iordy; /* 68: mini PIO cycle time with IORDY FC */ + u_int16_t __reserved3[2]; +/* words 71-72 are ATAPI only */ + u_int16_t atap_pkt_br; /* 71: time (ns) to bus release */ + u_int16_t atap_pkt_bsyclr; /* 72: tme to clear BSY after service */ + u_int16_t __reserved4[2]; + u_int16_t atap_queuedepth; /* 75: */ +#define WDC_QUEUE_DEPTH_MASK 0x0F + u_int16_t __reserved5[4]; + u_int16_t atap_ata_major; /* 80: Major version number */ +#define WDC_VER_ATA1 0x0002 +#define WDC_VER_ATA2 0x0004 +#define WDC_VER_ATA3 0x0008 +#define WDC_VER_ATA4 0x0010 +#define WDC_VER_ATA5 0x0020 + u_int16_t atap_ata_minor; /* 81: Minor version number */ + u_int16_t atap_cmd_set1; /* 82: command set suported */ +#define WDC_CMD1_NOP 0x4000 +#define WDC_CMD1_RB 0x2000 +#define WDC_CMD1_WB 0x1000 +#define WDC_CMD1_HPA 0x0400 +#define WDC_CMD1_DVRST 0x0200 +#define WDC_CMD1_SRV 0x0100 +#define WDC_CMD1_RLSE 0x0080 +#define WDC_CMD1_AHEAD 0x0040 +#define WDC_CMD1_CACHE 0x0020 +#define WDC_CMD1_PKT 0x0010 +#define WDC_CMD1_PM 0x0008 +#define WDC_CMD1_REMOV 0x0004 +#define WDC_CMD1_SEC 0x0002 +#define WDC_CMD1_SMART 0x0001 + u_int16_t atap_cmd_set2; /* 83: command set suported */ +#define WDC_CMD2_RMSN 0x0010 +#define WDC_CMD2_DM 0x0001 +#define ATA_CMD2_APM 0x0008 +#define ATA_CMD2_CFA 0x0004 +#define ATA_CMD2_RWQ 0x0002 + u_int16_t atap_cmd_ext; /* 84: command/features supp. ext. */ + u_int16_t atap_cmd1_en; /* 85: cmd/features enabled */ +/* bits are the same as atap_cmd_set1 */ + u_int16_t atap_cmd2_en; /* 86: cmd/features enabled */ +/* bits are the same as atap_cmd_set2 */ + u_int16_t atap_cmd_def; /* 87: cmd/features default */ +#if BYTE_ORDER == LITTLE_ENDIAN + u_int8_t atap_udmamode_supp; /* 88: Ultra-DMA mode supported */ + u_int8_t atap_udmamode_act; /* Ultra-DMA mode active */ +#else + u_int8_t atap_udmamode_act; /* Ultra-DMA mode active */ + u_int8_t atap_udmamode_supp; /* 88: Ultra-DMA mode supported */ +#endif +/* 89-92 are ATA-only */ + u_int16_t atap_seu_time; /* 89: Sec. Erase Unit compl. time */ + u_int16_t atap_eseu_time; /* 90: Enhanced SEU compl. time */ + u_int16_t atap_apm_val; /* 91: current APM value */ + u_int16_t __reserved6[35]; /* 92-126: reserved */ + u_int16_t atap_rmsn_supp; /* 127: remov. media status notif. */ +#define WDC_RMSN_SUPP_MASK 0x0003 +#define WDC_RMSN_SUPP 0x0001 + u_int16_t atap_sec_st; /* 128: security status */ +#define WDC_SEC_LEV_MAX 0x0100 +#define WDC_SEC_ESE_SUPP 0x0020 +#define WDC_SEC_EXP 0x0010 +#define WDC_SEC_FROZEN 0x0008 +#define WDC_SEC_LOCKED 0x0004 +#define WDC_SEC_EN 0x0002 +#define WDC_SEC_SUPP 0x0001 +}; diff --git a/sys/dev/ata/atavar.h b/sys/dev/ata/atavar.h new file mode 100644 index 00000000000..8e8cb7c5f2f --- /dev/null +++ b/sys/dev/ata/atavar.h @@ -0,0 +1,167 @@ +/* $OpenBSD: atavar.h,v 1.1 1999/07/18 21:25:17 csapuntz Exp $ */ +/* $NetBSD: atavar.h,v 1.13 1999/03/10 13:11:43 bouyer Exp $ */ + +/* + * Copyright (c) 1998 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + * + */ + +/* Hight-level functions and structures used by both ATA and ATAPI devices */ + +/* Datas common to drives and controller drivers */ +struct ata_drive_datas { + u_int8_t drive; /* drive number */ + int8_t ata_vers; /* ATA version supported */ + u_int16_t drive_flags; /* bitmask for drives present/absent and cap */ +#define DRIVE_ATA 0x0001 +#define DRIVE_ATAPI 0x0002 +#define DRIVE_OLD 0x0004 +#define DRIVE (DRIVE_ATA|DRIVE_ATAPI|DRIVE_OLD) +#define DRIVE_CAP32 0x0008 +#define DRIVE_DMA 0x0010 +#define DRIVE_UDMA 0x0020 +#define DRIVE_MODE 0x0040 /* the drive reported its mode */ +#define DRIVE_RESET 0x0080 /* reset the drive state at next xfer */ +#define DRIVE_DMAERR 0x0100 /* Udma transfer had crc error, don't try DMA */ + /* + * Current setting of drive's PIO, DMA and UDMA modes. + * Is initialised by the disks drivers at attach time, and may be + * changed later by the controller's code if needed + */ + u_int8_t PIO_mode; /* Current setting of drive's PIO mode */ + u_int8_t DMA_mode; /* Current setting of drive's DMA mode */ + u_int8_t UDMA_mode; /* Current setting of drive's UDMA mode */ + /* Supported modes for this drive */ + u_int8_t PIO_cap; /* supported drive's PIO mode */ + u_int8_t DMA_cap; /* supported drive's DMA mode */ + u_int8_t UDMA_cap; /* supported drive's UDMA mode */ + /* + * Drive state. This is drive-type (ATA or ATAPI) dependant + * This is reset to 0 after a channel reset. + */ + u_int8_t state; + + /* Number of DMA errors. Reset to 0 after every successful transfers. */ + u_int8_t n_dmaerrs; + /* downgrade mode after this many successive errors */ +#define NERRS_MAX 2 + + struct device *drv_softc; /* ATA drives softc, if any */ + void* chnl_softc; /* channel softc */ +}; + +/* ATA/ATAPI common attachement datas */ +struct ata_atapi_attach { + u_int8_t aa_type; /* Type of device */ +#define T_ATA 0 +#define T_ATAPI 1 + u_int8_t aa_channel; /* controller's channel */ + u_int8_t aa_openings; /* Number of simultaneous commands possible */ + struct ata_drive_datas *aa_drv_data; + void *aa_bus_private; /* infos specifics to this bus */ +}; + +/* User config flags that force (or disable) the use of a mode */ +#define ATA_CONFIG_PIO_MODES 0x0007 +#define ATA_CONFIG_PIO_SET 0x0008 +#define ATA_CONFIG_PIO_OFF 0 +#define ATA_CONFIG_DMA_MODES 0x0070 +#define ATA_CONFIG_DMA_SET 0x0080 +#define ATA_CONFIG_DMA_DISABLE 0x0070 +#define ATA_CONFIG_DMA_OFF 4 +#define ATA_CONFIG_UDMA_MODES 0x0700 +#define ATA_CONFIG_UDMA_SET 0x0800 +#define ATA_CONFIG_UDMA_DISABLE 0x0700 +#define ATA_CONFIG_UDMA_OFF 8 + +/* + * ATA/ATAPI commands description + * + * This structure defines the interface between the ATA/ATAPI device driver + * and the controller for short commands. It contains the command's parameter, + * the len of data's to read/write (if any), and a function to call upon + * completion. + * If no sleep is allowed, the driver can poll for command completion. + * Once the command completed, if the error registed is valid, the flag + * AT_ERROR is set and the error register value is copied to r_error . + * A separate interface is needed for read/write or ATAPI packet commands + * (which need multiple interrupts per commands). + */ +struct wdc_command { + u_int8_t r_command; /* Parameters to upload to registers */ + u_int8_t r_head; + u_int16_t r_cyl; + u_int8_t r_sector; + u_int8_t r_count; + u_int8_t r_precomp; + u_int8_t r_st_bmask; /* status register mask to wait for before command */ + u_int8_t r_st_pmask; /* status register mask to wait for after command */ + u_int8_t r_error; /* error register after command done */ + volatile u_int16_t flags; +#define AT_READ 0x0001 /* There is data to read */ +#define AT_WRITE 0x0002 /* There is data to write (excl. with AT_READ) */ +#define AT_WAIT 0x0008 /* wait in controller code for command completion */ +#define AT_POLL 0x0010 /* poll for command completion (no interrupts) */ +#define AT_DONE 0x0020 /* command is done */ +#define AT_ERROR 0x0040 /* command is done with error */ +#define AT_TIMEOU 0x0080 /* command timed out */ +#define AT_DF 0x0100 /* Drive fault */ +#define AT_READREG 0x0200 /* Read registers on completion */ + int timeout; /* timeout (in ms) */ + void *data; /* Data buffer address */ + int bcount; /* number of bytes to transfer */ + void (*callback) __P((void*)); /* command to call once command completed */ + void *callback_arg; /* argument passed to *callback() */ +}; + +int wdc_exec_command __P((struct ata_drive_datas *, struct wdc_command*)); +#define WDC_COMPLETE 0x01 +#define WDC_QUEUED 0x02 +#define WDC_TRY_AGAIN 0x03 + +void wdc_probe_caps __P((struct ata_drive_datas*)); +int wdc_downgrade_mode __P((struct ata_drive_datas*)); + +void wdc_reset_channel __P((struct ata_drive_datas *)); + +int wdc_ata_addref __P((struct ata_drive_datas *)); +void wdc_ata_delref __P((struct ata_drive_datas *)); + +struct ataparams; +int ata_get_params __P((struct ata_drive_datas*, u_int8_t, + struct ataparams *)); +int ata_set_mode __P((struct ata_drive_datas*, u_int8_t, u_int8_t)); +/* return code for these cmds */ +#define CMD_OK 0 +#define CMD_ERR 1 +#define CMD_AGAIN 2 + +void ata_perror __P((struct ata_drive_datas *, int, char *)); diff --git a/sys/dev/ata/files.ata b/sys/dev/ata/files.ata new file mode 100644 index 00000000000..18658a646fa --- /dev/null +++ b/sys/dev/ata/files.ata @@ -0,0 +1,15 @@ +# $OpenBSD: files.ata,v 1.1 1999/07/18 21:25:17 csapuntz Exp $ +# $NetBSD: files.ata,v 1.3 1998/10/12 16:09:16 bouyer Exp $ +# +# Config file and device description for machine-independent devices +# which attach to ATA busses. Included by ports that need it. Ports +# that use it must provide their own "major" declarations for the +# appropriate devices. + +# ATA disks +device wd: disk +attach wd at ata +file dev/ata/wd.c wd needs-flag +file dev/ata/ata_wdc.c wd & wdc_base + +file dev/ata/ata.c ata | atapi diff --git a/sys/dev/ata/wd.c b/sys/dev/ata/wd.c new file mode 100644 index 00000000000..21ece2bca60 --- /dev/null +++ b/sys/dev/ata/wd.c @@ -0,0 +1,1506 @@ +/* $OpenBSD: wd.c,v 1.1 1999/07/18 21:25:17 csapuntz Exp $ */ +/* $NetBSD: wd.c,v 1.193 1999/02/28 17:15:27 explorer Exp $ */ + +/* + * Copyright (c) 1998 Manuel Bouyer. All rights reserved. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Manuel Bouyer. + * 4. 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. + */ + +/*- + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Charles M. Hannum and by Onno van der Linden. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 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. + */ + +#ifndef WDCDEBUG +#define WDCDEBUG +#endif /* WDCDEBUG */ + +#if 0 +#include "rnd.h" +#endif + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/conf.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <sys/buf.h> +#include <sys/uio.h> +#include <sys/malloc.h> +#include <sys/device.h> +#include <sys/disklabel.h> +#include <sys/disk.h> +#include <sys/syslog.h> +#include <sys/proc.h> +#if NRND > 0 +#include <sys/rnd.h> +#endif + +#include <vm/vm.h> + +#include <machine/intr.h> +#include <machine/bus.h> + +#include <dev/ata/atareg.h> +#include <dev/ata/atavar.h> +#include <dev/ata/wdvar.h> +#include <dev/ic/wdcreg.h> +#include <sys/ataio.h> +#if 0 +#include "locators.h" +#endif + +#define WAITTIME (4 * hz) /* time to wait for a completion */ +#define WDIORETRIES_SINGLE 4 /* number of retries before single-sector */ +#define WDIORETRIES 5 /* number of retries before giving up */ +#define RECOVERYTIME hz/2 /* time to wait before retrying a cmd */ + +#define WDUNIT(dev) DISKUNIT(dev) +#define WDPART(dev) DISKPART(dev) +#define MAKEWDDEV(maj, unit, part) MAKEDISKDEV(maj, unit, part) + +#define WDLABELDEV(dev) (MAKEWDDEV(major(dev), WDUNIT(dev), RAW_PART)) + +#define DEBUG_INTR 0x01 +#define DEBUG_XFERS 0x02 +#define DEBUG_STATUS 0x04 +#define DEBUG_FUNCS 0x08 +#define DEBUG_PROBE 0x10 +#ifdef WDCDEBUG +extern int wdcdebug_wd_mask; /* init'ed in ata_wdc.c */ +#define WDCDEBUG_PRINT(args, level) \ + if (wdcdebug_wd_mask & (level)) \ + printf args +#else +#define WDCDEBUG_PRINT(args, level) +#endif + +struct wd_softc { + /* General disk infos */ + struct device sc_dev; + struct disk sc_dk; + struct buf sc_q; + /* IDE disk soft states */ + struct ata_bio sc_wdc_bio; /* current transfer */ + struct buf *sc_bp; /* buf being transfered */ + void *wdc_softc; /* pointer to our parent */ + struct ata_drive_datas *drvp; /* Our controller's infos */ + int openings; + struct ataparams sc_params;/* drive characteistics found */ + int sc_flags; +#define WDF_LOCKED 0x01 +#define WDF_WANTED 0x02 +#define WDF_WLABEL 0x04 /* label is writable */ +#define WDF_LABELLING 0x08 /* writing label */ +/* + * XXX Nothing resets this yet, but disk change sensing will when ATA-4 is + * more fully implemented. + */ +#define WDF_LOADED 0x10 /* parameters loaded */ +#define WDF_WAIT 0x20 /* waiting for resources */ +#define WDF_LBA 0x40 /* using LBA mode */ + int sc_capacity; + int cyl; /* actual drive parameters */ + int heads; + int sectors; + int retries; /* number of xfer retry */ +#if NRND > 0 + rndsource_element_t rnd_source; +#endif +}; + +#define sc_drive sc_wdc_bio.drive +#define sc_mode sc_wdc_bio.mode +#define sc_multi sc_wdc_bio.multi +#define sc_badsect sc_wdc_bio.badsect + +#ifndef __OpenBSD__ +int wdprobe __P((struct device *, struct cfdata *, void *)); +#else +int wdprobe __P((struct device *, void *, void *)); +#endif +void wdattach __P((struct device *, struct device *, void *)); +int wdprint __P((void *, char *)); + +struct cfattach wd_ca = { + sizeof(struct wd_softc), wdprobe, wdattach +}; + +#ifdef __OpenBSD__ +struct cfdriver wd_cd = { + NULL, "wd", DV_DISK +}; +#else +extern struct cfdriver wd_cd; +#endif + +/* + * Glue necessary to hook WDCIOCCOMMAND into physio + */ + +struct wd_ioctl { + LIST_ENTRY(wd_ioctl) wi_list; + struct buf wi_bp; + struct uio wi_uio; + struct iovec wi_iov; + atareq_t wi_atareq; + struct wd_softc *wi_softc; +}; + +LIST_HEAD(, wd_ioctl) wi_head; + +struct wd_ioctl *wi_find __P((struct buf *)); +void wi_free __P((struct wd_ioctl *)); +struct wd_ioctl *wi_get __P((void)); +void wdioctlstrategy __P((struct buf *)); + +void wdgetdefaultlabel __P((struct wd_softc *, struct disklabel *)); +static void wdgetdisklabel __P((dev_t dev, struct wd_softc *)); +void wdstrategy __P((struct buf *)); +void wdstart __P((void *)); +void __wdstart __P((struct wd_softc*, struct buf *)); +void wdrestart __P((void*)); +int wd_get_params __P((struct wd_softc *, u_int8_t, struct ataparams *)); +void wd_flushcache __P((struct wd_softc *, int)); +void wd_shutdown __P((void*)); + +struct dkdriver wddkdriver = { wdstrategy }; + +/* XXX: these should go elsewhere */ +cdev_decl(wd); +bdev_decl(wd); + +#ifdef HAS_BAD144_HANDLING +static void bad144intern __P((struct wd_softc *)); +#endif +int wdlock __P((struct wd_softc *)); +void wdunlock __P((struct wd_softc *)); + +int +wdprobe(parent, match_, aux) + struct device *parent; +#ifndef __OpenBSD__ + struct cfdata *match; +#else + void *match_; +#endif + void *aux; +{ + struct ata_atapi_attach *aa_link = aux; + struct cfdata *match = match_; + + if (aa_link == NULL) + return 0; + if (aa_link->aa_type != T_ATA) + return 0; + +#ifndef __OpenBSD__ + if (match->cf_loc[ATACF_CHANNEL] != ATACF_CHANNEL_DEFAULT && + match->cf_loc[ATACF_CHANNEL] != aa_link->aa_channel) + return 0; + + if (match->cf_loc[ATACF_DRIVE] != ATACF_DRIVE_DEFAULT && + match->cf_loc[ATACF_DRIVE] != aa_link->aa_drv_data->drive) + return 0; +#else + if (match->cf_loc[0] != -1 && + match->cf_loc[0] != aa_link->aa_channel) + return 0; + + if (match->cf_loc[1] != -1 && + match->cf_loc[1] != aa_link->aa_drv_data->drive) + return 0; +#endif + + return 1; +} + +void +wdattach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct wd_softc *wd = (void *)self; + struct ata_atapi_attach *aa_link= aux; + int i, blank; + char buf[41], c, *p, *q; + WDCDEBUG_PRINT(("wdattach\n"), DEBUG_FUNCS | DEBUG_PROBE); + + wd->openings = aa_link->aa_openings; + wd->drvp = aa_link->aa_drv_data;; + wd->wdc_softc = parent; + /* give back our softc to our caller */ + wd->drvp->drv_softc = &wd->sc_dev; + + /* read our drive info */ + if (wd_get_params(wd, AT_POLL, &wd->sc_params) != 0) { + printf("%s: IDENTIFY failed\n", wd->sc_dev.dv_xname); + return; + } + + for (blank = 0, p = wd->sc_params.atap_model, q = buf, i = 0; + i < sizeof(wd->sc_params.atap_model); i++) { + c = *p++; + if (c == '\0') + break; + if (c != ' ') { + if (blank) { + *q++ = ' '; + blank = 0; + } + *q++ = c; + } else + blank = 1; + } + *q++ = '\0'; + + printf(": <%s>\n", buf); + + if ((wd->sc_params.atap_multi & 0xff) > 1) { + wd->sc_multi = wd->sc_params.atap_multi & 0xff; + } else { + wd->sc_multi = 1; + } + + printf("%s: drive supports %d-sector pio transfers,", + wd->sc_dev.dv_xname, wd->sc_multi); + + /* Prior to ATA-4, LBA was optional. */ + if ((wd->sc_params.atap_capabilities1 & WDC_CAP_LBA) != 0) + wd->sc_flags |= WDF_LBA; +#if 0 + /* ATA-4 requires LBA. */ + if (wd->sc_params.atap_ataversion != 0xffff && + wd->sc_params.atap_ataversion >= WDC_VER_ATA4) + wd->sc_flags |= WDF_LBA; +#endif + + if ((wd->sc_flags & WDF_LBA) != 0) { + printf(" lba addressing\n"); + wd->sc_capacity = + (wd->sc_params.atap_capacity[1] << 16) | + wd->sc_params.atap_capacity[0]; + printf("%s: %dMB, %d cyl, %d head, %d sec, " + "%d bytes/sect x %d sectors\n", + self->dv_xname, + wd->sc_capacity / (1048576 / DEV_BSIZE), + wd->sc_params.atap_cylinders, + wd->sc_params.atap_heads, + wd->sc_params.atap_sectors, + DEV_BSIZE, + wd->sc_capacity); + } else { + printf(" chs addressing\n"); + wd->sc_capacity = + wd->sc_params.atap_cylinders * + wd->sc_params.atap_heads * + wd->sc_params.atap_sectors; + printf("%s: %dMB, %d cyl, %d head, %d sec, %d bytes/sect x %d " + "sectors\n", self->dv_xname, + wd->sc_capacity / (1048576 / DEV_BSIZE), + wd->sc_params.atap_cylinders, + wd->sc_params.atap_heads, + wd->sc_params.atap_sectors, + DEV_BSIZE, + wd->sc_capacity); + } + WDCDEBUG_PRINT(("%s: atap_dmatiming_mimi=%d, atap_dmatiming_recom=%d\n", + self->dv_xname, wd->sc_params.atap_dmatiming_mimi, + wd->sc_params.atap_dmatiming_recom), DEBUG_PROBE); + /* + * Initialize and attach the disk structure. + */ + wd->sc_dk.dk_driver = &wddkdriver; + wd->sc_dk.dk_name = wd->sc_dev.dv_xname; + disk_attach(&wd->sc_dk); + wd->sc_wdc_bio.lp = wd->sc_dk.dk_label; + if (shutdownhook_establish(wd_shutdown, wd) == NULL) + printf("%s: WARNING: unable to establish shutdown hook\n", + wd->sc_dev.dv_xname); +#if NRND > 0 + rnd_attach_source(&wd->rnd_source, wd->sc_dev.dv_xname, + RND_TYPE_DISK, 0); +#endif +} + +/* + * Read/write routine for a buffer. Validates the arguments and schedules the + * transfer. Does not wait for the transfer to complete. + */ +void +wdstrategy(bp) + struct buf *bp; +{ + struct wd_softc *wd = wd_cd.cd_devs[WDUNIT(bp->b_dev)]; + int s; + WDCDEBUG_PRINT(("wdstrategy (%s)\n", wd->sc_dev.dv_xname), + DEBUG_XFERS); + + /* Valid request? */ + if (bp->b_blkno < 0 || + (bp->b_bcount % wd->sc_dk.dk_label->d_secsize) != 0 || + (bp->b_bcount / wd->sc_dk.dk_label->d_secsize) >= (1 << NBBY)) { + bp->b_error = EINVAL; + goto bad; + } + + /* If device invalidated (e.g. media change, door open), error. */ + if ((wd->sc_flags & WDF_LOADED) == 0) { + bp->b_error = EIO; + goto bad; + } + + /* If it's a null transfer, return immediately. */ + if (bp->b_bcount == 0) + goto done; + + /* + * Do bounds checking, adjust transfer. if error, process. + * If end of partition, just return. + */ + if (WDPART(bp->b_dev) != RAW_PART && + bounds_check_with_label(bp, wd->sc_dk.dk_label, wd->sc_dk.dk_cpulabel, + (wd->sc_flags & (WDF_WLABEL|WDF_LABELLING)) != 0) <= 0) + goto done; + /* Queue transfer on drive, activate drive and controller if idle. */ + s = splbio(); + disksort(&wd->sc_q, bp); + wdstart(wd); + splx(s); + return; +bad: + bp->b_flags |= B_ERROR; +done: + /* Toss transfer; we're done early. */ + bp->b_resid = bp->b_bcount; + biodone(bp); +} + +/* + * Queue a drive for I/O. + */ +void +wdstart(arg) + void *arg; +{ + struct wd_softc *wd = arg; + struct buf *dp, *bp=0; + + WDCDEBUG_PRINT(("wdstart %s\n", wd->sc_dev.dv_xname), + DEBUG_XFERS); + while (wd->openings > 0) { + + /* Is there a buf for us ? */ + dp = &wd->sc_q; + if ((bp = dp->b_actf) == NULL) /* yes, an assign */ + return; + dp->b_actf = bp->b_actf; + + /* + * Make the command. First lock the device + */ + wd->openings--; + + wd->retries = 0; + __wdstart(wd, bp); + } +} + +void +__wdstart(wd, bp) + struct wd_softc *wd; + struct buf *bp; +{ + daddr_t p_offset; + if (WDPART(bp->b_dev) != RAW_PART) + p_offset = + wd->sc_dk.dk_label->d_partitions[WDPART(bp->b_dev)].p_offset; + else + p_offset = 0; + wd->sc_wdc_bio.blkno = bp->b_blkno + p_offset; + wd->sc_wdc_bio.blkno /= (wd->sc_dk.dk_label->d_secsize / DEV_BSIZE); + wd->sc_wdc_bio.blkdone =0; + wd->sc_bp = bp; + /* + * If we're retrying, retry in single-sector mode. This will give us + * the sector number of the problem, and will eventually allow the + * transfer to succeed. + */ + if (wd->sc_multi == 1 || wd->retries >= WDIORETRIES_SINGLE) + wd->sc_wdc_bio.flags = ATA_SINGLE; + else + wd->sc_wdc_bio.flags = 0; + if (wd->sc_flags & WDF_LBA) + wd->sc_wdc_bio.flags |= ATA_LBA; + if (bp->b_flags & B_READ) + wd->sc_wdc_bio.flags |= ATA_READ; + wd->sc_wdc_bio.bcount = bp->b_bcount; + wd->sc_wdc_bio.databuf = bp->b_data; + /* Instrumentation. */ + disk_busy(&wd->sc_dk); + switch (wdc_ata_bio(wd->drvp, &wd->sc_wdc_bio)) { + case WDC_TRY_AGAIN: + timeout(wdrestart, wd, hz); + break; + case WDC_QUEUED: + break; + case WDC_COMPLETE: + wddone(wd); + break; + default: + panic("__wdstart: bad return code from wdc_ata_bio()"); + } +} + +void +wddone(v) + void *v; +{ + struct wd_softc *wd = v; + struct buf *bp = wd->sc_bp; + char buf[256], *errbuf = buf; + WDCDEBUG_PRINT(("wddone %s\n", wd->sc_dev.dv_xname), + DEBUG_XFERS); + + bp->b_resid = wd->sc_wdc_bio.bcount; + errbuf[0] = '\0'; + switch (wd->sc_wdc_bio.error) { + case ERR_DMA: + errbuf = "DMA error"; + goto retry; + case ERR_DF: + errbuf = "device fault"; + goto retry; + case TIMEOUT: + errbuf = "device timeout"; + goto retry; + case ERROR: + /* Don't care about media change bits */ + if (wd->sc_wdc_bio.r_error != 0 && + (wd->sc_wdc_bio.r_error & ~(WDCE_MC | WDCE_MCR)) == 0) + goto noerror; + ata_perror(wd->drvp, wd->sc_wdc_bio.r_error, errbuf); +retry: /* Just reset and retry. Can we do more ? */ + wdc_reset_channel(wd->drvp); + diskerr(bp, "wd", errbuf, LOG_PRINTF, + wd->sc_wdc_bio.blkdone, wd->sc_dk.dk_label); + if (wd->retries++ < WDIORETRIES) { + printf(", retrying\n"); + timeout(wdrestart, wd, RECOVERYTIME); + return; + } + printf("\n"); + bp->b_flags |= B_ERROR; + bp->b_error = EIO; + break; + case NOERROR: +noerror: if ((wd->sc_wdc_bio.flags & ATA_CORR) || wd->retries > 0) + printf("%s: soft error (corrected)\n", + wd->sc_dev.dv_xname); + } + disk_unbusy(&wd->sc_dk, (bp->b_bcount - bp->b_resid)); +#if NRND > 0 + rnd_add_uint32(&wd->rnd_source, bp->b_blkno); +#endif + biodone(bp); + wd->openings++; + wdstart(wd); +} + +void +wdrestart(v) + void *v; +{ + struct wd_softc *wd = v; + struct buf *bp = wd->sc_bp; + int s; + WDCDEBUG_PRINT(("wdrestart %s\n", wd->sc_dev.dv_xname), + DEBUG_XFERS); + + s = splbio(); + __wdstart(v, bp); + splx(s); +} + +int +wdread(dev, uio, flags) + dev_t dev; + struct uio *uio; + int flags; +{ + + WDCDEBUG_PRINT(("wdread\n"), DEBUG_XFERS); + return (physio(wdstrategy, NULL, dev, B_READ, minphys, uio)); +} + +int +wdwrite(dev, uio, flags) + dev_t dev; + struct uio *uio; + int flags; +{ + + WDCDEBUG_PRINT(("wdwrite\n"), DEBUG_XFERS); + return (physio(wdstrategy, NULL, dev, B_WRITE, minphys, uio)); +} + +/* + * Wait interruptibly for an exclusive lock. + * + * XXX + * Several drivers do this; it should be abstracted and made MP-safe. + */ +int +wdlock(wd) + struct wd_softc *wd; +{ + int error; + int s; + + WDCDEBUG_PRINT(("wdlock\n"), DEBUG_FUNCS); + + s = splbio(); + + while ((wd->sc_flags & WDF_LOCKED) != 0) { + wd->sc_flags |= WDF_WANTED; + if ((error = tsleep(wd, PRIBIO | PCATCH, + "wdlck", 0)) != 0) { + splx(s); + return error; + } + } + wd->sc_flags |= WDF_LOCKED; + splx(s); + return 0; +} + +/* + * Unlock and wake up any waiters. + */ +void +wdunlock(wd) + struct wd_softc *wd; +{ + + WDCDEBUG_PRINT(("wdunlock\n"), DEBUG_FUNCS); + + wd->sc_flags &= ~WDF_LOCKED; + if ((wd->sc_flags & WDF_WANTED) != 0) { + wd->sc_flags &= ~WDF_WANTED; + wakeup(wd); + } +} + +int +wdopen(dev, flag, fmt, p) + dev_t dev; + int flag, fmt; + struct proc *p; +{ + struct wd_softc *wd; + int unit, part; + int error; + + WDCDEBUG_PRINT(("wdopen\n"), DEBUG_FUNCS); + unit = WDUNIT(dev); + if (unit >= wd_cd.cd_ndevs) + return ENXIO; + wd = wd_cd.cd_devs[unit]; + if (wd == NULL) + return ENXIO; + + /* + * If this is the first open of this device, add a reference + * to the adapter. + */ +#ifndef __OpenBSD__ + if (wd->sc_dk.dk_openmask == 0 && + (error = wdc_ata_addref(wd->drvp)) != 0) + return (error); +#endif + + if ((error = wdlock(wd)) != 0) + goto bad4; + + if (wd->sc_dk.dk_openmask != 0) { + /* + * If any partition is open, but the disk has been invalidated, + * disallow further opens. + */ + if ((wd->sc_flags & WDF_LOADED) == 0) { + error = EIO; + goto bad3; + } + } else { + if ((wd->sc_flags & WDF_LOADED) == 0) { + int ret; + + wd->sc_flags |= WDF_LOADED; + + /* Load the physical device parameters. */ + ret = wd_get_params(wd, AT_WAIT, &wd->sc_params); + if (ret != 0) { + panic ("Parameter loading problem: %d\n"); + } + + /* Load the partition info if not already loaded. */ + wdgetdisklabel(dev, wd); + } + } + + part = WDPART(dev); + + /* Check that the partition exists. */ + if (part != RAW_PART && + (part >= wd->sc_dk.dk_label->d_npartitions || + wd->sc_dk.dk_label->d_partitions[part].p_fstype == FS_UNUSED)) { + error = ENXIO; + goto bad; + } + + /* Insure only one open at a time. */ + switch (fmt) { + case S_IFCHR: + wd->sc_dk.dk_copenmask |= (1 << part); + break; + case S_IFBLK: + wd->sc_dk.dk_bopenmask |= (1 << part); + break; + } + wd->sc_dk.dk_openmask = + wd->sc_dk.dk_copenmask | wd->sc_dk.dk_bopenmask; + + wdunlock(wd); + return 0; + +bad: + if (wd->sc_dk.dk_openmask == 0) { + } + +bad3: + wdunlock(wd); +bad4: +#ifndef __OpenBSD__ + if (wd->sc_dk.dk_openmask == 0) + wdc_ata_delref(wd->drvp); +#endif + return error; +} + +int +wdclose(dev, flag, fmt, p) + dev_t dev; + int flag, fmt; + struct proc *p; +{ + struct wd_softc *wd = wd_cd.cd_devs[WDUNIT(dev)]; + int part = WDPART(dev); + int error; + + WDCDEBUG_PRINT(("wdclose\n"), DEBUG_FUNCS); + if ((error = wdlock(wd)) != 0) + return error; + + switch (fmt) { + case S_IFCHR: + wd->sc_dk.dk_copenmask &= ~(1 << part); + break; + case S_IFBLK: + wd->sc_dk.dk_bopenmask &= ~(1 << part); + break; + } + wd->sc_dk.dk_openmask = + wd->sc_dk.dk_copenmask | wd->sc_dk.dk_bopenmask; + + if (wd->sc_dk.dk_openmask == 0) { + wd_flushcache(wd,0); + /* XXXX Must wait for I/O to complete! */ +#ifndef __OpenBSD__ + wdc_ata_delref(wd->drvp); +#endif + } + + wdunlock(wd); + return 0; +} + +void +wdgetdefaultlabel(wd, lp) + struct wd_softc *wd; + struct disklabel *lp; +{ + + WDCDEBUG_PRINT(("wdgetdefaultlabel\n"), DEBUG_FUNCS); + 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 0 + if (strcmp(wd->sc_params.atap_model, "ST506") == 0) { + lp->d_type = DTYPE_ST506; + strncpy(lp->d_typename, "ST506 disk", 16); + } else { + lp->d_type = DTYPE_ESDI; + strncpy(lp->d_typename, "ESDI/IDE", + sizeof lp->d_typename); + } +#endif + strncpy(lp->d_typename, wd->sc_params.atap_model, 16); + strncpy(lp->d_packname, "fictitious", 16); + 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 = RAW_PART + 1; + + lp->d_magic = DISKMAGIC; + lp->d_magic2 = DISKMAGIC; + lp->d_checksum = dkcksum(lp); +} + +/* + * Fabricate a default disk label, and try to read the correct one. + */ +static void +wdgetdisklabel(dev, wd) + dev_t dev; + struct wd_softc *wd; +{ + struct disklabel *lp = wd->sc_dk.dk_label; + char *errstring; + + WDCDEBUG_PRINT(("wdgetdisklabel\n"), DEBUG_FUNCS); + + memset(wd->sc_dk.dk_cpulabel, 0, sizeof(struct cpu_disklabel)); + + wdgetdefaultlabel(wd, lp); + + wd->sc_badsect[0] = -1; + + if (wd->drvp->state > RECAL) + wd->drvp->drive_flags |= DRIVE_RESET; + errstring = readdisklabel(WDLABELDEV(dev), + wdstrategy, lp, wd->sc_dk.dk_cpulabel, 0); + if (errstring) { + /* + * This probably happened because the drive's default + * geometry doesn't match the DOS geometry. We + * assume the DOS geometry is now in the label and try + * again. XXX This is a kluge. + */ + if (wd->drvp->state > RECAL) + wd->drvp->drive_flags |= DRIVE_RESET; + errstring = readdisklabel(WDLABELDEV(dev), + wdstrategy, lp, wd->sc_dk.dk_cpulabel, 0); + } + if (errstring) { + printf("%s: %s\n", wd->sc_dev.dv_xname, errstring); + return; + } + + if (wd->drvp->state > RECAL) + wd->drvp->drive_flags |= DRIVE_RESET; +#ifdef HAS_BAD144_HANDLING + if ((lp->d_flags & D_BADSECT) != 0) + bad144intern(wd); +#endif +} + +int +wdioctl(dev, xfer, addr, flag, p) + dev_t dev; + u_long xfer; + caddr_t addr; + int flag; + struct proc *p; +{ + struct wd_softc *wd = wd_cd.cd_devs[WDUNIT(dev)]; + int error; + + WDCDEBUG_PRINT(("wdioctl\n"), DEBUG_FUNCS); + + if ((wd->sc_flags & WDF_LOADED) == 0) + return EIO; + + switch (xfer) { +#ifdef HAS_BAD144_HANDLING + case DIOCSBAD: + if ((flag & FWRITE) == 0) + return EBADF; + wd->sc_dk.dk_cpulabel->bad = *(struct dkbad *)addr; + wd->sc_dk.dk_label->d_flags |= D_BADSECT; + bad144intern(wd); + return 0; +#endif + + case DIOCGDINFO: + *(struct disklabel *)addr = *(wd->sc_dk.dk_label); + return 0; + + case DIOCGPART: + ((struct partinfo *)addr)->disklab = wd->sc_dk.dk_label; + ((struct partinfo *)addr)->part = + &wd->sc_dk.dk_label->d_partitions[WDPART(dev)]; + return 0; + + case DIOCWDINFO: + case DIOCSDINFO: + if ((flag & FWRITE) == 0) + return EBADF; + + if ((error = wdlock(wd)) != 0) + return error; + wd->sc_flags |= WDF_LABELLING; + + error = setdisklabel(wd->sc_dk.dk_label, + (struct disklabel *)addr, /*wd->sc_dk.dk_openmask : */0, + wd->sc_dk.dk_cpulabel); + if (error == 0) { + if (wd->drvp->state > RECAL) + wd->drvp->drive_flags |= DRIVE_RESET; + if (xfer == DIOCWDINFO) + error = writedisklabel(WDLABELDEV(dev), + wdstrategy, wd->sc_dk.dk_label, + wd->sc_dk.dk_cpulabel); + } + + wd->sc_flags &= ~WDF_LABELLING; + wdunlock(wd); + return error; + + case DIOCWLABEL: + if ((flag & FWRITE) == 0) + return EBADF; + if (*(int *)addr) + wd->sc_flags |= WDF_WLABEL; + else + wd->sc_flags &= ~WDF_WLABEL; + return 0; + +#ifndef __OpenBSD__ + case DIOCGDEFLABEL: + wdgetdefaultlabel(wd, (struct disklabel *)addr); + return 0; +#endif + +#ifdef notyet + case DIOCWFORMAT: + if ((flag & FWRITE) == 0) + return EBADF; + { + register struct format_op *fop; + struct iovec aiov; + struct uio auio; + + fop = (struct format_op *)addr; + aiov.iov_base = fop->df_buf; + aiov.iov_len = fop->df_count; + auio.uio_iov = &aiov; + auio.uio_iovcnt = 1; + auio.uio_resid = fop->df_count; + auio.uio_segflg = 0; + auio.uio_offset = + fop->df_startblk * wd->sc_dk.dk_label->d_secsize; + auio.uio_procp = p; + error = physio(wdformat, NULL, dev, B_WRITE, minphys, + &auio); + fop->df_count -= auio.uio_resid; + fop->df_reg[0] = wdc->sc_status; + fop->df_reg[1] = wdc->sc_error; + return error; + } +#endif + + case ATAIOCCOMMAND: + /* + * Make sure this command is (relatively) safe first + */ + if ((((atareq_t *) addr)->flags & ATACMD_READ) == 0 && + (flag & FWRITE) == 0) + return (EBADF); + { + struct wd_ioctl *wi; + atareq_t *atareq = (atareq_t *) addr; + int error; + + wi = wi_get(); + wi->wi_softc = wd; + wi->wi_atareq = *atareq; + + if (atareq->datalen && atareq->flags & + (ATACMD_READ | ATACMD_WRITE)) { + wi->wi_iov.iov_base = atareq->databuf; + wi->wi_iov.iov_len = atareq->datalen; + wi->wi_uio.uio_iov = &wi->wi_iov; + wi->wi_uio.uio_iovcnt = 1; + wi->wi_uio.uio_resid = atareq->datalen; + wi->wi_uio.uio_offset = 0; + wi->wi_uio.uio_segflg = UIO_USERSPACE; + wi->wi_uio.uio_rw = + (atareq->flags & ATACMD_READ) ? B_READ : B_WRITE; + wi->wi_uio.uio_procp = p; + error = physio(wdioctlstrategy, &wi->wi_bp, dev, + (atareq->flags & ATACMD_READ) ? B_READ : B_WRITE, + minphys, &wi->wi_uio); + } else { + /* No need to call physio if we don't have any + user data */ + wi->wi_bp.b_flags = 0; + wi->wi_bp.b_data = 0; + wi->wi_bp.b_bcount = 0; + wi->wi_bp.b_dev = 0; + wi->wi_bp.b_proc = p; + wdioctlstrategy(&wi->wi_bp); + error = wi->wi_bp.b_error; + } + *atareq = wi->wi_atareq; + wi_free(wi); + return(error); + } + + default: + return ENOTTY; + } + +#ifdef DIAGNOSTIC + panic("wdioctl: impossible"); +#endif +} + +#ifdef B_FORMAT +int +wdformat(struct buf *bp) +{ + + bp->b_flags |= B_FORMAT; + return wdstrategy(bp); +} +#endif + +int +wdsize(dev) + dev_t dev; +{ + struct wd_softc *wd; + int part, unit, omask; + int size; + + WDCDEBUG_PRINT(("wdsize\n"), DEBUG_FUNCS); + + unit = WDUNIT(dev); + if (unit >= wd_cd.cd_ndevs) + return (-1); + wd = wd_cd.cd_devs[unit]; + if (wd == NULL) + return (-1); + + part = WDPART(dev); + omask = wd->sc_dk.dk_openmask & (1 << part); + + if (omask == 0 && wdopen(dev, 0, S_IFBLK, NULL) != 0) + return (-1); + if (wd->sc_dk.dk_label->d_partitions[part].p_fstype != FS_SWAP) + size = -1; + else + size = wd->sc_dk.dk_label->d_partitions[part].p_size * + (wd->sc_dk.dk_label->d_secsize / DEV_BSIZE); + if (omask == 0 && wdclose(dev, 0, S_IFBLK, NULL) != 0) + return (-1); + return (size); +} + +#ifndef __BDEVSW_DUMP_OLD_TYPE +/* #define WD_DUMP_NOT_TRUSTED if you just want to watch */ +static int wddoingadump = 0; +static int wddumprecalibrated = 0; +static int wddumpmulti = 1; + +/* + * Dump core after a system crash. + */ +int +wddump(dev, blkno, va, size) + dev_t dev; + daddr_t blkno; + caddr_t va; + size_t size; +{ + struct wd_softc *wd; /* disk unit to do the I/O */ + struct disklabel *lp; /* disk's disklabel */ + int unit, part; + int nblks; /* total number of sectors left to write */ + int err; + char errbuf[256]; + + /* Check if recursive dump; if so, punt. */ + if (wddoingadump) + return EFAULT; + wddoingadump = 1; + + unit = WDUNIT(dev); + if (unit >= wd_cd.cd_ndevs) + return ENXIO; + wd = wd_cd.cd_devs[unit]; + if (wd == (struct wd_softc *)0) + return ENXIO; + + part = WDPART(dev); + + /* Make sure it was initialized. */ + if (wd->drvp->state < READY) + return ENXIO; + + /* Convert to disk sectors. Request must be a multiple of size. */ + lp = wd->sc_dk.dk_label; + if ((size % lp->d_secsize) != 0) + return EFAULT; + nblks = size / lp->d_secsize; + blkno = blkno / (lp->d_secsize / DEV_BSIZE); + + /* Check transfer bounds against partition size. */ + if ((blkno < 0) || ((blkno + nblks) > lp->d_partitions[part].p_size)) + return EINVAL; + + /* Offset block number to start of partition. */ + blkno += lp->d_partitions[part].p_offset; + + /* Recalibrate, if first dump transfer. */ + if (wddumprecalibrated == 0) { + wddumpmulti = wd->sc_multi; + wddumprecalibrated = 1; + wd->drvp->state = RECAL; + } + + while (nblks > 0) { +again: + wd->sc_wdc_bio.blkno = blkno; + wd->sc_wdc_bio.flags = ATA_POLL; + if (wddumpmulti == 1) + wd->sc_wdc_bio.flags |= ATA_SINGLE; + if (wd->sc_flags & WDF_LBA) + wd->sc_wdc_bio.flags |= ATA_LBA; + wd->sc_wdc_bio.bcount = + min(nblks, wddumpmulti) * lp->d_secsize; + wd->sc_wdc_bio.databuf = va; +#ifndef WD_DUMP_NOT_TRUSTED + switch (wdc_ata_bio(wd->drvp, &wd->sc_wdc_bio)) { + case WDC_TRY_AGAIN: + panic("wddump: try again"); + break; + case WDC_QUEUED: + panic("wddump: polled command has been queued"); + break; + case WDC_COMPLETE: + break; + } + switch(wd->sc_wdc_bio.error) { + case TIMEOUT: + printf("wddump: device timed out"); + err = EIO; + break; + case ERR_DF: + printf("wddump: drive fault"); + err = EIO; + break; + case ERR_DMA: + printf("wddump: DMA error"); + err = EIO; + break; + case ERROR: + errbuf[0] = '\0'; + ata_perror(wd->drvp, wd->sc_wdc_bio.r_error, errbuf); + printf("wddump: %s", errbuf); + err = EIO; + break; + case NOERROR: + err = 0; + break; + default: + panic("wddump: unknown error type"); + } + if (err != 0) { + if (wddumpmulti != 1) { + wddumpmulti = 1; /* retry in single-sector */ + printf(", retrying\n"); + goto again; + } + printf("\n"); + return err; + } +#else /* WD_DUMP_NOT_TRUSTED */ + /* Let's just talk about this first... */ + printf("wd%d: dump addr 0x%x, cylin %d, head %d, sector %d\n", + unit, va, cylin, head, sector); + delay(500 * 1000); /* half a second */ +#endif + + /* update block count */ + nblks -= min(nblks, wddumpmulti); + blkno += min(nblks, wddumpmulti); + va += min(nblks, wddumpmulti) * lp->d_secsize; + } + + wddoingadump = 0; + return 0; +} +#else /* __BDEVSW_DUMP_NEW_TYPE */ + + +int +wddump(dev, blkno, va, size) + dev_t dev; + daddr_t blkno; + caddr_t va; + size_t size; +{ + + /* Not implemented. */ + return ENXIO; +} +#endif /* __BDEVSW_DUMP_NEW_TYPE */ + +#ifdef HAS_BAD144_HANDLING +/* + * Internalize the bad sector table. + */ +void +bad144intern(wd) + struct wd_softc *wd; +{ + struct dkbad *bt = &wd->sc_dk.dk_cpulabel->bad; + struct disklabel *lp = wd->sc_dk.dk_label; + int i = 0; + + WDCDEBUG_PRINT(("bad144intern\n"), DEBUG_XFERS); + + for (; i < NBT_BAD; i++) { + if (bt->bt_bad[i].bt_cyl == 0xffff) + break; + wd->sc_badsect[i] = + bt->bt_bad[i].bt_cyl * lp->d_secpercyl + + (bt->bt_bad[i].bt_trksec >> 8) * lp->d_nsectors + + (bt->bt_bad[i].bt_trksec & 0xff); + } + for (; i < NBT_BAD+1; i++) + wd->sc_badsect[i] = -1; +} +#endif + +int +wd_get_params(wd, flags, params) + struct wd_softc *wd; + u_int8_t flags; + struct ataparams *params; +{ + switch (ata_get_params(wd->drvp, flags, params)) { + case CMD_AGAIN: + return 1; + case CMD_ERR: + /* + * We `know' there's a drive here; just assume it's old. + * This geometry is only used to read the MBR and print a + * (false) attach message. + */ + strncpy(params->atap_model, "ST506", + sizeof params->atap_model); + params->atap_config = ATA_CFG_FIXED; + params->atap_cylinders = 1024; + params->atap_heads = 8; + params->atap_sectors = 17; + params->atap_multi = 1; + params->atap_capabilities1 = params->atap_capabilities2 = 0; + wd->drvp->ata_vers = -1; /* Mark it as pre-ATA */ + return 0; + case CMD_OK: + return 0; + default: + panic("wd_get_params: bad return code from ata_get_params"); + /* NOTREACHED */ + } +} + +void +wd_flushcache(wd, flags) + struct wd_softc *wd; + int flags; +{ + struct wdc_command wdc_c; + + if (wd->drvp->ata_vers < 4) /* WDCC_FLUSHCACHE is here since ATA-4 */ + return; + memset(&wdc_c, 0, sizeof(struct wdc_command)); + wdc_c.r_command = WDCC_FLUSHCACHE; + wdc_c.r_st_bmask = WDCS_DRDY; + wdc_c.r_st_pmask = WDCS_DRDY; + wdc_c.flags = flags | AT_WAIT; + wdc_c.timeout = 30000; /* 30s timeout */ + if (wdc_exec_command(wd->drvp, &wdc_c) != WDC_COMPLETE) { + printf("%s: flush cache command didn't complete\n", + wd->sc_dev.dv_xname); + } + if (wdc_c.flags & AT_TIMEOU) { + printf("%s: flush cache command timeout\n", + wd->sc_dev.dv_xname); + } + if (wdc_c.flags & AT_DF) { + printf("%s: flush cache command: drive fault\n", + wd->sc_dev.dv_xname); + } + /* + * Ignore error register, it shouldn't report anything else + * than COMMAND ABORTED, which means the device doesn't support + * flush cache + */ +} + +void +wd_shutdown(arg) + void *arg; +{ + struct wd_softc *wd = arg; + wd_flushcache(wd, ATA_POLL); +} + +/* + * Allocate space for a ioctl queue structure. Mostly taken from + * scsipi_ioctl.c + */ +struct wd_ioctl * +wi_get() +{ + struct wd_ioctl *wi; + int s; + + wi = malloc(sizeof(struct wd_ioctl), M_TEMP, M_WAITOK); + memset(wi, 0, sizeof (struct wd_ioctl)); + s = splbio(); + LIST_INSERT_HEAD(&wi_head, wi, wi_list); + splx(s); + return (wi); +} + +/* + * Free an ioctl structure and remove it from our list + */ + +void +wi_free(wi) + struct wd_ioctl *wi; +{ + int s; + + s = splbio(); + LIST_REMOVE(wi, wi_list); + splx(s); + free(wi, M_TEMP); +} + +/* + * Find a wd_ioctl structure based on the struct buf. + */ + +struct wd_ioctl * +wi_find(bp) + struct buf *bp; +{ + struct wd_ioctl *wi; + int s; + + s = splbio(); + for (wi = wi_head.lh_first; wi != 0; wi = wi->wi_list.le_next) + if (bp == &wi->wi_bp) + break; + splx(s); + return (wi); +} + +/* + * Ioctl pseudo strategy routine + * + * This is mostly stolen from scsipi_ioctl.c:scsistrategy(). What + * happens here is: + * + * - wdioctl() queues a wd_ioctl structure. + * + * - wdioctl() calls physio/wdioctlstrategy based on whether or not + * user space I/O is required. If physio() is called, physio() eventually + * calls wdioctlstrategy(). + * + * - In either case, wdioctlstrategy() calls wdc_exec_command() + * to perform the actual command + * + * The reason for the use of the pseudo strategy routine is because + * when doing I/O to/from user space, physio _really_ wants to be in + * the loop. We could put the entire buffer into the ioctl request + * structure, but that won't scale if we want to do things like download + * microcode. + */ + +void +wdioctlstrategy(bp) + struct buf *bp; +{ + struct wd_ioctl *wi; + struct wdc_command wdc_c; + int error = 0; + + wi = wi_find(bp); + if (wi == NULL) { + printf("user_strat: No ioctl\n"); + error = EINVAL; + goto bad; + } + + memset(&wdc_c, 0, sizeof(wdc_c)); + + /* + * Abort if physio broke up the transfer + */ + + if (bp->b_bcount != wi->wi_atareq.datalen) { + printf("physio split wd ioctl request... cannot proceed\n"); + error = EIO; + goto bad; + } + + /* + * Abort if we didn't get a buffer size that was a multiple of + * our sector size (or was larger than NBBY) + */ + + if ((bp->b_bcount % wi->wi_softc->sc_dk.dk_label->d_secsize) != 0 || + (bp->b_bcount / wi->wi_softc->sc_dk.dk_label->d_secsize) >= + (1 << NBBY)) { + error = EINVAL; + goto bad; + } + + /* + * Make sure a timeout was supplied in the ioctl request + */ + + if (wi->wi_atareq.timeout == 0) { + error = EINVAL; + goto bad; + } + + if (wi->wi_atareq.flags & ATACMD_READ) + wdc_c.flags |= AT_READ; + else if (wi->wi_atareq.flags & ATACMD_WRITE) + wdc_c.flags |= AT_WRITE; + + if (wi->wi_atareq.flags & ATACMD_READREG) + wdc_c.flags |= AT_READREG; + + wdc_c.flags |= AT_WAIT; + + wdc_c.timeout = wi->wi_atareq.timeout; + wdc_c.r_command = wi->wi_atareq.command; + wdc_c.r_head = wi->wi_atareq.head & 0x0f; + wdc_c.r_cyl = wi->wi_atareq.cylinder; + wdc_c.r_sector = wi->wi_atareq.sec_num; + wdc_c.r_count = wi->wi_atareq.sec_count; + wdc_c.r_precomp = wi->wi_atareq.features; + wdc_c.r_st_bmask = WDCS_DRDY; + wdc_c.r_st_pmask = WDCS_DRDY; + wdc_c.data = wi->wi_bp.b_data; + wdc_c.bcount = wi->wi_bp.b_bcount; + + if (wdc_exec_command(wi->wi_softc->drvp, &wdc_c) != WDC_COMPLETE) { + wi->wi_atareq.retsts = ATACMD_ERROR; + goto bad; + } + + if (wdc_c.flags & (AT_ERROR | AT_TIMEOU | AT_DF)) { + if (wdc_c.flags & AT_ERROR) { + wi->wi_atareq.retsts = ATACMD_ERROR; + wi->wi_atareq.error = wdc_c.r_error; + } else if (wdc_c.flags & AT_DF) + wi->wi_atareq.retsts = ATACMD_DF; + else + wi->wi_atareq.retsts = ATACMD_TIMEOUT; + } else { + wi->wi_atareq.retsts = ATACMD_OK; + if (wi->wi_atareq.flags & ATACMD_READREG) { + wi->wi_atareq.head = wdc_c.r_head ; + wi->wi_atareq.cylinder = wdc_c.r_cyl; + wi->wi_atareq.sec_num = wdc_c.r_sector; + wi->wi_atareq.sec_count = wdc_c.r_count; + wi->wi_atareq.features = wdc_c.r_precomp; + wi->wi_atareq.error = wdc_c.r_error; + } + } + + bp->b_error = 0; + biodone(bp); + return; +bad: + bp->b_flags |= B_ERROR; + bp->b_error = error; + biodone(bp); +} diff --git a/sys/dev/ata/wdvar.h b/sys/dev/ata/wdvar.h new file mode 100644 index 00000000000..46fb4ee0c18 --- /dev/null +++ b/sys/dev/ata/wdvar.h @@ -0,0 +1,80 @@ +/* $OpenBSD: wdvar.h,v 1.1 1999/07/18 21:25:18 csapuntz Exp $ */ +/* $NetBSD: wdvar.h,v 1.3 1998/11/11 19:38:27 bouyer Exp $ */ + +/* + * Copyright (c) 1998 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + * + */ + +/* Params needed by the controller to perform an ATA bio */ +struct ata_bio { + volatile u_int16_t flags; /* cmd flags */ +#define ATA_NOSLEEP 0x0001 /* Can't sleep */ +#define ATA_POLL 0x0002 /* poll for completion */ +#define ATA_ITSDONE 0x0004 /* the transfer is as done as it gets */ +#define ATA_SINGLE 0x0008 /* transfer has to be done in single-sector mode */ +#define ATA_LBA 0x0010 /* tranfert uses LBA adressing */ +#define ATA_READ 0x0020 /* tranfert is a read (otherwise a write) */ +#define ATA_CORR 0x0040 /* transfer had a corrected error */ + int multi; /* number of blocks to transfer in multi-mode */ + struct disklabel *lp; /* pointer to drive's label info */ + daddr_t blkno; /* block addr */ + daddr_t blkdone; /* number of blks transfered */ + daddr_t nblks; /* number of block currently transfering */ + int nbytes; /* number of bytes currently transfering */ + long bcount; /* total number of bytes */ + char* databuf; /* data buffer adress */ + volatile int error; +#define NOERROR 0 /* There was no error (r_error invalid) */ +#define ERROR 1 /* check r_error */ +#define ERR_DF 2 /* Drive fault */ +#define ERR_DMA 3 /* DMA error */ +#define TIMEOUT 4 /* device timed out */ + u_int8_t r_error; /* copy of error register */ + daddr_t badsect[127]; /* 126 plus trailing -1 marker */ +}; + +/* drive states stored in ata_drive_datas */ +#define RECAL 0 +#define RECAL_WAIT 1 +#define PIOMODE 2 +#define PIOMODE_WAIT 3 +#define DMAMODE 4 +#define DMAMODE_WAIT 5 +#define GEOMETRY 6 +#define GEOMETRY_WAIT 7 +#define MULTIMODE 8 +#define MULTIMODE_WAIT 9 +#define READY 10 + +int wdc_ata_bio __P((struct ata_drive_datas*, struct ata_bio*)); + +void wddone __P((void *)); diff --git a/sys/dev/atapiscsi/atapiconf.h b/sys/dev/atapiscsi/atapiconf.h new file mode 100644 index 00000000000..6a2995b7ed9 --- /dev/null +++ b/sys/dev/atapiscsi/atapiconf.h @@ -0,0 +1,52 @@ +/* $OpenBSD: atapiconf.h,v 1.1 1999/07/18 21:25:18 csapuntz Exp $ */ +/* $NetBSD: atapiconf.h,v 1.7 1998/10/12 16:09:24 bouyer Exp $ */ + +/* + * Copyright (c) 1996 Manuel Bouyer. All rights reserved. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Manuel Bouyer. + * 4. 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. + */ + +#include <scsi/scsiconf.h> + +/* drive states stored in ata_drive_datas */ +#define PIOMODE 0 +#define PIOMODE_WAIT 1 +#define DMAMODE 2 +#define DMAMODE_WAIT 3 +#define READY 4 + +struct atapi_mode_header; +struct ataparams; + +void atapi_print_addr __P((struct scsi_link *)); +int atapi_interpret_sense __P((struct scsi_xfer *)); +int atapi_scsi_cmd __P((struct scsi_link *, struct scsi_generic *, + int, u_char *, int, int, int, struct buf *, int)); +int atapi_mode_select __P((struct scsi_link *, + struct atapi_mode_header *, int, int, int, int)); +int atapi_mode_sense __P((struct scsi_link *, int, + struct atapi_mode_header *, int, int, int, int)); diff --git a/sys/dev/atapiscsi/atapiscsi.c b/sys/dev/atapiscsi/atapiscsi.c new file mode 100644 index 00000000000..01aefe60750 --- /dev/null +++ b/sys/dev/atapiscsi/atapiscsi.c @@ -0,0 +1,994 @@ +/* $OpenBSD: atapiscsi.c,v 1.1 1999/07/18 21:25:18 csapuntz Exp $ */ + +/* + * This code is derived from code with the copyright below. + */ + +/* + * Copyright (c) 1996, 1998 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Manuel Bouyer. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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/systm.h> +#include <sys/kernel.h> +#include <sys/device.h> +#include <sys/buf.h> +#include <sys/dkstat.h> +#include <sys/disklabel.h> +#include <sys/dkstat.h> +#include <sys/malloc.h> +#include <sys/proc.h> +#include <sys/reboot.h> +#include <sys/file.h> +#include <sys/ioctl.h> +#include <scsi/scsi_all.h> +#include <scsi/scsi_disk.h> +#include <scsi/scsiconf.h> + +#include <vm/vm.h> + +#include <machine/bus.h> +#include <machine/cpu.h> +#include <machine/intr.h> + +#ifndef __BUS_SPACE_HAS_STREAM_METHODS +#define bus_space_write_multi_stream_2 bus_space_write_multi_2 +#define bus_space_write_multi_stream_4 bus_space_write_multi_4 +#define bus_space_read_multi_stream_2 bus_space_read_multi_2 +#define bus_space_read_multi_stream_4 bus_space_read_multi_4 +#endif /* __BUS_SPACE_HAS_STREAM_METHODS */ + +#include <dev/ata/atareg.h> +#include <dev/ata/atavar.h> +#include <dev/ic/wdcreg.h> +#include <dev/ic/wdcvar.h> + +#include <dev/atapiscsi/atapiconf.h> + +#define WDCDEBUG +#define DEBUG_INTR 0x01 +#define DEBUG_XFERS 0x02 +#define DEBUG_STATUS 0x04 +#define DEBUG_FUNCS 0x08 +#define DEBUG_PROBE 0x10 +#ifdef WDCDEBUG +int wdcdebug_atapi_mask = 0xFF; +#define WDCDEBUG_PRINT(args, level) \ + if (wdcdebug_atapi_mask & (level)) \ + printf args +#else +#define WDCDEBUG_PRINT(args, level) +#endif + +#define ATAPI_DELAY 10 /* 10 ms, this is used only before sending a cmd */ + +void wdc_atapi_minphys __P((struct buf *bp)); +void wdc_atapi_start __P((struct channel_softc *,struct wdc_xfer *)); +int wdc_atapi_intr __P((struct channel_softc *, struct wdc_xfer *, int)); +int wdc_atapi_ctrl __P((struct channel_softc *, struct wdc_xfer *, int)); +void wdc_atapi_done __P((struct channel_softc *, struct wdc_xfer *)); +void wdc_atapi_reset __P((struct channel_softc *, struct wdc_xfer *)); +int wdc_atapi_send_cmd __P((struct scsi_xfer *sc_xfer)); + +#define MAX_SIZE MAXPHYS + +struct atapiscsi_softc; +struct atapiscsi_xfer; + +static int atapiscsi_match __P((struct device *, void *, void *)); +static void atapiscsi_attach __P((struct device *, struct device *, void *)); + +int wdc_atapi_get_params __P((struct atapiscsi_softc *, u_int8_t, int, + struct ataparams *)); + +#define XS_SHORTSENSE XS_SENSE +#define XS_RESET XS_DRIVER_STUFFUP +#define ATAPI_TO_SCSI_SENSE(sc, atapi_error) \ + (sc)->error_code = XS_SHORTSENSE; (sc)->flags = (atapi_error) >> 4; + +struct atapiscsi_softc { + struct device sc_dev; + struct scsi_link sc_adapterlink; + struct wdc_softc *sc_wdc; + + u_int8_t sc_channel; /* Channel we represent */ + struct scsi_link sc_link[2]; + int valid[2]; + struct ata_drive_datas *sc_drvs; /* array supplied by adapter */ +}; + +static struct scsi_adapter atapiscsi_switch = +{ + wdc_atapi_send_cmd, + wdc_atapi_minphys, + NULL, + NULL, +}; + +static struct scsi_device atapiscsi_dev = +{ + NULL, + NULL, + NULL, + NULL, +}; + +/* Inital version shares bus_link structure so it can easily + be "attached to current" wdc driver */ + +struct cfattach atapiscsi_ca = { + sizeof(struct atapiscsi_softc), atapiscsi_match, atapiscsi_attach +}; + +struct cfdriver atapiscsi_cd = { + NULL, "atapiscsi", DV_DULL +}; + + +int atapiscsi_match(parent, match, aux) + struct device *parent; + void *match, *aux; + +{ + struct ata_atapi_attach *aa_link = aux; + struct cfdata *cf = match; + + if (aa_link == NULL) + return (0); + if (aa_link->aa_type != T_ATAPI) + return (0); + + if (cf->cf_loc[0] != aa_link->aa_channel && + cf->cf_loc[0] != -1) + return (0); + + return (1); +} + +void +atapiscsi_attach(parent, self, aux) + struct device *parent, *self; + void *aux; + +{ + struct atapiscsi_softc *as = (struct atapiscsi_softc *)self; + struct ata_atapi_attach *aa_link = aux; + struct ataparams ids; + struct ataparams *id = &ids; + int drive; + + /* Ouch */ + as->valid[0] = as->valid[1] = 0; + + as->sc_wdc = (struct wdc_softc *)parent; + as->sc_drvs = aa_link->aa_drv_data; + as->sc_channel = aa_link->aa_channel; + as->sc_adapterlink.adapter_softc = as; + as->sc_adapterlink.adapter_target = 7; + as->sc_adapterlink.adapter = &atapiscsi_switch; + as->sc_adapterlink.device = &atapiscsi_dev; + as->sc_adapterlink.openings = 1; + as->sc_wdc->channels[as->sc_channel]->ch_as = as; + + for (drive = 0; drive < 2 ; drive++ ) { + if (wdc_atapi_get_params(as, drive, + SCSI_POLL|SCSI_NOSLEEP, id) == COMPLETE) { + struct ata_drive_datas *drvp = &as->sc_drvs[drive]; + + as->valid[drive] = 1; + + /* This is wrong. All the devices on the ATAPI bus + will be called atapibus by the IDE side of the + driver. */ + drvp->drv_softc = (struct device *)as; + wdc_probe_caps(drvp); + } + } + +#if 0 + config_found(self, &as->sc_adapterlink, scsiprint); +#endif +} + + +void +wdc_atapibus_final_attach(chp) + struct channel_softc *chp; +{ + if (chp->ch_as) + config_found((struct device *)chp->ch_as, + &chp->ch_as->sc_adapterlink, scsiprint); +} + +void +wdc_atapibus_attach(chp) + struct channel_softc *chp; +{ + struct wdc_softc *wdc = chp->wdc; + int channel = chp->channel; + struct ata_atapi_attach aa_link; + + /* + * Fill in the adapter. + */ + memset(&aa_link, 0, sizeof(struct ata_atapi_attach)); + aa_link.aa_type = T_ATAPI; + aa_link.aa_channel = channel; + aa_link.aa_openings = 1; + aa_link.aa_drv_data = chp->ch_drive; /* pass the whole array */ +#if 0 + aa_link.aa_bus_private = &wdc->sc_atapi_adapter; +#endif + (void)config_found(&wdc->sc_dev, (void *)&aa_link, atapi_print); +} + +void +wdc_atapi_minphys (struct buf *bp) +{ + if(bp->b_bcount > MAX_SIZE) + bp->b_bcount = MAX_SIZE; + minphys(bp); +} + + +/* + * The scsi_cmd interface works as follows: + * + */ + +int +wdc_atapi_get_params(as, drive, flags, id) + struct atapiscsi_softc *as; + u_int8_t drive; + int flags; + struct ataparams *id; +{ + struct wdc_softc *wdc = as->sc_wdc; + struct channel_softc *chp = + wdc->channels[as->sc_channel]; + struct wdc_command wdc_c; + + /* if no ATAPI device detected at wdc attach time, skip */ + /* + * XXX this will break scsireprobe if this is of any interest for + * ATAPI devices one day. + */ + if ((chp->ch_drive[drive].drive_flags & DRIVE_ATAPI) == 0) { + WDCDEBUG_PRINT(("wdc_atapi_get_params: drive %d not present\n", + drive), DEBUG_PROBE); + return -1; + } + memset(&wdc_c, 0, sizeof(struct wdc_command)); + wdc_c.r_command = ATAPI_SOFT_RESET; + wdc_c.r_st_bmask = 0; + wdc_c.r_st_pmask = 0; + wdc_c.flags = AT_POLL; + wdc_c.timeout = WDC_RESET_WAIT; + if (wdc_exec_command(&chp->ch_drive[drive], &wdc_c) != WDC_COMPLETE) { + printf("wdc_atapi_get_params: ATAPI_SOFT_RESET failed for" + " drive %s:%d:%d: driver failed\n", + chp->wdc->sc_dev.dv_xname, chp->channel, drive); + panic("wdc_atapi_get_params"); + } + if (wdc_c.flags & (AT_ERROR | AT_TIMEOU | AT_DF)) { + WDCDEBUG_PRINT(("wdc_atapi_get_params: ATAPI_SOFT_RESET " + "failed for drive %s:%d:%d: error 0x%x\n", + chp->wdc->sc_dev.dv_xname, chp->channel, drive, + wdc_c.r_error), DEBUG_PROBE); + return -1; + } + chp->ch_drive[drive].state = 0; + + bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_status); + + /* Some ATAPI devices need a bit more time after software reset. */ + delay(5000); + if (ata_get_params(&chp->ch_drive[drive], AT_POLL, id) != 0) { + WDCDEBUG_PRINT(("wdc_atapi_get_params: ATAPI_IDENTIFY_DEVICE " + "failed for drive %s:%d:%d: error 0x%x\n", + chp->wdc->sc_dev.dv_xname, chp->channel, drive, + wdc_c.r_error), DEBUG_PROBE); + return -1; + } + return COMPLETE; +} + +int +wdc_atapi_send_cmd(sc_xfer) + struct scsi_xfer *sc_xfer; +{ + struct atapiscsi_softc *as = sc_xfer->sc_link->adapter_softc; + struct wdc_softc *wdc = as->sc_wdc; + struct wdc_xfer *xfer; + int flags = sc_xfer->flags; + int channel = as->sc_channel; + int drive = sc_xfer->sc_link->target; + int s, ret; + + WDCDEBUG_PRINT(("wdc_atapi_send_cmd %s:%d:%d\n", + wdc->sc_dev.dv_xname, channel, drive), DEBUG_XFERS); + + if (drive > 1 || !as->valid[drive]) { + sc_xfer->error = XS_DRIVER_STUFFUP; + return COMPLETE; + } + + xfer = wdc_get_xfer(flags & SCSI_NOSLEEP ? WDC_NOSLEEP : WDC_CANSLEEP); + if (xfer == NULL) { + return TRY_AGAIN_LATER; + } + if (sc_xfer->flags & SCSI_POLL) + xfer->c_flags |= C_POLL; + xfer->drive = drive; + xfer->c_flags |= C_ATAPI; + xfer->cmd = sc_xfer; + xfer->databuf = sc_xfer->data; + xfer->c_bcount = sc_xfer->datalen; + xfer->c_start = wdc_atapi_start; + xfer->c_intr = wdc_atapi_intr; + s = splbio(); + wdc_exec_xfer(wdc->channels[channel], xfer); +#ifdef DIAGNOSTIC + if ((sc_xfer->flags & SCSI_POLL) != 0 && + (sc_xfer->flags & ITSDONE) == 0) + panic("wdc_atapi_send_cmd: polled command not done"); +#endif + ret = (sc_xfer->flags & ITSDONE) ? COMPLETE : SUCCESSFULLY_QUEUED; + splx(s); + return ret; +} + +void +wdc_atapi_start(chp, xfer) + struct channel_softc *chp; + struct wdc_xfer *xfer; +{ + struct scsi_xfer *sc_xfer = xfer->cmd; + struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive]; + + WDCDEBUG_PRINT(("wdc_atapi_start %s:%d:%d, scsi flags 0x%x \n", + chp->wdc->sc_dev.dv_xname, chp->channel, drvp->drive, + sc_xfer->flags), DEBUG_XFERS); + /* Adjust C_DMA, it may have changed if we are requesting sense */ + if ((drvp->drive_flags & (DRIVE_DMA | DRIVE_UDMA)) && + (sc_xfer->datalen > 0 || (xfer->c_flags & C_SENSE))) + xfer->c_flags |= C_DMA; + else + xfer->c_flags &= ~C_DMA; + /* Do control operations specially. */ + if (drvp->state < READY) { + if (drvp->state != PIOMODE) { + printf("%s:%d:%d: bad state %d in wdc_atapi_start\n", + chp->wdc->sc_dev.dv_xname, chp->channel, + xfer->drive, drvp->state); + panic("wdc_atapi_start: bad state"); + } + wdc_atapi_ctrl(chp, xfer, 0); + return; + } + bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sdh, + WDSD_IBM | (xfer->drive << 4)); + if (wait_for_unbusy(chp, ATAPI_DELAY) < 0) { + printf("wdc_atapi_start: not ready, st = %02x\n", + chp->ch_status); + sc_xfer->error = XS_TIMEOUT; + wdc_atapi_reset(chp, xfer); + return; + } + + /* + * Even with WDCS_ERR, the device should accept a command packet + * Limit length to what can be stuffed into the cylinder register + * (16 bits). Some CD-ROMs seem to interpret '0' as 65536, + * but not all devices do that and it's not obvious from the + * ATAPI spec that that behaviour should be expected. If more + * data is necessary, multiple data transfer phases will be done. + */ + + wdccommand(chp, xfer->drive, ATAPI_PKT_CMD, + sc_xfer->datalen <= 0xffff ? sc_xfer->datalen : 0xffff, + 0, 0, 0, + (xfer->c_flags & C_DMA) ? ATAPI_PKT_CMD_FTRE_DMA : 0); + + /* + * If there is no interrupt for CMD input, busy-wait for it (done in + * the interrupt routine. If it is a polled command, call the interrupt + * routine until command is done. + */ + if ( +#ifndef __OpenBSD__ + (sc_xfer->sc_link->scsi_atapi.cap & ATAPI_CFG_DRQ_MASK) != + ATAPI_CFG_IRQ_DRQ || (sc_xfer->flags & SCSI_POLL) +#else + 1 +#endif + ) { + /* Wait for at last 400ns for status bit to be valid */ + DELAY(1); + wdc_atapi_intr(chp, xfer, 0); + } else { + chp->ch_flags |= WDCF_IRQ_WAIT; + timeout(wdctimeout, chp, hz); + } + + if (sc_xfer->flags & SCSI_POLL) { + while ((sc_xfer->flags & ITSDONE) == 0) { + /* Wait for at last 400ns for status bit to be valid */ + DELAY(1); + wdc_atapi_intr(chp, xfer, 0); + } + } +} + +int +wdc_atapi_intr(chp, xfer, irq) + struct channel_softc *chp; + struct wdc_xfer *xfer; + int irq; +{ + struct scsi_xfer *sc_xfer = xfer->cmd; + struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive]; + int len, phase, i, retries=0; + int ire, dma_err = 0; + int dma_flags = 0; + struct scsi_generic _cmd_reqsense; + struct scsi_sense *cmd_reqsense = + (struct scsi_sense *)&_cmd_reqsense; + u_int32_t cmd[4]; + int cmdlen =12; /* XXX - Not true for all ATAPI devices */ + + memset (cmd, 0, sizeof(cmd)); + + WDCDEBUG_PRINT(("wdc_atapi_intr %s:%d:%d\n", + chp->wdc->sc_dev.dv_xname, chp->channel, drvp->drive), DEBUG_INTR); + + /* Is it not a transfer, but a control operation? */ + if (drvp->state < READY) { + printf("%s:%d:%d: bad state %d in wdc_atapi_intr\n", + chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive, + drvp->state); + panic("wdc_atapi_intr: bad state\n"); + } + /* Ack interrupt done in wait_for_unbusy */ + bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sdh, + WDSD_IBM | (xfer->drive << 4)); + if (wait_for_unbusy(chp, + (irq == 0) ? sc_xfer->timeout : 0) != 0) { + if (irq && (xfer->c_flags & C_TIMEOU) == 0) + return 0; /* IRQ was not for us */ + printf("%s:%d:%d: device timeout, c_bcount=%d, c_skip=%d\n", + chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive, + xfer->c_bcount, xfer->c_skip); + if (xfer->c_flags & C_DMA) + drvp->n_dmaerrs++; + sc_xfer->error = XS_TIMEOUT; + wdc_atapi_reset(chp, xfer); + return 1; + } + /* If we missed an IRQ and were using DMA, flag it as a DMA error */ + if ((xfer->c_flags & C_TIMEOU) && (xfer->c_flags & C_DMA)) + drvp->n_dmaerrs++; + /* + * if the request sense command was aborted, report the short sense + * previously recorded, else continue normal processing + */ + + if ((xfer->c_flags & C_SENSE) != 0 && + (chp->ch_status & WDCS_ERR) != 0 && + (chp->ch_error & WDCE_ABRT) != 0) { + WDCDEBUG_PRINT(("wdc_atapi_intr: request_sense aborted, " + "calling wdc_atapi_done()" + ), DEBUG_INTR); + wdc_atapi_done(chp, xfer); + return 1; + } + + if (xfer->c_flags & C_DMA) { + dma_flags = ((sc_xfer->flags & SCSI_DATA_IN) || + (xfer->c_flags & C_SENSE)) ? WDC_DMA_READ : 0; + dma_flags |= sc_xfer->flags & SCSI_POLL ? WDC_DMA_POLL : 0; + } +again: + len = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_cyl_lo) + + 256 * bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_cyl_hi); + ire = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_ireason); + phase = (ire & (WDCI_CMD | WDCI_IN)) | (chp->ch_status & WDCS_DRQ); + WDCDEBUG_PRINT(("wdc_atapi_intr: c_bcount %d len %d st 0x%x err 0x%x " + "ire 0x%x :", xfer->c_bcount, + len, chp->ch_status, chp->ch_error, ire), DEBUG_INTR); + + switch (phase) { + case PHASE_CMDOUT: + if (xfer->c_flags & C_SENSE) { + memset(cmd_reqsense, 0, sizeof(struct scsi_generic)); + cmd_reqsense->opcode = REQUEST_SENSE; + cmd_reqsense->length = xfer->c_bcount; + bcopy(cmd_reqsense, cmd, sizeof(struct scsi_generic)); + } else { + bcopy(sc_xfer->cmd, cmd, sc_xfer->cmdlen); + } + WDCDEBUG_PRINT(("PHASE_CMDOUT\n"), DEBUG_INTR); + /* Init the DMA channel if necessary */ + if (xfer->c_flags & C_DMA) { + if ((*chp->wdc->dma_init)(chp->wdc->dma_arg, + chp->channel, xfer->drive, + xfer->databuf, xfer->c_bcount, dma_flags) != 0) { + sc_xfer->error = XS_DRIVER_STUFFUP; + break; + } + } + /* send packet command */ + /* Commands are 12 or 16 bytes long. It's 32-bit aligned */ + if ((chp->wdc->cap & WDC_CAPABILITY_ATAPI_NOSTREAM)) { + if (drvp->drive_flags & DRIVE_CAP32) { + bus_space_write_multi_4(chp->data32iot, + chp->data32ioh, 0, + (u_int32_t *)cmd, + cmdlen >> 2); + } else { + bus_space_write_multi_2(chp->cmd_iot, + chp->cmd_ioh, wd_data, + (u_int16_t *)cmd, + cmdlen >> 1); + } + } else { + if (drvp->drive_flags & DRIVE_CAP32) { + bus_space_write_multi_stream_4(chp->data32iot, + chp->data32ioh, 0, + (u_int32_t *)cmd, + cmdlen >> 2); + } else { + bus_space_write_multi_stream_2(chp->cmd_iot, + chp->cmd_ioh, wd_data, + (u_int16_t *)cmd, + cmdlen >> 1); + } + } + /* Start the DMA channel if necessary */ + if (xfer->c_flags & C_DMA) { + (*chp->wdc->dma_start)(chp->wdc->dma_arg, + chp->channel, xfer->drive, dma_flags); + } + + if ((sc_xfer->flags & SCSI_POLL) == 0) { + chp->ch_flags |= WDCF_IRQ_WAIT; + timeout(wdctimeout, chp, sc_xfer->timeout * hz / 1000); + } + return 1; + + case PHASE_DATAOUT: + /* write data */ + WDCDEBUG_PRINT(("PHASE_DATAOUT\n"), DEBUG_INTR); + if ((sc_xfer->flags & SCSI_DATA_OUT) == 0 || + (xfer->c_flags & C_DMA) != 0) { + printf("wdc_atapi_intr: bad data phase DATAOUT\n"); + if (xfer->c_flags & C_DMA) { + (*chp->wdc->dma_finish)(chp->wdc->dma_arg, + chp->channel, xfer->drive, dma_flags); + drvp->n_dmaerrs++; + } + sc_xfer->error = XS_TIMEOUT; + wdc_atapi_reset(chp, xfer); + return 1; + } + if (xfer->c_bcount < len) { + printf("wdc_atapi_intr: warning: write only " + "%d of %d requested bytes\n", xfer->c_bcount, len); + if ((chp->wdc->cap & WDC_CAPABILITY_ATAPI_NOSTREAM)) { + bus_space_write_multi_2(chp->cmd_iot, + chp->cmd_ioh, wd_data, + (u_int16_t *)((char *)xfer->databuf + + xfer->c_skip), + xfer->c_bcount >> 1); + } else { + bus_space_write_multi_stream_2(chp->cmd_iot, + chp->cmd_ioh, wd_data, + (u_int16_t *)((char *)xfer->databuf + + xfer->c_skip), + xfer->c_bcount >> 1); + } + for (i = xfer->c_bcount; i < len; i += 2) + bus_space_write_2(chp->cmd_iot, chp->cmd_ioh, + wd_data, 0); + xfer->c_skip += xfer->c_bcount; + xfer->c_bcount = 0; + } else { + if (drvp->drive_flags & DRIVE_CAP32) { + if ((chp->wdc->cap & WDC_CAPABILITY_ATAPI_NOSTREAM)) + bus_space_write_multi_4(chp->data32iot, + chp->data32ioh, 0, + (u_int32_t *)((char *)xfer->databuf + + xfer->c_skip), + len >> 2); + else + bus_space_write_multi_stream_4(chp->data32iot, + chp->data32ioh, wd_data, + (u_int32_t *)((char *)xfer->databuf + + xfer->c_skip), + len >> 2); + + xfer->c_skip += len & 0xfffffffc; + xfer->c_bcount -= len & 0xfffffffc; + len = len & 0x03; + } + if (len > 0) { + if ((chp->wdc->cap & WDC_CAPABILITY_ATAPI_NOSTREAM)) + bus_space_write_multi_2(chp->cmd_iot, + chp->cmd_ioh, wd_data, + (u_int16_t *)((char *)xfer->databuf + + xfer->c_skip), + len >> 1); + else + bus_space_write_multi_stream_2(chp->cmd_iot, + chp->cmd_ioh, wd_data, + (u_int16_t *)((char *)xfer->databuf + + xfer->c_skip), + len >> 1); + xfer->c_skip += len; + xfer->c_bcount -= len; + } + } + if ((sc_xfer->flags & SCSI_POLL) == 0) { + chp->ch_flags |= WDCF_IRQ_WAIT; + timeout(wdctimeout, chp, sc_xfer->timeout * hz / 1000); + } + return 1; + + case PHASE_DATAIN: + /* Read data */ + WDCDEBUG_PRINT(("PHASE_DATAIN\n"), DEBUG_INTR); + if (((sc_xfer->flags & SCSI_DATA_IN) == 0 && + (xfer->c_flags & C_SENSE) == 0) || + (xfer->c_flags & C_DMA) != 0) { + printf("wdc_atapi_intr: bad data phase DATAIN\n"); + if (xfer->c_flags & C_DMA) { + (*chp->wdc->dma_finish)(chp->wdc->dma_arg, + chp->channel, xfer->drive, dma_flags); + drvp->n_dmaerrs++; + } + sc_xfer->error = XS_TIMEOUT; + wdc_atapi_reset(chp, xfer); + return 1; + } + if (xfer->c_bcount < len) { + printf("wdc_atapi_intr: warning: reading only " + "%d of %d bytes\n", xfer->c_bcount, len); + if ((chp->wdc->cap & WDC_CAPABILITY_ATAPI_NOSTREAM)) { + bus_space_read_multi_2(chp->cmd_iot, + chp->cmd_ioh, wd_data, + (u_int16_t *)((char *)xfer->databuf + + xfer->c_skip), + xfer->c_bcount >> 1); + } else { + bus_space_read_multi_stream_2(chp->cmd_iot, + chp->cmd_ioh, wd_data, + (u_int16_t *)((char *)xfer->databuf + + xfer->c_skip), + xfer->c_bcount >> 1); + } + wdcbit_bucket(chp, len - xfer->c_bcount); + xfer->c_skip += xfer->c_bcount; + xfer->c_bcount = 0; + } else { + if (drvp->drive_flags & DRIVE_CAP32) { + if ((chp->wdc->cap & WDC_CAPABILITY_ATAPI_NOSTREAM)) + bus_space_read_multi_4(chp->data32iot, + chp->data32ioh, 0, + (u_int32_t *)((char *)xfer->databuf + + xfer->c_skip), + len >> 2); + else + bus_space_read_multi_stream_4(chp->data32iot, + chp->data32ioh, wd_data, + (u_int32_t *)((char *)xfer->databuf + + xfer->c_skip), + len >> 2); + + xfer->c_skip += len & 0xfffffffc; + xfer->c_bcount -= len & 0xfffffffc; + len = len & 0x03; + } + if (len > 0) { + if ((chp->wdc->cap & WDC_CAPABILITY_ATAPI_NOSTREAM)) + bus_space_read_multi_2(chp->cmd_iot, + chp->cmd_ioh, wd_data, + (u_int16_t *)((char *)xfer->databuf + + xfer->c_skip), + len >> 1); + else + bus_space_read_multi_stream_2(chp->cmd_iot, + chp->cmd_ioh, wd_data, + (u_int16_t *)((char *)xfer->databuf + + xfer->c_skip), + len >> 1); + xfer->c_skip += len; + xfer->c_bcount -=len; + } + } + if ((sc_xfer->flags & SCSI_POLL) == 0) { + chp->ch_flags |= WDCF_IRQ_WAIT; + timeout(wdctimeout, chp, sc_xfer->timeout * hz / 1000); + } + return 1; + + case PHASE_ABORTED: + case PHASE_COMPLETED: + WDCDEBUG_PRINT(("PHASE_COMPLETED\n"), DEBUG_INTR); + /* turn off DMA channel */ + if (xfer->c_flags & C_DMA) { + dma_err = (*chp->wdc->dma_finish)(chp->wdc->dma_arg, + chp->channel, xfer->drive, dma_flags); + if (xfer->c_flags & C_SENSE) + xfer->c_bcount -= + sizeof(sc_xfer->sense); + else + xfer->c_bcount -= sc_xfer->datalen; + } + if (xfer->c_flags & C_SENSE) { + if ((chp->ch_status & WDCS_ERR) || dma_err < 0) { + /* + * request sense failed ! it's not suppossed + * to be possible + */ + if (dma_err < 0) + drvp->n_dmaerrs++; + sc_xfer->error = XS_RESET; + wdc_atapi_reset(chp, xfer); + return (1); + } else if (xfer->c_bcount < + sizeof(sc_xfer->sense)) { + /* use the sense we just read */ + sc_xfer->error = XS_SENSE; + } else { + /* + * command completed, but no data was read. + * use the short sense we saved previsouly. + */ + sc_xfer->error = XS_SHORTSENSE; + } + } else { + sc_xfer->resid = xfer->c_bcount; + if (chp->ch_status & WDCS_ERR) { + /* save the short sense */ + sc_xfer->error = XS_SHORTSENSE; + ATAPI_TO_SCSI_SENSE(&sc_xfer->sense, chp->ch_error); + if ((sc_xfer->sc_link->quirks & + SDEV_NOSENSE) == 0) { + /* + * let the driver issue a + * 'request sense' + */ + xfer->databuf = &sc_xfer->sense; + xfer->c_bcount = + sizeof(sc_xfer->sense); + xfer->c_skip = 0; + xfer->c_flags |= C_SENSE; + wdc_atapi_start(chp, xfer); + return 1; + } + } else if (dma_err < 0) { + drvp->n_dmaerrs++; + sc_xfer->error = XS_RESET; + wdc_atapi_reset(chp, xfer); + return (1); + } + } + if (xfer->c_bcount != 0) { + WDCDEBUG_PRINT(("wdc_atapi_intr: bcount value is " + "%d after io\n", xfer->c_bcount), DEBUG_XFERS); + } +#ifdef DIAGNOSTIC + if (xfer->c_bcount < 0) { + printf("wdc_atapi_intr warning: bcount value " + "is %d after io\n", xfer->c_bcount); + } +#endif + break; + + default: + if (++retries<500) { + DELAY(100); + chp->ch_status = bus_space_read_1(chp->cmd_iot, + chp->cmd_ioh, wd_status); + chp->ch_error = bus_space_read_1(chp->cmd_iot, + chp->cmd_ioh, wd_error); + goto again; + } + printf("wdc_atapi_intr: unknown phase 0x%x\n", phase); + if (chp->ch_status & WDCS_ERR) { + sc_xfer->error = XS_SHORTSENSE; + ATAPI_TO_SCSI_SENSE(&sc_xfer->sense, chp->ch_error); + } else { + sc_xfer->error = XS_RESET; + wdc_atapi_reset(chp, xfer); + return (1); + } + } + WDCDEBUG_PRINT(("wdc_atapi_intr: wdc_atapi_done() (end), error 0x%x " + "\n", sc_xfer->error), + DEBUG_INTR); + wdc_atapi_done(chp, xfer); + return (1); +} + +int +wdc_atapi_ctrl(chp, xfer, irq) + struct channel_softc *chp; + struct wdc_xfer *xfer; + int irq; +{ + struct scsi_xfer *sc_xfer = xfer->cmd; + struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive]; + char *errstring = NULL; + int delay = (irq == 0) ? ATAPI_DELAY : 0; + + /* Ack interrupt done in wait_for_unbusy */ +again: + WDCDEBUG_PRINT(("wdc_atapi_ctrl %s:%d:%d state %d\n", + chp->wdc->sc_dev.dv_xname, chp->channel, drvp->drive, drvp->state), + DEBUG_INTR | DEBUG_FUNCS); + bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sdh, + WDSD_IBM | (xfer->drive << 4)); + switch (drvp->state) { + case PIOMODE: +piomode: + /* Don't try to set mode if controller can't be adjusted */ + if ((chp->wdc->cap & WDC_CAPABILITY_MODE) == 0) + goto ready; + /* Also don't try if the drive didn't report its mode */ + if ((drvp->drive_flags & DRIVE_MODE) == 0) + goto ready;; + wdccommand(chp, drvp->drive, SET_FEATURES, 0, 0, 0, + 0x08 | drvp->PIO_mode, WDSF_SET_MODE); + drvp->state = PIOMODE_WAIT; + break; + case PIOMODE_WAIT: + errstring = "piomode"; + if (wait_for_unbusy(chp, delay)) + goto timeout; + if (chp->ch_status & WDCS_ERR) { + if (drvp->PIO_mode < 3) { + drvp->PIO_mode = 3; + goto piomode; + } else { + goto error; + } + } + /* fall through */ + + case DMAMODE: + if (drvp->drive_flags & DRIVE_UDMA) { + wdccommand(chp, drvp->drive, SET_FEATURES, 0, 0, 0, + 0x40 | drvp->UDMA_mode, WDSF_SET_MODE); + } else if (drvp->drive_flags & DRIVE_DMA) { + wdccommand(chp, drvp->drive, SET_FEATURES, 0, 0, 0, + 0x20 | drvp->DMA_mode, WDSF_SET_MODE); + } else { + goto ready; + } + drvp->state = DMAMODE_WAIT; + break; + case DMAMODE_WAIT: + errstring = "dmamode"; + if (wait_for_unbusy(chp, delay)) + goto timeout; + if (chp->ch_status & WDCS_ERR) + goto error; + /* fall through */ + + case READY: + ready: + drvp->state = READY; + xfer->c_intr = wdc_atapi_intr; + wdc_atapi_start(chp, xfer); + return 1; + } + if ((sc_xfer->flags & SCSI_POLL) == 0) { + chp->ch_flags |= WDCF_IRQ_WAIT; + xfer->c_intr = wdc_atapi_ctrl; + timeout(wdctimeout, chp, sc_xfer->timeout * hz / 1000); + } else { + goto again; + } + return 1; + +timeout: + if (irq && (xfer->c_flags & C_TIMEOU) == 0) { + return 0; /* IRQ was not for us */ + } + printf("%s:%d:%d: %s timed out\n", + chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive, errstring); + sc_xfer->error = XS_TIMEOUT; + wdc_atapi_reset(chp, xfer); + return 1; +error: + printf("%s:%d:%d: %s ", + chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive, + errstring); + printf("error (0x%x)\n", chp->ch_error); + sc_xfer->error = XS_SHORTSENSE; + ATAPI_TO_SCSI_SENSE(&sc_xfer->sense, chp->ch_error); + wdc_atapi_reset(chp, xfer); + return 1; +} + +void +wdc_atapi_done(chp, xfer) + struct channel_softc *chp; + struct wdc_xfer *xfer; +{ + struct scsi_xfer *sc_xfer = xfer->cmd; + int need_done = xfer->c_flags & C_NEEDDONE; + struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive]; + + WDCDEBUG_PRINT(("wdc_atapi_done %s:%d:%d: flags 0x%x\n", + chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive, + (u_int)xfer->c_flags), DEBUG_XFERS); + /* remove this command from xfer queue */ + wdc_free_xfer(chp, xfer); + sc_xfer->flags |= ITSDONE; + if (drvp->n_dmaerrs || + (sc_xfer->error != XS_NOERROR && sc_xfer->error != XS_SENSE && + sc_xfer->error != XS_SHORTSENSE)) { + drvp->n_dmaerrs = 0; + wdc_downgrade_mode(drvp); + } + + if (need_done) { + WDCDEBUG_PRINT(("wdc_atapi_done: scsi_done\n"), DEBUG_XFERS); + scsi_done(sc_xfer); + } + WDCDEBUG_PRINT(("wdcstart from wdc_atapi_done, flags 0x%x\n", + chp->ch_flags), DEBUG_XFERS); + wdcstart(chp); +} + +void +wdc_atapi_reset(chp, xfer) + struct channel_softc *chp; + struct wdc_xfer *xfer; +{ + struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive]; + struct scsi_xfer *sc_xfer = xfer->cmd; + + wdccommandshort(chp, xfer->drive, ATAPI_SOFT_RESET); + drvp->state = 0; + if (wait_for_unbusy(chp, WDC_RESET_WAIT) != 0) { + printf("%s:%d:%d: reset failed\n", + chp->wdc->sc_dev.dv_xname, chp->channel, + xfer->drive); + sc_xfer->error = XS_SELTIMEOUT; + } + wdc_atapi_done(chp, xfer); + return; +} + diff --git a/sys/dev/atapiscsi/files.atapiscsi b/sys/dev/atapiscsi/files.atapiscsi new file mode 100644 index 00000000000..24744a98c0d --- /dev/null +++ b/sys/dev/atapiscsi/files.atapiscsi @@ -0,0 +1,8 @@ +# $OpenBSD: files.atapiscsi,v 1.1 1999/07/18 21:25:18 csapuntz Exp $ +# ATAPI transport for SCSI protocl +# + +device atapiscsi: scsi +attach atapiscsi at atapi +file dev/atapiscsi/atapiscsi.c atapiscsi +#file dev/atapiscsi/atapi_base.c atapiscsi diff --git a/sys/dev/ic/wdc.c b/sys/dev/ic/wdc.c new file mode 100644 index 00000000000..da7afbdce7d --- /dev/null +++ b/sys/dev/ic/wdc.c @@ -0,0 +1,1398 @@ +/* $OpenBSD: wdc.c,v 1.1 1999/07/18 21:25:16 csapuntz Exp $ */ +/* $NetBSD: wdc.c,v 1.68 1999/06/23 19:00:17 bouyer Exp $ */ + + +/* + * Copyright (c) 1998 Manuel Bouyer. All rights reserved. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Manuel Bouyer. + * 4. 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. + */ + +/*- + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Charles M. Hannum, by Onno van der Linden and 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 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. + */ + +/* + * CODE UNTESTED IN THE CURRENT REVISION: + * + */ + +#ifndef WDCDEBUG +#define WDCDEBUG +#endif /* WDCDEBUG */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/conf.h> +#include <sys/buf.h> +#include <sys/device.h> +#include <sys/malloc.h> +#include <sys/syslog.h> +#include <sys/proc.h> + +#include <vm/vm.h> + +#include <machine/intr.h> +#include <machine/bus.h> + +#ifndef __BUS_SPACE_HAS_STREAM_METHODS +#define bus_space_write_multi_stream_2 bus_space_write_multi_2 +#define bus_space_write_multi_stream_4 bus_space_write_multi_4 +#define bus_space_read_multi_stream_2 bus_space_read_multi_2 +#define bus_space_read_multi_stream_4 bus_space_read_multi_4 +#endif /* __BUS_SPACE_HAS_STREAM_METHODS */ + +#include <dev/ata/atavar.h> +#include <dev/ata/atareg.h> +#include <dev/ic/wdcreg.h> +#include <dev/ic/wdcvar.h> + +#include "atapibus.h" + +#define WDCDELAY 100 /* 100 microseconds */ +#define WDCNDELAY_RST (WDC_RESET_WAIT * 1000 / WDCDELAY) +#if 0 +/* If you enable this, it will report any delays more than WDCDELAY * N long. */ +#define WDCNDELAY_DEBUG 50 +#endif + +LIST_HEAD(xfer_free_list, wdc_xfer) xfer_free_list; + +static void __wdcerror __P((struct channel_softc*, char *)); +static int __wdcwait_reset __P((struct channel_softc *, int)); +void __wdccommand_done __P((struct channel_softc *, struct wdc_xfer *)); +void __wdccommand_start __P((struct channel_softc *, struct wdc_xfer *)); +int __wdccommand_intr __P((struct channel_softc *, struct wdc_xfer *, int)); +int wdprint __P((void *, const char *)); + + +#define DEBUG_INTR 0x01 +#define DEBUG_XFERS 0x02 +#define DEBUG_STATUS 0x04 +#define DEBUG_FUNCS 0x08 +#define DEBUG_PROBE 0x10 +#ifdef WDCDEBUG +int wdcdebug_mask = 0; +int wdc_nxfer = 0; +#define WDCDEBUG_PRINT(args, level) if (wdcdebug_mask & (level)) printf args +#else +#define WDCDEBUG_PRINT(args, level) +#endif + +int +wdprint(aux, pnp) + void *aux; + const char *pnp; +{ + struct ata_atapi_attach *aa_link = aux; + if (pnp) + printf("drive at %s", pnp); + printf(" channel %d drive %d", aa_link->aa_channel, + aa_link->aa_drv_data->drive); + return (UNCONF); +} + +int +atapi_print(aux, pnp) + void *aux; + const char *pnp; +{ + struct ata_atapi_attach *aa_link = aux; + if (pnp) + printf("atapibus at %s", pnp); + printf(" channel %d", aa_link->aa_channel); + return (UNCONF); +} + +/* 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). If no drive at all -> return. + * - reset the controller, wait for it to complete (may take up to 31s !). + * If timeout -> return. + * - test ATA/ATAPI signatures. If at last one drive found -> return. + * - try an ATA command on the master. + */ + +int +wdcprobe(chp) + struct channel_softc *chp; +{ + u_int8_t st0, st1, sc, sn, cl, ch; + u_int8_t ret_value = 0x03; + u_int8_t drive; + + /* + * Sanity check to see if the wdc channel responds at all. + */ + + if (chp->wdc == NULL || + (chp->wdc->cap & WDC_CAPABILITY_NO_EXTRA_RESETS) == 0) { + bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sdh, + WDSD_IBM); + delay(10); + st0 = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_status); + bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sdh, + WDSD_IBM | 0x10); + delay(10); + st1 = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_status); + + WDCDEBUG_PRINT(("%s:%d: before reset, st0=0x%x, st1=0x%x\n", + chp->wdc ? chp->wdc->sc_dev.dv_xname : "wdcprobe", + chp->channel, st0, st1), DEBUG_PROBE); + + if (st0 == 0xff) + ret_value &= ~0x01; + if (st1 == 0xff) + ret_value &= ~0x02; + if (ret_value == 0) + return 0; + } + + /* assert SRST, wait for reset to complete */ + bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sdh, + WDSD_IBM); + delay(10); + bus_space_write_1(chp->ctl_iot, chp->ctl_ioh, wd_aux_ctlr, + WDCTL_RST | WDCTL_IDS); + DELAY(1000); + bus_space_write_1(chp->ctl_iot, chp->ctl_ioh, wd_aux_ctlr, + WDCTL_IDS); + delay(1000); + (void) bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_error); + bus_space_write_1(chp->ctl_iot, chp->ctl_ioh, wd_aux_ctlr, WDCTL_4BIT); + delay(10); + + ret_value = __wdcwait_reset(chp, ret_value); + WDCDEBUG_PRINT(("%s:%d: after reset, ret_value=0x%d\n", + chp->wdc ? chp->wdc->sc_dev.dv_xname : "wdcprobe", chp->channel, + ret_value), DEBUG_PROBE); + + /* if reset failed, there's nothing here */ + if (ret_value == 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 ((ret_value & (0x01 << drive)) == 0) + continue; + bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sdh, + WDSD_IBM | (drive << 4)); + delay(10); + /* Save registers contents */ + sc = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_seccnt); + sn = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_sector); + cl = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_cyl_lo); + ch = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_cyl_hi); + + WDCDEBUG_PRINT(("%s:%d:%d: after reset, sc=0x%x sn=0x%x " + "cl=0x%x ch=0x%x\n", + chp->wdc ? chp->wdc->sc_dev.dv_xname : "wdcprobe", + chp->channel, drive, sc, sn, cl, ch), DEBUG_PROBE); + /* + * sc is supposted to be 0x1 for ATAPI but at last one drive + * set it to 0x0 - or maybe it's the controller. + */ + if ((sc == 0x00 || sc == 0x01) && sn == 0x01 && + cl == 0x14 && ch == 0xeb) { + chp->ch_drive[drive].drive_flags |= DRIVE_ATAPI; + } else { + chp->ch_drive[drive].drive_flags |= DRIVE_ATA; + if (chp->wdc == NULL || + (chp->wdc->cap & WDC_CAPABILITY_PREATA) != 0) + chp->ch_drive[drive].drive_flags |= DRIVE_OLD; + } + } + return (ret_value); +} + +void +wdcattach(chp) + struct channel_softc *chp; +{ + int channel_flags, ctrl_flags, i; +#ifndef __OpenBSD__ + int error; +#endif + struct ata_atapi_attach aa_link; + struct ataparams params; + static int inited = 0; + +#ifndef __OpenBSD__ + if ((error = wdc_addref(chp)) != 0) { + printf("%s: unable to enable controller\n", + chp->wdc->sc_dev.dv_xname); + return; + } +#endif + + if (wdcprobe(chp) == 0) { + /* If no drives, abort attach here. */ +#ifndef __OpenBSD__ + wdc_delref(chp); +#endif + return; + } + + /* init list only once */ + if (inited == 0) { + LIST_INIT(&xfer_free_list); + inited++; + } + TAILQ_INIT(&chp->ch_queue->sc_xfer); + + for (i = 0; i < 2; i++) { + chp->ch_drive[i].chnl_softc = chp; + chp->ch_drive[i].drive = i; + /* If controller can't do 16bit flag the drives as 32bit */ + if ((chp->wdc->cap & + (WDC_CAPABILITY_DATA16 | WDC_CAPABILITY_DATA32)) == + WDC_CAPABILITY_DATA32) + chp->ch_drive[i].drive_flags |= DRIVE_CAP32; + if ((chp->ch_drive[i].drive_flags & DRIVE) == 0) + continue; + + /* Issue a IDENTIFY command, to try to detect slave ghost */ + if (ata_get_params(&chp->ch_drive[i], AT_POLL, ¶ms) == + CMD_OK) { + /* If IDENTIFY succeded, this is not an OLD ctrl */ + chp->ch_drive[0].drive_flags &= ~DRIVE_OLD; + chp->ch_drive[1].drive_flags &= ~DRIVE_OLD; + } else { + chp->ch_drive[i].drive_flags &= + ~(DRIVE_ATA | DRIVE_ATAPI); + WDCDEBUG_PRINT(("%s:%d:%d: IDENTIFY failed\n", + chp->wdc->sc_dev.dv_xname, + chp->channel, i), DEBUG_PROBE); + if ((chp->ch_drive[i].drive_flags & DRIVE_OLD) == 0) + continue; + /* + * Pre-ATA drive ? + * Test registers writability (Error register not + * writable, but cyllo is), then try an ATA command. + */ + bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sdh, + WDSD_IBM | (i << 4)); + delay(10); + bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, + wd_error, 0x58); + bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, + wd_cyl_lo, 0xa5); + if (bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, + wd_error == 0x58) || + bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, + wd_cyl_lo) != 0xa5) { + WDCDEBUG_PRINT(("%s:%d:%d: register " + "writability failed\n", + chp->wdc->sc_dev.dv_xname, + chp->channel, i), DEBUG_PROBE); + chp->ch_drive[i].drive_flags &= ~DRIVE_OLD; + } + bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sdh, + WDSD_IBM | (i << 4)); + delay(100); + if (wait_for_ready(chp, 10000) != 0) { + WDCDEBUG_PRINT(("%s:%d:%d: not ready\n", + chp->wdc->sc_dev.dv_xname, + chp->channel, i), DEBUG_PROBE); + chp->ch_drive[i].drive_flags &= ~DRIVE_OLD; + continue; + } + bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, + wd_command, WDCC_RECAL); + if (wait_for_ready(chp, 10000) != 0) { + WDCDEBUG_PRINT(("%s:%d:%d: WDCC_RECAL failed\n", + chp->wdc->sc_dev.dv_xname, + chp->channel, i), DEBUG_PROBE); + chp->ch_drive[i].drive_flags &= ~DRIVE_OLD; + } + } + } + ctrl_flags = chp->wdc->sc_dev.dv_cfdata->cf_flags; + channel_flags = (ctrl_flags >> (NBBY * chp->channel)) & 0xff; + + WDCDEBUG_PRINT(("wdcattach: ch_drive_flags 0x%x 0x%x\n", + chp->ch_drive[0].drive_flags, chp->ch_drive[1].drive_flags), + DEBUG_PROBE); + + /* If no drives, abort here */ + if ((chp->ch_drive[0].drive_flags & DRIVE) == 0 && + (chp->ch_drive[1].drive_flags & DRIVE) == 0) + return; + + /* + * Attach an ATAPI bus, if needed. + */ + if ((chp->ch_drive[0].drive_flags & DRIVE_ATAPI) || + (chp->ch_drive[1].drive_flags & DRIVE_ATAPI)) { +#if NATAPIBUS > 0 + wdc_atapibus_attach(chp); +#else + /* + * Fills in a fake aa_link and call config_found, so that + * the config machinery will print + * "atapibus at xxx not configured" + */ + memset(&aa_link, 0, sizeof(struct ata_atapi_attach)); + aa_link.aa_type = T_ATAPI; + aa_link.aa_channel = chp->channel; + aa_link.aa_openings = 1; + aa_link.aa_drv_data = 0; + aa_link.aa_bus_private = NULL; + (void)config_found(&chp->wdc->sc_dev, (void *)&aa_link, + atapi_print); +#endif + } + + for (i = 0; i < 2; i++) { + if ((chp->ch_drive[i].drive_flags & + (DRIVE_ATA | DRIVE_OLD)) == 0) { + continue; + } + memset(&aa_link, 0, sizeof(struct ata_atapi_attach)); + aa_link.aa_type = T_ATA; + aa_link.aa_channel = chp->channel; + aa_link.aa_openings = 1; + aa_link.aa_drv_data = &chp->ch_drive[i]; + if (config_found(&chp->wdc->sc_dev, (void *)&aa_link, wdprint)) + wdc_probe_caps(&chp->ch_drive[i]); + } + + /* + * reset drive_flags for unnatached devices, reset state for attached + * ones + */ + for (i = 0; i < 2; i++) { + if (chp->ch_drive[i].drv_softc == NULL) + chp->ch_drive[i].drive_flags = 0; + else + chp->ch_drive[i].state = 0; + } + + /* + * Reset channel. The probe, with some combinations of ATA/ATAPI + * devices keep it in a mostly working, but strange state (with busy + * led on) + */ + if ((chp->wdc->cap & WDC_CAPABILITY_NO_EXTRA_RESETS) == 0) { + wdcreset(chp, VERBOSE); + /* + * Read status registers to avoid spurious interrupts. + */ + for (i = 1; i >= 0; i--) { + if (chp->ch_drive[i].drive_flags & DRIVE) { + bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, + wd_sdh, WDSD_IBM | (i << 4)); + if (wait_for_unbusy(chp, 10000) < 0) + printf("%s:%d:%d: device busy\n", + chp->wdc->sc_dev.dv_xname, + chp->channel, i); + } + } + } +#ifndef __OpenBSD__ + wdc_delref(chp); +#endif +} + +void +wdc_final_attach(chp) + struct channel_softc *chp; +{ +#if NATAPIBUS > 0 + wdc_atapibus_final_attach(chp); +#endif +} + +/* + * Start I/O on a controller, for the given channel. + * The first xfer may be not for our channel if the channel queues + * are shared. + */ +void +wdcstart(chp) + struct channel_softc *chp; +{ + struct wdc_xfer *xfer; + +#ifdef WDC_DIAGNOSTIC + int spl1, spl2; + + spl1 = splbio(); + spl2 = splbio(); + if (spl2 != spl1) { + printf("wdcstart: not at splbio()\n"); + panic("wdcstart"); + } + splx(spl2); + splx(spl1); +#endif /* WDC_DIAGNOSTIC */ + + /* is there a xfer ? */ + if ((xfer = chp->ch_queue->sc_xfer.tqh_first) == NULL) + return; + + /* adjust chp, in case we have a shared queue */ + chp = xfer->chp; + + if ((chp->ch_flags & WDCF_ACTIVE) != 0 ) { + return; /* channel aleady active */ + } +#ifdef DIAGNOSTIC + if ((chp->ch_flags & WDCF_IRQ_WAIT) != 0) + panic("wdcstart: channel waiting for irq\n"); +#endif + if (chp->wdc->cap & WDC_CAPABILITY_HWLOCK) + if (!(*chp->wdc->claim_hw)(chp, 0)) + return; + + WDCDEBUG_PRINT(("wdcstart: xfer %p channel %d drive %d\n", xfer, + chp->channel, xfer->drive), DEBUG_XFERS); + chp->ch_flags |= WDCF_ACTIVE; + if (chp->ch_drive[xfer->drive].drive_flags & DRIVE_RESET) { + chp->ch_drive[xfer->drive].drive_flags &= ~DRIVE_RESET; + chp->ch_drive[xfer->drive].state = 0; + } + xfer->c_start(chp, xfer); +} + +/* restart an interrupted I/O */ +void +wdcrestart(v) + void *v; +{ + struct channel_softc *chp = v; + int s; + + s = splbio(); + wdcstart(chp); + splx(s); +} + + +/* + * Interrupt routine for the controller. Acknowledge the interrupt, check for + * errors on the current operation, mark it done if necessary, and start the + * next request. Also check for a partially done transfer, and continue with + * the next chunk if so. + */ +int +wdcintr(arg) + void *arg; +{ + struct channel_softc *chp = arg; + struct wdc_xfer *xfer; + + if ((chp->ch_flags & WDCF_IRQ_WAIT) == 0) { + WDCDEBUG_PRINT(("wdcintr: inactive controller\n"), DEBUG_INTR); + return 0; + } + + WDCDEBUG_PRINT(("wdcintr\n"), DEBUG_INTR); + untimeout(wdctimeout, chp); + chp->ch_flags &= ~WDCF_IRQ_WAIT; + xfer = chp->ch_queue->sc_xfer.tqh_first; + return xfer->c_intr(chp, xfer, 1); +} + +/* Put all disk in RESET state */ +void wdc_reset_channel(drvp) + struct ata_drive_datas *drvp; +{ + struct channel_softc *chp = drvp->chnl_softc; + int drive; + WDCDEBUG_PRINT(("ata_reset_channel %s:%d for drive %d\n", + chp->wdc->sc_dev.dv_xname, chp->channel, drvp->drive), + DEBUG_FUNCS); + (void) wdcreset(chp, VERBOSE); + for (drive = 0; drive < 2; drive++) { + chp->ch_drive[drive].state = 0; + } +} + +int +wdcreset(chp, verb) + struct channel_softc *chp; + int verb; +{ + int drv_mask1, drv_mask2; + + bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sdh, + WDSD_IBM); /* master */ + bus_space_write_1(chp->ctl_iot, chp->ctl_ioh, wd_aux_ctlr, + WDCTL_RST | WDCTL_IDS); + delay(1000); + bus_space_write_1(chp->ctl_iot, chp->ctl_ioh, wd_aux_ctlr, + WDCTL_IDS); + delay(1000); + (void) bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_error); + bus_space_write_1(chp->ctl_iot, chp->ctl_ioh, wd_aux_ctlr, + WDCTL_4BIT); + + drv_mask1 = (chp->ch_drive[0].drive_flags & DRIVE) ? 0x01:0x00; + drv_mask1 |= (chp->ch_drive[1].drive_flags & DRIVE) ? 0x02:0x00; + drv_mask2 = __wdcwait_reset(chp, drv_mask1); + if (verb && drv_mask2 != drv_mask1) { + printf("%s channel %d: reset failed for", + chp->wdc->sc_dev.dv_xname, chp->channel); + if ((drv_mask1 & 0x01) != 0 && (drv_mask2 & 0x01) == 0) + printf(" drive 0"); + if ((drv_mask1 & 0x02) != 0 && (drv_mask2 & 0x02) == 0) + printf(" drive 1"); + printf("\n"); + } + return (drv_mask1 != drv_mask2) ? 1 : 0; +} + +static int +__wdcwait_reset(chp, drv_mask) + struct channel_softc *chp; + int drv_mask; +{ + int timeout; + u_int8_t st0, st1; + /* wait for BSY to deassert */ + for (timeout = 0; timeout < WDCNDELAY_RST;timeout++) { + bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sdh, + WDSD_IBM); /* master */ + delay(10); + st0 = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_status); + bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sdh, + WDSD_IBM | 0x10); /* slave */ + delay(10); + st1 = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, 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 rigth */ + if (st0 & WDCS_BSY) + drv_mask &= ~0x01; + if (st1 & WDCS_BSY) + drv_mask &= ~0x02; +end: + WDCDEBUG_PRINT(("%s:%d: wdcwait_reset() end, st0=0x%x, st1=0x%x\n", + chp->wdc ? chp->wdc->sc_dev.dv_xname : "wdcprobe", chp->channel, + st0, st1), DEBUG_PROBE); + + return drv_mask; +} + +/* + * Wait for a drive to be !BSY, and have mask in its status register. + * return -1 for a timeout after "timeout" ms. + */ +int +wdcwait(chp, mask, bits, timeout) + struct channel_softc *chp; + int mask, bits, timeout; +{ + u_char status; + int time = 0; +#ifdef WDCNDELAY_DEBUG + extern int cold; +#endif + + WDCDEBUG_PRINT(("wdcwait %s:%d\n", chp->wdc ?chp->wdc->sc_dev.dv_xname + :"none", chp->channel), DEBUG_STATUS); + chp->ch_error = 0; + + timeout = timeout * 1000 / WDCDELAY; /* delay uses microseconds */ + + for (;;) { + chp->ch_status = status = + bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_status); + if ((status & WDCS_BSY) == 0 && (status & mask) == bits) + break; + if (++time > timeout) { + WDCDEBUG_PRINT(("wdcwait: timeout, status %x " + "error %x\n", status, + bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, + wd_error)), + DEBUG_STATUS); + return -1; + } + delay(WDCDELAY); + } + if (status & WDCS_ERR) + chp->ch_error = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, + wd_error); +#ifdef WDCNDELAY_DEBUG + /* After autoconfig, there should be no long delays. */ + if (!cold && time > WDCNDELAY_DEBUG) { + struct wdc_xfer *xfer = chp->ch_queue->sc_xfer.tqh_first; + if (xfer == NULL) + printf("%s channel %d: warning: busy-wait took %dus\n", + chp->wdc->sc_dev.dv_xname, chp->channel, + WDCDELAY * time); + else + printf("%s:%d:%d: warning: busy-wait took %dus\n", + chp->wdc->sc_dev.dv_xname, chp->channel, + xfer->drive, + WDCDELAY * time); + } +#endif + return 0; +} + +void +wdctimeout(arg) + void *arg; +{ + struct channel_softc *chp = (struct channel_softc *)arg; + struct wdc_xfer *xfer = chp->ch_queue->sc_xfer.tqh_first; + int s; + + WDCDEBUG_PRINT(("wdctimeout\n"), DEBUG_FUNCS); + + s = splbio(); + if ((chp->ch_flags & WDCF_IRQ_WAIT) != 0) { + __wdcerror(chp, "lost interrupt"); + printf("\ttype: %s\n", (xfer->c_flags & C_ATAPI) ? + "atapi":"ata"); + printf("\tc_bcount: %d\n", xfer->c_bcount); + printf("\tc_skip: %d\n", xfer->c_skip); + /* + * Call the interrupt routine. If we just missed and interrupt, + * it will do what's needed. Else, it will take the needed + * action (reset the device). + */ + xfer->c_flags |= C_TIMEOU; + chp->ch_flags &= ~WDCF_IRQ_WAIT; + xfer->c_intr(chp, xfer, 1); + } else + __wdcerror(chp, "missing untimeout"); + splx(s); +} + +/* + * Probe drive's capabilites, for use by the controller later + * Assumes drvp points to an existing drive. + * XXX this should be a controller-indep function + */ +void +wdc_probe_caps(drvp) + struct ata_drive_datas *drvp; +{ + struct ataparams params, params2; + struct channel_softc *chp = drvp->chnl_softc; + struct device *drv_dev = drvp->drv_softc; + struct wdc_softc *wdc = chp->wdc; + int i, printed; + char *sep = ""; + int cf_flags; + + if (ata_get_params(drvp, AT_POLL, ¶ms) != CMD_OK) { + /* IDENTIFY failed. Can't tell more about the device */ + return; + } + if ((wdc->cap & (WDC_CAPABILITY_DATA16 | WDC_CAPABILITY_DATA32)) == + (WDC_CAPABILITY_DATA16 | WDC_CAPABILITY_DATA32)) { + /* + * Controller claims 16 and 32 bit transfers. + * Re-do an IDENTIFY with 32-bit transfers, + * and compare results. + */ + drvp->drive_flags |= DRIVE_CAP32; + ata_get_params(drvp, AT_POLL, ¶ms2); + if (memcmp(¶ms, ¶ms2, sizeof(struct ataparams)) != 0) { + /* Not good. fall back to 16bits */ + drvp->drive_flags &= ~DRIVE_CAP32; + } else { + printf("%s: 32-bits data port", drv_dev->dv_xname); + } + } +#if 0 /* Some ultra-DMA drives claims to only support ATA-3. sigh */ + if (params.atap_ata_major > 0x01 && + params.atap_ata_major != 0xffff) { + for (i = 14; i > 0; i--) { + if (params.atap_ata_major & (1 << i)) { + if ((drvp->drive_flags & DRIVE_CAP32) == 0) + printf("%s: ", drv_dev->dv_xname); + else + printf(", "); + printf("ATA version %d\n", i); + drvp->ata_vers = i; + break; + } + } + } else +#endif + if (drvp->drive_flags & DRIVE_CAP32) + printf("\n"); + + /* An ATAPI device is at last PIO mode 3 */ + if (drvp->drive_flags & DRIVE_ATAPI) + drvp->PIO_mode = 3; + + /* + * It's not in the specs, but it seems that some drive + * returns 0xffff in atap_extensions when this field is invalid + */ + if (params.atap_extensions != 0xffff && + (params.atap_extensions & WDC_EXT_MODES)) { + printed = 0; + /* + * XXX some drives report something wrong here (they claim to + * support PIO mode 8 !). As mode is coded on 3 bits in + * SET FEATURE, limit it to 7 (so limit i to 4). + * If higther mode than 7 is found, abort. + */ + for (i = 7; i >= 0; i--) { + if ((params.atap_piomode_supp & (1 << i)) == 0) + continue; + if (i > 4) + return; + /* + * See if mode is accepted. + * If the controller can't set its PIO mode, + * assume the defaults are good, so don't try + * to set it + */ + if ((wdc->cap & WDC_CAPABILITY_MODE) != 0) + if (ata_set_mode(drvp, 0x08 | (i + 3), + AT_POLL) != CMD_OK) + continue; + if (!printed) { + printf("%s: drive supports PIO mode %d", + drv_dev->dv_xname, i + 3); + sep = ","; + printed = 1; + } + /* + * If controller's driver can't set its PIO mode, + * get the highter one for the drive. + */ + if ((wdc->cap & WDC_CAPABILITY_MODE) == 0 || + wdc->PIO_cap >= i + 3) { + drvp->PIO_mode = i + 3; + drvp->PIO_cap = i + 3; + break; + } + } + if (!printed) { + /* + * We didn't find a valid PIO mode. + * Assume the values returned for DMA are buggy too + */ + return; + } + drvp->drive_flags |= DRIVE_MODE; + printed = 0; + for (i = 7; i >= 0; i--) { + if ((params.atap_dmamode_supp & (1 << i)) == 0) + continue; + if ((wdc->cap & WDC_CAPABILITY_DMA) && + (wdc->cap & WDC_CAPABILITY_MODE)) + if (ata_set_mode(drvp, 0x20 | i, AT_POLL) + != CMD_OK) + continue; + if (!printed) { + printf("%s DMA mode %d", sep, i); + sep = ","; + printed = 1; + } + if (wdc->cap & WDC_CAPABILITY_DMA) { + if ((wdc->cap & WDC_CAPABILITY_MODE) && + wdc->DMA_cap < i) + continue; + drvp->DMA_mode = i; + drvp->DMA_cap = i; + drvp->drive_flags |= DRIVE_DMA; + } + break; + } + if (params.atap_extensions & WDC_EXT_UDMA_MODES) { + for (i = 7; i >= 0; i--) { + if ((params.atap_udmamode_supp & (1 << i)) + == 0) + continue; + if ((wdc->cap & WDC_CAPABILITY_MODE) && + (wdc->cap & WDC_CAPABILITY_UDMA)) + if (ata_set_mode(drvp, 0x40 | i, + AT_POLL) != CMD_OK) + continue; + printf("%s Ultra-DMA mode %d", sep, i); + sep = ","; + if (wdc->cap & WDC_CAPABILITY_UDMA) { + if ((wdc->cap & WDC_CAPABILITY_MODE) && + wdc->UDMA_cap < i) + continue; + drvp->UDMA_mode = i; + drvp->UDMA_cap = i; + drvp->drive_flags |= DRIVE_UDMA; + } + break; + } + } + printf("\n"); + } + + /* Try to guess ATA version here, if it didn't get reported */ + if (drvp->ata_vers == 0) { + if (drvp->drive_flags & DRIVE_UDMA) + drvp->ata_vers = 4; /* should be at last ATA-4 */ + else if (drvp->PIO_cap > 2) + drvp->ata_vers = 2; /* should be at last ATA-2 */ + } + cf_flags = drv_dev->dv_cfdata->cf_flags; + if (cf_flags & ATA_CONFIG_PIO_SET) { + drvp->PIO_mode = + (cf_flags & ATA_CONFIG_PIO_MODES) >> ATA_CONFIG_PIO_OFF; + drvp->drive_flags |= DRIVE_MODE; + } + if ((wdc->cap & WDC_CAPABILITY_DMA) == 0) { + /* don't care about DMA modes */ + return; + } + if (cf_flags & ATA_CONFIG_DMA_SET) { + if ((cf_flags & ATA_CONFIG_DMA_MODES) == + ATA_CONFIG_DMA_DISABLE) { + drvp->drive_flags &= ~DRIVE_DMA; + } else { + drvp->DMA_mode = (cf_flags & ATA_CONFIG_DMA_MODES) >> + ATA_CONFIG_DMA_OFF; + drvp->drive_flags |= DRIVE_DMA | DRIVE_MODE; + } + } + if (cf_flags & ATA_CONFIG_UDMA_SET) { + if ((cf_flags & ATA_CONFIG_UDMA_MODES) == + ATA_CONFIG_UDMA_DISABLE) { + drvp->drive_flags &= ~DRIVE_UDMA; + } else { + drvp->UDMA_mode = (cf_flags & ATA_CONFIG_UDMA_MODES) >> + ATA_CONFIG_UDMA_OFF; + drvp->drive_flags |= DRIVE_UDMA | DRIVE_MODE; + } + } +} + +/* + * downgrade the transfer mode of a drive after an error. return 1 if + * downgrade was possible, 0 otherwise. + */ +int +wdc_downgrade_mode(drvp) + struct ata_drive_datas *drvp; +{ + struct channel_softc *chp = drvp->chnl_softc; + struct device *drv_dev = drvp->drv_softc; + struct wdc_softc *wdc = chp->wdc; + int cf_flags = drv_dev->dv_cfdata->cf_flags; + + /* if drive or controller don't know its mode, we can't do much */ + if ((drvp->drive_flags & DRIVE_MODE) == 0 || + (wdc->cap & WDC_CAPABILITY_MODE) == 0) + return 0; + /* current drive mode was set by a config flag, let it this way */ + if ((cf_flags & ATA_CONFIG_PIO_SET) || + (cf_flags & ATA_CONFIG_DMA_SET) || + (cf_flags & ATA_CONFIG_UDMA_SET)) + return 0; + + /* + * If we were using ultra-DMA, don't downgrade to multiword DMA + * if we noticed a CRC error. It has been noticed that CRC errors + * in ultra-DMA lead to silent data corruption in multiword DMA. + * Data corruption is less likely to occur in PIO mode. + */ + + if ((drvp->drive_flags & DRIVE_UDMA) && + (drvp->drive_flags & DRIVE_DMAERR) == 0) { + drvp->drive_flags &= ~DRIVE_UDMA; + drvp->drive_flags |= DRIVE_DMA; + drvp->DMA_mode = drvp->DMA_cap; + printf("%s: transfer error, downgrading to DMA mode %d\n", + drv_dev->dv_xname, drvp->DMA_mode); + } else if (drvp->drive_flags & (DRIVE_DMA | DRIVE_UDMA)) { + drvp->drive_flags &= ~(DRIVE_DMA | DRIVE_UDMA); + drvp->PIO_mode = drvp->PIO_cap; + printf("%s: transfer error, downgrading to PIO mode %d\n", + drv_dev->dv_xname, drvp->PIO_mode); + } else /* already using PIO, can't downgrade */ + return 0; + + wdc->set_modes(chp); + /* reset the channel, which will shedule all drives for setup */ + wdc_reset_channel(drvp); + return 1; +} + +int +wdc_exec_command(drvp, wdc_c) + struct ata_drive_datas *drvp; + struct wdc_command *wdc_c; +{ + struct channel_softc *chp = drvp->chnl_softc; + struct wdc_xfer *xfer; + int s, ret; + + WDCDEBUG_PRINT(("wdc_exec_command %s:%d:%d\n", + chp->wdc->sc_dev.dv_xname, chp->channel, drvp->drive), + DEBUG_FUNCS); + + /* set up an xfer and queue. Wait for completion */ + xfer = wdc_get_xfer(wdc_c->flags & AT_WAIT ? WDC_CANSLEEP : + WDC_NOSLEEP); + if (xfer == NULL) { + return WDC_TRY_AGAIN; + } + + if (wdc_c->flags & AT_POLL) + xfer->c_flags |= C_POLL; + xfer->drive = drvp->drive; + xfer->databuf = wdc_c->data; + xfer->c_bcount = wdc_c->bcount; + xfer->cmd = wdc_c; + xfer->c_start = __wdccommand_start; + xfer->c_intr = __wdccommand_intr; + + s = splbio(); + wdc_exec_xfer(chp, xfer); +#ifdef DIAGNOSTIC + if ((wdc_c->flags & AT_POLL) != 0 && + (wdc_c->flags & AT_DONE) == 0) + panic("wdc_exec_command: polled command not done\n"); +#endif + if (wdc_c->flags & AT_DONE) { + ret = WDC_COMPLETE; + } else { + if (wdc_c->flags & AT_WAIT) { + WDCDEBUG_PRINT(("wdc_exec_command sleeping"), + DEBUG_FUNCS); + + while (!(wdc_c->flags & AT_DONE)) { + int error; + error = tsleep(wdc_c, PRIBIO, "wdccmd", 0); + + if (error) { + printf ("tsleep error: %d\n", error); + } + } + + WDCDEBUG_PRINT(("wdc_exec_command waking"), + DEBUG_FUNCS); + + ret = WDC_COMPLETE; + } else { + ret = WDC_QUEUED; + } + } + splx(s); + return ret; +} + +void +__wdccommand_start(chp, xfer) + struct channel_softc *chp; + struct wdc_xfer *xfer; +{ + int drive = xfer->drive; + struct wdc_command *wdc_c = xfer->cmd; + + WDCDEBUG_PRINT(("__wdccommand_start %s:%d:%d\n", + chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive), + DEBUG_FUNCS); + + bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sdh, + WDSD_IBM | (drive << 4)); + if (wdcwait(chp, wdc_c->r_st_bmask, wdc_c->r_st_bmask, + wdc_c->timeout) != 0) { + wdc_c->flags |= AT_TIMEOU; + __wdccommand_done(chp, xfer); + return; + } + wdccommand(chp, drive, wdc_c->r_command, wdc_c->r_cyl, wdc_c->r_head, + wdc_c->r_sector, wdc_c->r_count, wdc_c->r_precomp); + if ((wdc_c->flags & AT_POLL) == 0) { + chp->ch_flags |= WDCF_IRQ_WAIT; /* wait for interrupt */ + timeout(wdctimeout, chp, wdc_c->timeout / 1000 * hz); + return; + } + /* + * Polled command. Wait for drive ready or drq. Done in intr(). + * Wait for at last 400ns for status bit to be valid. + */ + delay(10); + __wdccommand_intr(chp, xfer, 0); +} + +int +__wdccommand_intr(chp, xfer, irq) + struct channel_softc *chp; + struct wdc_xfer *xfer; + int irq; +{ + struct wdc_command *wdc_c = xfer->cmd; + int bcount = wdc_c->bcount; + char *data = wdc_c->data; + + WDCDEBUG_PRINT(("__wdccommand_intr %s:%d:%d\n", + chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive), DEBUG_INTR); + if (wdcwait(chp, wdc_c->r_st_pmask, wdc_c->r_st_pmask, + (irq == 0) ? wdc_c->timeout : 0)) { + if (irq && (xfer->c_flags & C_TIMEOU) == 0) + return 0; /* IRQ was not for us */ + wdc_c->flags |= AT_TIMEOU; + __wdccommand_done(chp, xfer); + WDCDEBUG_PRINT(("__wdccommand_intr returned\n"), DEBUG_INTR); + return 1; + } + if (wdc_c->flags & AT_READ) { + if (chp->ch_drive[xfer->drive].drive_flags & DRIVE_CAP32) { + bus_space_read_multi_4(chp->data32iot, chp->data32ioh, + 0, (u_int32_t*)data, bcount >> 2); + data += bcount & 0xfffffffc; + bcount = bcount & 0x03; + } + if (bcount > 0) + bus_space_read_multi_2(chp->cmd_iot, chp->cmd_ioh, + wd_data, (u_int16_t *)data, bcount >> 1); + } else if (wdc_c->flags & AT_WRITE) { + if (chp->ch_drive[xfer->drive].drive_flags & DRIVE_CAP32) { + bus_space_write_multi_4(chp->data32iot, chp->data32ioh, + 0, (u_int32_t*)data, bcount >> 2); + data += bcount & 0xfffffffc; + bcount = bcount & 0x03; + } + if (bcount > 0) + bus_space_write_multi_2(chp->cmd_iot, chp->cmd_ioh, + wd_data, (u_int16_t *)data, bcount >> 1); + } + __wdccommand_done(chp, xfer); + WDCDEBUG_PRINT(("__wdccommand_intr returned\n"), DEBUG_INTR); + return 1; +} + +void +__wdccommand_done(chp, xfer) + struct channel_softc *chp; + struct wdc_xfer *xfer; +{ + int needdone = xfer->c_flags & C_NEEDDONE; + struct wdc_command *wdc_c = xfer->cmd; + + WDCDEBUG_PRINT(("__wdccommand_done %s:%d:%d\n", + chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive), DEBUG_FUNCS); + if (chp->ch_status & WDCS_DWF) + wdc_c->flags |= AT_DF; + if (chp->ch_status & WDCS_ERR) { + wdc_c->flags |= AT_ERROR; + wdc_c->r_error = chp->ch_error; + } + wdc_c->flags |= AT_DONE; + if (wdc_c->flags & AT_READREG && (wdc_c->flags & (AT_ERROR | AT_DF)) + == 0) { + wdc_c->r_head = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, + wd_sdh); + wdc_c->r_cyl = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, + wd_cyl_hi) << 8; + wdc_c->r_cyl |= bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, + wd_cyl_lo); + wdc_c->r_sector = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, + wd_sector); + wdc_c->r_count = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, + wd_seccnt); + wdc_c->r_error = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, + wd_error); + wdc_c->r_precomp = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, + wd_precomp); + } + wdc_free_xfer(chp, xfer); + WDCDEBUG_PRINT(("__wdccommand_done before callback\n"), DEBUG_INTR); + if (needdone) { + if (wdc_c->flags & AT_WAIT) + wakeup(wdc_c); + else + wdc_c->callback(wdc_c->callback_arg); + } + wdcstart(chp); + WDCDEBUG_PRINT(("__wdccommand_done returned\n"), DEBUG_INTR); + return; +} + +/* + * Send a command. The drive should be ready. + * Assumes interrupts are blocked. + */ +void +wdccommand(chp, drive, command, cylin, head, sector, count, precomp) + struct channel_softc *chp; + u_int8_t drive; + u_int8_t command; + u_int16_t cylin; + u_int8_t head, sector, count, precomp; +{ + WDCDEBUG_PRINT(("wdccommand %s:%d:%d: command=0x%x cylin=%d head=%d " + "sector=%d count=%d precomp=%d\n", chp->wdc->sc_dev.dv_xname, + chp->channel, drive, command, cylin, head, sector, count, precomp), + DEBUG_FUNCS); + + /* Select drive, head, and addressing mode. */ + bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sdh, + WDSD_IBM | (drive << 4) | head); + /* Load parameters. wd_features(ATA/ATAPI) = wd_precomp(ST506) */ + bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_precomp, + precomp); + bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_cyl_lo, cylin); + bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_cyl_hi, cylin >> 8); + bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sector, sector); + bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_seccnt, count); + + /* Send command. */ + bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_command, command); + return; +} + +/* + * Simplified version of wdccommand(). Unbusy/ready/drq must be + * tested by the caller. + */ +void +wdccommandshort(chp, drive, command) + struct channel_softc *chp; + int drive; + int command; +{ + + WDCDEBUG_PRINT(("wdccommandshort %s:%d:%d command 0x%x\n", + chp->wdc->sc_dev.dv_xname, chp->channel, drive, command), + DEBUG_FUNCS); + + /* Select drive. */ + bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sdh, + WDSD_IBM | (drive << 4)); + + bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_command, command); +} + +/* Add a command to the queue and start controller. Must be called at splbio */ + +void +wdc_exec_xfer(chp, xfer) + struct channel_softc *chp; + struct wdc_xfer *xfer; +{ + WDCDEBUG_PRINT(("wdc_exec_xfer %p channel %d drive %d\n", xfer, + chp->channel, xfer->drive), DEBUG_XFERS); + + /* complete xfer setup */ + xfer->chp = chp; + + /* + * If we are a polled command, and the list is not empty, + * we are doing a dump. Drop the list to allow the polled command + * to complete, we're going to reboot soon anyway. + */ + if ((xfer->c_flags & C_POLL) != 0 && + chp->ch_queue->sc_xfer.tqh_first != NULL) { + TAILQ_INIT(&chp->ch_queue->sc_xfer); + } + /* insert at the end of command list */ + TAILQ_INSERT_TAIL(&chp->ch_queue->sc_xfer,xfer , c_xferchain); + WDCDEBUG_PRINT(("wdcstart from wdc_exec_xfer, flags 0x%x\n", + chp->ch_flags), DEBUG_XFERS); + wdcstart(chp); + xfer->c_flags |= C_NEEDDONE; /* we can now call upper level done() */ +} + +struct wdc_xfer * +wdc_get_xfer(flags) + int flags; +{ + struct wdc_xfer *xfer; + int s; + + s = splbio(); + if ((xfer = xfer_free_list.lh_first) != NULL) { + LIST_REMOVE(xfer, free_list); + splx(s); +#ifdef DIAGNOSTIC + if ((xfer->c_flags & C_INUSE) != 0) + panic("wdc_get_xfer: xfer already in use\n"); +#endif + } else { + splx(s); + WDCDEBUG_PRINT(("wdc:making xfer %d\n",wdc_nxfer), DEBUG_XFERS); + xfer = malloc(sizeof(*xfer), M_DEVBUF, + ((flags & WDC_NOSLEEP) != 0 ? M_NOWAIT : M_WAITOK)); + if (xfer == NULL) + return 0; +#ifdef DIAGNOSTIC + xfer->c_flags &= ~C_INUSE; +#endif +#ifdef WDCDEBUG + wdc_nxfer++; +#endif + } +#ifdef DIAGNOSTIC + if ((xfer->c_flags & C_INUSE) != 0) + panic("wdc_get_xfer: xfer already in use\n"); +#endif + memset(xfer, 0, sizeof(struct wdc_xfer)); + xfer->c_flags = C_INUSE; + return xfer; +} + +void +wdc_free_xfer(chp, xfer) + struct channel_softc *chp; + struct wdc_xfer *xfer; +{ + struct wdc_softc *wdc = chp->wdc; + int s; + + if (wdc->cap & WDC_CAPABILITY_HWLOCK) + (*wdc->free_hw)(chp); + s = splbio(); + chp->ch_flags &= ~WDCF_ACTIVE; + TAILQ_REMOVE(&chp->ch_queue->sc_xfer, xfer, c_xferchain); + xfer->c_flags &= ~C_INUSE; + LIST_INSERT_HEAD(&xfer_free_list, xfer, free_list); + splx(s); +} + +static void +__wdcerror(chp, msg) + struct channel_softc *chp; + char *msg; +{ + struct wdc_xfer *xfer = chp->ch_queue->sc_xfer.tqh_first; + if (xfer == NULL) + printf("%s:%d: %s\n", chp->wdc->sc_dev.dv_xname, chp->channel, + msg); + else + printf("%s:%d:%d: %s\n", chp->wdc->sc_dev.dv_xname, + chp->channel, xfer->drive, msg); +} + +/* + * the bit bucket + */ +void +wdcbit_bucket(chp, size) + struct channel_softc *chp; + int size; +{ + + for (; size >= 2; size -= 2) + (void)bus_space_read_2(chp->cmd_iot, chp->cmd_ioh, wd_data); + if (size) + (void)bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_data); +} + +#ifndef __OpenBSD__ +int +wdc_addref(chp) + struct channel_softc *chp; +{ + struct wdc_softc *wdc = chp->wdc; + struct scsipi_adapter *adapter = &wdc->sc_atapi_adapter; + int s, error = 0; + + s = splbio(); + if (adapter->scsipi_refcnt++ == 0 && + adapter->scsipi_enable != NULL) { + error = (*adapter->scsipi_enable)(wdc, 1); + if (error) + adapter->scsipi_refcnt--; + } + splx(s); + return (error); +} + +void +wdc_delref(chp) + struct channel_softc *chp; +{ + struct wdc_softc *wdc = chp->wdc; + struct scsipi_adapter *adapter = &wdc->sc_atapi_adapter; + int s; + + s = splbio(); + if (adapter->scsipi_refcnt-- == 1 && + adapter->scsipi_enable != NULL) + (void) (*adapter->scsipi_enable)(wdc, 0); + splx(s); +} +#endif diff --git a/sys/dev/ic/wdcreg.h b/sys/dev/ic/wdcreg.h new file mode 100644 index 00000000000..ec823a4a864 --- /dev/null +++ b/sys/dev/ic/wdcreg.h @@ -0,0 +1,181 @@ +/* $OpenBSD: wdcreg.h,v 1.1 1999/07/18 21:25:16 csapuntz Exp $ */ +/* $NetBSD: wdcreg.h,v 1.22 1999/03/07 14:02:54 bouyer Exp $ */ + +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * William Jolitz. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + * + * @(#)wdreg.h 7.1 (Berkeley) 5/9/91 + */ + +/* + * Disk Controller register definitions. + */ + +/* offsets of registers in the 'regular' register region */ +#define wd_data 0 /* data register (R/W - 16 bits) */ +#define wd_error 1 /* error register (R) */ +#define wd_precomp 1 /* write precompensation (W) */ +#define wd_features 1 /* features (W), same as wd_precomp */ +#define wd_seccnt 2 /* sector count (R/W) */ +#define wd_ireason 2 /* interrupt reason (R/W) (for atapi) */ +#define wd_sector 3 /* first sector number (R/W) */ +#define wd_cyl_lo 4 /* cylinder address, low byte (R/W) */ +#define wd_cyl_hi 5 /* cylinder address, high byte (R/W) */ +#define wd_sdh 6 /* sector size/drive/head (R/W) */ +#define wd_command 7 /* command register (W) */ +#define wd_status 7 /* immediate status (R) */ + +/* offsets of registers in the auxiliary register region */ +#define wd_aux_altsts 0 /* alternate fixed disk status (R) */ +#define wd_aux_ctlr 0 /* fixed disk controller control (W) */ +#define WDCTL_4BIT 0x08 /* use four head bits (wd1003) */ +#define WDCTL_RST 0x04 /* reset the controller */ +#define WDCTL_IDS 0x02 /* disable controller interrupts */ +#if 0 /* NOT MAPPED; fd uses this register on PCs */ +#define wd_digin 1 /* disk controller input (R) */ +#endif + +/* + * Status bits. + */ +#define WDCS_BSY 0x80 /* busy */ +#define WDCS_DRDY 0x40 /* drive ready */ +#define WDCS_DWF 0x20 /* drive write fault */ +#define WDCS_DSC 0x10 /* drive seek complete */ +#define WDCS_DRQ 0x08 /* data request */ +#define WDCS_CORR 0x04 /* corrected data */ +#define WDCS_IDX 0x02 /* index */ +#define WDCS_ERR 0x01 /* error */ +#define WDCS_BITS "\020\010bsy\007drdy\006dwf\005dsc\004drq\003corr\002idx\001err" + +/* + * Error bits. + */ +#define WDCE_BBK 0x80 /* bad block detected */ +#define WDCE_CRC 0x80 /* CRC error (Ultra-DMA only) */ +#define WDCE_UNC 0x40 /* uncorrectable data error */ +#define WDCE_MC 0x20 /* media changed */ +#define WDCE_IDNF 0x10 /* id not found */ +#define WDCE_MCR 0x08 /* media change requested */ +#define WDCE_ABRT 0x04 /* aborted command */ +#define WDCE_TK0NF 0x02 /* track 0 not found */ +#define WDCE_AMNF 0x01 /* address mark not found */ + +/* + * Commands for Disk Controller. + */ +#define WDCC_NOP 0x00 /* NOP - Always fail with "aborted command" */ +#define WDCC_RECAL 0x10 /* disk restore code -- resets cntlr */ + +#define WDCC_READ 0x20 /* disk read code */ +#define WDCC_WRITE 0x30 /* disk write code */ +#define WDCC__LONG 0x02 /* modifier -- access ecc bytes */ +#define WDCC__NORETRY 0x01 /* modifier -- no retrys */ + +#define WDCC_FORMAT 0x50 /* disk format code */ +#define WDCC_DIAGNOSE 0x90 /* controller diagnostic */ +#define WDCC_IDP 0x91 /* initialize drive parameters */ + +#define WDCC_READMULTI 0xc4 /* read multiple */ +#define WDCC_WRITEMULTI 0xc5 /* write multiple */ +#define WDCC_SETMULTI 0xc6 /* set multiple mode */ + +#define WDCC_READDMA 0xc8 /* read with DMA */ +#define WDCC_WRITEDMA 0xca /* write with DMA */ + +#define WDCC_ACKMC 0xdb /* acknowledge media change */ +#define WDCC_LOCK 0xde /* lock drawer */ +#define WDCC_UNLOCK 0xdf /* unlock drawer */ + +#define WDCC_FLUSHCACHE 0xe7 /* Flush cache */ +#define WDCC_IDENTIFY 0xec /* read parameters from controller */ +#define SET_FEATURES 0xef /* set features */ + +#define WDCC_IDLE 0xe3 /* set idle timer & enter idle mode */ +#define WDCC_IDLE_IMMED 0xe1 /* enter idle mode */ +#define WDCC_SLEEP 0xe6 /* enter sleep mode */ +#define WDCC_STANDBY 0xe2 /* set standby timer & enter standby mode */ +#define WDCC_STANDBY_IMMED 0xe0 /* enter standby mode */ +#define WDCC_CHECK_PWR 0xe5 /* check power mode */ + +/* Subcommands for SET_FEATURES (features register ) */ +#define WDSF_EN_WR_CACHE 0x02 +#define WDSF_SET_MODE 0x03 +#define WDSF_REASSIGN_EN 0x04 +#define WDSF_RETRY_DS 0x33 +#define WDSF_SET_CACHE_SGMT 0x54 +#define WDSF_READAHEAD_DS 0x55 +#define WDSF_POD_DS 0x66 +#define WDSF_ECC_DS 0x77 +#define WDSF_WRITE_CACHE_DS 0x82 +#define WDSF_REASSIGN_DS 0x84 +#define WDSF_ECC_EN 0x88 +#define WDSF_RETRY_EN 0x99 +#define WDSF_SET_CURRENT 0x9A +#define WDSF_READAHEAD_EN 0xAA +#define WDSF_PREFETCH_SET 0xAB +#define WDSF_POD_EN 0xCC + +/* parameters uploaded to device/heads register */ +#define WDSD_IBM 0xa0 /* forced to 512 byte sector, ecc */ +#define WDSD_CHS 0x00 /* cylinder/head/sector addressing */ +#define WDSD_LBA 0x40 /* logical block addressing */ + +/* Commands for ATAPI devices */ +#define ATAPI_CHECK_POWER_MODE 0xe5 +#define ATAPI_EXEC_DRIVE_DIAGS 0x90 +#define ATAPI_IDLE_IMMEDIATE 0xe1 +#define ATAPI_NOP 0x00 +#define ATAPI_PKT_CMD 0xa0 +#define ATAPI_IDENTIFY_DEVICE 0xa1 +#define ATAPI_SOFT_RESET 0x08 +#define ATAPI_SLEEP 0xe6 +#define ATAPI_STANDBY_IMMEDIATE 0xe0 + +/* Bytes used by ATAPI_PACKET_COMMAND ( feature register) */ +#define ATAPI_PKT_CMD_FTRE_DMA 0x01 +#define ATAPI_PKT_CMD_FTRE_OVL 0x02 + +/* ireason */ +#define WDCI_CMD 0x01 /* command(1) or data(0) */ +#define WDCI_IN 0x02 /* transfer to(1) or from(0) the host */ +#define WDCI_RELEASE 0x04 /* bus released until completion */ + +#define PHASE_CMDOUT (WDCS_DRQ | WDCI_CMD) +#define PHASE_DATAIN (WDCS_DRQ | WDCI_IN) +#define PHASE_DATAOUT WDCS_DRQ +#define PHASE_COMPLETED (WDCI_IN | WDCI_CMD) +#define PHASE_ABORTED 0 + diff --git a/sys/dev/ic/wdcvar.h b/sys/dev/ic/wdcvar.h new file mode 100644 index 00000000000..1d4fb228154 --- /dev/null +++ b/sys/dev/ic/wdcvar.h @@ -0,0 +1,204 @@ +/* $OpenBSD: wdcvar.h,v 1.1 1999/07/18 21:25:16 csapuntz Exp $ */ +/* $NetBSD: wdcvar.h,v 1.17 1999/04/11 20:50:29 bouyer Exp $ */ + +/*- + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Charles M. Hannum, by Onno van der Linden and 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 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. + */ + +#if 0 +/* XXX for scsipi_adapter */ +#include <dev/scsipi/scsipi_all.h> +#include <dev/scsipi/scsipiconf.h> +#endif + +struct atapiscsi_softc; + +#define WAITTIME (10 * hz) /* time to wait for a completion */ + /* this is a lot for hard drives, but not for cdroms */ + +struct channel_queue { /* per channel queue (may be shared) */ + TAILQ_HEAD(xferhead, wdc_xfer) sc_xfer; +}; + +struct channel_softc { /* Per channel data */ + /* Our location */ + int channel; + /* Our controller's softc */ + struct wdc_softc *wdc; + /* Our registers */ + bus_space_tag_t cmd_iot; + bus_space_handle_t cmd_ioh; + bus_space_tag_t ctl_iot; + bus_space_handle_t ctl_ioh; + /* data32{iot,ioh} are only used for 32 bit xfers */ + bus_space_tag_t data32iot; + bus_space_handle_t data32ioh; + /* Our state */ + int ch_flags; +#define WDCF_ACTIVE 0x01 /* channel is active */ +#define WDCF_IRQ_WAIT 0x10 /* controller is waiting for irq */ + u_int8_t ch_status; /* copy of status register */ + u_int8_t ch_error; /* copy of error register */ + /* per-drive infos */ + struct ata_drive_datas ch_drive[2]; + + struct atapiscsi_softc *ch_as; + + /* + * channel queues. May be the same for all channels, if hw channels + * are not independants + */ + struct channel_queue *ch_queue; +}; + +struct wdc_softc { /* Per controller state */ + struct device sc_dev; + /* mandatory fields */ + int cap; +/* Capabilities supported by the controller */ +#define WDC_CAPABILITY_DATA16 0x0001 /* can do 16-bit data access */ +#define WDC_CAPABILITY_DATA32 0x0002 /* can do 32-bit data access */ +#define WDC_CAPABILITY_MODE 0x0004 /* controller knows its PIO/DMA modes */ +#define WDC_CAPABILITY_DMA 0x0008 /* DMA */ +#define WDC_CAPABILITY_UDMA 0x0010 /* Ultra-DMA/33 */ +#define WDC_CAPABILITY_HWLOCK 0x0020 /* Needs to lock HW */ +#define WDC_CAPABILITY_ATA_NOSTREAM 0x0040 /* Don't use stream funcs on ATA */ +#define WDC_CAPABILITY_ATAPI_NOSTREAM 0x0080 /* Don't use stream f on ATAPI */ +#define WDC_CAPABILITY_NO_EXTRA_RESETS 0x0100 /* only reset once */ +#define WDC_CAPABILITY_PREATA 0x0200 /* ctrl can be a pre-ata one */ + u_int8_t PIO_cap; /* highest PIO mode supported */ + u_int8_t DMA_cap; /* highest DMA mode supported */ + u_int8_t UDMA_cap; /* highest UDMA mode supported */ + int nchannels; /* Number of channels on this controller */ + struct channel_softc **channels; /* channels-specific datas (array) */ + +#if 0 + /* + * The reference count here is used for both IDE and ATAPI devices. + */ + struct scsipi_adapter sc_atapi_adapter; +#endif + + /* if WDC_CAPABILITY_DMA set in 'cap' */ + void *dma_arg; + int (*dma_init) __P((void *, int, int, void *, size_t, + int)); + void (*dma_start) __P((void *, int, int, int)); + int (*dma_finish) __P((void *, int, int, int)); +/* flags passed to DMA functions */ +#define WDC_DMA_READ 0x01 +#define WDC_DMA_POLL 0x02 + + /* if WDC_CAPABILITY_HWLOCK set in 'cap' */ + int (*claim_hw) __P((void *, int)); + void (*free_hw) __P((void *)); + + /* if WDC_CAPABILITY_MODE set in 'cap' */ + void (*set_modes) __P((struct channel_softc *)); +}; + + /* + * Description of a command to be handled by a controller. + * These commands are queued in a list. + */ +struct wdc_xfer { + volatile u_int c_flags; +#define C_INUSE 0x0001 /* xfer struct is in use */ +#define C_ATAPI 0x0002 /* xfer is ATAPI request */ +#define C_TIMEOU 0x0004 /* xfer processing timed out */ +#define C_NEEDDONE 0x0010 /* need to call upper-level done */ +#define C_POLL 0x0020 /* cmd is polled */ +#define C_DMA 0x0040 /* cmd uses DMA */ +#define C_SENSE 0x0080 /* cmd is a internal command */ + + /* Informations about our location */ + struct channel_softc *chp; + u_int8_t drive; + + /* Information about the current transfer */ + void *cmd; /* wdc, ata or scsipi command structure */ + void *databuf; + int c_bcount; /* byte count left */ + int c_skip; /* bytes already transferred */ + TAILQ_ENTRY(wdc_xfer) c_xferchain; + LIST_ENTRY(wdc_xfer) free_list; + void (*c_start) __P((struct channel_softc *, struct wdc_xfer *)); + int (*c_intr) __P((struct channel_softc *, struct wdc_xfer *, int)); +}; + +/* + * Public functions which can be called by ATA or ATAPI specific parts, + * or bus-specific backends. + */ + +int wdcprobe __P((struct channel_softc *)); +void wdcattach __P((struct channel_softc *)); +void wdc_final_attach __P((struct channel_softc *)); +int wdcintr __P((void *)); +void wdc_exec_xfer __P((struct channel_softc *, struct wdc_xfer *)); +struct wdc_xfer *wdc_get_xfer __P((int)); /* int = WDC_NOSLEEP/CANSLEEP */ +#define WDC_CANSLEEP 0x00 +#define WDC_NOSLEEP 0x01 +void wdc_free_xfer __P((struct channel_softc *, struct wdc_xfer *)); +void wdcstart __P((struct channel_softc *)); +void wdcrestart __P((void*)); +int wdcreset __P((struct channel_softc *, int)); +#define VERBOSE 1 +#define SILENT 0 /* wdcreset will not print errors */ +int wdcwait __P((struct channel_softc *, int, int, int)); +void wdcbit_bucket __P(( struct channel_softc *, int)); +void wdccommand __P((struct channel_softc *, u_int8_t, u_int8_t, u_int16_t, + u_int8_t, u_int8_t, u_int8_t, u_int8_t)); +void wdccommandshort __P((struct channel_softc *, int, int)); +void wdctimeout __P((void *arg)); + +int wdc_addref __P((struct channel_softc *)); +void wdc_delref __P((struct channel_softc *)); + +/* + * ST506 spec says that if READY or SEEKCMPLT go off, then the read or write + * command is aborted. + */ +#define wait_for_drq(chp, timeout) wdcwait((chp), WDCS_DRQ, WDCS_DRQ, (timeout)) +#define wait_for_unbusy(chp, timeout) wdcwait((chp), 0, 0, (timeout)) +#define wait_for_ready(chp, timeout) wdcwait((chp), WDCS_DRDY, \ + WDCS_DRDY, (timeout)) +/* ATA/ATAPI specs says a device can take 31s to reset */ +#define WDC_RESET_WAIT 31000 + +void wdc_atapibus_attach __P((struct channel_softc *)); +void wdc_atapibus_final_attach __P((struct channel_softc *)); + +int atapi_print __P((void *, const char *)); diff --git a/sys/dev/isa/files.isa b/sys/dev/isa/files.isa index 341f47f0a71..0fc3b856954 100644 --- a/sys/dev/isa/files.isa +++ b/sys/dev/isa/files.isa @@ -1,4 +1,4 @@ -# $OpenBSD: files.isa,v 1.54 1999/07/05 20:08:36 deraadt Exp $ +# $OpenBSD: files.isa,v 1.55 1999/07/18 21:25:18 csapuntz Exp $ # $NetBSD: files.isa,v 1.21 1996/05/16 03:45:55 mycroft Exp $ # # Config file and device description for machine-independent ISA code. @@ -117,7 +117,13 @@ device pcd: disk, opti attach pcd at isa file dev/isa/pcd.c pcd needs-flag -# ISA "wd" (ESDI/IDE/etc.) controllers, ATAPI bus +#Start uncommenting for new ATA stuff +## ISA "wd" (ESDI/IDE/etc.) controllers, ATAPI bus +#attach wdc at isa with wdc_isa: isa_dma +#file dev/isa/wdc_isa.c wdc_isa +#end uncommenting for new ATA stuff + +#Start comment this out for new ATA stuff define ata {drive = -1} device wdc {drive = -1}: atapi, ata attach wdc at isa with wdc_isa @@ -125,6 +131,7 @@ device wd: disk attach wd at wdc file dev/isa/wdc.c wdc & (wdc_isa | wdc_isapnp) needs-flag file dev/isa/wd.c wd needs-flag +#End comment this out for new ATA stuff # Wangtek- and Archive-compatible tape controller boards device wt: tape, isa_dma diff --git a/sys/dev/isa/wdc_isa.c b/sys/dev/isa/wdc_isa.c new file mode 100644 index 00000000000..ae8a28842d0 --- /dev/null +++ b/sys/dev/isa/wdc_isa.c @@ -0,0 +1,235 @@ +/* $OpenBSD: wdc_isa.c,v 1.1 1999/07/18 21:25:18 csapuntz Exp $ */ +/* $NetBSD: wdc_isa.c,v 1.15 1999/05/19 14:41:25 bouyer Exp $ */ + +/*- + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Charles M. Hannum and by Onno van der Linden. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 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/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/malloc.h> + +#include <machine/bus.h> +#include <machine/intr.h> + +#include <dev/isa/isavar.h> +#include <dev/isa/isadmavar.h> + +#include <dev/ata/atavar.h> +#include <dev/ic/wdcvar.h> + +#define WDC_ISA_REG_NPORTS 8 +#define WDC_ISA_AUXREG_OFFSET 0x206 +#define WDC_ISA_AUXREG_NPORTS 1 /* XXX "fdc" owns ports 0x3f7/0x377 */ + +/* options passed via the 'flags' config keyword */ +#define WDC_OPTIONS_32 0x01 /* try to use 32bit data I/O */ + +struct wdc_isa_softc { + struct wdc_softc sc_wdcdev; + struct channel_softc *wdc_chanptr; + struct channel_softc wdc_channel; + isa_chipset_tag_t sc_ic; + void *sc_ih; + int sc_drq; +}; + +#ifndef __OpenBSD__ +int wdc_isa_probe __P((struct device *, struct cfdata *, void *)); +#else +int wdc_isa_probe __P((struct device *, void *, void *)); +#endif +void wdc_isa_attach __P((struct device *, struct device *, void *)); + +struct cfattach wdc_isa_ca = { + sizeof(struct wdc_isa_softc), wdc_isa_probe, wdc_isa_attach +}; + +static void wdc_isa_dma_setup __P((struct wdc_isa_softc *)); +static int wdc_isa_dma_init __P((void*, int, int, void *, size_t, int)); +static void wdc_isa_dma_start __P((void*, int, int, int)); +static int wdc_isa_dma_finish __P((void*, int, int, int)); + +int +wdc_isa_probe(parent, match, aux) + struct device *parent; +#ifndef __OpenBSD__ + struct cfdata *match; +#else + void *match; +#endif + void *aux; +{ + struct channel_softc ch = { 0 }; + struct isa_attach_args *ia = aux; + int result = 0; + + ch.cmd_iot = ia->ia_iot; + if (bus_space_map(ch.cmd_iot, ia->ia_iobase, WDC_ISA_REG_NPORTS, 0, + &ch.cmd_ioh)) + goto out; + + ch.ctl_iot = ia->ia_iot; + if (bus_space_map(ch.ctl_iot, ia->ia_iobase + WDC_ISA_AUXREG_OFFSET, + WDC_ISA_AUXREG_NPORTS, 0, &ch.ctl_ioh)) + goto outunmap; + + result = wdcprobe(&ch); + if (result) { + ia->ia_iosize = WDC_ISA_REG_NPORTS; + ia->ia_msize = 0; + } + + bus_space_unmap(ch.ctl_iot, ch.ctl_ioh, WDC_ISA_AUXREG_NPORTS); +outunmap: + bus_space_unmap(ch.cmd_iot, ch.cmd_ioh, WDC_ISA_REG_NPORTS); +out: + return (result); +} + +void +wdc_isa_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct wdc_isa_softc *sc = (void *)self; + struct isa_attach_args *ia = aux; + + printf("\n"); + + sc->wdc_channel.cmd_iot = ia->ia_iot; + sc->wdc_channel.ctl_iot = ia->ia_iot; + sc->sc_ic = ia->ia_ic; + if (bus_space_map(sc->wdc_channel.cmd_iot, ia->ia_iobase, + WDC_ISA_REG_NPORTS, 0, &sc->wdc_channel.cmd_ioh) || + bus_space_map(sc->wdc_channel.ctl_iot, + ia->ia_iobase + WDC_ISA_AUXREG_OFFSET, WDC_ISA_AUXREG_NPORTS, + 0, &sc->wdc_channel.ctl_ioh)) { + printf("%s: couldn't map registers\n", + sc->sc_wdcdev.sc_dev.dv_xname); + } + sc->wdc_channel.data32iot = sc->wdc_channel.cmd_iot; + sc->wdc_channel.data32ioh = sc->wdc_channel.cmd_ioh; + +#ifdef __OpenBSD__ + sc->sc_ih = isa_intr_establish(ia->ia_ic, ia->ia_irq, IST_EDGE, + IPL_BIO, wdcintr, &sc->wdc_channel, sc->sc_wdcdev.sc_dev.dv_xname); +#else + sc->sc_ih = isa_intr_establish(ia->ia_ic, ia->ia_irq, IST_EDGE, + IPL_BIO, wdcintr, &sc->wdc_channel); +#endif + if (ia->ia_drq != DRQUNK) { + sc->sc_drq = ia->ia_drq; + + sc->sc_wdcdev.cap |= WDC_CAPABILITY_DMA; + sc->sc_wdcdev.dma_arg = sc; + sc->sc_wdcdev.dma_init = wdc_isa_dma_init; + sc->sc_wdcdev.dma_start = wdc_isa_dma_start; + sc->sc_wdcdev.dma_finish = wdc_isa_dma_finish; + wdc_isa_dma_setup(sc); + } + sc->sc_wdcdev.cap |= WDC_CAPABILITY_DATA16 | WDC_CAPABILITY_PREATA; + if (sc->sc_wdcdev.sc_dev.dv_cfdata->cf_flags & WDC_OPTIONS_32) + sc->sc_wdcdev.cap |= WDC_CAPABILITY_DATA32; + sc->sc_wdcdev.PIO_cap = 0; + sc->wdc_chanptr = &sc->wdc_channel; + sc->sc_wdcdev.channels = &sc->wdc_chanptr; + sc->sc_wdcdev.nchannels = 1; + sc->wdc_channel.channel = 0; + sc->wdc_channel.wdc = &sc->sc_wdcdev; + sc->wdc_channel.ch_queue = malloc(sizeof(struct channel_queue), + M_DEVBUF, M_NOWAIT); + if (sc->wdc_channel.ch_queue == NULL) { + printf("%s: can't allocate memory for command queue", + sc->sc_wdcdev.sc_dev.dv_xname); + return; + } + wdcattach(&sc->wdc_channel); + wdc_final_attach(&sc->wdc_channel); +} + +static void +wdc_isa_dma_setup(sc) + struct wdc_isa_softc *sc; +{ + if (isa_dmamap_create(sc->sc_ic, sc->sc_drq, + MAXPHYS, BUS_DMA_NOWAIT|BUS_DMA_ALLOCNOW)) { + printf("%s: can't create map for drq %d\n", + sc->sc_wdcdev.sc_dev.dv_xname, sc->sc_drq); + sc->sc_wdcdev.cap &= ~WDC_CAPABILITY_DMA; + } +} + +static int +wdc_isa_dma_init(v, channel, drive, databuf, datalen, read) + void *v; + void *databuf; + size_t datalen; + int read; +{ + struct wdc_isa_softc *sc = v; + + isa_dmastart(sc->sc_ic, sc->sc_drq, databuf, datalen, NULL, + (read ? DMAMODE_READ : DMAMODE_WRITE) +#ifndef __OpenBSD__ +| DMAMODE_DEMAND, +#else +, +#endif + BUS_DMA_NOWAIT); + return 0; +} + +static void +wdc_isa_dma_start(v, channel, drive, read) + void *v; + int channel, drive; +{ + /* nothing to do */ +} + +static int +wdc_isa_dma_finish(v, channel, drive, read) + void *v; + int channel, drive; + int read; +{ + struct wdc_isa_softc *sc = v; + + isa_dmadone(sc->sc_ic, sc->sc_drq); + return 0; +} diff --git a/sys/dev/pci/files.pci b/sys/dev/pci/files.pci index 046c9597630..71ed1154cdc 100644 --- a/sys/dev/pci/files.pci +++ b/sys/dev/pci/files.pci @@ -1,4 +1,4 @@ -# $OpenBSD: files.pci,v 1.40 1999/07/18 03:26:43 csapuntz Exp $ +# $OpenBSD: files.pci,v 1.41 1999/07/18 21:25:19 csapuntz Exp $ # $NetBSD: files.pci,v 1.20 1996/09/24 17:47:15 christos Exp $ # # Config file and device description for machine-independent PCI code. @@ -85,10 +85,12 @@ device ncr: scsi attach ncr at pci file dev/pci/ncr.c ncr -# PCI IDE controllers +#Start uncommenting for new ATA stuff +## PCI IDE controllers #device pciide {[channel = -1]}: wdc_base, ata, atapi #attach pciide at pci #file dev/pci/pciide.c pciide +#End uncommenting for new ATA stuff # PCI-PCI bridge chips device ppb: pcibus diff --git a/sys/dev/pci/pci_map.c b/sys/dev/pci/pci_map.c index a68ba971ada..7038aa8e721 100644 --- a/sys/dev/pci/pci_map.c +++ b/sys/dev/pci/pci_map.c @@ -1,3 +1,4 @@ +/* $OpenBSD: pci_map.c,v 1.2 1999/07/18 21:25:19 csapuntz Exp $ */ /* $NetBSD: pci_map.c,v 1.5 1998/08/15 10:10:54 mycroft Exp $ */ /*- diff --git a/sys/dev/pci/pciide.c b/sys/dev/pci/pciide.c new file mode 100644 index 00000000000..1b17e58ceca --- /dev/null +++ b/sys/dev/pci/pciide.c @@ -0,0 +1,2325 @@ +/* $OpenBSD: pciide.c,v 1.1 1999/07/18 21:25:19 csapuntz Exp $ */ +/* $NetBSD: pciide.c,v 1.40 1999/07/12 13:49:38 bouyer Exp $ */ + +/* + * Copyright (c) 1996, 1998 Christopher G. Demetriou. All rights reserved. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Christopher G. Demetriou + * for the NetBSD Project. + * 4. 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. + */ + +/* + * PCI IDE controller driver. + * + * Author: Christopher G. Demetriou, March 2, 1998 (derived from NetBSD + * sys/dev/pci/ppb.c, revision 1.16). + * + * See "PCI IDE Controller Specification, Revision 1.0 3/4/94" and + * "Programming Interface for Bus Master IDE Controller, Revision 1.0 + * 5/16/94" from the PCI SIG. + * + */ + +#ifndef WDCDEBUG +#define WDCDEBUG +#endif + +#define DEBUG_DMA 0x01 +#define DEBUG_XFERS 0x02 +#define DEBUG_FUNCS 0x08 +#define DEBUG_PROBE 0x10 + +#ifdef WDCDEBUG +int wdcdebug_pciide_mask; +#define WDCDEBUG_PRINT(args, level) \ + if (wdcdebug_pciide_mask & (level)) printf args +#else +#define WDCDEBUG_PRINT(args, level) +#endif +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/malloc.h> + +#include <vm/vm.h> +#include <vm/vm_param.h> +#include <vm/vm_kern.h> + +#include <dev/pci/pcireg.h> +#include <dev/pci/pcivar.h> +#include <dev/pci/pcidevs.h> +#include <dev/pci/pciidereg.h> +#include <dev/pci/pciidevar.h> +#include <dev/pci/pciide_piix_reg.h> +#include <dev/pci/pciide_apollo_reg.h> +#include <dev/pci/pciide_cmd_reg.h> +#include <dev/pci/pciide_cy693_reg.h> +#include <dev/pci/pciide_sis_reg.h> +#include <dev/pci/pciide_acer_reg.h> +#include <dev/ata/atavar.h> +#include <dev/ic/wdcreg.h> +#include <dev/ic/wdcvar.h> + +#if BYTE_ORDER == BIG_ENDIAN +#include <machine/bswap.h> +#define htopci(x) bswap32(x) +#define pcitoh(x) bswap32(x) +#else +#define htopci(x) (x) +#define pcitoh(x) (x) +#endif + +/* inlines for reading/writing 8-bit PCI registers */ +static __inline u_int8_t pciide_pci_read __P((pci_chipset_tag_t, pcitag_t, + int)); +static __inline void pciide_pci_write __P((pci_chipset_tag_t, pcitag_t, + int, u_int8_t)); + +static __inline u_int8_t +pciide_pci_read(pc, pa, reg) + pci_chipset_tag_t pc; + pcitag_t pa; + int reg; +{ + + return (pci_conf_read(pc, pa, (reg & ~0x03)) >> + ((reg & 0x03) * 8) & 0xff); +} + +static __inline void +pciide_pci_write(pc, pa, reg, val) + pci_chipset_tag_t pc; + pcitag_t pa; + int reg; + u_int8_t val; +{ + pcireg_t pcival; + + pcival = pci_conf_read(pc, pa, (reg & ~0x03)); + pcival &= ~(0xff << ((reg & 0x03) * 8)); + pcival |= (val << ((reg & 0x03) * 8)); + pci_conf_write(pc, pa, (reg & ~0x03), pcival); +} + +struct pciide_softc { + struct wdc_softc sc_wdcdev; /* common wdc definitions */ + pci_chipset_tag_t sc_pc; /* PCI registers info */ + pcitag_t sc_tag; + void *sc_pci_ih; /* PCI interrupt handle */ + int sc_dma_ok; /* bus-master DMA info */ + bus_space_tag_t sc_dma_iot; + bus_space_handle_t sc_dma_ioh; + bus_dma_tag_t sc_dmat; + /* Chip description */ + const struct pciide_product_desc *sc_pp; + /* common definitions */ + struct channel_softc *wdc_chanarray[PCIIDE_NUM_CHANNELS]; + /* internal bookkeeping */ + struct pciide_channel { /* per-channel data */ + struct channel_softc wdc_channel; /* generic part */ + char *name; + int hw_ok; /* hardware mapped & OK? */ + int compat; /* is it compat? */ + void *ih; /* compat or pci handle */ + /* DMA tables and DMA map for xfer, for each drive */ + struct pciide_dma_maps { + bus_dmamap_t dmamap_table; + struct idedma_table *dma_table; + bus_dmamap_t dmamap_xfer; + } dma_maps[2]; + } pciide_channels[PCIIDE_NUM_CHANNELS]; +}; + +void default_setup_cap __P((struct pciide_softc*)); +void default_setup_chip __P((struct pciide_softc*)); +void default_channel_map __P((struct pci_attach_args *, + struct pciide_channel *)); + +void piix_setup_cap __P((struct pciide_softc*)); +void piix_setup_chip __P((struct pciide_softc*)); +void piix_setup_channel __P((struct channel_softc*)); +void piix3_4_setup_chip __P((struct pciide_softc*)); +void piix3_4_setup_channel __P((struct channel_softc*)); +void piix_channel_map __P((struct pci_attach_args *, struct pciide_channel *)); +static u_int32_t piix_setup_idetim_timings __P((u_int8_t, u_int8_t, u_int8_t)); +static u_int32_t piix_setup_idetim_drvs __P((struct ata_drive_datas*)); +static u_int32_t piix_setup_sidetim_timings __P((u_int8_t, u_int8_t, u_int8_t)); + +void apollo_setup_cap __P((struct pciide_softc*)); +void apollo_setup_chip __P((struct pciide_softc*)); +void apollo_setup_channel __P((struct channel_softc*)); +void apollo_channel_map __P((struct pci_attach_args *, + struct pciide_channel *)); + +void cmd0643_6_setup_cap __P((struct pciide_softc*)); +void cmd0643_6_setup_chip __P((struct pciide_softc*)); +void cmd0643_6_setup_channel __P((struct channel_softc*)); +void cmd_channel_map __P((struct pci_attach_args *, struct pciide_channel *)); + +void cy693_setup_cap __P((struct pciide_softc*)); +void cy693_setup_chip __P((struct pciide_softc*)); +void cy693_setup_channel __P((struct channel_softc*)); +void cy693_channel_map __P((struct pci_attach_args *, struct pciide_channel *)); + +void sis_setup_cap __P((struct pciide_softc*)); +void sis_setup_chip __P((struct pciide_softc*)); +void sis_setup_channel __P((struct channel_softc*)); +void sis_channel_map __P((struct pci_attach_args *, struct pciide_channel *)); + +void acer_setup_cap __P((struct pciide_softc*)); +void acer_setup_chip __P((struct pciide_softc*)); +void acer_setup_channel __P((struct channel_softc*)); +void acer_channel_map __P((struct pci_attach_args *, struct pciide_channel *)); + +void pciide_channel_dma_setup __P((struct pciide_channel *)); +int pciide_dma_table_setup __P((struct pciide_softc*, int, int)); +int pciide_dma_init __P((void*, int, int, void *, size_t, int)); +void pciide_dma_start __P((void*, int, int, int)); +int pciide_dma_finish __P((void*, int, int, int)); +void pciide_print_modes __P((struct pciide_channel *)); + +struct pciide_product_desc { + u_int32_t ide_product; + int ide_flags; + int ide_num_channels; + const char *ide_name; + /* init controller's capabilities for drives probe */ + void (*setup_cap) __P((struct pciide_softc*)); + /* init controller after drives probe */ + void (*setup_chip) __P((struct pciide_softc*)); + /* map channel if possible/necessary */ + void (*channel_map) __P((struct pci_attach_args *, + struct pciide_channel *)); +}; + +/* Flags for ide_flags */ +#define CMD_PCI064x_IOEN 0x01 /* CMD-style PCI_COMMAND_IO_ENABLE */ +#define ONE_QUEUE 0x02 /* device need serialised access */ + +/* Default product description for devices not known from this controller */ +const struct pciide_product_desc default_product_desc = { + 0, + 0, + PCIIDE_NUM_CHANNELS, + "Generic PCI IDE controller", + default_setup_cap, + default_setup_chip, + default_channel_map +}; + +const struct pciide_product_desc pciide_intel_products[] = { + { PCI_PRODUCT_INTEL_82092AA, + 0, + PCIIDE_NUM_CHANNELS, + "Intel 82092AA IDE controller", + default_setup_cap, + default_setup_chip, + default_channel_map + }, + { PCI_PRODUCT_INTEL_82371FB_IDE, + 0, + PCIIDE_NUM_CHANNELS, + "Intel 82371FB IDE controller (PIIX)", + piix_setup_cap, + piix_setup_chip, + piix_channel_map + }, + { PCI_PRODUCT_INTEL_82371SB_IDE, + 0, + PCIIDE_NUM_CHANNELS, + "Intel 82371SB IDE Interface (PIIX3)", + piix_setup_cap, + piix3_4_setup_chip, + piix_channel_map + }, + { PCI_PRODUCT_INTEL_82371AB_IDE, + 0, + PCIIDE_NUM_CHANNELS, + "Intel 82371AB IDE controller (PIIX4)", + piix_setup_cap, + piix3_4_setup_chip, + piix_channel_map + }, + { 0, + 0, + 0, + NULL, + } +}; + +const struct pciide_product_desc pciide_cmd_products[] = { + { PCI_PRODUCT_CMDTECH_640, + ONE_QUEUE | CMD_PCI064x_IOEN, + PCIIDE_NUM_CHANNELS, + "CMD Technology PCI0640", + default_setup_cap, + default_setup_chip, + cmd_channel_map + }, + { PCI_PRODUCT_CMDTECH_643, + ONE_QUEUE | CMD_PCI064x_IOEN, + PCIIDE_NUM_CHANNELS, + "CMD Technology PCI0643", + cmd0643_6_setup_cap, + cmd0643_6_setup_chip, + cmd_channel_map + }, + { PCI_PRODUCT_CMDTECH_646, + ONE_QUEUE | CMD_PCI064x_IOEN, + PCIIDE_NUM_CHANNELS, + "CMD Technology PCI0646", + cmd0643_6_setup_cap, + cmd0643_6_setup_chip, + cmd_channel_map + }, + { 0, + 0, + 0, + NULL, + } +}; + +const struct pciide_product_desc pciide_via_products[] = { + { PCI_PRODUCT_VIATECH_VT82C586_IDE, + 0, + PCIIDE_NUM_CHANNELS, + "VIA Technologies VT82C586 (Apollo VP) IDE Controller", + apollo_setup_cap, + apollo_setup_chip, + apollo_channel_map + }, + { PCI_PRODUCT_VIATECH_VT82C586A_IDE, + 0, + PCIIDE_NUM_CHANNELS, + "VIA Technologies VT82C586A IDE Controller", + apollo_setup_cap, + apollo_setup_chip, + apollo_channel_map + }, + { 0, + 0, + 0, + NULL, + } +}; + +const struct pciide_product_desc pciide_cypress_products[] = { + { PCI_PRODUCT_CONTAQ_82C693, + 0, + 1, + "Contaq Microsystems CY82C693 IDE Controller", + cy693_setup_cap, + cy693_setup_chip, + cy693_channel_map + }, + { 0, + 0, + 0, + NULL, + } +}; + +const struct pciide_product_desc pciide_sis_products[] = { + { PCI_PRODUCT_SIS_5597_IDE, + 0, + PCIIDE_NUM_CHANNELS, + "Silicon Integrated System 5597/5598 IDE controller", + sis_setup_cap, + sis_setup_chip, + sis_channel_map + }, + { 0, + 0, + 0, + NULL, + } +}; + +const struct pciide_product_desc pciide_acer_products[] = { + { PCI_PRODUCT_ALI_M5229, + 0, + PCIIDE_NUM_CHANNELS, + "Acer Labs M5229 UDMA IDE Controller", + acer_setup_cap, + acer_setup_chip, + acer_channel_map + }, + { 0, + 0, + 0, + NULL, + } +}; + +struct pciide_vendor_desc { + u_int32_t ide_vendor; + const struct pciide_product_desc *ide_products; +}; + +const struct pciide_vendor_desc pciide_vendors[] = { + { PCI_VENDOR_INTEL, pciide_intel_products }, + { PCI_VENDOR_CMDTECH, pciide_cmd_products }, + { PCI_VENDOR_VIATECH, pciide_via_products }, + { PCI_VENDOR_CONTAQ, pciide_cypress_products }, + { PCI_VENDOR_SIS, pciide_sis_products }, + { PCI_VENDOR_ALI, pciide_acer_products }, + { 0, NULL } +}; + +#define PCIIDE_CHANNEL_NAME(chan) ((chan) == 0 ? "primary" : "secondary") + +/* options passed via the 'flags' config keyword */ +#define PCIIDE_OPTIONS_DMA 0x01 + +#ifndef __OpenBSD__ +int pciide_match __P((struct device *, struct cfdata *, void *)); +#else +int pciide_match __P((struct device *, void *, void *)); +#endif +void pciide_attach __P((struct device *, struct device *, void *)); + +struct cfattach pciide_ca = { + sizeof(struct pciide_softc), pciide_match, pciide_attach +}; + +#ifdef __OpenBSD__ +struct cfdriver pciide_cd = { + NULL, "pciide", DV_DULL +}; +#endif +int pciide_mapregs_compat __P(( struct pci_attach_args *, + struct pciide_channel *, int, bus_size_t *, bus_size_t*)); +int pciide_mapregs_native __P((struct pci_attach_args *, + struct pciide_channel *, bus_size_t *, bus_size_t *)); +void pciide_mapchan __P((struct pci_attach_args *, + struct pciide_channel *, int, bus_size_t *, bus_size_t *)); +int pciiide_chan_candisable __P((struct pciide_channel *)); +void pciide_map_compat_intr __P(( struct pci_attach_args *, + struct pciide_channel *, int, int)); +int pciide_print __P((void *, const char *pnp)); +int pciide_compat_intr __P((void *)); +int pciide_pci_intr __P((void *)); +const struct pciide_product_desc* pciide_lookup_product __P((u_int32_t)); + +const struct pciide_product_desc * +pciide_lookup_product(id) + u_int32_t id; +{ + const struct pciide_product_desc *pp; + const struct pciide_vendor_desc *vp; + + for (vp = pciide_vendors; vp->ide_products != NULL; vp++) + if (PCI_VENDOR(id) == vp->ide_vendor) + break; + + if ((pp = vp->ide_products) == NULL) + return NULL; + + for (; pp->ide_name != NULL; pp++) + if (PCI_PRODUCT(id) == pp->ide_product) + break; + + if (pp->ide_name == NULL) + return NULL; + return pp; +} + +int +pciide_match(parent, match, aux) + struct device *parent; +#ifdef __OpenBSD__ + void *match; +#else + struct cfdata *match; +#endif + void *aux; +{ + struct pci_attach_args *pa = aux; + + /* + * Check the ID register to see that it's a PCI IDE controller. + * If it is, we assume that we can deal with it; it _should_ + * work in a standardized way... + */ + if (PCI_CLASS(pa->pa_class) == PCI_CLASS_MASS_STORAGE && + PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_MASS_STORAGE_IDE) { + return (1); + } + + return (0); +} + +void +pciide_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct pci_attach_args *pa = aux; + pci_chipset_tag_t pc = pa->pa_pc; + pcitag_t tag = pa->pa_tag; + struct pciide_softc *sc = (struct pciide_softc *)self; + struct pciide_channel *cp; + pcireg_t class, interface, csr; + char devinfo[256]; + int i; + + sc->sc_pp = pciide_lookup_product(pa->pa_id); + if (sc->sc_pp == NULL) { + sc->sc_pp = &default_product_desc; + pci_devinfo(pa->pa_id, pa->pa_class, 0, devinfo); + printf(": %s (rev. 0x%02x)\n", devinfo, + PCI_REVISION(pa->pa_class)); + } else { + printf(": %s\n", sc->sc_pp->ide_name); + } + + if ((pa->pa_flags & PCI_FLAGS_IO_ENABLED) == 0) { + csr = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG); + /* + * For a CMD PCI064x, the use of PCI_COMMAND_IO_ENABLE + * and base adresses registers can be disabled at + * hardware level. In this case, the device is wired + * in compat mode and its first channel is always enabled, + * but we can't rely on PCI_COMMAND_IO_ENABLE. + * In fact, it seems that the first channel of the CMD PCI0640 + * can't be disabled. + */ +#ifndef PCIIDE_CMD064x_DISABLE + if ((sc->sc_pp->ide_flags & CMD_PCI064x_IOEN) == 0) { +#else + if (1) { +#endif + printf("%s: device disabled (at %s)\n", + sc->sc_wdcdev.sc_dev.dv_xname, + (csr & PCI_COMMAND_IO_ENABLE) == 0 ? + "device" : "bridge"); + return; + } + } + + sc->sc_pc = pa->pa_pc; + sc->sc_tag = pa->pa_tag; + + class = pci_conf_read(pc, tag, PCI_CLASS_REG); + interface = PCI_INTERFACE(class); + + /* + * Map DMA registers, if DMA is supported. + * + * Note that sc_dma_ok is the right variable to test to see if + * DMA can be done. If the interface doesn't support DMA, + * sc_dma_ok will never be non-zero. If the DMA regs couldn't + * be mapped, it'll be zero. I.e., sc_dma_ok will only be + * non-zero if the interface supports DMA and the registers + * could be mapped. + * + * XXX Note that despite the fact that the Bus Master IDE specs + * XXX say that "The bus master IDE function uses 16 bytes of IO + * XXX space," some controllers (at least the United + * XXX Microelectronics UM8886BF) place it in memory space. + * XXX eventually, we should probably read the register and check + * XXX which type it is. Either that or 'quirk' certain devices. + */ + if (interface & PCIIDE_INTERFACE_BUS_MASTER_DMA) { + printf("%s: bus-master DMA support present", + sc->sc_wdcdev.sc_dev.dv_xname); + if (sc->sc_pp == &default_product_desc && + (sc->sc_wdcdev.sc_dev.dv_cfdata->cf_flags & + PCIIDE_OPTIONS_DMA) == 0) { + printf(", but unused (no driver support)"); + sc->sc_dma_ok = 0; + } else { + sc->sc_dma_ok = (pci_mapreg_map(pa, + PCIIDE_REG_BUS_MASTER_DMA, PCI_MAPREG_TYPE_IO, 0, + &sc->sc_dma_iot, &sc->sc_dma_ioh, NULL, NULL) == 0); + sc->sc_dmat = pa->pa_dmat; + if (sc->sc_dma_ok == 0) { + printf(", but unused (couldn't map registers)"); + } else { + if (sc->sc_pp == &default_product_desc) + printf(", used without full driver " + "support"); + sc->sc_wdcdev.dma_arg = sc; + sc->sc_wdcdev.dma_init = pciide_dma_init; + sc->sc_wdcdev.dma_start = pciide_dma_start; + sc->sc_wdcdev.dma_finish = pciide_dma_finish; + } + } + } else { + printf("%s: hardware does not support DMA", + sc->sc_wdcdev.sc_dev.dv_xname); + } + printf("\n"); + sc->sc_pp->setup_cap(sc); + sc->sc_wdcdev.channels = sc->wdc_chanarray; + sc->sc_wdcdev.nchannels = sc->sc_pp->ide_num_channels;; + sc->sc_wdcdev.cap |= WDC_CAPABILITY_DATA16; + + for (i = 0; i < sc->sc_wdcdev.nchannels; i++) { + cp = &sc->pciide_channels[i]; + sc->wdc_chanarray[i] = &cp->wdc_channel; + + cp->name = PCIIDE_CHANNEL_NAME(i); + + cp->wdc_channel.channel = i; + cp->wdc_channel.wdc = &sc->sc_wdcdev; + if (i > 0 && (sc->sc_pp->ide_flags & ONE_QUEUE)) { + cp->wdc_channel.ch_queue = + sc->pciide_channels[0].wdc_channel.ch_queue; + } else { + cp->wdc_channel.ch_queue = + malloc(sizeof(struct channel_queue), M_DEVBUF, + M_NOWAIT); + } + if (cp->wdc_channel.ch_queue == NULL) { + printf("%s %s channel: " + "can't allocate memory for command queue", + sc->sc_wdcdev.sc_dev.dv_xname, cp->name); + continue; + } + printf("%s: %s channel %s to %s mode\n", + sc->sc_wdcdev.sc_dev.dv_xname, cp->name, + (interface & PCIIDE_INTERFACE_SETTABLE(i)) ? + "configured" : "wired", + (interface & PCIIDE_INTERFACE_PCI(i)) ? "native-PCI" : + "compatibility"); + + /* + * sc->sc_pp->channel_map() will also call wdcattach. + * Eventually the channel will be disabled if there's no + * drive present. sc->hw_ok will be updated accordingly. + */ + sc->sc_pp->channel_map(pa, cp); + + } + /* Now that all drives are know, setup DMA, etc ...*/ + sc->sc_pp->setup_chip(sc); + if (sc->sc_dma_ok) { + csr = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG); + csr |= PCI_COMMAND_MASTER_ENABLE; + pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG, csr); + } + + /* This is a hook so that the ATAPI-SCSI stuff can + attach the SCSI BUS now and do the probes */ + for (i = 0; i < sc->sc_wdcdev.nchannels; i++) { + cp = &sc->pciide_channels[i]; + + wdc_final_attach(&cp->wdc_channel); + } + + WDCDEBUG_PRINT(("pciide: command/status register=%x\n", + pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG)), DEBUG_PROBE); +} + +int +pciide_mapregs_compat(pa, cp, compatchan, cmdsizep, ctlsizep) + struct pci_attach_args *pa; + struct pciide_channel *cp; + int compatchan; + bus_size_t *cmdsizep, *ctlsizep; +{ + struct pciide_softc *sc = (struct pciide_softc *)cp->wdc_channel.wdc; + struct channel_softc *wdc_cp = &cp->wdc_channel; + int rv = 1; + + cp->compat = 1; + *cmdsizep = PCIIDE_COMPAT_CMD_SIZE; + *ctlsizep = PCIIDE_COMPAT_CTL_SIZE; + + wdc_cp->cmd_iot = pa->pa_iot; + if (bus_space_map(wdc_cp->cmd_iot, PCIIDE_COMPAT_CMD_BASE(compatchan), + PCIIDE_COMPAT_CMD_SIZE, 0, &wdc_cp->cmd_ioh) != 0) { + printf("%s: couldn't map %s channel cmd regs\n", + sc->sc_wdcdev.sc_dev.dv_xname, cp->name); + rv = 0; + } + + wdc_cp->ctl_iot = pa->pa_iot; + if (bus_space_map(wdc_cp->ctl_iot, PCIIDE_COMPAT_CTL_BASE(compatchan), + PCIIDE_COMPAT_CTL_SIZE, 0, &wdc_cp->ctl_ioh) != 0) { + printf("%s: couldn't map %s channel ctl regs\n", + sc->sc_wdcdev.sc_dev.dv_xname, cp->name); + bus_space_unmap(wdc_cp->cmd_iot, wdc_cp->cmd_ioh, + PCIIDE_COMPAT_CMD_SIZE); + rv = 0; + } + + return (rv); +} + +int +pciide_mapregs_native(pa, cp, cmdsizep, ctlsizep) + struct pci_attach_args * pa; + struct pciide_channel *cp; + bus_size_t *cmdsizep, *ctlsizep; +{ + struct pciide_softc *sc = (struct pciide_softc *)cp->wdc_channel.wdc; + struct channel_softc *wdc_cp = &cp->wdc_channel; + const char *intrstr; + pci_intr_handle_t intrhandle; + + cp->compat = 0; + + if (sc->sc_pci_ih == NULL) { + if (pci_intr_map(pa->pa_pc, pa->pa_intrtag, pa->pa_intrpin, + pa->pa_intrline, &intrhandle) != 0) { + printf("%s: couldn't map native-PCI interrupt\n", + sc->sc_wdcdev.sc_dev.dv_xname); + return 0; + } + intrstr = pci_intr_string(pa->pa_pc, intrhandle); + sc->sc_pci_ih = pci_intr_establish(pa->pa_pc, + intrhandle, IPL_BIO, pciide_pci_intr, sc +#ifdef __OpenBSD__ + , sc->sc_wdcdev.sc_dev.dv_xname +#endif + ); + if (sc->sc_pci_ih != NULL) { + printf("%s: using %s for native-PCI interrupt\n", + sc->sc_wdcdev.sc_dev.dv_xname, + intrstr ? intrstr : "unknown interrupt"); + } else { + printf("%s: couldn't establish native-PCI interrupt", + sc->sc_wdcdev.sc_dev.dv_xname); + if (intrstr != NULL) + printf(" at %s", intrstr); + printf("\n"); + return 0; + } + } + cp->ih = sc->sc_pci_ih; + if (pci_mapreg_map(pa, PCIIDE_REG_CMD_BASE(wdc_cp->channel), + PCI_MAPREG_TYPE_IO, 0, + &wdc_cp->cmd_iot, &wdc_cp->cmd_ioh, NULL, cmdsizep) != 0) { + printf("%s: couldn't map %s channel cmd regs\n", + sc->sc_wdcdev.sc_dev.dv_xname, cp->name); + return 0; + } + + if (pci_mapreg_map(pa, PCIIDE_REG_CTL_BASE(wdc_cp->channel), + PCI_MAPREG_TYPE_IO, 0, + &wdc_cp->ctl_iot, &wdc_cp->ctl_ioh, NULL, ctlsizep) != 0) { + printf("%s: couldn't map %s channel ctl regs\n", + sc->sc_wdcdev.sc_dev.dv_xname, cp->name); + bus_space_unmap(wdc_cp->cmd_iot, wdc_cp->cmd_ioh, *cmdsizep); + return 0; + } + return (1); +} + +int +pciide_compat_intr(arg) + void *arg; +{ + struct pciide_channel *cp = arg; + +#ifdef DIAGNOSTIC + /* should only be called for a compat channel */ + if (cp->compat == 0) + panic("pciide compat intr called for non-compat chan %p\n", cp); +#endif + return (wdcintr(&cp->wdc_channel)); +} + +int +pciide_pci_intr(arg) + void *arg; +{ + struct pciide_softc *sc = arg; + struct pciide_channel *cp; + struct channel_softc *wdc_cp; + int i, rv, crv; + + rv = 0; + for (i = 0; i < sc->sc_wdcdev.nchannels; i++) { + cp = &sc->pciide_channels[i]; + wdc_cp = &cp->wdc_channel; + + /* If a compat channel skip. */ + if (cp->compat) + continue; + /* if this channel not waiting for intr, skip */ + if ((wdc_cp->ch_flags & WDCF_IRQ_WAIT) == 0) + continue; + + crv = wdcintr(wdc_cp); + if (crv == 0) + ; /* leave rv alone */ + else if (crv == 1) + rv = 1; /* claim the intr */ + else if (rv == 0) /* crv should be -1 in this case */ + rv = crv; /* if we've done no better, take it */ + } + return (rv); +} + +void +pciide_channel_dma_setup(cp) + struct pciide_channel *cp; +{ + int drive; + struct pciide_softc *sc = (struct pciide_softc *)cp->wdc_channel.wdc; + struct ata_drive_datas *drvp; + + for (drive = 0; drive < 2; drive++) { + drvp = &cp->wdc_channel.ch_drive[drive]; + /* If no drive, skip */ + if ((drvp->drive_flags & DRIVE) == 0) + continue; + /* setup DMA if needed */ + if (((drvp->drive_flags & DRIVE_DMA) == 0 && + (drvp->drive_flags & DRIVE_UDMA) == 0) || + sc->sc_dma_ok == 0) { + drvp->drive_flags &= ~(DRIVE_DMA | DRIVE_UDMA); + continue; + } + if (pciide_dma_table_setup(sc, cp->wdc_channel.channel, drive) + != 0) { + /* Abort DMA setup */ + drvp->drive_flags &= ~(DRIVE_DMA | DRIVE_UDMA); + continue; + } + } +} + +int +pciide_dma_table_setup(sc, channel, drive) + struct pciide_softc *sc; + int channel, drive; +{ + bus_dma_segment_t seg; + int error, rseg; + const bus_size_t dma_table_size = + sizeof(struct idedma_table) * NIDEDMA_TABLES; + struct pciide_dma_maps *dma_maps = + &sc->pciide_channels[channel].dma_maps[drive]; + + printf ("pciide_dma_table_setup: 1\n"); + /* If table was already allocated, just return */ + if (dma_maps->dma_table) + return 0; + + printf ("pciide_dma_table_setup: 2\n"); + /* Allocate memory for the DMA tables and map it */ + if ((error = bus_dmamem_alloc(sc->sc_dmat, dma_table_size, + IDEDMA_TBL_ALIGN, IDEDMA_TBL_ALIGN, &seg, 1, &rseg, + BUS_DMA_NOWAIT)) != 0) { + printf("%s:%d: unable to allocate table DMA for " + "drive %d, error=%d\n", sc->sc_wdcdev.sc_dev.dv_xname, + channel, drive, error); + return error; + } + +#ifdef __OpenBSD__ +#define BUS_DMA_COHERENT 0 +#endif + if ((error = bus_dmamem_map(sc->sc_dmat, &seg, rseg, + dma_table_size, + (caddr_t *)&dma_maps->dma_table, + BUS_DMA_NOWAIT|BUS_DMA_COHERENT)) != 0) { + printf("%s:%d: unable to map table DMA for" + "drive %d, error=%d\n", sc->sc_wdcdev.sc_dev.dv_xname, + channel, drive, error); + return error; + } +#ifdef __OpenBSD__ +#undef BUS_DMA_COHERENT +#endif + + WDCDEBUG_PRINT(("pciide_dma_table_setup: table at %p len %ld, " + "phy 0x%lx\n", dma_maps->dma_table, dma_table_size, + seg.ds_addr), DEBUG_PROBE); + + /* Create and load table DMA map for this disk */ + if ((error = bus_dmamap_create(sc->sc_dmat, dma_table_size, + 1, dma_table_size, IDEDMA_TBL_ALIGN, BUS_DMA_NOWAIT, + &dma_maps->dmamap_table)) != 0) { + printf("%s:%d: unable to create table DMA map for " + "drive %d, error=%d\n", sc->sc_wdcdev.sc_dev.dv_xname, + channel, drive, error); + return error; + } + if ((error = bus_dmamap_load(sc->sc_dmat, + dma_maps->dmamap_table, + dma_maps->dma_table, + dma_table_size, NULL, BUS_DMA_NOWAIT)) != 0) { + printf("%s:%d: unable to load table DMA map for " + "drive %d, error=%d\n", sc->sc_wdcdev.sc_dev.dv_xname, + channel, drive, error); + return error; + } + WDCDEBUG_PRINT(("pciide_dma_table_setup: phy addr of table 0x%lx\n", + dma_maps->dmamap_table->dm_segs[0].ds_addr), DEBUG_PROBE); + /* Create a xfer DMA map for this drive */ + if ((error = bus_dmamap_create(sc->sc_dmat, IDEDMA_BYTE_COUNT_MAX, + NIDEDMA_TABLES, IDEDMA_BYTE_COUNT_MAX, IDEDMA_BYTE_COUNT_ALIGN, + BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, + &dma_maps->dmamap_xfer)) != 0) { + printf("%s:%d: unable to create xfer DMA map for " + "drive %d, error=%d\n", sc->sc_wdcdev.sc_dev.dv_xname, + channel, drive, error); + return error; + } + return 0; +} + +int +pciide_dma_init(v, channel, drive, databuf, datalen, flags) + void *v; + int channel, drive; + void *databuf; + size_t datalen; + int flags; +{ + struct pciide_softc *sc = v; + int error, seg; + struct pciide_dma_maps *dma_maps = + &sc->pciide_channels[channel].dma_maps[drive]; + + error = bus_dmamap_load(sc->sc_dmat, + dma_maps->dmamap_xfer, + databuf, datalen, NULL, BUS_DMA_NOWAIT); + if (error) { + printf("%s:%d: unable to load xfer DMA map for" + "drive %d, error=%d\n", sc->sc_wdcdev.sc_dev.dv_xname, + channel, drive, error); + return error; + } + +#ifndef __OpenBSD__ + bus_dmamap_sync(sc->sc_dmat, dma_maps->dmamap_xfer, + 0, + dma_maps->dmamap_xfer->dm_mapsize, + (flags & WDC_DMA_READ) ? + BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE); +#else + bus_dmamap_sync(sc->sc_dmat, dma_maps->dmamap_xfer, + (flags & WDC_DMA_READ) ? + BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE); +#endif + + for (seg = 0; seg < dma_maps->dmamap_xfer->dm_nsegs; seg++) { +#ifdef DIAGNOSTIC + /* A segment must not cross a 64k boundary */ + { + u_long phys = dma_maps->dmamap_xfer->dm_segs[seg].ds_addr; + u_long len = dma_maps->dmamap_xfer->dm_segs[seg].ds_len; + if ((phys & ~IDEDMA_BYTE_COUNT_MASK) != + ((phys + len - 1) & ~IDEDMA_BYTE_COUNT_MASK)) { + printf("pciide_dma: segment %d physical addr 0x%lx" + " len 0x%lx not properly aligned\n", + seg, phys, len); + panic("pciide_dma: buf align"); + } + } +#endif + dma_maps->dma_table[seg].base_addr = + htopci(dma_maps->dmamap_xfer->dm_segs[seg].ds_addr); + dma_maps->dma_table[seg].byte_count = + htopci(dma_maps->dmamap_xfer->dm_segs[seg].ds_len & + IDEDMA_BYTE_COUNT_MASK); + WDCDEBUG_PRINT(("\t seg %d len %d addr 0x%x\n", + seg, pcitoh(dma_maps->dma_table[seg].byte_count), + pcitoh(dma_maps->dma_table[seg].base_addr)), DEBUG_DMA); + + } + dma_maps->dma_table[dma_maps->dmamap_xfer->dm_nsegs -1].byte_count |= + htopci(IDEDMA_BYTE_COUNT_EOT); + +#ifndef __OpenBSD__ + bus_dmamap_sync(sc->sc_dmat, dma_maps->dmamap_table, + 0, + dma_maps->dmamap_table->dm_mapsize, + BUS_DMASYNC_PREWRITE); +#else + bus_dmamap_sync(sc->sc_dmat, dma_maps->dmamap_table, + BUS_DMASYNC_PREWRITE); +#endif + + /* Maps are ready. Start DMA function */ +#ifdef DIAGNOSTIC + if (dma_maps->dmamap_table->dm_segs[0].ds_addr & ~IDEDMA_TBL_MASK) { + printf("pciide_dma_init: addr 0x%lx not properly aligned\n", + dma_maps->dmamap_table->dm_segs[0].ds_addr); + panic("pciide_dma_init: table align"); + } +#endif + + /* Clear status bits */ + bus_space_write_1(sc->sc_dma_iot, sc->sc_dma_ioh, + IDEDMA_CTL + IDEDMA_SCH_OFFSET * channel, + bus_space_read_1(sc->sc_dma_iot, sc->sc_dma_ioh, + IDEDMA_CTL + IDEDMA_SCH_OFFSET * channel)); + /* Write table addr */ + bus_space_write_4(sc->sc_dma_iot, sc->sc_dma_ioh, + IDEDMA_TBL + IDEDMA_SCH_OFFSET * channel, + dma_maps->dmamap_table->dm_segs[0].ds_addr); + /* set read/write */ + bus_space_write_1(sc->sc_dma_iot, sc->sc_dma_ioh, + IDEDMA_CMD + IDEDMA_SCH_OFFSET * channel, + (flags & WDC_DMA_READ) ? IDEDMA_CMD_WRITE: 0); + return 0; +} + +void +pciide_dma_start(v, channel, drive, flags) + void *v; + int channel, drive, flags; +{ + struct pciide_softc *sc = v; + + WDCDEBUG_PRINT(("pciide_dma_start\n"),DEBUG_XFERS); + bus_space_write_1(sc->sc_dma_iot, sc->sc_dma_ioh, + IDEDMA_CMD + IDEDMA_SCH_OFFSET * channel, + bus_space_read_1(sc->sc_dma_iot, sc->sc_dma_ioh, + IDEDMA_CMD + IDEDMA_SCH_OFFSET * channel) | IDEDMA_CMD_START); +} + +int +pciide_dma_finish(v, channel, drive, flags) + void *v; + int channel, drive; + int flags; +{ + struct pciide_softc *sc = v; + u_int8_t status; + struct pciide_dma_maps *dma_maps = + &sc->pciide_channels[channel].dma_maps[drive]; + + /* Unload the map of the data buffer */ +#ifndef __OpenBSD__ + bus_dmamap_sync(sc->sc_dmat, dma_maps->dmamap_xfer, + 0, + dma_maps->dmamap_xfer->dm_mapsize, + (flags & WDC_DMA_READ) ? + BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE); +#else + bus_dmamap_sync(sc->sc_dmat, dma_maps->dmamap_xfer, + (flags & WDC_DMA_READ) ? + BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE); +#endif + + bus_dmamap_unload(sc->sc_dmat, dma_maps->dmamap_xfer); + + status = bus_space_read_1(sc->sc_dma_iot, sc->sc_dma_ioh, + IDEDMA_CTL + IDEDMA_SCH_OFFSET * channel); + WDCDEBUG_PRINT(("pciide_dma_finish: status 0x%x\n", status), + DEBUG_XFERS); + + /* stop DMA channel */ + bus_space_write_1(sc->sc_dma_iot, sc->sc_dma_ioh, + IDEDMA_CMD + IDEDMA_SCH_OFFSET * channel, + bus_space_read_1(sc->sc_dma_iot, sc->sc_dma_ioh, + IDEDMA_CMD + IDEDMA_SCH_OFFSET * channel) & ~IDEDMA_CMD_START); + + /* Clear status bits */ + bus_space_write_1(sc->sc_dma_iot, sc->sc_dma_ioh, + IDEDMA_CTL + IDEDMA_SCH_OFFSET * channel, + status); + + if ((status & IDEDMA_CTL_ERR) != 0) { + printf("%s:%d:%d: Bus-Master DMA error: status=0x%x\n", + sc->sc_wdcdev.sc_dev.dv_xname, channel, drive, status); + return -1; + } + + if ((flags & WDC_DMA_POLL) == 0 && (status & IDEDMA_CTL_INTR) == 0) { + printf("%s:%d:%d: Bus-Master DMA error: missing interrupt, " + "status=0x%x\n", sc->sc_wdcdev.sc_dev.dv_xname, channel, + drive, status); + return -1; + } + + if ((status & IDEDMA_CTL_ACT) != 0) { + /* data underrun, may be a valid condition for ATAPI */ + return 1; + } + return 0; +} + +/* some common code used by several chip channel_map */ +void +pciide_mapchan(pa, cp, interface, cmdsizep, ctlsizep) + struct pci_attach_args *pa; + int interface; + struct pciide_channel *cp; + bus_size_t *cmdsizep, *ctlsizep; +{ + struct channel_softc *wdc_cp = &cp->wdc_channel; + + if (interface & PCIIDE_INTERFACE_PCI(wdc_cp->channel)) + cp->hw_ok = pciide_mapregs_native(pa, cp, cmdsizep, ctlsizep); + else + cp->hw_ok = pciide_mapregs_compat(pa, cp, + wdc_cp->channel, cmdsizep, ctlsizep); + if (cp->hw_ok == 0) + return; + wdc_cp->data32iot = wdc_cp->cmd_iot; + wdc_cp->data32ioh = wdc_cp->cmd_ioh; + wdcattach(wdc_cp); +} + +/* + * Generic code to call to know if a channel can be disabled. Return 1 + * if channel can be disabled, 0 if not + */ +int +pciiide_chan_candisable(cp) + struct pciide_channel *cp; +{ + struct pciide_softc *sc = (struct pciide_softc *)cp->wdc_channel.wdc; + struct channel_softc *wdc_cp = &cp->wdc_channel; + + if ((wdc_cp->ch_drive[0].drive_flags & DRIVE) == 0 && + (wdc_cp->ch_drive[1].drive_flags & DRIVE) == 0) { + printf("%s: disabling %s channel (no drives)\n", + sc->sc_wdcdev.sc_dev.dv_xname, cp->name); + cp->hw_ok = 0; + return 1; + } + return 0; +} + +/* + * generic code to map the compat intr if hw_ok=1 and it is a compat channel. + * Set hw_ok=0 on failure + */ +void +pciide_map_compat_intr(pa, cp, compatchan, interface) + struct pci_attach_args *pa; + struct pciide_channel *cp; + int compatchan, interface; +{ + struct pciide_softc *sc = (struct pciide_softc *)cp->wdc_channel.wdc; + struct channel_softc *wdc_cp = &cp->wdc_channel; + + if (cp->hw_ok == 0) + return; + if ((interface & PCIIDE_INTERFACE_PCI(wdc_cp->channel)) != 0) + return; + + cp->ih = pciide_machdep_compat_intr_establish(&sc->sc_wdcdev.sc_dev, + pa, compatchan, pciide_compat_intr, cp); + if (cp->ih == NULL) { + printf("%s: no compatibility interrupt for use by %s " + "channel\n", sc->sc_wdcdev.sc_dev.dv_xname, cp->name); + cp->hw_ok = 0; + } +} + +void +pciide_print_modes(cp) + struct pciide_channel *cp; +{ + struct pciide_softc *sc = (struct pciide_softc *)cp->wdc_channel.wdc; + int drive; + struct channel_softc *chp; + struct ata_drive_datas *drvp; + + chp = &cp->wdc_channel; + for (drive = 0; drive < 2; drive++) { + drvp = &chp->ch_drive[drive]; + if ((drvp->drive_flags & DRIVE) == 0) + continue; + printf("%s(%s:%d:%d): using PIO mode %d", + drvp->drv_softc->dv_xname, + sc->sc_wdcdev.sc_dev.dv_xname, + chp->channel, drive, drvp->PIO_mode); + if (drvp->drive_flags & DRIVE_DMA) + printf(", DMA mode %d", drvp->DMA_mode); + if (drvp->drive_flags & DRIVE_UDMA) + printf(", Ultra-DMA mode %d", drvp->UDMA_mode); + if (drvp->drive_flags & (DRIVE_DMA | DRIVE_UDMA)) + printf(" (using DMA data transfers)"); + printf("\n"); + } +} + +void +default_setup_cap(sc) + struct pciide_softc *sc; +{ + if (sc->sc_dma_ok) + sc->sc_wdcdev.cap |= WDC_CAPABILITY_DMA; + sc->sc_wdcdev.PIO_cap = 0; + sc->sc_wdcdev.DMA_cap = 0; +} + +void +default_setup_chip(sc) + struct pciide_softc *sc; +{ + int channel, drive, idedma_ctl; + struct channel_softc *chp; + struct ata_drive_datas *drvp; + + if (sc->sc_dma_ok == 0) + return; /* nothing to do */ + + /* Allocate DMA maps */ + for (channel = 0; channel < sc->sc_wdcdev.nchannels; channel++) { + idedma_ctl = 0; + chp = &sc->pciide_channels[channel].wdc_channel; + for (drive = 0; drive < 2; drive++) { + drvp = &chp->ch_drive[drive]; + /* If no drive, skip */ + if ((drvp->drive_flags & DRIVE) == 0) + continue; + if ((drvp->drive_flags & DRIVE_DMA) == 0) + continue; + if (pciide_dma_table_setup(sc, channel, drive) != 0) { + /* Abort DMA setup */ + printf("%s:%d:%d: can't allocate DMA maps, " + "using PIO transfers\n", + sc->sc_wdcdev.sc_dev.dv_xname, + channel, drive); + drvp->drive_flags &= ~DRIVE_DMA; + } + printf("%s:%d:%d: using DMA data transfers\n", + sc->sc_wdcdev.sc_dev.dv_xname, + channel, drive); + idedma_ctl |= IDEDMA_CTL_DRV_DMA(drive); + } + if (idedma_ctl != 0) { + /* Add software bits in status register */ + bus_space_write_1(sc->sc_dma_iot, sc->sc_dma_ioh, + IDEDMA_CTL + (IDEDMA_SCH_OFFSET * channel), + idedma_ctl); + } + } + +} + +void +default_channel_map(pa, cp) + struct pci_attach_args *pa; + struct pciide_channel *cp; +{ + struct pciide_softc *sc = (struct pciide_softc *)cp->wdc_channel.wdc; + bus_size_t cmdsize, ctlsize; + pcireg_t csr; + const char *failreason = NULL; + struct channel_softc *wdc_cp = &cp->wdc_channel; + int interface = + PCI_INTERFACE(pci_conf_read(sc->sc_pc, sc->sc_tag, PCI_CLASS_REG)); + + if (interface & PCIIDE_INTERFACE_PCI(wdc_cp->channel)) + cp->hw_ok = pciide_mapregs_native(pa, cp, &cmdsize, &ctlsize); + else + cp->hw_ok = pciide_mapregs_compat(pa, cp, wdc_cp->channel, + &cmdsize, &ctlsize); + if (cp->hw_ok == 0) + return; + + /* + * Check to see if something appears to be there. + */ + if (!wdcprobe(wdc_cp)) { + failreason = "not responding; disabled or no drives?"; + goto out; + } + + /* + * Now, make sure it's actually attributable to this PCI IDE + * channel by trying to access the channel again while the + * PCI IDE controller's I/O space is disabled. (If the + * channel no longer appears to be there, it belongs to + * this controller.) YUCK! + */ + csr = pci_conf_read(sc->sc_pc, sc->sc_tag, PCI_COMMAND_STATUS_REG); + pci_conf_write(sc->sc_pc, sc->sc_tag, PCI_COMMAND_STATUS_REG, + csr & ~PCI_COMMAND_IO_ENABLE); + if (wdcprobe(wdc_cp)) + failreason = "other hardware responding at addresses"; + pci_conf_write(sc->sc_pc, sc->sc_tag, PCI_COMMAND_STATUS_REG, csr); + +out: + if (failreason) { + printf("%s: %s channel ignored (%s)\n", + sc->sc_wdcdev.sc_dev.dv_xname, cp->name, + failreason); + cp->hw_ok = 0; + bus_space_unmap(wdc_cp->cmd_iot, wdc_cp->cmd_ioh, cmdsize); + bus_space_unmap(wdc_cp->ctl_iot, wdc_cp->ctl_ioh, ctlsize); + } + pciide_map_compat_intr(pa, cp, wdc_cp->channel, interface); + if (cp->hw_ok) { + wdc_cp->data32iot = wdc_cp->cmd_iot; + wdc_cp->data32ioh = wdc_cp->cmd_ioh; + wdcattach(wdc_cp); + } +} + +void +piix_setup_cap(sc) + struct pciide_softc *sc; +{ + if (sc->sc_pp->ide_product == PCI_PRODUCT_INTEL_82371AB_IDE) + sc->sc_wdcdev.cap |= WDC_CAPABILITY_UDMA; + sc->sc_wdcdev.cap |= WDC_CAPABILITY_DATA32 | WDC_CAPABILITY_MODE | + WDC_CAPABILITY_DMA; + sc->sc_wdcdev.PIO_cap = 4; + sc->sc_wdcdev.DMA_cap = 2; + sc->sc_wdcdev.UDMA_cap = 2; + if (sc->sc_pp->ide_product == PCI_PRODUCT_INTEL_82371SB_IDE || + sc->sc_pp->ide_product == PCI_PRODUCT_INTEL_82371AB_IDE) + sc->sc_wdcdev.set_modes = piix3_4_setup_channel; + else + sc->sc_wdcdev.set_modes = piix_setup_channel; +} + +void +piix_setup_chip(sc) + struct pciide_softc *sc; +{ + u_int8_t channel; + + + WDCDEBUG_PRINT(("piix_setup_chip: old idetim=0x%x\n", + pci_conf_read(sc->sc_pc, sc->sc_tag, PIIX_IDETIM)), DEBUG_PROBE); + + for (channel = 0; channel < sc->sc_wdcdev.nchannels; channel++) { + piix_setup_channel(&sc->pciide_channels[channel].wdc_channel); + } + WDCDEBUG_PRINT(("piix_setup_chip: idetim=0x%x\n", + pci_conf_read(sc->sc_pc, sc->sc_tag, PIIX_IDETIM)), DEBUG_PROBE); +} + +void +piix_setup_channel(chp) + struct channel_softc *chp; +{ + u_int8_t mode[2], drive; + u_int32_t oidetim, idetim, idedma_ctl; + struct pciide_channel *cp = (struct pciide_channel*)chp; + struct pciide_softc *sc = (struct pciide_softc *)cp->wdc_channel.wdc; + struct ata_drive_datas *drvp = cp->wdc_channel.ch_drive; + + oidetim = pci_conf_read(sc->sc_pc, sc->sc_tag, PIIX_IDETIM); + idetim = PIIX_IDETIM_CLEAR(oidetim, 0xffff, chp->channel); + idedma_ctl = 0; + + /* set up new idetim: Enable IDE registers decode */ + idetim = PIIX_IDETIM_SET(idetim, PIIX_IDETIM_IDE, + chp->channel); + + /* setup DMA */ + pciide_channel_dma_setup(cp); + + /* + * Here we have to mess up with drives mode: PIIX can't have + * different timings for master and slave drives. + * We need to find the best combination. + */ + + /* If both drives supports DMA, take the lower mode */ + if ((drvp[0].drive_flags & DRIVE_DMA) && + (drvp[1].drive_flags & DRIVE_DMA)) { + mode[0] = mode[1] = + min(drvp[0].DMA_mode, drvp[1].DMA_mode); + drvp[0].DMA_mode = mode[0]; + drvp[1].DMA_mode = mode[1]; + goto ok; + } + /* + * If only one drive supports DMA, use its mode, and + * put the other one in PIO mode 0 if mode not compatible + */ + if (drvp[0].drive_flags & DRIVE_DMA) { + mode[0] = drvp[0].DMA_mode; + mode[1] = drvp[1].PIO_mode; + if (piix_isp_pio[mode[1]] != piix_isp_dma[mode[0]] || + piix_rtc_pio[mode[1]] != piix_rtc_dma[mode[0]]) + mode[1] = drvp[1].PIO_mode = 0; + goto ok; + } + if (drvp[1].drive_flags & DRIVE_DMA) { + mode[1] = drvp[1].DMA_mode; + mode[0] = drvp[0].PIO_mode; + if (piix_isp_pio[mode[0]] != piix_isp_dma[mode[1]] || + piix_rtc_pio[mode[0]] != piix_rtc_dma[mode[1]]) + mode[0] = drvp[0].PIO_mode = 0; + goto ok; + } + /* + * If both drives are not DMA, takes the lower mode, unless + * one of them is PIO mode < 2 + */ + if (drvp[0].PIO_mode < 2) { + mode[0] = drvp[0].PIO_mode = 0; + mode[1] = drvp[1].PIO_mode; + } else if (drvp[1].PIO_mode < 2) { + mode[1] = drvp[1].PIO_mode = 0; + mode[0] = drvp[0].PIO_mode; + } else { + mode[0] = mode[1] = + min(drvp[1].PIO_mode, drvp[0].PIO_mode); + drvp[0].PIO_mode = mode[0]; + drvp[1].PIO_mode = mode[1]; + } +ok: /* The modes are setup */ + for (drive = 0; drive < 2; drive++) { + if (drvp[drive].drive_flags & DRIVE_DMA) { + idetim |= piix_setup_idetim_timings( + mode[drive], 1, chp->channel); + goto end; + } + } + /* If we are there, none of the drives are DMA */ + if (mode[0] >= 2) + idetim |= piix_setup_idetim_timings( + mode[0], 0, chp->channel); + else + idetim |= piix_setup_idetim_timings( + mode[1], 0, chp->channel); +end: /* + * timing mode is now set up in the controller. Enable + * it per-drive + */ + for (drive = 0; drive < 2; drive++) { + /* If no drive, skip */ + if ((drvp[drive].drive_flags & DRIVE) == 0) + continue; + idetim |= piix_setup_idetim_drvs(&drvp[drive]); + if (drvp[drive].drive_flags & DRIVE_DMA) + idedma_ctl |= IDEDMA_CTL_DRV_DMA(drive); + } + if (idedma_ctl != 0) { + /* Add software bits in status register */ + bus_space_write_1(sc->sc_dma_iot, sc->sc_dma_ioh, + IDEDMA_CTL + (IDEDMA_SCH_OFFSET * chp->channel), + idedma_ctl); + } + pci_conf_write(sc->sc_pc, sc->sc_tag, PIIX_IDETIM, idetim); + pciide_print_modes(cp); +} + +void +piix3_4_setup_chip(sc) + struct pciide_softc *sc; +{ + int channel; + + WDCDEBUG_PRINT(("piix3_4_setup_chip: old idetim=0x%x, sidetim=0x%x", + pci_conf_read(sc->sc_pc, sc->sc_tag, PIIX_IDETIM), + pci_conf_read(sc->sc_pc, sc->sc_tag, PIIX_SIDETIM)), DEBUG_PROBE); + if (sc->sc_wdcdev.cap & WDC_CAPABILITY_UDMA) { + WDCDEBUG_PRINT((", udamreg 0x%x", + pci_conf_read(sc->sc_pc, sc->sc_tag, PIIX_UDMAREG)), + DEBUG_PROBE); + } + WDCDEBUG_PRINT(("\n"), DEBUG_PROBE); + + for (channel = 0; channel < sc->sc_wdcdev.nchannels; channel++) { + piix3_4_setup_channel( + &sc->pciide_channels[channel].wdc_channel); + } + + WDCDEBUG_PRINT(("piix3_4_setup_chip: idetim=0x%x, sidetim=0x%x", + pci_conf_read(sc->sc_pc, sc->sc_tag, PIIX_IDETIM), + pci_conf_read(sc->sc_pc, sc->sc_tag, PIIX_SIDETIM)), DEBUG_PROBE); + if (sc->sc_wdcdev.cap & WDC_CAPABILITY_UDMA) { + WDCDEBUG_PRINT((", udmareg=0x%x", + pci_conf_read(sc->sc_pc, sc->sc_tag, PIIX_UDMAREG)), + DEBUG_PROBE); + } + WDCDEBUG_PRINT(("\n"), DEBUG_PROBE); +} + +void +piix3_4_setup_channel(chp) + struct channel_softc *chp; +{ + struct ata_drive_datas *drvp; + u_int32_t oidetim, idetim, sidetim, udmareg, idedma_ctl; + struct pciide_channel *cp = (struct pciide_channel*)chp; + struct pciide_softc *sc = (struct pciide_softc *)cp->wdc_channel.wdc; + int drive; + + oidetim = pci_conf_read(sc->sc_pc, sc->sc_tag, PIIX_IDETIM); + sidetim = pci_conf_read(sc->sc_pc, sc->sc_tag, PIIX_SIDETIM); + udmareg = pci_conf_read(sc->sc_pc, sc->sc_tag, PIIX_UDMAREG); + idetim = PIIX_IDETIM_CLEAR(oidetim, 0xffff, chp->channel); + sidetim &= ~(PIIX_SIDETIM_ISP_MASK(chp->channel) | + PIIX_SIDETIM_RTC_MASK(chp->channel)); + + idedma_ctl = 0; + /* If channel disabled, no need to go further */ + if ((PIIX_IDETIM_READ(oidetim, chp->channel) & PIIX_IDETIM_IDE) == 0) + return; + /* set up new idetim: Enable IDE registers decode */ + idetim = PIIX_IDETIM_SET(idetim, PIIX_IDETIM_IDE, chp->channel); + + /* setup DMA if needed */ + pciide_channel_dma_setup(cp); + + for (drive = 0; drive < 2; drive++) { + udmareg &= ~(PIIX_UDMACTL_DRV_EN(chp->channel, drive) | + PIIX_UDMATIM_SET(0x3, chp->channel, drive)); + drvp = &chp->ch_drive[drive]; + /* If no drive, skip */ + if ((drvp->drive_flags & DRIVE) == 0) + continue; + if (((drvp->drive_flags & DRIVE_DMA) == 0 && + (drvp->drive_flags & DRIVE_UDMA) == 0)) + goto pio; + + if ((chp->wdc->cap & WDC_CAPABILITY_UDMA) && + (drvp->drive_flags & DRIVE_UDMA)) { + /* use Ultra/DMA */ + drvp->drive_flags &= ~DRIVE_DMA; + udmareg |= PIIX_UDMACTL_DRV_EN( + chp->channel, drive); + udmareg |= PIIX_UDMATIM_SET( + piix4_sct_udma[drvp->UDMA_mode], + chp->channel, drive); + } else { + /* use Multiword DMA */ + drvp->drive_flags &= ~DRIVE_UDMA; + if (drive == 0) { + idetim |= piix_setup_idetim_timings( + drvp->DMA_mode, 1, chp->channel); + } else { + sidetim |= piix_setup_sidetim_timings( + drvp->DMA_mode, 1, chp->channel); + idetim =PIIX_IDETIM_SET(idetim, + PIIX_IDETIM_SITRE, chp->channel); + } + } + idedma_ctl |= IDEDMA_CTL_DRV_DMA(drive); + +pio: /* use PIO mode */ + idetim |= piix_setup_idetim_drvs(drvp); + if (drive == 0) { + idetim |= piix_setup_idetim_timings( + drvp->PIO_mode, 0, chp->channel); + } else { + sidetim |= piix_setup_sidetim_timings( + drvp->PIO_mode, 0, chp->channel); + idetim =PIIX_IDETIM_SET(idetim, + PIIX_IDETIM_SITRE, chp->channel); + } + } + if (idedma_ctl != 0) { + /* Add software bits in status register */ + bus_space_write_1(sc->sc_dma_iot, sc->sc_dma_ioh, + IDEDMA_CTL + (IDEDMA_SCH_OFFSET * chp->channel), + idedma_ctl); + } + pci_conf_write(sc->sc_pc, sc->sc_tag, PIIX_IDETIM, idetim); + pci_conf_write(sc->sc_pc, sc->sc_tag, PIIX_SIDETIM, sidetim); + pci_conf_write(sc->sc_pc, sc->sc_tag, PIIX_UDMAREG, udmareg); + pciide_print_modes(cp); +} + + +/* setup ISP and RTC fields, based on mode */ +static u_int32_t +piix_setup_idetim_timings(mode, dma, channel) + u_int8_t mode; + u_int8_t dma; + u_int8_t channel; +{ + + if (dma) + return PIIX_IDETIM_SET(0, + PIIX_IDETIM_ISP_SET(piix_isp_dma[mode]) | + PIIX_IDETIM_RTC_SET(piix_rtc_dma[mode]), + channel); + else + return PIIX_IDETIM_SET(0, + PIIX_IDETIM_ISP_SET(piix_isp_pio[mode]) | + PIIX_IDETIM_RTC_SET(piix_rtc_pio[mode]), + channel); +} + +/* setup DTE, PPE, IE and TIME field based on PIO mode */ +static u_int32_t +piix_setup_idetim_drvs(drvp) + struct ata_drive_datas *drvp; +{ + u_int32_t ret = 0; + struct channel_softc *chp = drvp->chnl_softc; + u_int8_t channel = chp->channel; + u_int8_t drive = drvp->drive; + + /* + * If drive is using UDMA, timings setups are independant + * So just check DMA and PIO here. + */ + if (drvp->drive_flags & DRIVE_DMA) { + /* if mode = DMA mode 0, use compatible timings */ + if ((drvp->drive_flags & DRIVE_DMA) && + drvp->DMA_mode == 0) { + drvp->PIO_mode = 0; + return ret; + } + ret = PIIX_IDETIM_SET(ret, PIIX_IDETIM_TIME(drive), channel); + /* + * PIO and DMA timings are the same, use fast timings for PIO + * too, else use compat timings. + */ + if ((piix_isp_pio[drvp->PIO_mode] != + piix_isp_dma[drvp->DMA_mode]) || + (piix_rtc_pio[drvp->PIO_mode] != + piix_rtc_dma[drvp->DMA_mode])) + drvp->PIO_mode = 0; + /* if PIO mode <= 2, use compat timings for PIO */ + if (drvp->PIO_mode <= 2) { + ret = PIIX_IDETIM_SET(ret, PIIX_IDETIM_DTE(drive), + channel); + return ret; + } + } + + /* + * Now setup PIO modes. If mode < 2, use compat timings. + * Else enable fast timings. Enable IORDY and prefetch/post + * if PIO mode >= 3. + */ + + if (drvp->PIO_mode < 2) + return ret; + + ret = PIIX_IDETIM_SET(ret, PIIX_IDETIM_TIME(drive), channel); + if (drvp->PIO_mode >= 3) { + ret = PIIX_IDETIM_SET(ret, PIIX_IDETIM_IE(drive), channel); + ret = PIIX_IDETIM_SET(ret, PIIX_IDETIM_PPE(drive), channel); + } + return ret; +} + +/* setup values in SIDETIM registers, based on mode */ +static u_int32_t +piix_setup_sidetim_timings(mode, dma, channel) + u_int8_t mode; + u_int8_t dma; + u_int8_t channel; +{ + if (dma) + return PIIX_SIDETIM_ISP_SET(piix_isp_dma[mode], channel) | + PIIX_SIDETIM_RTC_SET(piix_rtc_dma[mode], channel); + else + return PIIX_SIDETIM_ISP_SET(piix_isp_pio[mode], channel) | + PIIX_SIDETIM_RTC_SET(piix_rtc_pio[mode], channel); +} + +void +piix_channel_map(pa, cp) + struct pci_attach_args *pa; + struct pciide_channel *cp; +{ + struct pciide_softc *sc = (struct pciide_softc *)cp->wdc_channel.wdc; + bus_size_t cmdsize, ctlsize; + struct channel_softc *wdc_cp = &cp->wdc_channel; + u_int32_t idetim = pci_conf_read(sc->sc_pc, sc->sc_tag, PIIX_IDETIM); + + if ((PIIX_IDETIM_READ(idetim, wdc_cp->channel) & + PIIX_IDETIM_IDE) == 0) { + printf("%s: %s channel ignored (disabled)\n", + sc->sc_wdcdev.sc_dev.dv_xname, cp->name); + return; + } + + /* PIIX are compat-only pciide devices */ + pciide_mapchan(pa, cp, 0, &cmdsize, &ctlsize); + if (cp->hw_ok == 0) + return; + if (pciiide_chan_candisable(cp)) { + idetim = PIIX_IDETIM_CLEAR(idetim, PIIX_IDETIM_IDE, + wdc_cp->channel); + pci_conf_write(sc->sc_pc, sc->sc_tag, PIIX_IDETIM, idetim); + } + pciide_map_compat_intr(pa, cp, wdc_cp->channel, 0); +} + +void +apollo_setup_cap(sc) + struct pciide_softc *sc; +{ + if (sc->sc_pp->ide_product == PCI_PRODUCT_VIATECH_VT82C586A_IDE) + sc->sc_wdcdev.cap |= WDC_CAPABILITY_UDMA; + sc->sc_wdcdev.cap |= WDC_CAPABILITY_DATA32 | WDC_CAPABILITY_MODE | + WDC_CAPABILITY_DMA; + sc->sc_wdcdev.PIO_cap = 4; + sc->sc_wdcdev.DMA_cap = 2; + sc->sc_wdcdev.UDMA_cap = 2; + sc->sc_wdcdev.set_modes = apollo_setup_channel; + +} + +void +apollo_setup_chip(sc) + struct pciide_softc *sc; +{ + int channel; + + WDCDEBUG_PRINT(("apollo_setup_chip: old APO_IDECONF=0x%x, " + "APO_CTLMISC=0x%x, APO_DATATIM=0x%x, APO_UDMA=0x%x\n", + pci_conf_read(sc->sc_pc, sc->sc_tag, APO_IDECONF), + pci_conf_read(sc->sc_pc, sc->sc_tag, APO_CTLMISC), + pci_conf_read(sc->sc_pc, sc->sc_tag, APO_DATATIM), + pci_conf_read(sc->sc_pc, sc->sc_tag, APO_UDMA)), + DEBUG_PROBE); + + for (channel = 0; channel < sc->sc_wdcdev.nchannels; channel++) { + apollo_setup_channel(&sc->pciide_channels[channel].wdc_channel); + } + WDCDEBUG_PRINT(("apollo_setup_chip: APO_DATATIM=0x%x, APO_UDMA=0x%x\n", + pci_conf_read(sc->sc_pc, sc->sc_tag, APO_DATATIM), + pci_conf_read(sc->sc_pc, sc->sc_tag, APO_UDMA)), DEBUG_PROBE); +} + +void +apollo_setup_channel(chp) + struct channel_softc *chp; +{ + u_int32_t udmatim_reg, datatim_reg; + u_int8_t idedma_ctl; + int mode, drive; + struct ata_drive_datas *drvp; + struct pciide_channel *cp = (struct pciide_channel*)chp; + struct pciide_softc *sc = (struct pciide_softc *)cp->wdc_channel.wdc; + + idedma_ctl = 0; + datatim_reg = pci_conf_read(sc->sc_pc, sc->sc_tag, APO_DATATIM); + udmatim_reg = pci_conf_read(sc->sc_pc, sc->sc_tag, APO_UDMA); + datatim_reg &= ~APO_DATATIM_MASK(chp->channel); + udmatim_reg &= ~AP0_UDMA_MASK(chp->channel); + + /* setup DMA if needed */ + pciide_channel_dma_setup(cp); + + for (drive = 0; drive < 2; drive++) { + drvp = &chp->ch_drive[drive]; + /* If no drive, skip */ + if ((drvp->drive_flags & DRIVE) == 0) + continue; + /* add timing values, setup DMA if needed */ + if (((drvp->drive_flags & DRIVE_DMA) == 0 && + (drvp->drive_flags & DRIVE_UDMA) == 0)) { + mode = drvp->PIO_mode; + goto pio; + } + if ((chp->wdc->cap & WDC_CAPABILITY_UDMA) && + (drvp->drive_flags & DRIVE_UDMA)) { + /* use Ultra/DMA */ + drvp->drive_flags &= ~DRIVE_DMA; + udmatim_reg |= APO_UDMA_EN(chp->channel, drive) | + APO_UDMA_EN_MTH(chp->channel, drive) | + APO_UDMA_TIME(chp->channel, drive, + apollo_udma_tim[drvp->UDMA_mode]); + /* can use PIO timings, MW DMA unused */ + mode = drvp->PIO_mode; + } else { + /* use Multiword DMA */ + drvp->drive_flags &= ~DRIVE_UDMA; + /* mode = min(pio, dma+2) */ + if (drvp->PIO_mode <= (drvp->DMA_mode +2)) + mode = drvp->PIO_mode; + else + mode = drvp->DMA_mode + 2; + } + idedma_ctl |= IDEDMA_CTL_DRV_DMA(drive); + +pio: /* setup PIO mode */ + if (mode <= 2) { + drvp->DMA_mode = 0; + drvp->PIO_mode = 0; + mode = 0; + } else { + drvp->PIO_mode = mode; + drvp->DMA_mode = mode - 2; + } + datatim_reg |= + APO_DATATIM_PULSE(chp->channel, drive, + apollo_pio_set[mode]) | + APO_DATATIM_RECOV(chp->channel, drive, + apollo_pio_rec[mode]); + } + if (idedma_ctl != 0) { + /* Add software bits in status register */ + bus_space_write_1(sc->sc_dma_iot, sc->sc_dma_ioh, + IDEDMA_CTL + (IDEDMA_SCH_OFFSET * chp->channel), + idedma_ctl); + } + pciide_print_modes(cp); + pci_conf_write(sc->sc_pc, sc->sc_tag, APO_DATATIM, datatim_reg); + pci_conf_write(sc->sc_pc, sc->sc_tag, APO_UDMA, udmatim_reg); +} + +void +apollo_channel_map(pa, cp) + struct pci_attach_args *pa; + struct pciide_channel *cp; +{ + struct pciide_softc *sc = (struct pciide_softc *)cp->wdc_channel.wdc; + bus_size_t cmdsize, ctlsize; + struct channel_softc *wdc_cp = &cp->wdc_channel; + u_int32_t ideconf = pci_conf_read(sc->sc_pc, sc->sc_tag, APO_IDECONF); + int interface = + PCI_INTERFACE(pci_conf_read(sc->sc_pc, sc->sc_tag, PCI_CLASS_REG)); + + if ((ideconf & APO_IDECONF_EN(wdc_cp->channel)) == 0) { + printf("%s: %s channel ignored (disabled)\n", + sc->sc_wdcdev.sc_dev.dv_xname, cp->name); + return; + } + + pciide_mapchan(pa, cp, interface, &cmdsize, &ctlsize); + if (cp->hw_ok == 0) + return; + if (pciiide_chan_candisable(cp)) { + ideconf &= ~APO_IDECONF_EN(wdc_cp->channel); + pci_conf_write(sc->sc_pc, sc->sc_tag, APO_IDECONF, ideconf); + } + pciide_map_compat_intr(pa, cp, wdc_cp->channel, interface); +} + +void +cmd_channel_map(pa, cp) + struct pci_attach_args *pa; + struct pciide_channel *cp; +{ + struct pciide_softc *sc = (struct pciide_softc *)cp->wdc_channel.wdc; + bus_size_t cmdsize, ctlsize; + struct channel_softc *wdc_cp = &cp->wdc_channel; + u_int8_t ctrl = pciide_pci_read(sc->sc_pc, sc->sc_tag, CMD_CTRL); + int interface = + PCI_INTERFACE(pci_conf_read(sc->sc_pc, sc->sc_tag, PCI_CLASS_REG)); + + /* + * with a CMD PCI64x, if we get here, the first channel is enabled: + * there's no way to disable the first channel without disabling + * the whole device + */ + if (wdc_cp->channel != 0 && (ctrl & CMD_CTRL_2PORT) == 0) { + printf("%s: %s channel ignored (disabled)\n", + sc->sc_wdcdev.sc_dev.dv_xname, cp->name); + return; + } + + pciide_mapchan(pa, cp, interface, &cmdsize, &ctlsize); + if (cp->hw_ok == 0) + return; + if (wdc_cp->channel == 1) { + if (pciiide_chan_candisable(cp)) { + ctrl &= ~CMD_CTRL_2PORT; + pciide_pci_write(pa->pa_pc, pa->pa_tag, + CMD_CTRL, ctrl); + } + } + pciide_map_compat_intr(pa, cp, wdc_cp->channel, interface); +} + +void +cmd0643_6_setup_cap(sc) + struct pciide_softc *sc; +{ + + sc->sc_wdcdev.cap |= WDC_CAPABILITY_DATA32 | WDC_CAPABILITY_MODE | + WDC_CAPABILITY_DMA; + sc->sc_wdcdev.PIO_cap = 4; + sc->sc_wdcdev.DMA_cap = 2; + sc->sc_wdcdev.set_modes = cmd0643_6_setup_channel; +} + +void +cmd0643_6_setup_chip(sc) + struct pciide_softc *sc; +{ + int channel; + + WDCDEBUG_PRINT(("cmd0643_6_setup_chip: old timings reg 0x%x 0x%x\n", + pci_conf_read(sc->sc_pc, sc->sc_tag, 0x54), + pci_conf_read(sc->sc_pc, sc->sc_tag, 0x58)), + DEBUG_PROBE); + for (channel = 0; channel < sc->sc_wdcdev.nchannels; channel++) { + cmd0643_6_setup_channel( + &sc->pciide_channels[channel].wdc_channel); + } + /* configure for DMA read multiple */ + pciide_pci_write(sc->sc_pc, sc->sc_tag, CMD_DMA_MODE, CMD_DMA_MULTIPLE); + WDCDEBUG_PRINT(("cmd0643_6_setup_chip: timings reg now 0x%x 0x%x\n", + pci_conf_read(sc->sc_pc, sc->sc_tag, 0x54), + pci_conf_read(sc->sc_pc, sc->sc_tag, 0x58)), + DEBUG_PROBE); +} + +void +cmd0643_6_setup_channel(chp) + struct channel_softc *chp; +{ + struct ata_drive_datas *drvp; + u_int8_t tim; + u_int32_t idedma_ctl; + int drive; + struct pciide_channel *cp = (struct pciide_channel*)chp; + struct pciide_softc *sc = (struct pciide_softc *)cp->wdc_channel.wdc; + + idedma_ctl = 0; + /* setup DMA if needed */ + pciide_channel_dma_setup(cp); + + for (drive = 0; drive < 2; drive++) { + drvp = &chp->ch_drive[drive]; + /* If no drive, skip */ + if ((drvp->drive_flags & DRIVE) == 0) + continue; + /* add timing values, setup DMA if needed */ + tim = cmd0643_6_data_tim_pio[drvp->PIO_mode]; + if (drvp->drive_flags & DRIVE_DMA) { + /* + * use Multiword DMA. + * Timings will be used for both PIO and DMA, so adjust + * DMA mode if needed + */ + if (drvp->PIO_mode >= 3 && + (drvp->DMA_mode + 2) > drvp->PIO_mode) { + drvp->DMA_mode = drvp->PIO_mode - 2; + } + tim = cmd0643_6_data_tim_dma[drvp->DMA_mode]; + idedma_ctl |= IDEDMA_CTL_DRV_DMA(drive); + } + pciide_pci_write(sc->sc_pc, sc->sc_tag, + CMD_DATA_TIM(chp->channel, drive), tim); + } + if (idedma_ctl != 0) { + /* Add software bits in status register */ + bus_space_write_1(sc->sc_dma_iot, sc->sc_dma_ioh, + IDEDMA_CTL + (IDEDMA_SCH_OFFSET * chp->channel), + idedma_ctl); + } + pciide_print_modes(cp); +} + +void +cy693_setup_cap(sc) + struct pciide_softc *sc; +{ + + sc->sc_wdcdev.cap |= WDC_CAPABILITY_DATA32 | WDC_CAPABILITY_MODE | + WDC_CAPABILITY_DMA; + sc->sc_wdcdev.PIO_cap = 4; + sc->sc_wdcdev.DMA_cap = 2; + sc->sc_wdcdev.set_modes = cy693_setup_channel; +} + +void +cy693_setup_chip(sc) + struct pciide_softc *sc; +{ + + WDCDEBUG_PRINT(("cy693_setup_chip: old timings reg 0x%x\n", + pci_conf_read(sc->sc_pc, sc->sc_tag, CY_CMD_CTRL)), + DEBUG_PROBE); + cy693_setup_channel(&sc->pciide_channels[0].wdc_channel); + WDCDEBUG_PRINT(("cy693_setup_chip: new timings reg 0x%x\n", + pci_conf_read(sc->sc_pc, sc->sc_tag, CY_CMD_CTRL)), DEBUG_PROBE); +} + +void +cy693_setup_channel(chp) + struct channel_softc *chp; +{ + struct ata_drive_datas *drvp; + int drive; + u_int32_t cy_cmd_ctrl; + u_int32_t idedma_ctl; + struct pciide_channel *cp = (struct pciide_channel*)chp; + struct pciide_softc *sc = (struct pciide_softc *)cp->wdc_channel.wdc; + + cy_cmd_ctrl = idedma_ctl = 0; + + /* setup DMA if needed */ + pciide_channel_dma_setup(cp); + + for (drive = 0; drive < 2; drive++) { + drvp = &chp->ch_drive[drive]; + /* If no drive, skip */ + if ((drvp->drive_flags & DRIVE) == 0) + continue; + /* add timing values, setup DMA if needed */ + if (drvp->drive_flags & DRIVE_DMA) { + idedma_ctl |= IDEDMA_CTL_DRV_DMA(drive); + /* + * use Multiword DMA + * Timings will be used for both PIO and DMA, so adjust + * DMA mode if needed + */ + if (drvp->PIO_mode > (drvp->DMA_mode + 2)) + drvp->PIO_mode = drvp->DMA_mode + 2; + if (drvp->DMA_mode == 0) + drvp->PIO_mode = 0; + } + cy_cmd_ctrl |= (cy_pio_pulse[drvp->PIO_mode] << + CY_CMD_CTRL_IOW_PULSE_OFF(drive)); + cy_cmd_ctrl |= (cy_pio_rec[drvp->PIO_mode] << + CY_CMD_CTRL_IOW_REC_OFF(drive)); + cy_cmd_ctrl |= (cy_pio_pulse[drvp->PIO_mode] << + CY_CMD_CTRL_IOR_PULSE_OFF(drive)); + cy_cmd_ctrl |= (cy_pio_rec[drvp->PIO_mode] << + CY_CMD_CTRL_IOR_REC_OFF(drive)); + } + pci_conf_write(sc->sc_pc, sc->sc_tag, CY_CMD_CTRL, cy_cmd_ctrl); + pciide_print_modes(cp); + if (idedma_ctl != 0) { + /* Add software bits in status register */ + bus_space_write_1(sc->sc_dma_iot, sc->sc_dma_ioh, + IDEDMA_CTL, idedma_ctl); + } +} + +void +cy693_channel_map(pa, cp) + struct pci_attach_args *pa; + struct pciide_channel *cp; +{ + struct pciide_softc *sc = (struct pciide_softc *)cp->wdc_channel.wdc; + bus_size_t cmdsize, ctlsize; + struct channel_softc *wdc_cp = &cp->wdc_channel; + int interface = + PCI_INTERFACE(pci_conf_read(sc->sc_pc, sc->sc_tag, PCI_CLASS_REG)); + int compatchan; + +#ifdef DIAGNOSTIC + if (wdc_cp->channel != 0) + panic("cy693_channel_map: channel %d", wdc_cp->channel); +#endif + + /* + * this chip has 2 PCI IDE functions, one for primary and one for + * secondary. So we need to call pciide_mapregs_compat() with + * the real channel + */ + if (pa->pa_function == 1) { + compatchan = 0; + } else if (pa->pa_function == 2) { + compatchan = 1; + } else { + printf("%s: unexpected PCI function %d\n", + sc->sc_wdcdev.sc_dev.dv_xname, pa->pa_function); + cp->hw_ok = 0; + return; + } + + /* Only one channel for this chip; if we are here it's enabled */ + if (interface & PCIIDE_INTERFACE_PCI(0)) + cp->hw_ok = pciide_mapregs_native(pa, cp, &cmdsize, &ctlsize); + else + cp->hw_ok = pciide_mapregs_compat(pa, cp, compatchan, + &cmdsize, &ctlsize); + if (cp->hw_ok == 0) + return; + wdc_cp->data32iot = wdc_cp->cmd_iot; + wdc_cp->data32ioh = wdc_cp->cmd_ioh; + wdcattach(wdc_cp); + if (pciiide_chan_candisable(cp)) { + pci_conf_write(sc->sc_pc, sc->sc_tag, + PCI_COMMAND_STATUS_REG, 0); + } + pciide_map_compat_intr(pa, cp, compatchan, interface); +} + +void +sis_setup_cap(sc) + struct pciide_softc *sc; +{ + + sc->sc_wdcdev.cap |= WDC_CAPABILITY_DATA32 | WDC_CAPABILITY_MODE | + WDC_CAPABILITY_DMA | WDC_CAPABILITY_UDMA; + sc->sc_wdcdev.PIO_cap = 4; + sc->sc_wdcdev.DMA_cap = 2; + sc->sc_wdcdev.UDMA_cap = 2; + sc->sc_wdcdev.set_modes = sis_setup_channel; +} + +void +sis_setup_chip(sc) + struct pciide_softc *sc; +{ + int channel; + + for (channel = 0; channel < sc->sc_wdcdev.nchannels; channel++) { + sis_setup_channel(&sc->pciide_channels[channel].wdc_channel); + } + pciide_pci_write(sc->sc_pc, sc->sc_tag, SIS_MISC, + pciide_pci_read(sc->sc_pc, sc->sc_tag, SIS_MISC) | + SIS_MISC_TIM_SEL | SIS_MISC_FIFO_SIZE); +} + +void +sis_setup_channel(chp) + struct channel_softc *chp; +{ + struct ata_drive_datas *drvp; + int drive; + u_int32_t sis_tim; + u_int32_t idedma_ctl; + struct pciide_channel *cp = (struct pciide_channel*)chp; + struct pciide_softc *sc = (struct pciide_softc *)cp->wdc_channel.wdc; + + WDCDEBUG_PRINT(("sis_setup_chip: old timings reg for " + "channel %d 0x%x\n", chp->channel, + pci_conf_read(sc->sc_pc, sc->sc_tag, SIS_TIM(chp->channel))), + DEBUG_PROBE); + sis_tim = 0; + idedma_ctl = 0; + /* setup DMA if needed */ + pciide_channel_dma_setup(cp); + + for (drive = 0; drive < 2; drive++) { + drvp = &chp->ch_drive[drive]; + /* If no drive, skip */ + if ((drvp->drive_flags & DRIVE) == 0) + continue; + /* add timing values, setup DMA if needed */ + if ((drvp->drive_flags & DRIVE_DMA) == 0 && + (drvp->drive_flags & DRIVE_UDMA) == 0) + goto pio; + + if (drvp->drive_flags & DRIVE_UDMA) { + /* use Ultra/DMA */ + drvp->drive_flags &= ~DRIVE_DMA; + sis_tim |= sis_udma_tim[drvp->UDMA_mode] << + SIS_TIM_UDMA_TIME_OFF(drive); + sis_tim |= SIS_TIM_UDMA_EN(drive); + } else { + /* + * use Multiword DMA + * Timings will be used for both PIO and DMA, + * so adjust DMA mode if needed + */ + if (drvp->PIO_mode > (drvp->DMA_mode + 2)) + drvp->PIO_mode = drvp->DMA_mode + 2; + if (drvp->DMA_mode + 2 > (drvp->PIO_mode)) + drvp->DMA_mode = (drvp->PIO_mode > 2) ? + drvp->PIO_mode - 2 : 0; + if (drvp->DMA_mode == 0) + drvp->PIO_mode = 0; + } + idedma_ctl |= IDEDMA_CTL_DRV_DMA(drive); +pio: sis_tim |= sis_pio_act[drvp->PIO_mode] << + SIS_TIM_ACT_OFF(drive); + sis_tim |= sis_pio_rec[drvp->PIO_mode] << + SIS_TIM_REC_OFF(drive); + } + WDCDEBUG_PRINT(("sis_setup_chip: new timings reg for " + "channel %d 0x%x\n", chp->channel, sis_tim), DEBUG_PROBE); + pci_conf_write(sc->sc_pc, sc->sc_tag, SIS_TIM(chp->channel), sis_tim); + if (idedma_ctl != 0) { + /* Add software bits in status register */ + bus_space_write_1(sc->sc_dma_iot, sc->sc_dma_ioh, + IDEDMA_CTL, idedma_ctl); + } + pciide_print_modes(cp); +} + +void +sis_channel_map(pa, cp) + struct pci_attach_args *pa; + struct pciide_channel *cp; +{ + struct pciide_softc *sc = (struct pciide_softc *)cp->wdc_channel.wdc; + bus_size_t cmdsize, ctlsize; + struct channel_softc *wdc_cp = &cp->wdc_channel; + u_int8_t sis_ctr0 = pciide_pci_read(sc->sc_pc, sc->sc_tag, SIS_CTRL0); + int interface = + PCI_INTERFACE(pci_conf_read(sc->sc_pc, sc->sc_tag, PCI_CLASS_REG)); + + if ((wdc_cp->channel == 0 && (sis_ctr0 & SIS_CTRL0_CHAN0_EN) == 0) || + (wdc_cp->channel == 1 && (sis_ctr0 & SIS_CTRL0_CHAN1_EN) == 0)) { + printf("%s: %s channel ignored (disabled)\n", + sc->sc_wdcdev.sc_dev.dv_xname, cp->name); + return; + } + + pciide_mapchan(pa, cp, interface, &cmdsize, &ctlsize); + if (cp->hw_ok == 0) + return; + if (pciiide_chan_candisable(cp)) { + if (wdc_cp->channel == 0) + sis_ctr0 &= ~SIS_CTRL0_CHAN0_EN; + else + sis_ctr0 &= ~SIS_CTRL0_CHAN1_EN; + pciide_pci_write(sc->sc_pc, sc->sc_tag, SIS_CTRL0, sis_ctr0); + } + pciide_map_compat_intr(pa, cp, wdc_cp->channel, interface); +} + +void +acer_setup_cap(sc) + struct pciide_softc *sc; +{ + + sc->sc_wdcdev.cap |= WDC_CAPABILITY_DATA32 | WDC_CAPABILITY_MODE | + WDC_CAPABILITY_DMA | WDC_CAPABILITY_UDMA; + sc->sc_wdcdev.PIO_cap = 4; + sc->sc_wdcdev.DMA_cap = 2; + sc->sc_wdcdev.UDMA_cap = 2; + sc->sc_wdcdev.set_modes = acer_setup_channel; +} + +void +acer_setup_chip(sc) + struct pciide_softc *sc; +{ + int channel; + + pciide_pci_write(sc->sc_pc, sc->sc_tag, ACER_CDRC, + (pciide_pci_read(sc->sc_pc, sc->sc_tag, ACER_CDRC) | + ACER_CDRC_DMA_EN) & ~ACER_CDRC_FIFO_DISABLE); + + for (channel = 0; channel < sc->sc_wdcdev.nchannels; channel++) { + acer_setup_channel(&sc->pciide_channels[channel].wdc_channel); + } +} + +void +acer_setup_channel(chp) + struct channel_softc *chp; +{ + struct ata_drive_datas *drvp; + int drive; + u_int32_t acer_fifo_udma; + u_int32_t idedma_ctl; + struct pciide_channel *cp = (struct pciide_channel*)chp; + struct pciide_softc *sc = (struct pciide_softc *)cp->wdc_channel.wdc; + + idedma_ctl = 0; + acer_fifo_udma = pci_conf_read(sc->sc_pc, sc->sc_tag, ACER_FTH_UDMA); + WDCDEBUG_PRINT(("acer_setup_chip: old fifo/udma reg 0x%x\n", + acer_fifo_udma), DEBUG_PROBE); + /* setup DMA if needed */ + pciide_channel_dma_setup(cp); + + for (drive = 0; drive < 2; drive++) { + drvp = &chp->ch_drive[drive]; + /* If no drive, skip */ + if ((drvp->drive_flags & DRIVE) == 0) + continue; + WDCDEBUG_PRINT(("acer_setup_chip: old timings reg for " + "channel %d drive %d 0x%x\n", chp->channel, drive, + pciide_pci_read(sc->sc_pc, sc->sc_tag, + ACER_IDETIM(chp->channel, drive))), DEBUG_PROBE); + /* clear FIFO/DMA mode */ + acer_fifo_udma &= ~(ACER_FTH_OPL(chp->channel, drive, 0x3) | + ACER_UDMA_EN(chp->channel, drive) | + ACER_UDMA_TIM(chp->channel, drive, 0x7)); + + /* add timing values, setup DMA if needed */ + if ((drvp->drive_flags & DRIVE_DMA) == 0 && + (drvp->drive_flags & DRIVE_UDMA) == 0) { + acer_fifo_udma |= + ACER_FTH_OPL(chp->channel, drive, 0x1); + goto pio; + } + + acer_fifo_udma |= ACER_FTH_OPL(chp->channel, drive, 0x2); + if (drvp->drive_flags & DRIVE_UDMA) { + /* use Ultra/DMA */ + drvp->drive_flags &= ~DRIVE_DMA; + acer_fifo_udma |= ACER_UDMA_EN(chp->channel, drive); + acer_fifo_udma |= + ACER_UDMA_TIM(chp->channel, drive, + acer_udma[drvp->UDMA_mode]); + } else { + /* + * use Multiword DMA + * Timings will be used for both PIO and DMA, + * so adjust DMA mode if needed + */ + if (drvp->PIO_mode > (drvp->DMA_mode + 2)) + drvp->PIO_mode = drvp->DMA_mode + 2; + if (drvp->DMA_mode + 2 > (drvp->PIO_mode)) + drvp->DMA_mode = (drvp->PIO_mode > 2) ? + drvp->PIO_mode - 2 : 0; + if (drvp->DMA_mode == 0) + drvp->PIO_mode = 0; + } + idedma_ctl |= IDEDMA_CTL_DRV_DMA(drive); +pio: pciide_pci_write(sc->sc_pc, sc->sc_tag, + ACER_IDETIM(chp->channel, drive), + acer_pio[drvp->PIO_mode]); + } + WDCDEBUG_PRINT(("acer_setup_chip: new fifo/udma reg 0x%x\n", + acer_fifo_udma), DEBUG_PROBE); + pci_conf_write(sc->sc_pc, sc->sc_tag, ACER_FTH_UDMA, acer_fifo_udma); + if (idedma_ctl != 0) { + /* Add software bits in status register */ + bus_space_write_1(sc->sc_dma_iot, sc->sc_dma_ioh, + IDEDMA_CTL, idedma_ctl); + } + pciide_print_modes(cp); +} + +void +acer_channel_map(pa, cp) + struct pci_attach_args *pa; + struct pciide_channel *cp; +{ + struct pciide_softc *sc = (struct pciide_softc *)cp->wdc_channel.wdc; + bus_size_t cmdsize, ctlsize; + struct channel_softc *wdc_cp = &cp->wdc_channel; + u_int32_t cr; + int interface; + + /* + * Enable "microsoft register bits" R/W. Will be done 2 times + * (one for each channel) but should'nt be a problem. There's no + * better place where to put this. + */ + pciide_pci_write(sc->sc_pc, sc->sc_tag, ACER_CCAR3, + pciide_pci_read(sc->sc_pc, sc->sc_tag, ACER_CCAR3) | ACER_CCAR3_PI); + pciide_pci_write(sc->sc_pc, sc->sc_tag, ACER_CCAR1, + pciide_pci_read(sc->sc_pc, sc->sc_tag, ACER_CCAR1) & + ~(ACER_CHANSTATUS_RO|PCIIDE_CHAN_RO(0)|PCIIDE_CHAN_RO(1))); + pciide_pci_write(sc->sc_pc, sc->sc_tag, ACER_CCAR2, + pciide_pci_read(sc->sc_pc, sc->sc_tag, ACER_CCAR2) & + ~ACER_CHANSTATUSREGS_RO); + cr = pci_conf_read(sc->sc_pc, sc->sc_tag, PCI_CLASS_REG); + cr |= (PCIIDE_CHANSTATUS_EN << PCI_INTERFACE_SHIFT); + pci_conf_write(sc->sc_pc, sc->sc_tag, PCI_CLASS_REG, cr); + /* Don't use cr, re-read the real register content instead */ + interface = PCI_INTERFACE(pci_conf_read(sc->sc_pc, sc->sc_tag, + PCI_CLASS_REG)); + + if ((interface & PCIIDE_CHAN_EN(wdc_cp->channel)) == 0) { + printf("%s: %s channel ignored (disabled)\n", + sc->sc_wdcdev.sc_dev.dv_xname, cp->name); + return; + } + + pciide_mapchan(pa, cp, interface, &cmdsize, &ctlsize); + if (cp->hw_ok == 0) + return; + if (pciiide_chan_candisable(cp)) { + cr &= ~(PCIIDE_CHAN_EN(wdc_cp->channel) << PCI_INTERFACE_SHIFT); + pci_conf_write(sc->sc_pc, sc->sc_tag, PCI_CLASS_REG, cr); + } + pciide_map_compat_intr(pa, cp, wdc_cp->channel, interface); +} diff --git a/sys/dev/pci/pciide_acer_reg.h b/sys/dev/pci/pciide_acer_reg.h new file mode 100644 index 00000000000..97733161123 --- /dev/null +++ b/sys/dev/pci/pciide_acer_reg.h @@ -0,0 +1,83 @@ +/* $OpenBSD: pciide_acer_reg.h,v 1.1 1999/07/18 21:25:19 csapuntz Exp $ */ +/* $NetBSD: pciide_acer_reg.h,v 1.1 1999/02/02 16:13:59 bouyer Exp $ */ + +/* + * Copyright (c) 1999 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + * + */ + +/* class code attribute register 1 (1 byte) */ +#define ACER_CCAR1 0x43 +#define ACER_CHANSTATUS_RO 0x40 +#define PCIIDE_CHAN_RO(chan) (0x20 >> (chan)) + +/* class code attribute register 2 (1 byte) */ +#define ACER_CCAR2 0x4d +#define ACER_CHANSTATUSREGS_RO 0x80 + +/* class code attribute register 3 (1 byte) */ +#define ACER_CCAR3 0x50 +#define ACER_CCAR3_PI 0x02 + +/* flexible channel setting register */ +#define ACER_FCS 0x52 +#define ACER_FCS_TIMREG(chan,drv) ((0x8) >> ((drv) + (chan) * 2)) + +/* CD-ROM control register */ +#define ACER_CDRC 0x53 +#define ACER_CDRC_FIFO_DISABLE 0x02 +#define ACER_CDRC_DMA_EN 0x01 + +/* Fifo threshold and Ultra-DMA settings (4 bytes). */ +#define ACER_FTH_UDMA 0x54 +#define ACER_FTH_VAL(chan, drv, val) \ + (((val) & 0x3) << ((drv) * 4 + (chan) * 8)) +#define ACER_FTH_OPL(chan, drv, val) \ + (((val) & 0x3) << (2 + (drv) * 4 + (chan) * 8)) +#define ACER_UDMA_EN(chan, drv) \ + (0x8 << (16 + (drv) * 4 + (chan) * 8)) +#define ACER_UDMA_TIM(chan, drv, val) \ + (((val) & 0x7) << (16 + (drv) * 4 + (chan) * 8)) + +/* drives timings setup (1 byte) */ +#define ACER_IDETIM(chan, drv) (0x5a + (drv) + (chan) * 4) + +/* + * IDE bus frequency (1 byte) + * This should be setup by the BIOS - can we rely on this ? + */ +#define ACER_IDE_CLK 0x78 + +static int8_t acer_udma[] = {0x4, 0x3, 0x2}; +static int8_t acer_pio[] = {0x0c, 0x58, 0x44, 0x33, 0x31}; +#ifdef unused +static int8_t acer_dma[] = {0x08, 0x33, 0x31}; +#endif diff --git a/sys/dev/pci/pciide_apollo_reg.h b/sys/dev/pci/pciide_apollo_reg.h new file mode 100644 index 00000000000..2e32fbcfaaf --- /dev/null +++ b/sys/dev/pci/pciide_apollo_reg.h @@ -0,0 +1,95 @@ +/* $OpenBSD: pciide_apollo_reg.h,v 1.1 1999/07/18 21:25:19 csapuntz Exp $ */ +/* $NetBSD: pciide_apollo_reg.h,v 1.4 1998/12/16 12:48:46 bouyer Exp $ */ + +/* + * Copyright (c) 1998 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + * + */ + +/* + * Registers definitions for VIA technologies's Apollo controllers (VT82V580VO, + * VT82C586A and VT82C586B). Available from http://www.via.com.br/ + */ + +/* misc. configuration registers */ +#define APO_IDECONF 0x40 +#define APO_IDECONF_EN(channel) (0x00000001 << (1 - (channel))) +#define APO_IDECONF_SERR_EN 0x00000100 /* 580 only */ +#define APO_IDECONF_DS_SOURCE 0x00000200 /* 580 only */ +#define APO_IDECONF_ALT_INTR_EN 0x00000400 /* 580 only */ +#define APO_IDECONF_PERR_EN 0x00000800 /* 580 only */ +#define APO_IDECONF_WR_BUFF_EN(channel) (0x00001000 << ((1 - (channel)) << 1)) +#define APO_IDECONF_RD_PREF_EN(channel) (0x00002000 << ((1 - (channel)) << 1)) +#define APO_IDECONF_DEVSEL_TME 0x00010000 /* 580 only */ +#define APO_IDECONF_MAS_CMD_MON 0x00020000 /* 580 only */ +#define APO_IDECONF_IO_NAT(channel) \ + (0x00400000 << (1 - (channel))) /* 580 only */ +#define APO_IDECONF_FIFO_TRSH(channel, x) \ + ((x) & 0x3) << ((1 - (channel)) << 1 + 24) +#define APO_IDECONF_FIFO_CONF_MASK 0x60000000 + +/* Misc. controls register */ +#define APO_CTLMISC 0x44 +#define APO_CTLMISC_BM_STS_RTY 0x00000008 +#define APO_CTLMISC_FIFO_HWS 0x00000010 +#define APO_CTLMISC_WR_IRDY_WS 0x00000020 +#define APO_CTLMISC_RD_IRDY_WS 0x00000040 +#define APO_CTLMISC_INTR_SWP 0x00004000 +#define APO_CTLMISC_DRDY_TIME_MASK 0x00030000 +#define APO_CTLMISC_FIFO_FLSH_RD(channel) (0x00100000 << (1 - (channel))) +#define APO_CTLMISC_FIFO_FLSH_DMA(channel) (0x00400000 << (1 - (channel))) + +/* data port timings controls */ +#define APO_DATATIM 0x48 +#define APO_DATATIM_MASK(channel) (0xffff << ((1 - (channel)) << 4)) +#define APO_DATATIM_RECOV(channel, drive, x) (((x) & 0xf) << \ + (((1 - (channel)) << 4) + ((1 - (drive)) << 3))) +#define APO_DATATIM_PULSE(channel, drive, x) (((x) & 0xf) << \ + (((1 - (channel)) << 4) + ((1 - (drive)) << 3) + 4)) + +/* misc timings control */ +#define APO_MISCTIM 0x4c + +/* Ultra-DMA/33 control (586A/B only) */ +#define APO_UDMA 0x50 +#define AP0_UDMA_MASK(channel) (0xffff << ((1 - (channel)) << 4)) +#define APO_UDMA_TIME(channel, drive, x) (((x) & 0x3) << \ + (((1 - (channel)) << 4) + ((1 - (drive)) << 3))) +#define APO_UDMA_PIO_MODE(channel, drive) (0x20 << \ + (((1 - (channel)) << 4) + ((1 - (drive)) << 3))) +#define APO_UDMA_EN(channel, drive) (0x40 << \ + (((1 - (channel)) << 4) + ((1 - (drive)) << 3))) +#define APO_UDMA_EN_MTH(channel, drive) (0x80 << \ + (((1 - (channel)) << 4) + ((1 - (drive)) << 3))) + +static int8_t apollo_udma_tim[] = {0x03, 0x02, 0x00}; +static int8_t apollo_pio_set[] = {0x0a, 0x0a, 0x0a, 0x02, 0x02}; +static int8_t apollo_pio_rec[] = {0x08, 0x08, 0x08, 0x02, 0x00}; diff --git a/sys/dev/pci/pciide_cmd_reg.h b/sys/dev/pci/pciide_cmd_reg.h new file mode 100644 index 00000000000..7076a10ceae --- /dev/null +++ b/sys/dev/pci/pciide_cmd_reg.h @@ -0,0 +1,85 @@ +/* $OpenBSD: pciide_cmd_reg.h,v 1.1 1999/07/18 21:25:19 csapuntz Exp $ */ +/* $NetBSD: pciide_cmd_reg.h,v 1.4 1998/12/02 10:52:25 bouyer Exp $ */ + +/* + * Copyright (c) 1998 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + * + */ + +/* + * Registers definitions for CMD Technologies's PCI 064x IDE controllers. + * Available from http://www.cmd.com/ + */ + +/* Configuration (RO) */ +#define CMD_CONF 0x50 +#define CMD_CONF_REV_MASK 0x03 +#define CMD_CONF_DRV0_INTR 0x04 +#define CMD_CONF_DEVID 0x18 +#define CMD_CONF_VESAPRT 0x20 +#define CMD_CONF_DSA1 0x40 +#define CMD_CONF_DSA0 0x80 + +/* Control register (RW) */ +#define CMD_CTRL 0x51 +#define CMD_CTRL_HR_FIFO 0x01 +#define CMD_CTRL_HW_FIFO 0x02 +#define CMD_CTRL_DEVSEL 0x04 +#define CMD_CTRL_2PORT 0x08 +#define CMD_CTRL_PAR 0x10 +#define CMD_CTRL_HW_HLD 0x20 +#define CMD_CTRL_DRV0_RAHEAD 0x40 +#define CMD_CTRL_DRV1_RAHEAD 0x80 + +/* + * data read/write timing registers . 0640 uses the same for drive 0 and 1 + * on the secondary channel + */ +#define CMD_DATA_TIM(chan, drive) \ + (((chan) == 0) ? \ + ((drive) == 0) ? 0x54: 0x56 \ + : \ + ((drive) == 0) ? 0x58 : 0x5b) + +/* DMA master read mode select */ +#define CMD_DMA_MODE 0x71 +#define CMD_DMA 0x00 +#define CMD_DMA_MULTIPLE 0x01 +#define CMD_DMA_LINE 0x10 + + +/* + * timings values for the 0643 and 0x646 + * for all dma_mode we have to have + * DMA_timings(dma_mode) >= PIO_timings(dma_mode + 2) + */ +static int8_t cmd0643_6_data_tim_pio[] = {0xA9, 0x57, 0x44, 0x32, 0x3F}; +static int8_t cmd0643_6_data_tim_dma[] = {0x87, 0x32, 0x3F}; diff --git a/sys/dev/pci/pciide_cy693_reg.h b/sys/dev/pci/pciide_cy693_reg.h new file mode 100644 index 00000000000..231c4e4d036 --- /dev/null +++ b/sys/dev/pci/pciide_cy693_reg.h @@ -0,0 +1,73 @@ +/* $OpenBSD: pciide_cy693_reg.h,v 1.1 1999/07/18 21:25:20 csapuntz Exp $ */ +/* $NetBSD: pciide_cy693_reg.h,v 1.2 1998/12/03 14:06:16 bouyer Exp $ */ + +/* + * Copyright (c) 1998 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + * + */ + +/* + * Registers definitions for Contaq/Cypress's CY82693U PCI IDE controller. + * Available from http://www.cypress.com/japan/prodgate/chip/cy82c693.html + * This chip has 2 PCI IDE functions, each of them has only one channel + * So there's no primary/secodary distinction in the registers defs. + */ + +/* IDE control register */ +#define CY_CTRL 0x40 +#define CY_CTRL_RETRY 0x00002000 +#define CY_CTRL_SLAVE_PREFETCH 0x00000400 +#define CY_CTRL_POSTWRITE 0x00000200 +#define CY_CTRL_PREFETCH(drive) (0x00000100 << (2 * (drive))) +#define CY_CTRL_POSTWRITE_LENGTH_MASK 0x00000030 +#define CY_CTRL_POSTWRITE_LENGTH_OFF 4 +#define CY_CTRL_PREFETCH_LENGTH_MASK 0x00000003 +#define CY_CTRL_PREFETCH_LENGTH_OFF 0 + +/* IDE addr setup control register */ +#define CY_ADDR_CTRL 0x48 +#define CY_ADDR_CTRL_SETUP_OFF(drive) (4 * (drive)) +#define CY_ADDR_CTRL_SETUP_MASK(drive) \ + (0x00000007 << CY_ADDR_CTRL_SETUP_OFF(drive)) + +/* command control register */ +#define CY_CMD_CTRL 0x4c +#define CY_CMD_CTRL_IOW_PULSE_OFF(drive) (12 + 16 * (drive)) +#define CY_CMD_CTRL_IOW_REC_OFF(drive) (8 + 16 * (drive)) +#define CY_CMD_CTRL_IOR_PULSE_OFF(drive) (4 + 16 * (drive)) +#define CY_CMD_CTRL_IOR_REC_OFF(drive) (0 + 16 * (drive)) + +static int8_t cy_pio_pulse[] = {9, 4, 3, 2, 2}; +static int8_t cy_pio_rec[] = {9, 7, 4, 2, 0}; +#ifdef unused +static int8_t cy_dma_pulse[] = {7, 2, 2}; +static int8_t cy_dma_rec[] = {7, 1, 0}; +#endif diff --git a/sys/dev/pci/pciide_piix_reg.h b/sys/dev/pci/pciide_piix_reg.h new file mode 100644 index 00000000000..f7799a073c2 --- /dev/null +++ b/sys/dev/pci/pciide_piix_reg.h @@ -0,0 +1,110 @@ +/* $OpenBSD: pciide_piix_reg.h,v 1.1 1999/07/18 21:25:20 csapuntz Exp $ */ +/* $NetBSD: pciide_piix_reg.h,v 1.2 1998/10/12 16:09:21 bouyer Exp $ */ + +/* + * Copyright (c) 1998 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + * + */ + +/* + * Registers definitions for Intel's PIIX serie PCI IDE controllers. + * See Intel's + * "82371FB (PIIX) and 82371SB (PIIX3) PCI ISA IDE XCELERATOR" and + * "82371AB PCI-TO-ISA / IDE XCELERATOR (PIIX4)" + * available from http://www.intel.com/ + */ + +/* + * Bus master interface base address register + */ +#define PIIX_BMIBA 0x20 +#define PIIX_BMIBA_ADDR(x) (x & 0x0000FFFF0) +#define PIIX_BMIBA_RTE(x) (x & 0x000000001) +#define PIIX_BMIBA_RTE_IO 0x000000001 /* base addr maps to I/O space */ + +/* + * IDE timing register + * 0x40/0x41 is for primary, 0x42/0x43 for secondary channel + */ +#define PIIX_IDETIM 0x40 +#define PIIX_IDETIM_READ(x, channel) (((x) >> (16 * (channel))) & 0x0000FFFF) +#define PIIX_IDETIM_SET(x, bytes, channel) \ + ((x) | ((bytes) << (16 * (channel)))) +#define PIIX_IDETIM_CLEAR(x, bytes, channel) \ + ((x) & ~((bytes) << (16 * (channel)))) + +#define PIIX_IDETIM_IDE 0x8000 /* PIIX decode IDE registers */ +#define PIIX_IDETIM_SITRE 0x4000 /* slaves IDE timing registers + enabled (PIIX3/4 only) */ +#define PIIX_IDETIM_ISP_MASK 0x3000 /* IOrdy sample point */ +#define PIIX_IDETIM_ISP_SHIFT 12 +#define PIIX_IDETIM_ISP_SET(x) ((x) << PIIX_IDETIM_ISP_SHIFT) +#define PIIX_IDETIM_RTC_MASK 0x0300 /* recovery time */ +#define PIIX_IDETIM_RTC_SHIFT 8 +#define PIIX_IDETIM_RTC_SET(x) ((x) << PIIX_IDETIM_RTC_SHIFT) +#define PIIX_IDETIM_DTE(d) (0x0008 << (4 * (d))) /* DMA timing only */ +#define PIIX_IDETIM_PPE(d) (0x0004 << (4 * (d))) /* prefetch/posting */ +#define PIIX_IDETIM_IE(d) (0x0002 << (4 * (d))) /* IORDY enable */ +#define PIIX_IDETIM_TIME(d) (0x0001 << (4 * (d))) /* Fast timing enable */ +/* + * Slave IDE timing register (PIIX3/4 only) + * This register must be enabled via the PIIX_IDETIM_SITRE bit + */ +#define PIIX_SIDETIM 0x44 +#define PIIX_SIDETIM_ISP_MASK(channel) (0x0c << ((channel) * 4)) +#define PIIX_SIDETIM_ISP_SHIFT 2 +#define PIIX_SIDETIM_ISP_SET(x, channel) \ + (x << (PIIX_SIDETIM_ISP_SHIFT + ((channel) * 4))) +#define PIIX_SIDETIM_RTC_MASK(channel) (0x03 << ((channel) * 4)) +#define PIIX_SIDETIM_RTC_SHIFT 0 +#define PIIX_SIDETIM_RTC_SET(x, channel) \ + (x << (PIIX_SIDETIM_RTC_SHIFT + ((channel) * 4))) + +/* + * Ultra DMA/33 register (PIIX4 only) + */ +#define PIIX_UDMAREG 0x48 +/* Control register */ +#define PIIX_UDMACTL_DRV_EN(channel, drive) (0x01 << ((channel) * 2 + (drive))) +/* Ultra DMA/33 timing register (PIIX4 only) */ +#define PIIX_UDMATIM_SHIFT 16 +#define PIIX_UDMATIM_SET(x, channel, drive) \ + (((x) << ((channel * 8) + (drive * 4))) << PIIX_UDMATIM_SHIFT) +/* + * these tables define the differents values to upload to the + * ISP and RTC registers for the various PIO and DMA mode + * (from the PIIX4 doc). + */ +static int8_t piix_isp_pio[] = {0x00, 0x00, 0x01, 0x02, 0x02}; +static int8_t piix_rtc_pio[] = {0x00, 0x00, 0x00, 0x01, 0x03}; +static int8_t piix_isp_dma[] = {0x00, 0x02, 0x02}; +static int8_t piix_rtc_dma[] = {0x00, 0x02, 0x03}; +static int8_t piix4_sct_udma[] = {0x00, 0x01, 0x02}; diff --git a/sys/dev/pci/pciide_sis_reg.h b/sys/dev/pci/pciide_sis_reg.h new file mode 100644 index 00000000000..dc35200b154 --- /dev/null +++ b/sys/dev/pci/pciide_sis_reg.h @@ -0,0 +1,74 @@ +/* $OpenBSD: pciide_sis_reg.h,v 1.1 1999/07/18 21:25:20 csapuntz Exp $ */ +/* $NetBSD: pciide_sis_reg.h,v 1.5 1998/12/04 17:30:55 drochner Exp $ */ + +/* + * Copyright (c) 1998 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + * + */ + +/* + * Registers definitions for SiS SiS5597/98 PCI IDE controller. + * Available from http://www.sis.com.tw/html/databook.html + */ + +/* IDE timing control registers (32 bits) */ +#define SIS_TIM(channel) (0x40 + (channel * 4)) +#define SIS_TIM_REC_OFF(drive) (16 * (drive)) +#define SIS_TIM_ACT_OFF(drive) (8 + 16 * (drive)) +#define SIS_TIM_UDMA_TIME_OFF(drive) (13 + 16 * (drive)) +#define SIS_TIM_UDMA_EN(drive) (1 << (15 + 16 * (drive))) + +/* IDE general control register 0 (8 bits) */ +#define SIS_CTRL0 0x4a +#define SIS_CTRL0_PCIBURST 0x80 +#define SIS_CTRL0_FAST_PW 0x20 +#define SIS_CTRL0_BO 0x08 +#define SIS_CTRL0_CHAN0_EN 0x02 /* manual (v2.0) is wrong!!! */ +#define SIS_CTRL0_CHAN1_EN 0x04 /* manual (v2.0) is wrong!!! */ + +/* IDE general control register 1 (8 bits) */ +#define SIS_CTRL1 0x4b +#define SIS_CTRL1_POSTW_EN(chan, drv) (0x10 << ((drv) + 2 * (chan))) +#define SIS_CTRL1_PREFETCH_EN(chan, drv) (0x01 << ((drv) + 2 * (chan))) + +/* IDE misc control register (8 bit) */ +#define SIS_MISC 0x52 +#define SIS_MISC_TIM_SEL 0x08 +#define SIS_MISC_GTC 0x04 +#define SIS_MISC_FIFO_SIZE 0x01 + +static int8_t sis_pio_act[] = {7, 5, 4, 3, 3}; +static int8_t sis_pio_rec[] = {7, 0, 5, 3, 1}; +#ifdef unused +static int8_t sis_dma_act[] = {0, 3, 3}; +static int8_t sis_dma_rec[] = {0, 2, 1}; +#endif +static int8_t sis_udma_tim[] = {3, 2, 1}; diff --git a/sys/dev/pci/pciidereg.h b/sys/dev/pci/pciidereg.h index f1636c1aa95..f2aa7aa99a4 100644 --- a/sys/dev/pci/pciidereg.h +++ b/sys/dev/pci/pciidereg.h @@ -1,5 +1,5 @@ -/* $OpenBSD: pciidereg.h,v 1.1 1998/06/30 22:58:17 angelos Exp $ */ -/* $NetBSD: pciidereg.h,v 1.2 1998/03/04 19:17:10 cgd Exp $ */ +/* $OpenBSD: pciidereg.h,v 1.2 1999/07/18 21:25:20 csapuntz Exp $ */ +/* $NetBSD: pciidereg.h,v 1.4 1999/02/02 16:14:00 bouyer Exp $ */ /* * Copyright (c) 1998 Christopher G. Demetriou. All rights reserved. @@ -56,7 +56,12 @@ /* * Bits in the PCI Programming Interface register (some are per-channel). + * Bits 6-4 are defined as read-only in PCI 2.1 specification. + * Microsoft proposed to use these bits for independant channels + * enable/disable. This feature is enabled based on the value of bit 6. */ +#define PCIIDE_CHANSTATUS_EN 0x40 +#define PCIIDE_CHAN_EN(chan) (0x20 >> (chan)) #define PCIIDE_INTERFACE_PCI(chan) (0x01 << (2 * (chan))) #define PCIIDE_INTERFACE_SETTABLE(chan) (0x02 << (2 * (chan))) #define PCIIDE_INTERFACE_BUS_MASTER_DMA 0x80 @@ -69,3 +74,42 @@ #define PCIIDE_COMPAT_CTL_BASE(chan) ((chan) == 0 ? 0x3f6 : 0x376) #define PCIIDE_COMPAT_CTL_SIZE 1 #define PCIIDE_COMPAT_IRQ(chan) ((chan) == 0 ? 14 : 15) + +/* + * definitions for IDE DMA + * XXX maybe this should go elsewhere + */ + +/* secondary channel registers offset */ +#define IDEDMA_SCH_OFFSET 0x08 + +/* Bus master command register */ +#define IDEDMA_CMD 0x00 +#define IDEDMA_CMD_WRITE 0x08 +#define IDEDMA_CMD_START 0x01 + +/* Bus master status register */ +#define IDEDMA_CTL 0x02 +#define IDEDMA_CTL_DRV_DMA(d) (0x20 << (d)) +#define IDEDMA_CTL_INTR 0x04 +#define IDEDMA_CTL_ERR 0x02 +#define IDEDMA_CTL_ACT 0x01 + +/* Bus master table pointer register */ +#define IDEDMA_TBL 0x04 +#define IDEDMA_TBL_MASK 0xfffffffc +#define IDEDMA_TBL_ALIGN 0x00010000 + +/* bus master table descriptor */ +struct idedma_table { + u_int32_t base_addr; /* physical base addr of memory region */ + u_int32_t byte_count; /* memory region length */ +#define IDEDMA_BYTE_COUNT_MASK 0x0000FFFF +#define IDEDMA_BYTE_COUNT_EOT 0x80000000 +}; + +#define IDEDMA_BYTE_COUNT_MAX 0x00010000 /* Max I/O per table */ +#define IDEDMA_BYTE_COUNT_ALIGN 0x00010000 + +/* Number of idedma table needed */ +#define NIDEDMA_TABLES (MAXPHYS/NBPG + 1) diff --git a/sys/dev/pci/pciidevar.h b/sys/dev/pci/pciidevar.h index b9f6e8abb7b..5b000358ca7 100644 --- a/sys/dev/pci/pciidevar.h +++ b/sys/dev/pci/pciidevar.h @@ -1,5 +1,5 @@ -/* $OpenBSD: pciidevar.h,v 1.1 1998/06/30 22:58:18 angelos Exp $ */ -/* $NetBSD: pciidevar.h,v 1.1 1998/03/04 06:35:11 cgd Exp $ */ +/* $OpenBSD: pciidevar.h,v 1.2 1999/07/18 21:25:20 csapuntz Exp $ */ +/* $NetBSD: pciidevar.h,v 1.2 1998/10/12 16:09:22 bouyer Exp $ */ /* * Copyright (c) 1998 Christopher G. Demetriou. All rights reserved. @@ -38,18 +38,6 @@ */ struct device; -struct pci_attach_args; - -/* - * Attach args for devices that attach to pciide. Really, that's just wdc. - */ -struct pciide_attach_args { - int channel; - bus_space_tag_t cmd_iot, ctl_iot; - bus_space_handle_t cmd_ioh, ctl_ioh; - int (**ihandp) __P((void *)); - void **ihandargp; -}; /* * Functions defined by machine-dependent code. diff --git a/sys/sys/ataio.h b/sys/sys/ataio.h new file mode 100644 index 00000000000..c8360622738 --- /dev/null +++ b/sys/sys/ataio.h @@ -0,0 +1,39 @@ +/* $OpenBSD: ataio.h,v 1.1 1999/07/18 21:25:20 csapuntz Exp $ */ +/* $NetBSD: ataio.h,v 1.2 1998/11/23 22:58:23 kenh Exp $ */ + +#ifndef _SYS_ATAIO_H_ +#define _SYS_ATAIO_H_ + +#include <sys/types.h> +#include <sys/ioctl.h> + +typedef struct atareq { + u_long flags; /* info about the request status and type */ + u_char command; /* command code */ + u_char features; /* feature modifier bits for command */ + u_char sec_count; /* sector count */ + u_char sec_num; /* sector number */ + u_char head; /* head number */ + u_short cylinder; /* cylinder/lba address */ + + caddr_t databuf; /* Pointer to I/O data buffer */ + u_long datalen; /* length of data buffer */ + int timeout; /* Command timeout */ + u_char retsts; /* the return status for the command */ + u_char error; /* error bits */ +} atareq_t; + +/* bit defintions for flags */ +#define ATACMD_READ 0x00000001 +#define ATACMD_WRITE 0x00000002 +#define ATACMD_READREG 0x00000004 + +/* definitions for the return status (retsts) */ +#define ATACMD_OK 0x00 +#define ATACMD_TIMEOUT 0x01 +#define ATACMD_ERROR 0x02 +#define ATACMD_DF 0x03 + +#define ATAIOCCOMMAND _IOWR('Q', 8, atareq_t) + +#endif /* _SYS_ATAIO_H_ */ |