summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
Diffstat (limited to 'sys')
-rw-r--r--sys/arch/i386/conf/NEWATA297
-rw-r--r--sys/arch/i386/conf/files.i38610
-rw-r--r--sys/arch/i386/pci/pciide_machdep.c75
-rw-r--r--sys/conf/files11
-rw-r--r--sys/dev/ata/ata.c199
-rw-r--r--sys/dev/ata/ata_wdc.c802
-rw-r--r--sys/dev/ata/atareg.h158
-rw-r--r--sys/dev/ata/atavar.h167
-rw-r--r--sys/dev/ata/files.ata15
-rw-r--r--sys/dev/ata/wd.c1506
-rw-r--r--sys/dev/ata/wdvar.h80
-rw-r--r--sys/dev/atapiscsi/atapiconf.h52
-rw-r--r--sys/dev/atapiscsi/atapiscsi.c994
-rw-r--r--sys/dev/atapiscsi/files.atapiscsi8
-rw-r--r--sys/dev/ic/wdc.c1398
-rw-r--r--sys/dev/ic/wdcreg.h181
-rw-r--r--sys/dev/ic/wdcvar.h204
-rw-r--r--sys/dev/isa/files.isa11
-rw-r--r--sys/dev/isa/wdc_isa.c235
-rw-r--r--sys/dev/pci/files.pci6
-rw-r--r--sys/dev/pci/pci_map.c1
-rw-r--r--sys/dev/pci/pciide.c2325
-rw-r--r--sys/dev/pci/pciide_acer_reg.h83
-rw-r--r--sys/dev/pci/pciide_apollo_reg.h95
-rw-r--r--sys/dev/pci/pciide_cmd_reg.h85
-rw-r--r--sys/dev/pci/pciide_cy693_reg.h73
-rw-r--r--sys/dev/pci/pciide_piix_reg.h110
-rw-r--r--sys/dev/pci/pciide_sis_reg.h74
-rw-r--r--sys/dev/pci/pciidereg.h48
-rw-r--r--sys/dev/pci/pciidevar.h16
-rw-r--r--sys/sys/ataio.h39
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, &params) ==
+ 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, &params) != 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, &params2);
+ if (memcmp(&params, &params2, 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_ */