summaryrefslogtreecommitdiffstats
path: root/drivers/ide
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2000-03-19 01:28:40 +0000
committerRalf Baechle <ralf@linux-mips.org>2000-03-19 01:28:40 +0000
commit8abb719409c9060a7c0676f76e9182c1e0b8ca46 (patch)
treeb88cc5a6cd513a04a512b7e6215c873c90a1c5dd /drivers/ide
parentf01bd7aeafd95a08aafc9e3636bb26974df69d82 (diff)
Merge with 2.3.99-pre1.
Diffstat (limited to 'drivers/ide')
-rw-r--r--drivers/ide/Config.in153
-rw-r--r--drivers/ide/Makefile238
-rw-r--r--drivers/ide/aec6210.c372
-rw-r--r--drivers/ide/ali14xx.c225
-rw-r--r--drivers/ide/alim15x3.c689
-rw-r--r--drivers/ide/amd7409.c411
-rw-r--r--drivers/ide/buddha.c165
-rw-r--r--drivers/ide/cmd640.c855
-rw-r--r--drivers/ide/cmd64x.c721
-rw-r--r--drivers/ide/cs5530.c361
-rw-r--r--drivers/ide/cy82c693.c441
-rw-r--r--drivers/ide/dtc2278.c133
-rw-r--r--drivers/ide/falconide.c66
-rw-r--r--drivers/ide/gayle.c169
-rw-r--r--drivers/ide/hd.c883
-rw-r--r--drivers/ide/hpt34x.c419
-rw-r--r--drivers/ide/hpt366.c564
-rw-r--r--drivers/ide/ht6560b.c342
-rw-r--r--drivers/ide/icside.c643
-rw-r--r--drivers/ide/ide-cd.c2729
-rw-r--r--drivers/ide/ide-cd.h736
-rw-r--r--drivers/ide/ide-cs.c481
-rw-r--r--drivers/ide/ide-disk.c906
-rw-r--r--drivers/ide/ide-dma.c580
-rw-r--r--drivers/ide/ide-features.c312
-rw-r--r--drivers/ide/ide-floppy.c1680
-rw-r--r--drivers/ide/ide-geometry.c214
-rw-r--r--drivers/ide/ide-pci.c728
-rw-r--r--drivers/ide/ide-pmac.c969
-rw-r--r--drivers/ide/ide-pnp.c158
-rw-r--r--drivers/ide/ide-probe.c929
-rw-r--r--drivers/ide/ide-proc.c904
-rw-r--r--drivers/ide/ide-tape.c6031
-rw-r--r--drivers/ide/ide.c3639
-rw-r--r--drivers/ide/ide_modes.h233
-rw-r--r--drivers/ide/macide.c116
-rw-r--r--drivers/ide/ns87415.c185
-rw-r--r--drivers/ide/opti621.c315
-rw-r--r--drivers/ide/pdc202xx.c731
-rw-r--r--drivers/ide/pdc4030.c552
-rw-r--r--drivers/ide/pdc4030.h44
-rw-r--r--drivers/ide/piix.c434
-rw-r--r--drivers/ide/q40ide.c109
-rw-r--r--drivers/ide/qd6580.c71
-rw-r--r--drivers/ide/rapide.c92
-rw-r--r--drivers/ide/rz1000.c96
-rw-r--r--drivers/ide/sis5513.c549
-rw-r--r--drivers/ide/sl82c105.c138
-rw-r--r--drivers/ide/trm290.c282
-rw-r--r--drivers/ide/umc8672.c159
-rw-r--r--drivers/ide/via82cxxx.c834
51 files changed, 33786 insertions, 0 deletions
diff --git a/drivers/ide/Config.in b/drivers/ide/Config.in
new file mode 100644
index 000000000..bcd17994b
--- /dev/null
+++ b/drivers/ide/Config.in
@@ -0,0 +1,153 @@
+#
+# IDE ATA ATAPI Block device driver configuration
+#
+mainmenu_option next_comment
+comment 'IDE, ATA and ATAPI Block devices'
+
+dep_tristate 'Enhanced IDE/MFM/RLL disk/cdrom/tape/floppy support' CONFIG_BLK_DEV_IDE $CONFIG_IDE
+comment 'Please see Documentation/ide.txt for help/info on IDE drives'
+if [ "$CONFIG_BLK_DEV_IDE" != "n" ]; then
+ dep_bool ' Use old disk-only driver on primary interface' CONFIG_BLK_DEV_HD_IDE $CONFIG_X86
+ define_bool CONFIG_BLK_DEV_HD $CONFIG_BLK_DEV_HD_IDE
+
+ dep_tristate ' Include IDE/ATA-2 DISK support' CONFIG_BLK_DEV_IDEDISK $CONFIG_BLK_DEV_IDE
+ dep_mbool ' Use multi-mode by default' CONFIG_IDEDISK_MULTI_MODE $CONFIG_BLK_DEV_IDEDISK
+ dep_tristate ' PCMCIA IDE support' CONFIG_BLK_DEV_IDECS $CONFIG_BLK_DEV_IDE $CONFIG_PCMCIA
+ dep_tristate ' Include IDE/ATAPI CDROM support' CONFIG_BLK_DEV_IDECD $CONFIG_BLK_DEV_IDE
+ dep_tristate ' Include IDE/ATAPI TAPE support' CONFIG_BLK_DEV_IDETAPE $CONFIG_BLK_DEV_IDE
+ dep_tristate ' Include IDE/ATAPI FLOPPY support' CONFIG_BLK_DEV_IDEFLOPPY $CONFIG_BLK_DEV_IDE
+ dep_tristate ' SCSI emulation support' CONFIG_BLK_DEV_IDESCSI $CONFIG_BLK_DEV_IDE
+
+ comment 'IDE chipset support/bugfixes'
+ if [ "$CONFIG_BLK_DEV_IDE" != "n" ]; then
+ dep_bool ' CMD640 chipset bugfix/support' CONFIG_BLK_DEV_CMD640 $CONFIG_X86
+ dep_bool ' CMD640 enhanced support' CONFIG_BLK_DEV_CMD640_ENHANCED $CONFIG_BLK_DEV_CMD640
+ dep_bool ' ISA-PNP EIDE support' CONFIG_BLK_DEV_ISAPNP $CONFIG_ISAPNP
+ if [ "$CONFIG_PCI" = "y" ]; then
+ dep_bool ' RZ1000 chipset bugfix/support' CONFIG_BLK_DEV_RZ1000 $CONFIG_X86
+ bool ' Generic PCI IDE chipset support' CONFIG_BLK_DEV_IDEPCI
+ if [ "$CONFIG_BLK_DEV_IDEPCI" = "y" ]; then
+ bool ' Sharing PCI IDE interrupts support' CONFIG_IDEPCI_SHARE_IRQ
+ bool ' Generic PCI bus-master DMA support' CONFIG_BLK_DEV_IDEDMA_PCI
+ bool ' Boot off-board chipsets first support' CONFIG_BLK_DEV_OFFBOARD
+ dep_bool ' Use PCI DMA by default when available' CONFIG_IDEDMA_PCI_AUTO $CONFIG_BLK_DEV_IDEDMA_PCI
+ define_bool CONFIG_BLK_DEV_IDEDMA $CONFIG_BLK_DEV_IDEDMA_PCI
+ define_bool CONFIG_IDEDMA_AUTO $CONFIG_IDEDMA_PCI_AUTO
+ define_bool CONFIG_IDEDMA_PCI_EXPERIMENTAL $CONFIG_EXPERIMENTAL
+ dep_bool ' ATA Work(s) In Progress (EXPERIMENTAL)' CONFIG_IDEDMA_PCI_WIP $CONFIG_BLK_DEV_IDEDMA_PCI $CONFIG_EXPERIMENTAL
+ dep_bool ' Good-Bad DMA Model-Firmware (WIP)' CONFIG_IDEDMA_NEW_DRIVE_LISTINGS $CONFIG_IDEDMA_PCI_WIP
+ dep_bool ' AEC6210 chipset support' CONFIG_BLK_DEV_AEC6210 $CONFIG_BLK_DEV_IDEDMA_PCI
+ dep_mbool ' AEC6210 Tuning support (WIP)' CONFIG_AEC6210_TUNING $CONFIG_BLK_DEV_AEC6210 $CONFIG_IDEDMA_PCI_WIP
+ dep_bool ' ALI M15x3 chipset support' CONFIG_BLK_DEV_ALI15X3 $CONFIG_BLK_DEV_IDEDMA_PCI
+ dep_bool ' AMD Viper support' CONFIG_BLK_DEV_AMD7409 $CONFIG_BLK_DEV_IDEDMA_PCI
+ dep_mbool ' AMD Viper ATA-66 Override (WIP)' CONFIG_AMD7409_OVERRIDE $CONFIG_BLK_DEV_AMD7409 $CONFIG_IDEDMA_PCI_WIP
+ dep_bool ' CMD64X chipset support' CONFIG_BLK_DEV_CMD64X $CONFIG_BLK_DEV_IDEDMA_PCI
+ dep_mbool ' CMD64X chipset RAID (WIP)' CONFIG_CMD64X_RAID $CONFIG_BLK_DEV_CMD64X $CONFIG_IDEDMA_PCI_WIP
+ dep_bool ' CY82C693 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_CY82C693 $CONFIG_IDEDMA_PCI_EXPERIMENTAL
+ dep_bool ' Cyrix CS5530 MediaGX chipset support' CONFIG_BLK_DEV_CS5530 $CONFIG_BLK_DEV_IDEDMA_PCI
+ dep_bool ' HPT34X chipset support' CONFIG_BLK_DEV_HPT34X $CONFIG_BLK_DEV_IDEDMA_PCI
+ dep_mbool ' HPT34X AUTODMA support (WIP)' CONFIG_HPT34X_AUTODMA $CONFIG_BLK_DEV_HPT34X $CONFIG_IDEDMA_PCI_WIP
+ dep_bool ' HPT366 chipset support' CONFIG_BLK_DEV_HPT366 $CONFIG_BLK_DEV_IDEDMA_PCI
+ dep_bool ' HPT366 Fast Interrupts (WIP)' CONFIG_HPT366_FIP $CONFIG_BLK_DEV_HPT366 $CONFIG_IDEDMA_PCI_WIP
+ dep_mbool ' HPT366 mode Three (WIP)' CONFIG_HPT366_MODE3 $CONFIG_BLK_DEV_HPT366 $CONFIG_IDEDMA_PCI_WIP
+ if [ "$CONFIG_X86" = "y" -o "$CONFIG_IA64" = "y" ]; then
+ dep_mbool ' Intel PIIXn chipsets support' CONFIG_BLK_DEV_PIIX $CONFIG_BLK_DEV_IDEDMA_PCI
+ dep_mbool ' PIIXn Tuning support' CONFIG_PIIX_TUNING $CONFIG_BLK_DEV_PIIX $CONFIG_IDEDMA_PCI_AUTO
+ fi
+ dep_bool ' NS87415 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_NS87415 $CONFIG_IDEDMA_PCI_EXPERIMENTAL
+ dep_bool ' OPTi 82C621 chipset enhanced support (EXPERIMENTAL)' CONFIG_BLK_DEV_OPTI621 $CONFIG_EXPERIMENTAL
+ dep_bool ' PROMISE PDC20246/PDC20262 support' CONFIG_BLK_DEV_PDC202XX $CONFIG_BLK_DEV_IDEDMA_PCI
+ dep_bool ' Special UDMA Feature' CONFIG_PDC202XX_BURST $CONFIG_BLK_DEV_PDC202XX
+ dep_mbool ' Special Mode Feature (WIP)' CONFIG_PDC202XX_MASTER $CONFIG_BLK_DEV_PDC202XX $CONFIG_IDEDMA_PCI_WIP
+ dep_bool ' SiS5513 chipset support' CONFIG_BLK_DEV_SIS5513 $CONFIG_BLK_DEV_IDEDMA_PCI $CONFIG_X86
+ dep_bool ' Tekram TRM290 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_TRM290 $CONFIG_IDEDMA_PCI_EXPERIMENTAL
+ dep_bool ' VIA82CXXX chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_VIA82CXXX $CONFIG_IDEDMA_PCI_EXPERIMENTAL
+ fi
+ if [ "$CONFIG_PPC" = "y" -o "$CONFIG_ARM" = "y" ]; then
+ bool ' Winbond SL82c105 support' CONFIG_BLK_DEV_SL82C105
+ fi
+ fi
+ if [ "$CONFIG_PMAC" = "y" -o "$CONFIG_ALL_PPC" = "y" ]; then
+ bool ' Builtin PowerMac IDE support' CONFIG_BLK_DEV_IDE_PMAC
+ dep_bool ' PowerMac IDE DMA support' CONFIG_BLK_DEV_IDEDMA_PMAC $CONFIG_BLK_DEV_IDE_PMAC
+ dep_bool ' Use DMA by default' CONFIG_IDEDMA_PMAC_AUTO $CONFIG_BLK_DEV_IDEDMA_PMAC
+ define_bool CONFIG_BLK_DEV_IDEDMA $CONFIG_BLK_DEV_IDEDMA_PMAC
+ define_bool CONFIG_IDEDMA_AUTO $CONFIG_IDEDMA_PMAC_AUTO
+ fi
+ if [ "$CONFIG_ARCH_ACORN" = "y" ]; then
+ dep_bool ' ICS IDE interface support' CONFIG_BLK_DEV_IDE_ICSIDE $CONFIG_ARCH_ACORN
+ dep_bool ' ICS DMA support' CONFIG_BLK_DEV_IDEDMA_ICS $CONFIG_BLK_DEV_IDE_ICSIDE
+ dep_bool ' Use ICS DMA by default' CONFIG_IDEDMA_ICS_AUTO $CONFIG_BLK_DEV_IDEDMA_ICS
+ define_bool CONFIG_BLK_DEV_IDEDMA $CONFIG_BLK_DEV_IDEDMA_ICS
+ define_bool CONFIG_IDEDMA_AUTO $CONFIG_IDEDMA_ICS_AUTO
+ dep_bool ' RapIDE interface support' CONFIG_BLK_DEV_IDE_RAPIDE $CONFIG_ARCH_ACORN
+ fi
+ if [ "$CONFIG_AMIGA" = "y" ]; then
+ dep_bool ' Amiga Gayle IDE interface support' CONFIG_BLK_DEV_GAYLE $CONFIG_AMIGA
+ dep_mbool ' Amiga IDE Doubler support (EXPERIMENTAL)' CONFIG_BLK_DEV_IDEDOUBLER $CONFIG_BLK_DEV_GAYLE $CONFIG_EXPERIMENTAL
+ fi
+ if [ "$CONFIG_ZORRO" = "y" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ dep_mbool ' Buddha/Catweasel IDE interface support (EXPERIMENTAL)' CONFIG_BLK_DEV_BUDDHA $CONFIG_ZORRO $CONFIG_EXPERIMENTAL
+ fi
+ if [ "$CONFIG_ATARI" = "y" ]; then
+ dep_bool ' Falcon IDE interface support' CONFIG_BLK_DEV_FALCON_IDE $CONFIG_ATARI
+ fi
+ if [ "$CONFIG_MAC" = "y" ]; then
+ dep_bool ' Macintosh Quadra/Powerbook IDE interface support' CONFIG_BLK_DEV_MAC_IDE $CONFIG_MAC
+ fi
+
+ bool ' Other IDE chipset support' CONFIG_IDE_CHIPSETS
+ if [ "$CONFIG_IDE_CHIPSETS" = "y" ]; then
+ comment 'Note: most of these also require special kernel boot parameters'
+ bool ' Generic 4 drives/port support' CONFIG_BLK_DEV_4DRIVES
+ bool ' ALI M14xx support' CONFIG_BLK_DEV_ALI14XX
+ bool ' DTC-2278 support' CONFIG_BLK_DEV_DTC2278
+ bool ' Holtek HT6560B support' CONFIG_BLK_DEV_HT6560B
+ if [ "$CONFIG_BLK_DEV_IDEDISK" = "y" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ bool ' PROMISE DC4030 support (EXPERIMENTAL)' CONFIG_BLK_DEV_PDC4030
+ fi
+ bool ' QDI QD6580 support' CONFIG_BLK_DEV_QD6580
+ bool ' UMC-8672 support' CONFIG_BLK_DEV_UMC8672
+ fi
+ fi
+else
+ bool 'Old hard disk (MFM/RLL/IDE) driver' CONFIG_BLK_DEV_HD_ONLY
+ define_bool CONFIG_BLK_DEV_HD $CONFIG_BLK_DEV_HD_ONLY
+fi
+
+# if [ "$CONFIG_BLK_DEV_IDEDMA_PCI" = "y" -o \
+# "$CONFIG_BLK_DEV_IDEDMA_PMAC" = "y" -o \
+# "$CONFIG_BLK_DEV_IDEDMA_ICS" = "y" ]; then
+# define_bool CONFIG_BLK_DEV_IDEDMA y
+# if [ "$CONFIG_IDEDMA_PCI_AUTO" = "y" -o \
+# "$CONFIG_IDEDMA_PMAC_AUTO" = "y" -o \
+# "$CONFIG_IDEDMA_ICS_AUTO" = "y" ]; then
+# define_bool CONFIG_IDEDMA_AUTO y
+# fi
+# else
+# define_bool CONFIG_BLK_DEV_IDEDMA n
+# define_bool CONFIG_IDEDMA_AUTO n
+# fi
+
+if [ "$CONFIG_IDE_CHIPSETS" = "y" -o \
+ "$CONFIG_BLK_DEV_AEC6210" = "y" -o \
+ "$CONFIG_BLK_DEV_ALI15X3" = "y" -o \
+ "$CONFIG_BLK_DEV_AMD7409" = "y" -o \
+ "$CONFIG_BLK_DEV_CMD640" = "y" -o \
+ "$CONFIG_BLK_DEV_CMD64X" = "y" -o \
+ "$CONFIG_BLK_DEV_CS5530" = "y" -o \
+ "$CONFIG_BLK_DEV_CY82C693" = "y" -o \
+ "$CONFIG_BLK_DEV_HPT34X" = "y" -o \
+ "$CONFIG_BLK_DEV_HPT366" = "y" -o \
+ "$CONFIG_BLK_DEV_IDE_PMAC" = "y" -o \
+ "$CONFIG_BLK_DEV_OPTI621" = "y" -o \
+ "$CONFIG_BLK_DEV_PDC202XX" = "y" -o \
+ "$CONFIG_BLK_DEV_PIIX" = "y" -o \
+ "$CONFIG_BLK_DEV_SIS5513" = "y" -o \
+ "$CONFIG_BLK_DEV_SL82C105" = "y" ]; then
+ define_bool CONFIG_BLK_DEV_IDE_MODES y
+else
+ define_bool CONFIG_BLK_DEV_IDE_MODES n
+fi
+
+endmenu
diff --git a/drivers/ide/Makefile b/drivers/ide/Makefile
new file mode 100644
index 000000000..699c48b82
--- /dev/null
+++ b/drivers/ide/Makefile
@@ -0,0 +1,238 @@
+#
+# Makefile for the kernel ata, atapi, and ide block device drivers.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definition is now inherited from the
+# parent makefile.
+#
+
+#
+# Note : at this point, these files are compiled on all systems.
+# In the future, some of these should be built conditionally.
+#
+
+SUB_DIRS :=
+MOD_SUB_DIRS := $(SUB_DIRS)
+ALL_SUB_DIRS := $(SUB_DIRS)
+
+L_TARGET := ide.a
+L_OBJS := ide-geometry.o
+M_OBJS :=
+MOD_LIST_NAME := IDE_MODULES
+LX_OBJS :=
+MX_OBJS :=
+
+ifeq ($(CONFIG_BLK_DEV_AEC6210),y)
+IDE_OBJS += aec6210.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_ALI14XX),y)
+IDE_OBJS += ali14xx.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_ALI15X3),y)
+IDE_OBJS += alim15x3.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_AMD7409),y)
+IDE_OBJS += amd7409.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_BUDDHA),y)
+IDE_OBJS += buddha.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_CMD640),y)
+IDE_OBJS += cmd640.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_CMD64X),y)
+IDE_OBJS += cmd64x.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_CS5530),y)
+IDE_OBJS += cs5530.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_CY82C693),y)
+IDE_OBJS += cy82c693.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_DTC2278),y)
+IDE_OBJS += dtc2278.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_FALCON_IDE),y)
+IDE_OBJS += falconide.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_GAYLE),y)
+IDE_OBJS += gayle.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_Q40IDE),y)
+IDE_OBJS += q40ide.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_HD),y)
+L_OBJS += hd.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_HPT34X),y)
+IDE_OBJS += hpt34x.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_HPT366),y)
+IDE_OBJS += hpt366.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_HT6560B),y)
+IDE_OBJS += ht6560b.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_IDE_ICSIDE),y)
+IDE_OBJS += icside.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_IDEDMA),y)
+IDE_OBJS += ide-dma.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_IDEPCI),y)
+IDE_OBJS += ide-pci.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_ISAPNP),y)
+IDE_OBJS += ide-pnp.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_IDE_PMAC),y)
+IDE_OBJS += ide-pmac.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_MAC_IDE),y)
+IDE_OBJS += macide.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_NS87415),y)
+IDE_OBJS += ns87415.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_OPTI621),y)
+IDE_OBJS += opti621.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_PDC202XX),y)
+IDE_OBJS += pdc202xx.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_PDC4030),y)
+IDE_OBJS += pdc4030.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_PIIX),y)
+IDE_OBJS += piix.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_QD6580),y)
+IDE_OBJS += qd6580.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_IDE_RAPIDE),y)
+IDE_OBJS += rapide.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_RZ1000),y)
+IDE_OBJS += rz1000.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_SIS5513),y)
+IDE_OBJS += sis5513.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_SL82C105),y)
+IDE_OBJS += sl82c105.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_TRM290),y)
+IDE_OBJS += trm290.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_UMC8672),y)
+IDE_OBJS += umc8672.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_VIA82CXXX),y)
+IDE_OBJS += via82cxxx.o
+endif
+
+### if CONFIG_BLK_DEV_IDE is n, IDE_OBJS will be ignored
+
+ifeq ($(CONFIG_PROC_FS),y)
+IDE_OBJS += ide-proc.o
+endif
+
+###Collect
+
+ifeq ($(CONFIG_BLK_DEV_IDE),y)
+ LX_OBJS += ide.o ide-features.o
+ L_OBJS += ide-probe.o $(IDE_OBJS)
+else
+ ifeq ($(CONFIG_BLK_DEV_IDE),m)
+ MIX_OBJS += ide.o ide-features.o $(IDE_OBJS)
+ M_OBJS += ide-mod.o ide-probe-mod.o
+ endif
+endif
+
+############
+
+ifeq ($(CONFIG_BLK_DEV_IDECS),y)
+L_OBJS += ide-cs.o
+else
+ ifeq ($(CONFIG_BLK_DEV_IDECS),m)
+ M_OBJS += ide-cs.o
+ endif
+endif
+
+ifeq ($(CONFIG_BLK_DEV_IDEDISK),y)
+L_OBJS += ide-disk.o
+else
+ ifeq ($(CONFIG_BLK_DEV_IDEDISK),m)
+ M_OBJS += ide-disk.o
+ endif
+endif
+
+ifeq ($(CONFIG_BLK_DEV_IDECD),y)
+L_OBJS += ide-cd.o
+else
+ ifeq ($(CONFIG_BLK_DEV_IDECD),m)
+ M_OBJS += ide-cd.o
+ endif
+endif
+
+ifeq ($(CONFIG_BLK_DEV_IDETAPE),y)
+L_OBJS += ide-tape.o
+else
+ ifeq ($(CONFIG_BLK_DEV_IDETAPE),m)
+ M_OBJS += ide-tape.o
+ endif
+endif
+
+ifeq ($(CONFIG_BLK_DEV_IDEFLOPPY),y)
+L_OBJS += ide-floppy.o
+else
+ ifeq ($(CONFIG_BLK_DEV_IDEFLOPPY),m)
+ M_OBJS += ide-floppy.o
+ endif
+endif
+
+include $(TOPDIR)/Rules.make
+
+ide-mod.o: ide.o ide-features.o $(IDE_OBJS)
+ $(LD) $(LD_RFLAG) -r -o $@ ide.o ide-features.o $(IDE_OBJS)
+
+ide-probe-mod.o: ide-probe.o ide-geometry.o
+ $(LD) $(LD_RFLAG) -r -o $@ ide-probe.o ide-geometry.o
diff --git a/drivers/ide/aec6210.c b/drivers/ide/aec6210.c
new file mode 100644
index 000000000..cc0aca5fd
--- /dev/null
+++ b/drivers/ide/aec6210.c
@@ -0,0 +1,372 @@
+/*
+ * linux/drivers/block/aec6210.c Version 0.05 Feb. 10, 2000
+ *
+ * Copyright (C) 1998-2000 Andre Hedrick (andre@suse.com)
+ * May be copied or modified under the terms of the GNU General Public License
+ *
+ * pio 0 :: 40: 00 07 00 00 00 00 00 00 02 07 a6 04 00 02 00 02
+ * pio 1 :: 40: 0a 07 00 00 00 00 00 00 02 07 a6 05 00 02 00 02
+ * pio 2 :: 40: 08 07 00 00 00 00 00 00 02 07 a6 05 00 02 00 02
+ * pio 3 :: 40: 03 04 00 00 00 00 00 00 02 05 a6 05 00 02 00 02
+ * pio 4 :: 40: 01 04 00 00 00 00 00 00 02 05 a6 05 00 02 00 02
+ * dma 0 :: 40: 0a 07 00 00 00 00 00 00 02 05 a6 05 00 02 00 02
+ * dma 1 :: 40: 02 04 00 00 00 00 00 00 02 05 a6 05 00 02 00 02
+ * dma 2 :: 40: 01 04 00 00 00 00 00 00 02 05 a6 05 00 02 00 02
+ * 50: ff ff ff ff 00 06 04 00 00 00 00 00 00 00 00 00
+ *
+ * udma 0 :: 40: 01 04 00 00 00 00 00 00 02 05 a6 05 00 02 00 02
+ * 50: ff ff ff ff 01 06 04 00 00 00 00 00 00 00 00 00
+ *
+ * udma 1 :: 40: 01 04 00 00 00 00 00 00 02 05 a6 05 00 02 00 02
+ * 50: ff ff ff ff 01 06 04 00 00 00 00 00 00 00 00 00
+ *
+ * udma 2 :: 40: 01 04 00 00 00 00 00 00 02 05 a6 05 00 02 00 02
+ * 50: ff ff ff ff 02 06 04 00 00 00 00 00 00 00 00 00
+ *
+ * auto :: 40: 01 04 00 00 00 00 00 00 02 05 a6 05 00 02 00 02
+ * 50: ff ff ff ff 02 06 04 00 00 00 00 00 00 00 00 00
+ *
+ * auto :: 40: 01 04 01 04 01 04 01 04 02 05 a6 cf 00 02 00 02
+ * 50: ff ff ff ff aa 06 04 00 00 00 00 00 00 00 00 00
+ *
+ * NO-Devices
+ * 40: 00 00 00 00 00 00 00 00 02 05 a6 00 00 02 00 02
+ * 50: ff ff ff ff 00 06 00 00 00 00 00 00 00 00 00 00
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include "ide_modes.h"
+
+#define ACARD_DEBUG_DRIVE_INFO 0
+
+#undef DISPLAY_AEC6210_TIMINGS
+
+#if defined(DISPLAY_AEC6210_TIMINGS) && defined(CONFIG_PROC_FS)
+#include <linux/stat.h>
+#include <linux/proc_fs.h>
+
+static int aec6210_get_info(char *, char **, off_t, int);
+extern int (*aec6210_display_info)(char *, char **, off_t, int); /* ide-proc.c */
+extern char *ide_media_verbose(ide_drive_t *);
+static struct pci_dev *bmide_dev;
+
+static int aec6210_get_info (char *buffer, char **addr, off_t offset, int count)
+{
+ char *p = buffer;
+
+ u32 bibma = bmide_dev->resource[4].start;
+ u8 c0 = 0, c1 = 0;
+
+ p += sprintf(p, "\n AEC6210 Chipset.\n");
+
+ /*
+ * at that point bibma+0x2 et bibma+0xa are byte registers
+ * to investigate:
+ */
+ c0 = inb_p((unsigned short)bibma + 0x02);
+ c1 = inb_p((unsigned short)bibma + 0x0a);
+
+ p += sprintf(p, "--------------- Primary Channel ---------------- Secondary Channel -------------\n");
+ p += sprintf(p, " %sabled %sabled\n",
+ (c0&0x80) ? "dis" : " en",
+ (c1&0x80) ? "dis" : " en");
+ p += sprintf(p, "--------------- drive0 --------- drive1 -------- drive0 ---------- drive1 ------\n");
+ p += sprintf(p, "DMA enabled: %s %s %s %s\n",
+ (c0&0x20) ? "yes" : "no ", (c0&0x40) ? "yes" : "no ",
+ (c1&0x20) ? "yes" : "no ", (c1&0x40) ? "yes" : "no " );
+
+ p += sprintf(p, "UDMA\n");
+ p += sprintf(p, "DMA\n");
+ p += sprintf(p, "PIO\n");
+ return p-buffer;/* => must be less than 4k! */
+}
+#endif /* defined(DISPLAY_AEC6210_TIMINGS) && defined(CONFIG_PROC_FS) */
+
+byte aec6210_proc = 0;
+
+#ifdef CONFIG_AEC6210_TUNING
+
+struct chipset_bus_clock_list_entry {
+ byte xfer_speed;
+ unsigned short chipset_settings;
+ byte ultra_settings;
+};
+
+struct chipset_bus_clock_list_entry aec6210_base [] = {
+ { XFER_UDMA_2, 0x0401, 0x02 },
+ { XFER_UDMA_1, 0x0401, 0x01 },
+ { XFER_UDMA_0, 0x0401, 0x01 },
+
+ { XFER_MW_DMA_2, 0x0401, 0x00 },
+ { XFER_MW_DMA_1, 0x0402, 0x00 },
+ { XFER_MW_DMA_0, 0x070a, 0x00 },
+
+ { XFER_PIO_4, 0x0401, 0x00 },
+ { XFER_PIO_3, 0x0403, 0x00 },
+ { XFER_PIO_2, 0x0708, 0x00 },
+ { XFER_PIO_1, 0x070a, 0x00 },
+ { XFER_PIO_0, 0x0700, 0x00 },
+ { 0, 0x0000, 0x00 }
+};
+
+extern char *ide_xfer_verbose (byte xfer_rate);
+
+/*
+ * TO DO: active tuning and correction of cards without a bios.
+ */
+
+static unsigned short pci_bus_clock_list (byte speed, struct chipset_bus_clock_list_entry * chipset_table)
+{
+ for ( ; chipset_table->xfer_speed ; chipset_table++)
+ if (chipset_table->xfer_speed == speed) {
+ return chipset_table->chipset_settings;
+ }
+ return 0x0000;
+}
+
+static byte pci_bus_clock_list_ultra (byte speed, struct chipset_bus_clock_list_entry * chipset_table)
+{
+ for ( ; chipset_table->xfer_speed ; chipset_table++)
+ if (chipset_table->xfer_speed == speed) {
+ return chipset_table->ultra_settings;
+ }
+ return 0x00;
+}
+
+static int aec6210_tune_chipset (ide_drive_t *drive, byte speed)
+{
+ ide_hwif_t *hwif = HWIF(drive);
+
+ int err;
+ byte drive_pci;
+ unsigned short drive_conf = 0x0000;
+ byte ultra = 0x00, ultra_conf = 0x00;
+ byte tmp1 = 0x00, tmp2 = 0x00;
+
+ int drive_number = ((hwif->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
+
+ switch(drive_number) {
+ case 0: drive_pci = 0x40; break;
+ case 1: drive_pci = 0x42; break;
+ case 2: drive_pci = 0x44; break;
+ case 3: drive_pci = 0x46; break;
+ default: return -1;
+ }
+
+ pci_read_config_word(HWIF(drive)->pci_dev, drive_pci, &drive_conf);
+ drive_conf = pci_bus_clock_list(speed, aec6210_base);
+ pci_write_config_word(HWIF(drive)->pci_dev, drive_pci, drive_conf);
+
+ pci_read_config_byte(HWIF(drive)->pci_dev, 0x54, &ultra);
+ tmp1 = ((0x00 << (2*drive_number)) | (ultra & ~(3 << (2*drive_number))));
+ ultra_conf = pci_bus_clock_list_ultra(speed, aec6210_base);
+ tmp2 = ((ultra_conf << (2*drive_number)) | (tmp1 & ~(3 << (2*drive_number))));
+ pci_write_config_byte(HWIF(drive)->pci_dev, 0x54, tmp2);
+
+ err = ide_config_drive_speed(drive, speed);
+
+#if ACARD_DEBUG_DRIVE_INFO
+ printk("%s: %s drive%d 0x04%x 0x02%x 0x02%x 0x02%x 0x02%x\n",
+ drive->name, ide_xfer_verbose(speed), drive_number,
+ drive_conf, ultra, tmp1, ultra_conf, tmp2);
+#endif /* ACARD_DEBUG_DRIVE_INFO */
+
+ return(err);
+}
+
+static int config_chipset_for_dma (ide_drive_t *drive, byte ultra)
+{
+ struct hd_driveid *id = drive->id;
+ byte speed = -1;
+
+ if (drive->media != ide_disk)
+ return ((int) ide_dma_off_quietly);
+
+ if (((id->dma_ultra & 0x0010) ||
+ (id->dma_ultra & 0x0008) ||
+ (id->dma_ultra & 0x0004)) && (ultra)) {
+ speed = XFER_UDMA_2;
+ } else if ((id->dma_ultra & 0x0002) && (ultra)) {
+ speed = XFER_UDMA_1;
+ } else if ((id->dma_ultra & 0x0001) && (ultra)) {
+ speed = XFER_UDMA_0;
+ } else if (id->dma_mword & 0x0004) {
+ speed = XFER_MW_DMA_2;
+ } else if (id->dma_mword & 0x0002) {
+ speed = XFER_MW_DMA_1;
+ } else if (id->dma_mword & 0x0001) {
+ speed = XFER_MW_DMA_0;
+ } else if (id->dma_1word & 0x0004) {
+ speed = XFER_SW_DMA_2;
+ } else if (id->dma_1word & 0x0002) {
+ speed = XFER_SW_DMA_1;
+ } else if (id->dma_1word & 0x0001) {
+ speed = XFER_SW_DMA_0;
+ } else {
+ return ((int) ide_dma_off_quietly);
+ }
+ (void) aec6210_tune_chipset(drive, speed);
+
+ return ((int) ((id->dma_ultra >> 11) & 3) ? ide_dma_off :
+ ((id->dma_ultra >> 8) & 7) ? ide_dma_on :
+ ((id->dma_mword >> 8) & 7) ? ide_dma_on :
+ ((id->dma_1word >> 8) & 7) ? ide_dma_on :
+ ide_dma_off_quietly);
+}
+
+static void aec6210_tune_drive (ide_drive_t *drive, byte pio)
+{
+ byte speed;
+
+ switch(pio) {
+ case 5:
+ speed = XFER_PIO_0 + ide_get_best_pio_mode(drive, 255, 5, NULL);
+ case 4:
+ speed = XFER_PIO_4; break;
+ case 3:
+ speed = XFER_PIO_3; break;
+ case 2:
+ speed = XFER_PIO_2; break;
+ case 1:
+ speed = XFER_PIO_1; break;
+ default:
+ speed = XFER_PIO_0; break;
+ }
+ (void) aec6210_tune_chipset(drive, speed);
+}
+
+static int config_drive_xfer_rate (ide_drive_t *drive)
+{
+ struct hd_driveid *id = drive->id;
+ ide_dma_action_t dma_func = ide_dma_on;
+
+ if (id && (id->capability & 1) && HWIF(drive)->autodma) {
+ /* Consult the list of known "bad" drives */
+ if (ide_dmaproc(ide_dma_bad_drive, drive)) {
+ dma_func = ide_dma_off;
+ goto fast_ata_pio;
+ }
+ dma_func = ide_dma_off_quietly;
+ if (id->field_valid & 4) {
+ if (id->dma_ultra & 0x001F) {
+ /* Force if Capable UltraDMA */
+ dma_func = config_chipset_for_dma(drive, 1);
+ if ((id->field_valid & 2) &&
+ (dma_func != ide_dma_on))
+ goto try_dma_modes;
+ }
+ } else if (id->field_valid & 2) {
+try_dma_modes:
+ if ((id->dma_mword & 0x0007) ||
+ (id->dma_1word & 0x0007)) {
+ /* Force if Capable regular DMA modes */
+ dma_func = config_chipset_for_dma(drive, 0);
+ if (dma_func != ide_dma_on)
+ goto no_dma_set;
+ }
+ } else if (ide_dmaproc(ide_dma_good_drive, drive)) {
+ if (id->eide_dma_time > 150) {
+ goto no_dma_set;
+ }
+ /* Consult the list of known "good" drives */
+ dma_func = config_chipset_for_dma(drive, 0);
+ if (dma_func != ide_dma_on)
+ goto no_dma_set;
+ } else {
+ goto fast_ata_pio;
+ }
+ } else if ((id->capability & 8) || (id->field_valid & 2)) {
+fast_ata_pio:
+ dma_func = ide_dma_off_quietly;
+no_dma_set:
+ aec6210_tune_drive(drive, 5);
+ }
+ return HWIF(drive)->dmaproc(dma_func, drive);
+}
+
+/*
+ * aec6210_dmaproc() initiates/aborts (U)DMA read/write operations on a drive.
+ */
+int aec6210_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
+{
+ switch (func) {
+ case ide_dma_check:
+ return config_drive_xfer_rate(drive);
+ default:
+ break;
+ }
+ return ide_dmaproc(func, drive); /* use standard DMA stuff */
+}
+#endif /* CONFIG_AEC6210_TUNING */
+
+unsigned int __init pci_init_aec6210 (struct pci_dev *dev, const char *name)
+{
+ if (dev->resource[PCI_ROM_RESOURCE].start) {
+ pci_write_config_dword(dev, PCI_ROM_ADDRESS, dev->resource[PCI_ROM_RESOURCE].start | PCI_ROM_ADDRESS_ENABLE);
+ printk("%s: ROM enabled at 0x%08lx\n", name, dev->resource[PCI_ROM_RESOURCE].start);
+ }
+
+#if defined(DISPLAY_AEC6210_TIMINGS) && defined(CONFIG_PROC_FS)
+ aec6210_proc = 1;
+ bmide_dev = dev;
+ aec6210_display_info = &aec6210_get_info;
+#endif /* DISPLAY_AEC6210_TIMINGS && CONFIG_PROC_FS */
+
+ return dev->irq;
+}
+
+void __init ide_init_aec6210 (ide_hwif_t *hwif)
+{
+#ifdef CONFIG_AEC6210_TUNING
+ hwif->tuneproc = &aec6210_tune_drive;
+
+ if (hwif->dma_base) {
+ hwif->dmaproc = &aec6210_dmaproc;
+ } else {
+ hwif->drives[0].autotune = 1;
+ hwif->drives[1].autotune = 1;
+ }
+#endif /* CONFIG_AEC6210_TUNING */
+}
+
+void __init ide_dmacapable_aec6210 (ide_hwif_t *hwif, unsigned long dmabase)
+{
+ byte dma_new = 0;
+ byte dma_old = inb(dmabase+2);
+ byte reg54h = 0;
+ byte masterdma = hwif->channel ? 0x30 : 0x03;
+ byte slavedma = hwif->channel ? 0xc0 : 0x0c;
+ unsigned long flags;
+
+ __save_flags(flags); /* local CPU only */
+ __cli(); /* local CPU only */
+
+ dma_new = dma_old;
+
+ pci_read_config_byte(hwif->pci_dev, 0x54, &reg54h);
+
+ if (reg54h & masterdma) dma_new |= 0x20;
+ if (reg54h & slavedma) dma_new |= 0x40;
+ if (dma_new != dma_old) outb(dma_new, dmabase+2);
+
+ __restore_flags(flags); /* local CPU only */
+
+ ide_setup_dma(hwif, dmabase, 8);
+}
diff --git a/drivers/ide/ali14xx.c b/drivers/ide/ali14xx.c
new file mode 100644
index 000000000..b3ffaa529
--- /dev/null
+++ b/drivers/ide/ali14xx.c
@@ -0,0 +1,225 @@
+/*
+ * linux/drivers/block/ali14xx.c Version 0.03 Feb 09, 1996
+ *
+ * Copyright (C) 1996 Linus Torvalds & author (see below)
+ */
+
+/*
+ * ALI M14xx chipset EIDE controller
+ *
+ * Works for ALI M1439/1443/1445/1487/1489 chipsets.
+ *
+ * Adapted from code developed by derekn@vw.ece.cmu.edu. -ml
+ * Derek's notes follow:
+ *
+ * I think the code should be pretty understandable,
+ * but I'll be happy to (try to) answer questions.
+ *
+ * The critical part is in the setupDrive function. The initRegisters
+ * function doesn't seem to be necessary, but the DOS driver does it, so
+ * I threw it in.
+ *
+ * I've only tested this on my system, which only has one disk. I posted
+ * it to comp.sys.linux.hardware, so maybe some other people will try it
+ * out.
+ *
+ * Derek Noonburg (derekn@ece.cmu.edu)
+ * 95-sep-26
+ *
+ * Update 96-jul-13:
+ *
+ * I've since upgraded to two disks and a CD-ROM, with no trouble, and
+ * I've also heard from several others who have used it successfully.
+ * This driver appears to work with both the 1443/1445 and the 1487/1489
+ * chipsets. I've added support for PIO mode 4 for the 1487. This
+ * seems to work just fine on the 1443 also, although I'm not sure it's
+ * advertised as supporting mode 4. (I've been running a WDC AC21200 in
+ * mode 4 for a while now with no trouble.) -Derek
+ */
+
+#undef REALLY_SLOW_IO /* most systems can safely undef this */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+
+#include "ide_modes.h"
+
+/* port addresses for auto-detection */
+#define ALI_NUM_PORTS 4
+static int ports[ALI_NUM_PORTS] __initdata = {0x074, 0x0f4, 0x034, 0x0e4};
+
+/* register initialization data */
+typedef struct { byte reg, data; } RegInitializer;
+
+static RegInitializer initData[] __initdata = {
+ {0x01, 0x0f}, {0x02, 0x00}, {0x03, 0x00}, {0x04, 0x00},
+ {0x05, 0x00}, {0x06, 0x00}, {0x07, 0x2b}, {0x0a, 0x0f},
+ {0x25, 0x00}, {0x26, 0x00}, {0x27, 0x00}, {0x28, 0x00},
+ {0x29, 0x00}, {0x2a, 0x00}, {0x2f, 0x00}, {0x2b, 0x00},
+ {0x2c, 0x00}, {0x2d, 0x00}, {0x2e, 0x00}, {0x30, 0x00},
+ {0x31, 0x00}, {0x32, 0x00}, {0x33, 0x00}, {0x34, 0xff},
+ {0x35, 0x03}, {0x00, 0x00}
+};
+
+#define ALI_MAX_PIO 4
+
+/* timing parameter registers for each drive */
+static struct { byte reg1, reg2, reg3, reg4; } regTab[4] = {
+ {0x03, 0x26, 0x04, 0x27}, /* drive 0 */
+ {0x05, 0x28, 0x06, 0x29}, /* drive 1 */
+ {0x2b, 0x30, 0x2c, 0x31}, /* drive 2 */
+ {0x2d, 0x32, 0x2e, 0x33}, /* drive 3 */
+};
+
+static int basePort = 0; /* base port address */
+static int regPort = 0; /* port for register number */
+static int dataPort = 0; /* port for register data */
+static byte regOn; /* output to base port to access registers */
+static byte regOff; /* output to base port to close registers */
+
+/*------------------------------------------------------------------------*/
+
+/*
+ * Read a controller register.
+ */
+static inline byte inReg (byte reg)
+{
+ outb_p(reg, regPort);
+ return inb(dataPort);
+}
+
+/*
+ * Write a controller register.
+ */
+static void outReg (byte data, byte reg)
+{
+ outb_p(reg, regPort);
+ outb_p(data, dataPort);
+}
+
+/*
+ * Set PIO mode for the specified drive.
+ * This function computes timing parameters
+ * and sets controller registers accordingly.
+ */
+static void ali14xx_tune_drive (ide_drive_t *drive, byte pio)
+{
+ int driveNum;
+ int time1, time2;
+ byte param1, param2, param3, param4;
+ unsigned long flags;
+ ide_pio_data_t d;
+ int bus_speed = ide_system_bus_speed();
+
+ pio = ide_get_best_pio_mode(drive, pio, ALI_MAX_PIO, &d);
+
+ /* calculate timing, according to PIO mode */
+ time1 = d.cycle_time;
+ time2 = ide_pio_timings[pio].active_time;
+ param3 = param1 = (time2 * bus_speed + 999) / 1000;
+ param4 = param2 = (time1 * bus_speed + 999) / 1000 - param1;
+ if (pio < 3) {
+ param3 += 8;
+ param4 += 8;
+ }
+ printk("%s: PIO mode%d, t1=%dns, t2=%dns, cycles = %d+%d, %d+%d\n",
+ drive->name, pio, time1, time2, param1, param2, param3, param4);
+
+ /* stuff timing parameters into controller registers */
+ driveNum = (HWIF(drive)->index << 1) + drive->select.b.unit;
+ save_flags(flags); /* all CPUs */
+ cli(); /* all CPUs */
+ outb_p(regOn, basePort);
+ outReg(param1, regTab[driveNum].reg1);
+ outReg(param2, regTab[driveNum].reg2);
+ outReg(param3, regTab[driveNum].reg3);
+ outReg(param4, regTab[driveNum].reg4);
+ outb_p(regOff, basePort);
+ restore_flags(flags); /* all CPUs */
+}
+
+/*
+ * Auto-detect the IDE controller port.
+ */
+static int __init findPort (void)
+{
+ int i;
+ byte t;
+ unsigned long flags;
+
+ __save_flags(flags); /* local CPU only */
+ __cli(); /* local CPU only */
+ for (i = 0; i < ALI_NUM_PORTS; ++i) {
+ basePort = ports[i];
+ regOff = inb(basePort);
+ for (regOn = 0x30; regOn <= 0x33; ++regOn) {
+ outb_p(regOn, basePort);
+ if (inb(basePort) == regOn) {
+ regPort = basePort + 4;
+ dataPort = basePort + 8;
+ t = inReg(0) & 0xf0;
+ outb_p(regOff, basePort);
+ __restore_flags(flags); /* local CPU only */
+ if (t != 0x50)
+ return 0;
+ return 1; /* success */
+ }
+ }
+ outb_p(regOff, basePort);
+ }
+ __restore_flags(flags); /* local CPU only */
+ return 0;
+}
+
+/*
+ * Initialize controller registers with default values.
+ */
+static int __init initRegisters (void) {
+ RegInitializer *p;
+ byte t;
+ unsigned long flags;
+
+ __save_flags(flags); /* local CPU only */
+ __cli(); /* local CPU only */
+ outb_p(regOn, basePort);
+ for (p = initData; p->reg != 0; ++p)
+ outReg(p->data, p->reg);
+ outb_p(0x01, regPort);
+ t = inb(regPort) & 0x01;
+ outb_p(regOff, basePort);
+ __restore_flags(flags); /* local CPU only */
+ return t;
+}
+
+void __init init_ali14xx (void)
+{
+ /* auto-detect IDE controller port */
+ if (!findPort()) {
+ printk("\nali14xx: not found");
+ return;
+ }
+
+ printk("\nali14xx: base= 0x%03x, regOn = 0x%02x", basePort, regOn);
+ ide_hwifs[0].chipset = ide_ali14xx;
+ ide_hwifs[1].chipset = ide_ali14xx;
+ ide_hwifs[0].tuneproc = &ali14xx_tune_drive;
+ ide_hwifs[1].tuneproc = &ali14xx_tune_drive;
+ ide_hwifs[0].mate = &ide_hwifs[1];
+ ide_hwifs[1].mate = &ide_hwifs[0];
+ ide_hwifs[1].channel = 1;
+
+ /* initialize controller registers */
+ if (!initRegisters()) {
+ printk("\nali14xx: Chip initialization failed");
+ return;
+ }
+}
diff --git a/drivers/ide/alim15x3.c b/drivers/ide/alim15x3.c
new file mode 100644
index 000000000..bc3d2b9e3
--- /dev/null
+++ b/drivers/ide/alim15x3.c
@@ -0,0 +1,689 @@
+/*
+ * linux/drivers/block/alim15x3.c Version 0.08 Jan. 14, 2000
+ *
+ * Copyright (C) 1998-2000 Michel Aubry, Maintainer
+ * Copyright (C) 1998-2000 Andrzej Krzysztofowicz, Maintainer
+ *
+ * Copyright (C) 1998-2000 Andre Hedrick (andre@suse.com)
+ * May be copied or modified under the terms of the GNU General Public License
+ *
+ * (U)DMA capable version of ali 1533/1543(C), 1535(D)
+ *
+ * version: 1.0 beta2 (Sep. 2, 1999)
+ * e-mail your problems to cjtsai@ali.com.tw
+ *
+ **********************************************************************
+ * 9/7/99 --Parts from the above author are included and need to be
+ * converted into standard interface, once I finish the thought.
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/hdreg.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+
+#include "ide_modes.h"
+
+#define DISPLAY_ALI_TIMINGS
+
+#if defined(DISPLAY_ALI_TIMINGS) && defined(CONFIG_PROC_FS)
+#include <linux/stat.h>
+#include <linux/proc_fs.h>
+
+static int ali_get_info(char *buffer, char **addr, off_t offset, int count);
+extern int (*ali_display_info)(char *, char **, off_t, int); /* ide-proc.c */
+struct pci_dev *bmide_dev;
+
+char *fifo[4] = {
+ "FIFO Off",
+ "FIFO On ",
+ "DMA mode",
+ "PIO mode" };
+
+char *udmaT[8] = {
+ "1.5T",
+ " 2T",
+ "2.5T",
+ " 3T",
+ "3.5T",
+ " 4T",
+ " 6T",
+ " 8T"
+};
+
+char *channel_status[8] = {
+ "OK ",
+ "busy ",
+ "DRQ ",
+ "DRQ busy ",
+ "error ",
+ "error busy ",
+ "error DRQ ",
+ "error DRQ busy"
+};
+
+static int ali_get_info(char *buffer, char **addr, off_t offset, int count)
+{
+ byte reg53h, reg5xh, reg5yh, reg5xh1, reg5yh1;
+ unsigned int bibma;
+ byte c0, c1;
+ byte rev, tmp;
+ char *p = buffer;
+ char *q;
+
+ /* fetch rev. */
+ pci_read_config_byte(bmide_dev, 0x08, &rev);
+ if (rev >= 0xc1) /* M1543C or newer */
+ udmaT[7] = " ???";
+ else
+ fifo[3] = " ??? ";
+
+ /* first fetch bibma: */
+ pci_read_config_dword(bmide_dev, 0x20, &bibma);
+ bibma = (bibma & 0xfff0) ;
+ /*
+ * at that point bibma+0x2 et bibma+0xa are byte
+ * registers to investigate:
+ */
+ c0 = inb((unsigned short)bibma + 0x02);
+ c1 = inb((unsigned short)bibma + 0x0a);
+
+ p += sprintf(p,
+ "\n Ali M15x3 Chipset.\n");
+ p += sprintf(p,
+ " ------------------\n");
+ pci_read_config_byte(bmide_dev, 0x78, &reg53h);
+ p += sprintf(p, "PCI Clock: %d.\n", reg53h);
+
+ pci_read_config_byte(bmide_dev, 0x53, &reg53h);
+ p += sprintf(p,
+ "CD_ROM FIFO:%s, CD_ROM DMA:%s\n",
+ (reg53h & 0x02) ? "Yes" : "No ",
+ (reg53h & 0x01) ? "Yes" : "No " );
+ pci_read_config_byte(bmide_dev, 0x74, &reg53h);
+ p += sprintf(p,
+ "FIFO Status: contains %d Words, runs%s%s\n\n",
+ (reg53h & 0x3f),
+ (reg53h & 0x40) ? " OVERWR" : "",
+ (reg53h & 0x80) ? " OVERRD." : "." );
+
+ p += sprintf(p,
+ "-------------------primary channel-------------------secondary channel---------\n\n");
+
+ pci_read_config_byte(bmide_dev, 0x09, &reg53h);
+ p += sprintf(p,
+ "channel status: %s %s\n",
+ (reg53h & 0x20) ? "On " : "Off",
+ (reg53h & 0x10) ? "On " : "Off" );
+
+ p += sprintf(p,
+ "both channels togth: %s %s\n",
+ (c0&0x80) ? "No " : "Yes",
+ (c1&0x80) ? "No " : "Yes" );
+
+ pci_read_config_byte(bmide_dev, 0x76, &reg53h);
+ p += sprintf(p,
+ "Channel state: %s %s\n",
+ channel_status[reg53h & 0x07],
+ channel_status[(reg53h & 0x70) >> 4] );
+
+ pci_read_config_byte(bmide_dev, 0x58, &reg5xh);
+ pci_read_config_byte(bmide_dev, 0x5c, &reg5yh);
+ p += sprintf(p,
+ "Add. Setup Timing: %dT %dT\n",
+ (reg5xh & 0x07) ? (reg5xh & 0x07) : 8,
+ (reg5yh & 0x07) ? (reg5yh & 0x07) : 8 );
+
+ pci_read_config_byte(bmide_dev, 0x59, &reg5xh);
+ pci_read_config_byte(bmide_dev, 0x5d, &reg5yh);
+ p += sprintf(p,
+ "Command Act. Count: %dT %dT\n"
+ "Command Rec. Count: %dT %dT\n\n",
+ (reg5xh & 0x70) ? ((reg5xh & 0x70) >> 4) : 8,
+ (reg5yh & 0x70) ? ((reg5yh & 0x70) >> 4) : 8,
+ (reg5xh & 0x0f) ? (reg5xh & 0x0f) : 16,
+ (reg5yh & 0x0f) ? (reg5yh & 0x0f) : 16 );
+
+ p += sprintf(p,
+ "----------------drive0-----------drive1------------drive0-----------drive1------\n\n");
+ p += sprintf(p,
+ "DMA enabled: %s %s %s %s\n",
+ (c0&0x20) ? "Yes" : "No ",
+ (c0&0x40) ? "Yes" : "No ",
+ (c1&0x20) ? "Yes" : "No ",
+ (c1&0x40) ? "Yes" : "No " );
+
+ pci_read_config_byte(bmide_dev, 0x54, &reg5xh);
+ pci_read_config_byte(bmide_dev, 0x55, &reg5yh);
+ q = "FIFO threshold: %2d Words %2d Words %2d Words %2d Words\n";
+ if (rev < 0xc1) {
+ if ((rev == 0x20) && (pci_read_config_byte(bmide_dev, 0x4f, &tmp), (tmp &= 0x20))) {
+ p += sprintf(p, q, 8, 8, 8, 8);
+ } else {
+ p += sprintf(p, q,
+ (reg5xh & 0x03) + 12,
+ ((reg5xh & 0x30)>>4) + 12,
+ (reg5yh & 0x03) + 12,
+ ((reg5yh & 0x30)>>4) + 12 );
+ }
+ } else {
+ p += sprintf(p, q,
+ (tmp = (reg5xh & 0x03)) ? (tmp << 3) : 4,
+ (tmp = ((reg5xh & 0x30)>>4)) ? (tmp << 3) : 4,
+ (tmp = (reg5yh & 0x03)) ? (tmp << 3) : 4,
+ (tmp = ((reg5yh & 0x30)>>4)) ? (tmp << 3) : 4 );
+ }
+
+#if 0
+ p += sprintf(p,
+ "FIFO threshold: %2d Words %2d Words %2d Words %2d Words\n",
+ (reg5xh & 0x03) + 12,
+ ((reg5xh & 0x30)>>4) + 12,
+ (reg5yh & 0x03) + 12,
+ ((reg5yh & 0x30)>>4) + 12 );
+#endif
+
+ p += sprintf(p,
+ "FIFO mode: %s %s %s %s\n",
+ fifo[((reg5xh & 0x0c) >> 2)],
+ fifo[((reg5xh & 0xc0) >> 6)],
+ fifo[((reg5yh & 0x0c) >> 2)],
+ fifo[((reg5yh & 0xc0) >> 6)] );
+
+ pci_read_config_byte(bmide_dev, 0x5a, &reg5xh);
+ pci_read_config_byte(bmide_dev, 0x5b, &reg5xh1);
+ pci_read_config_byte(bmide_dev, 0x5e, &reg5yh);
+ pci_read_config_byte(bmide_dev, 0x5f, &reg5yh1);
+
+ p += sprintf(p,/*
+ "------------------drive0-----------drive1------------drive0-----------drive1------\n")*/
+ "Dt RW act. Cnt %2dT %2dT %2dT %2dT\n"
+ "Dt RW rec. Cnt %2dT %2dT %2dT %2dT\n\n",
+ (reg5xh & 0x70) ? ((reg5xh & 0x70) >> 4) : 8,
+ (reg5xh1 & 0x70) ? ((reg5xh1 & 0x70) >> 4) : 8,
+ (reg5yh & 0x70) ? ((reg5yh & 0x70) >> 4) : 8,
+ (reg5yh1 & 0x70) ? ((reg5yh1 & 0x70) >> 4) : 8,
+ (reg5xh & 0x0f) ? (reg5xh & 0x0f) : 16,
+ (reg5xh1 & 0x0f) ? (reg5xh1 & 0x0f) : 16,
+ (reg5yh & 0x0f) ? (reg5yh & 0x0f) : 16,
+ (reg5yh1 & 0x0f) ? (reg5yh1 & 0x0f) : 16 );
+
+ p += sprintf(p,
+ "-----------------------------------UDMA Timings--------------------------------\n\n");
+
+ pci_read_config_byte(bmide_dev, 0x56, &reg5xh);
+ pci_read_config_byte(bmide_dev, 0x57, &reg5yh);
+ p += sprintf(p,
+ "UDMA: %s %s %s %s\n"
+ "UDMA timings: %s %s %s %s\n\n",
+ (reg5xh & 0x08) ? "OK" : "No",
+ (reg5xh & 0x80) ? "OK" : "No",
+ (reg5yh & 0x08) ? "OK" : "No",
+ (reg5yh & 0x80) ? "OK" : "No",
+ udmaT[(reg5xh & 0x07)],
+ udmaT[(reg5xh & 0x70) >> 4],
+ udmaT[reg5yh & 0x07],
+ udmaT[(reg5yh & 0x70) >> 4] );
+
+ return p-buffer; /* => must be less than 4k! */
+}
+#endif /* defined(DISPLAY_ALI_TIMINGS) && defined(CONFIG_PROC_FS) */
+
+static byte m5229_revision = 0;
+static byte chip_is_1543c_e = 0;
+static byte cable_80_pin[2] = { 0, 0 };
+
+byte ali_proc = 0;
+static struct pci_dev *isa_dev;
+
+static void ali15x3_tune_drive (ide_drive_t *drive, byte pio)
+{
+ ide_pio_data_t d;
+ ide_hwif_t *hwif = HWIF(drive);
+ struct pci_dev *dev = hwif->pci_dev;
+ int s_time, a_time, c_time;
+ byte s_clc, a_clc, r_clc;
+ unsigned long flags;
+ int bus_speed = ide_system_bus_speed();
+ int port = hwif->index ? 0x5c : 0x58;
+ int portFIFO = hwif->channel ? 0x55 : 0x54;
+ byte cd_dma_fifo = 0;
+
+ pio = ide_get_best_pio_mode(drive, pio, 5, &d);
+ s_time = ide_pio_timings[pio].setup_time;
+ a_time = ide_pio_timings[pio].active_time;
+ if ((s_clc = (s_time * bus_speed + 999) / 1000) >= 8)
+ s_clc = 0;
+ if ((a_clc = (a_time * bus_speed + 999) / 1000) >= 8)
+ a_clc = 0;
+ c_time = ide_pio_timings[pio].cycle_time;
+
+#if 0
+ if ((r_clc = ((c_time - s_time - a_time) * bus_speed + 999) / 1000) >= 16)
+ r_clc = 0;
+#endif
+
+ if (!(r_clc = (c_time * bus_speed + 999) / 1000 - a_clc - s_clc)) {
+ r_clc = 1;
+ } else {
+ if (r_clc >= 16)
+ r_clc = 0;
+ }
+ __save_flags(flags);
+ __cli();
+
+ /*
+ * PIO mode => ATA FIFO on, ATAPI FIFO off
+ */
+ pci_read_config_byte(dev, portFIFO, &cd_dma_fifo);
+ if (drive->media==ide_disk) {
+ if (hwif->index) {
+ pci_write_config_byte(dev, portFIFO, (cd_dma_fifo & 0x0F) | 0x50);
+ } else {
+ pci_write_config_byte(dev, portFIFO, (cd_dma_fifo & 0xF0) | 0x05);
+ }
+ } else {
+ if (hwif->index) {
+ pci_write_config_byte(dev, portFIFO, cd_dma_fifo & 0x0F);
+ } else {
+ pci_write_config_byte(dev, portFIFO, cd_dma_fifo & 0xF0);
+ }
+ }
+
+ pci_write_config_byte(dev, port, s_clc);
+ pci_write_config_byte(dev, port+drive->select.b.unit+2, (a_clc << 4) | r_clc);
+ __restore_flags(flags);
+
+ /*
+ * setup active rec
+ * { 70, 165, 365 }, PIO Mode 0
+ * { 50, 125, 208 }, PIO Mode 1
+ * { 30, 100, 110 }, PIO Mode 2
+ * { 30, 80, 70 }, PIO Mode 3 with IORDY
+ * { 25, 70, 25 }, PIO Mode 4 with IORDY ns
+ * { 20, 50, 30 } PIO Mode 5 with IORDY (nonstandard)
+ */
+
+}
+
+static int ali15x3_tune_chipset (ide_drive_t *drive, byte speed)
+{
+ ide_hwif_t *hwif = HWIF(drive);
+ struct pci_dev *dev = hwif->pci_dev;
+ byte unit = (drive->select.b.unit & 0x01);
+ byte tmpbyte = 0x00;
+ int m5229_udma = hwif->channel? 0x57 : 0x56;
+ int err = 0;
+
+ if (speed < XFER_UDMA_0) {
+ byte ultra_enable = (unit) ? 0x7f : 0xf7;
+ /*
+ * clear "ultra enable" bit
+ */
+ pci_read_config_byte(dev, m5229_udma, &tmpbyte);
+ tmpbyte &= ultra_enable;
+ pci_write_config_byte(dev, m5229_udma, tmpbyte);
+ }
+
+ err = ide_config_drive_speed(drive, speed);
+
+ if (speed >= XFER_SW_DMA_0) {
+ unsigned long dma_base = hwif->dma_base;
+
+ outb(inb(dma_base+2)|(1<<(5+unit)), dma_base+2);
+ }
+
+ if (speed >= XFER_UDMA_0) {
+ pci_read_config_byte(dev, m5229_udma, &tmpbyte);
+ tmpbyte &= (0x0f << ((1-unit) << 2));
+ /*
+ * enable ultra dma and set timing
+ */
+ tmpbyte |= ((0x08 | (4-speed)) << (unit << 2));
+ pci_write_config_byte(dev, m5229_udma, tmpbyte);
+ if (speed >= XFER_UDMA_3) {
+ pci_read_config_byte(dev, 0x4b, &tmpbyte);
+ tmpbyte |= 1;
+ pci_write_config_byte(dev, 0x4b, tmpbyte);
+ }
+ }
+
+ return (err);
+}
+
+static int config_chipset_for_dma (ide_drive_t *drive, byte ultra33)
+{
+ struct hd_driveid *id = drive->id;
+ ide_hwif_t *hwif = HWIF(drive);
+ byte speed = 0x00;
+ byte ultra66 = ((hwif->udma_four) && (id->hw_config & 0x2000)) ? 1 : 0;
+ int rval;
+
+ if ((id->dma_ultra & 0x0010) && (ultra66) && (ultra33)) {
+ speed = XFER_UDMA_4;
+ } else if ((id->dma_ultra & 0x0008) && (ultra66) && (ultra33)) {
+ speed = XFER_UDMA_3;
+ } else if ((id->dma_ultra & 0x0004) && (ultra33)) {
+ speed = XFER_UDMA_2;
+ } else if ((id->dma_ultra & 0x0002) && (ultra33)) {
+ speed = XFER_UDMA_1;
+ } else if ((id->dma_ultra & 0x0001) && (ultra33)) {
+ speed = XFER_UDMA_0;
+ } else if (id->dma_mword & 0x0004) {
+ speed = XFER_MW_DMA_2;
+ } else if (id->dma_mword & 0x0002) {
+ speed = XFER_MW_DMA_1;
+ } else if (id->dma_mword & 0x0001) {
+ speed = XFER_MW_DMA_0;
+ } else if (id->dma_1word & 0x0004) {
+ speed = XFER_SW_DMA_2;
+ } else if (id->dma_1word & 0x0002) {
+ speed = XFER_SW_DMA_1;
+ } else if (id->dma_1word & 0x0001) {
+ speed = XFER_SW_DMA_0;
+ } else {
+ return ((int) ide_dma_off_quietly);
+ }
+
+ (void) ali15x3_tune_chipset(drive, speed);
+
+ rval = (int)( ((id->dma_ultra >> 11) & 3) ? ide_dma_on :
+ ((id->dma_ultra >> 8) & 7) ? ide_dma_on :
+ ((id->dma_mword >> 8) & 7) ? ide_dma_on :
+ ((id->dma_1word >> 8) & 7) ? ide_dma_on :
+ ide_dma_off_quietly);
+
+ return rval;
+}
+
+static void config_chipset_for_pio (ide_drive_t *drive)
+{
+ ali15x3_tune_drive(drive, 5);
+}
+
+
+static byte ali15x3_can_ultra (ide_drive_t *drive)
+{
+ struct hd_driveid *id = drive->id;
+
+#if 0
+ if (m5229_revision < 0x20) {
+#else
+ if (m5229_revision <= 0x20) {
+#endif
+ return 0;
+ } else if ((m5229_revision < 0xC2) &&
+ ((drive->media!=ide_disk) ||
+ (chip_is_1543c_e &&
+ strstr(id->model, "WDC ")))) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+static int ali15x3_config_drive_for_dma(ide_drive_t *drive)
+{
+ struct hd_driveid *id = drive->id;
+ ide_hwif_t *hwif = HWIF(drive);
+ ide_dma_action_t dma_func = ide_dma_on;
+ byte can_ultra_dma = ali15x3_can_ultra(drive);
+
+ if ((m5229_revision<=0x20) && (drive->media!=ide_disk))
+ return hwif->dmaproc(ide_dma_off_quietly, drive);
+
+ if ((id != NULL) && ((id->capability & 1) != 0) && hwif->autodma) {
+ /* Consult the list of known "bad" drives */
+ if (ide_dmaproc(ide_dma_bad_drive, drive)) {
+ dma_func = ide_dma_off;
+ goto fast_ata_pio;
+ }
+ dma_func = ide_dma_off_quietly;
+ if ((id->field_valid & 4) && (m5229_revision >= 0xC2)) {
+ if (id->dma_ultra & 0x001F) {
+ /* Force if Capable UltraDMA */
+ dma_func = config_chipset_for_dma(drive, can_ultra_dma);
+ if ((id->field_valid & 2) &&
+ (dma_func != ide_dma_on))
+ goto try_dma_modes;
+ }
+ } else if (id->field_valid & 2) {
+try_dma_modes:
+ if ((id->dma_mword & 0x0007) ||
+ (id->dma_1word & 0x0007)) {
+ /* Force if Capable regular DMA modes */
+ dma_func = config_chipset_for_dma(drive, can_ultra_dma);
+ if (dma_func != ide_dma_on)
+ goto no_dma_set;
+ }
+ } else if (ide_dmaproc(ide_dma_good_drive, drive)) {
+ if (id->eide_dma_time > 150) {
+ goto no_dma_set;
+ }
+ /* Consult the list of known "good" drives */
+ dma_func = config_chipset_for_dma(drive, can_ultra_dma);
+ if (dma_func != ide_dma_on)
+ goto no_dma_set;
+ } else {
+ goto fast_ata_pio;
+ }
+ } else if ((id->capability & 8) || (id->field_valid & 2)) {
+fast_ata_pio:
+ dma_func = ide_dma_off_quietly;
+no_dma_set:
+ config_chipset_for_pio(drive);
+ }
+ return hwif->dmaproc(dma_func, drive);
+}
+
+static int ali15x3_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
+{
+ switch(func) {
+ case ide_dma_check:
+ return ali15x3_config_drive_for_dma(drive);
+ case ide_dma_write:
+ if ((m5229_revision < 0xC2) && (drive->media != ide_disk))
+ return 1; /* try PIO instead of DMA */
+ break;
+ default:
+ break;
+ }
+ return ide_dmaproc(func, drive); /* use standard DMA stuff */
+}
+
+unsigned int __init pci_init_ali15x3 (struct pci_dev *dev, const char *name)
+{
+ unsigned long fixdma_base = dev->resource[4].start;
+
+ pci_read_config_byte(dev, PCI_REVISION_ID, &m5229_revision);
+
+ isa_dev = pci_find_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, NULL);
+
+ if (!fixdma_base || fixdma_base == PCI_BASE_ADDRESS_IO_MASK) {
+ /*
+ *
+ */
+ } else {
+ /*
+ * enable DMA capable bit, and "not" simplex only
+ */
+ outb(inb(fixdma_base+2) & 0x60, fixdma_base+2);
+
+ if (inb(fixdma_base+2) & 0x80)
+ printk("%s: simplex device: DMA will fail!!\n", name);
+ }
+
+#if defined(DISPLAY_ALI_TIMINGS) && defined(CONFIG_PROC_FS)
+ ali_proc = 1;
+ bmide_dev = dev;
+ ali_display_info = &ali_get_info;
+#endif /* defined(DISPLAY_ALI_TIMINGS) && defined(CONFIG_PROC_FS) */
+
+ return 0;
+}
+
+unsigned int __init ata66_ali15x3 (ide_hwif_t *hwif)
+{
+ struct pci_dev *dev = hwif->pci_dev;
+ byte ata66mask = hwif->channel ? 0x02 : 0x01;
+ unsigned int ata66 = 0;
+
+ unsigned long flags;
+ byte tmpbyte;
+
+ __save_flags(flags);
+ __cli();
+
+ if (m5229_revision >= 0xC2) {
+ /*
+ * 1543C-B?, 1535, 1535D, 1553
+ * Note 1: not all "motherboard" support this detection
+ * Note 2: if no udma 66 device, the detection may "error".
+ * but in this case, we will not set the device to
+ * ultra 66, the detection result is not important
+ */
+
+ /*
+ * enable "Cable Detection", m5229, 0x4b, bit3
+ */
+ pci_read_config_byte(dev, 0x4b, &tmpbyte);
+ pci_write_config_byte(dev, 0x4b, tmpbyte | 0x08);
+
+ /*
+ * set south-bridge's enable bit, m1533, 0x79
+ */
+ pci_read_config_byte(isa_dev, 0x79, &tmpbyte);
+ if (m5229_revision == 0xC2) {
+ /*
+ * 1543C-B0 (m1533, 0x79, bit 2)
+ */
+ pci_write_config_byte(isa_dev, 0x79, tmpbyte | 0x04);
+ } else if (m5229_revision == 0xC3) {
+ /*
+ * 1553/1535 (m1533, 0x79, bit 1)
+ */
+ pci_write_config_byte(isa_dev, 0x79, tmpbyte | 0x02);
+ }
+ /*
+ * Ultra66 cable detection (from Host View)
+ * m5229, 0x4a, bit0: primary, bit1: secondary 80 pin
+ */
+ pci_read_config_byte(dev, 0x4a, &tmpbyte);
+ /*
+ * 0x4a, bit0 is 0 => primary channel
+ * has 80-pin (from host view)
+ */
+ if (!(tmpbyte & 0x01)) cable_80_pin[0] = 1;
+ /*
+ * 0x4a, bit1 is 0 => secondary channel
+ * has 80-pin (from host view)
+ */
+ if (!(tmpbyte & 0x02)) cable_80_pin[1] = 1;
+ } else {
+ /*
+ * revision 0x20 (1543-E, 1543-F)
+ * revision 0xC0, 0xC1 (1543C-C, 1543C-D, 1543C-E)
+ * clear CD-ROM DMA write bit, m5229, 0x4b, bit 7
+ */
+ pci_read_config_byte(dev, 0x4b, &tmpbyte);
+ /*
+ * clear bit 7
+ */
+ pci_write_config_byte(dev, 0x4b, tmpbyte & 0x7F);
+ /*
+ * check m1533, 0x5e, bit 1~4 == 1001 => & 00011110 = 00010010
+ */
+ pci_read_config_byte(isa_dev, 0x5e, &tmpbyte);
+ chip_is_1543c_e = ((tmpbyte & 0x1e) == 0x12) ? 1: 0;
+ }
+
+ /*
+ * CD_ROM DMA on (m5229, 0x53, bit0)
+ * Enable this bit even if we want to use PIO
+ * PIO FIFO off (m5229, 0x53, bit1)
+ * The hardware will use 0x54h and 0x55h to control PIO FIFO
+ */
+ pci_read_config_byte(dev, 0x53, &tmpbyte);
+ tmpbyte = (tmpbyte & (~0x02)) | 0x01;
+
+ pci_write_config_byte(dev, 0x53, tmpbyte);
+
+ /*
+ * Ultra66 cable detection (from Host View)
+ * m5229, 0x4a, bit0: primary, bit1: secondary 80 pin
+ *
+ * 0x4a, bit0 is 0 => primary channel
+ * has 80-pin (from host view)
+ *
+ * 0x4a, bit1 is 0 => secondary channel
+ * has 80-pin (from host view)
+ */
+ pci_read_config_byte(dev, 0x4a, &tmpbyte);
+ ata66 = (!(tmpbyte & ata66mask)) ? 0 : 1;
+ __restore_flags(flags);
+
+ return(ata66);
+}
+
+void __init ide_init_ali15x3 (ide_hwif_t *hwif)
+{
+ byte ideic, inmir;
+ byte irq_routing_table[] = { -1, 9, 3, 10, 4, 5, 7, 6,
+ 1, 11, 0, 12, 0, 14, 0, 15 };
+
+ hwif->irq = hwif->channel ? 15 : 14;
+
+ if (isa_dev) {
+ /*
+ * read IDE interface control
+ */
+ pci_read_config_byte(isa_dev, 0x58, &ideic);
+
+ /* bit0, bit1 */
+ ideic = ideic & 0x03;
+
+ /* get IRQ for IDE Controller */
+ if ((hwif->channel && ideic == 0x03) || (!hwif->channel && !ideic)) {
+ /*
+ * get SIRQ1 routing table
+ */
+ pci_read_config_byte(isa_dev, 0x44, &inmir);
+ inmir = inmir & 0x0f;
+ hwif->irq = irq_routing_table[inmir];
+ } else if (hwif->channel && !(ideic & 0x01)) {
+ /*
+ * get SIRQ2 routing table
+ */
+ pci_read_config_byte(isa_dev, 0x75, &inmir);
+ inmir = inmir & 0x0f;
+ hwif->irq = irq_routing_table[inmir];
+ }
+ }
+
+ hwif->tuneproc = &ali15x3_tune_drive;
+ if ((hwif->dma_base) && (m5229_revision >= 0x20)) {
+ /*
+ * M1543C or newer for DMAing
+ */
+ hwif->dmaproc = &ali15x3_dmaproc;
+ hwif->autodma = 1;
+ } else {
+ hwif->autodma = 0;
+ hwif->drives[0].autotune = 1;
+ hwif->drives[1].autotune = 1;
+ }
+ return;
+}
+
+void ide_dmacapable_ali15x3 (ide_hwif_t *hwif, unsigned long dmabase)
+{
+ if ((dmabase) && (m5229_revision < 0x20)) {
+ return;
+ }
+ ide_setup_dma(hwif, dmabase, 8);
+}
diff --git a/drivers/ide/amd7409.c b/drivers/ide/amd7409.c
new file mode 100644
index 000000000..7d2018029
--- /dev/null
+++ b/drivers/ide/amd7409.c
@@ -0,0 +1,411 @@
+/*
+ * linux/drivers/block/amd7409.c Version 0.03 Feb. 10, 2000
+ *
+ * Copyright (C) 2000 Andre Hedrick <andre@suse.com>
+ * May be copied or modified under the terms of the GNU General Public License
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include "ide_modes.h"
+
+#define DISPLAY_VIPER_TIMINGS
+
+#if defined(DISPLAY_VIPER_TIMINGS) && defined(CONFIG_PROC_FS)
+#include <linux/stat.h>
+#include <linux/proc_fs.h>
+
+static int amd7409_get_info(char *, char **, off_t, int);
+extern int (*amd7409_display_info)(char *, char **, off_t, int); /* ide-proc.c */
+extern char *ide_media_verbose(ide_drive_t *);
+static struct pci_dev *bmide_dev;
+
+static int amd7409_get_info (char *buffer, char **addr, off_t offset, int count)
+{
+ char *p = buffer;
+ u32 bibma = bmide_dev->resource[4].start;
+ u8 c0 = 0, c1 = 0;
+
+ /*
+ * at that point bibma+0x2 et bibma+0xa are byte registers
+ * to investigate:
+ */
+ c0 = inb_p((unsigned short)bibma + 0x02);
+ c1 = inb_p((unsigned short)bibma + 0x0a);
+
+ p += sprintf(p, "\n AMD 7409 VIPER Chipset.\n");
+ p += sprintf(p, "--------------- Primary Channel ---------------- Secondary Channel -------------\n");
+ p += sprintf(p, " %sabled %sabled\n",
+ (c0&0x80) ? "dis" : " en",
+ (c1&0x80) ? "dis" : " en");
+ p += sprintf(p, "--------------- drive0 --------- drive1 -------- drive0 ---------- drive1 ------\n");
+ p += sprintf(p, "DMA enabled: %s %s %s %s\n",
+ (c0&0x20) ? "yes" : "no ", (c0&0x40) ? "yes" : "no ",
+ (c1&0x20) ? "yes" : "no ", (c1&0x40) ? "yes" : "no " );
+ p += sprintf(p, "UDMA\n");
+ p += sprintf(p, "DMA\n");
+ p += sprintf(p, "PIO\n");
+
+ return p-buffer; /* => must be less than 4k! */
+}
+#endif /* defined(DISPLAY_VIPER_TIMINGS) && defined(CONFIG_PROC_FS) */
+
+byte amd7409_proc = 0;
+
+extern char *ide_xfer_verbose (byte xfer_rate);
+
+/*
+ * Here is where all the hard work goes to program the chipset.
+ *
+ */
+static int amd7409_tune_chipset (ide_drive_t *drive, byte speed)
+{
+ ide_hwif_t *hwif = HWIF(drive);
+ struct pci_dev *dev = hwif->pci_dev;
+ int err = 0;
+ byte unit = (drive->select.b.unit & 0x01);
+ int drive_number = ((HWIF(drive)->channel ? 2 : 0) + unit);
+ unsigned long dma_base = hwif->dma_base;
+ byte drive_pci = 0x00;
+ byte drive_pci2 = 0x00;
+ byte ultra_timing = 0x00;
+ byte dma_pio_timing = 0x00;
+ byte pio_timing = 0x00;
+
+ switch (drive_number) {
+ case 0: drive_pci = 0x53; drive_pci2 = 0x4b; break;
+ case 1: drive_pci = 0x52; drive_pci2 = 0x4a; break;
+ case 2: drive_pci = 0x51; drive_pci2 = 0x49; break;
+ case 3: drive_pci = 0x50; drive_pci2 = 0x48; break;
+ default:
+ return ((int) ide_dma_off_quietly);
+ }
+
+ pci_read_config_byte(dev, drive_pci, &ultra_timing);
+ pci_read_config_byte(dev, drive_pci2, &dma_pio_timing);
+ pci_read_config_byte(dev, 0x4c, &pio_timing);
+
+#ifdef DEBUG
+ printk("%s: UDMA 0x%02x DMAPIO 0x%02x PIO 0x%02x ",
+ drive->name, ultra_timing, dma_pio_timing, pio_timing);
+#endif
+
+ ultra_timing &= ~0xC7;
+ dma_pio_timing &= ~0xFF;
+ pio_timing &= ~(0x03 << drive_number);
+
+#ifdef DEBUG
+ printk(":: UDMA 0x%02x DMAPIO 0x%02x PIO 0x%02x ",
+ ultra_timing, dma_pio_timing, pio_timing);
+#endif
+
+ switch(speed) {
+ case XFER_UDMA_4:
+ ultra_timing |= 0x45;
+ dma_pio_timing |= 0x20;
+ pio_timing |= (0x03 << drive_number);
+ break;
+ case XFER_UDMA_3:
+ ultra_timing |= 0x44;
+ dma_pio_timing |= 0x20;
+ pio_timing |= (0x03 << drive_number);
+ break;
+ case XFER_UDMA_2:
+ ultra_timing |= 0x40;
+ dma_pio_timing |= 0x20;
+ pio_timing |= (0x03 << drive_number);
+ break;
+ case XFER_UDMA_1:
+ ultra_timing |= 0x41;
+ dma_pio_timing |= 0x20;
+ pio_timing |= (0x03 << drive_number);
+ break;
+ case XFER_UDMA_0:
+ ultra_timing |= 0x42;
+ dma_pio_timing |= 0x20;
+ pio_timing |= (0x03 << drive_number);
+ break;
+ case XFER_MW_DMA_2:
+ dma_pio_timing |= 0x20;
+ pio_timing |= (0x03 << drive_number);
+ break;
+ case XFER_MW_DMA_1:
+ dma_pio_timing |= 0x21;
+ pio_timing |= (0x03 << drive_number);
+ break;
+ case XFER_MW_DMA_0:
+ dma_pio_timing |= 0x77;
+ pio_timing |= (0x03 << drive_number);
+ break;
+ case XFER_PIO_4:
+ dma_pio_timing |= 0x20;
+ pio_timing |= (0x03 << drive_number);
+ break;
+ case XFER_PIO_3:
+ dma_pio_timing |= 0x22;
+ pio_timing |= (0x03 << drive_number);
+ break;
+ case XFER_PIO_2:
+ dma_pio_timing |= 0x42;
+ pio_timing |= (0x03 << drive_number);
+ break;
+ case XFER_PIO_1:
+ dma_pio_timing |= 0x65;
+ pio_timing |= (0x03 << drive_number);
+ break;
+ case XFER_PIO_0:
+ default:
+ dma_pio_timing |= 0xA8;
+ pio_timing |= (0x03 << drive_number);
+ break;
+ }
+
+ pci_write_config_byte(dev, drive_pci, ultra_timing);
+ pci_write_config_byte(dev, drive_pci2, dma_pio_timing);
+ pci_write_config_byte(dev, 0x4c, pio_timing);
+
+#ifdef DEBUG
+ printk(":: UDMA 0x%02x DMAPIO 0x%02x PIO 0x%02x\n",
+ ultra_timing, dma_pio_timing, pio_timing);
+#endif
+
+ if (speed > XFER_PIO_4) {
+ outb(inb(dma_base+2)|(1<<(5+unit)), dma_base+2);
+ } else {
+ outb(inb(dma_base+2) & ~(1<<(5+unit)), dma_base+2);
+ }
+ err = ide_config_drive_speed(drive, speed);
+ return (err);
+}
+
+/*
+ * This allows the configuration of ide_pci chipset registers
+ * for cards that learn about the drive's UDMA, DMA, PIO capabilities
+ * after the drive is reported by the OS.
+ */
+static int config_chipset_for_dma (ide_drive_t *drive)
+{
+ struct hd_driveid *id = drive->id;
+ byte udma_66 = ((id->hw_config & 0x2000) &&
+ (HWIF(drive)->udma_four)) ? 1 : 0;
+ byte speed = 0x00;
+ int rval;
+
+ if ((id->dma_ultra & 0x0010) && (udma_66)) {
+ speed = XFER_UDMA_4;
+ } else if ((id->dma_ultra & 0x0008) && (udma_66)) {
+ speed = XFER_UDMA_3;
+ } else if (id->dma_ultra & 0x0004) {
+ speed = XFER_UDMA_2;
+ } else if (id->dma_ultra & 0x0002) {
+ speed = XFER_UDMA_1;
+ } else if (id->dma_ultra & 0x0001) {
+ speed = XFER_UDMA_0;
+ } else if (id->dma_mword & 0x0004) {
+ speed = XFER_MW_DMA_2;
+ } else if (id->dma_mword & 0x0002) {
+ speed = XFER_MW_DMA_1;
+ } else if (id->dma_mword & 0x0001) {
+ speed = XFER_MW_DMA_0;
+ } else {
+ return ((int) ide_dma_off_quietly);
+ }
+
+ (void) amd7409_tune_chipset(drive, speed);
+
+ rval = (int)( ((id->dma_ultra >> 11) & 3) ? ide_dma_on :
+ ((id->dma_ultra >> 8) & 7) ? ide_dma_on :
+ ((id->dma_mword >> 8) & 7) ? ide_dma_on :
+ ide_dma_off_quietly);
+
+ return rval;
+}
+
+static void config_chipset_for_pio (ide_drive_t *drive)
+{
+ unsigned short eide_pio_timing[6] = {960, 480, 240, 180, 120, 90};
+ unsigned short xfer_pio = drive->id->eide_pio_modes;
+ byte timing, speed, pio;
+
+ pio = ide_get_best_pio_mode(drive, 255, 5, NULL);
+
+ if (xfer_pio> 4)
+ xfer_pio = 0;
+
+ if (drive->id->eide_pio_iordy > 0) {
+ for (xfer_pio = 5;
+ xfer_pio>0 &&
+ drive->id->eide_pio_iordy>eide_pio_timing[xfer_pio];
+ xfer_pio--);
+ } else {
+ xfer_pio = (drive->id->eide_pio_modes & 4) ? 0x05 :
+ (drive->id->eide_pio_modes & 2) ? 0x04 :
+ (drive->id->eide_pio_modes & 1) ? 0x03 :
+ (drive->id->tPIO & 2) ? 0x02 :
+ (drive->id->tPIO & 1) ? 0x01 : xfer_pio;
+ }
+
+ timing = (xfer_pio >= pio) ? xfer_pio : pio;
+
+ switch(timing) {
+ case 4: speed = XFER_PIO_4;break;
+ case 3: speed = XFER_PIO_3;break;
+ case 2: speed = XFER_PIO_2;break;
+ case 1: speed = XFER_PIO_1;break;
+ default:
+ speed = (!drive->id->tPIO) ? XFER_PIO_0 : XFER_PIO_SLOW;
+ break;
+ }
+ (void) amd7409_tune_chipset(drive, speed);
+}
+
+static void amd7409_tune_drive (ide_drive_t *drive, byte pio)
+{
+ byte speed;
+ switch(pio) {
+ case 4: speed = XFER_PIO_4;break;
+ case 3: speed = XFER_PIO_3;break;
+ case 2: speed = XFER_PIO_2;break;
+ case 1: speed = XFER_PIO_1;break;
+ default: speed = XFER_PIO_0;break;
+ }
+ (void) amd7409_tune_chipset(drive, speed);
+}
+
+static int config_drive_xfer_rate (ide_drive_t *drive)
+{
+ struct hd_driveid *id = drive->id;
+ ide_dma_action_t dma_func = ide_dma_on;
+
+ if (id && (id->capability & 1) && HWIF(drive)->autodma) {
+ /* Consult the list of known "bad" drives */
+ if (ide_dmaproc(ide_dma_bad_drive, drive)) {
+ dma_func = ide_dma_off;
+ goto fast_ata_pio;
+ }
+ dma_func = ide_dma_off_quietly;
+ if (id->field_valid & 4) {
+ if (id->dma_ultra & 0x001F) {
+ /* Force if Capable UltraDMA */
+ dma_func = config_chipset_for_dma(drive);
+ if ((id->field_valid & 2) &&
+ (dma_func != ide_dma_on))
+ goto try_dma_modes;
+ }
+ } else if (id->field_valid & 2) {
+try_dma_modes:
+ if (id->dma_mword & 0x0007) {
+ /* Force if Capable regular DMA modes */
+ dma_func = config_chipset_for_dma(drive);
+ if (dma_func != ide_dma_on)
+ goto no_dma_set;
+ }
+ } else if (ide_dmaproc(ide_dma_good_drive, drive)) {
+ if (id->eide_dma_time > 150) {
+ goto no_dma_set;
+ }
+ /* Consult the list of known "good" drives */
+ dma_func = config_chipset_for_dma(drive);
+ if (dma_func != ide_dma_on)
+ goto no_dma_set;
+ } else {
+ goto fast_ata_pio;
+ }
+ } else if ((id->capability & 8) || (id->field_valid & 2)) {
+fast_ata_pio:
+ dma_func = ide_dma_off_quietly;
+no_dma_set:
+
+ config_chipset_for_pio(drive);
+ }
+ return HWIF(drive)->dmaproc(dma_func, drive);
+}
+
+/*
+ * amd7409_dmaproc() initiates/aborts (U)DMA read/write operations on a drive.
+ */
+
+int amd7409_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
+{
+ switch (func) {
+ case ide_dma_check:
+ return config_drive_xfer_rate(drive);
+ default:
+ break;
+ }
+ return ide_dmaproc(func, drive); /* use standard DMA stuff */
+}
+
+unsigned int __init pci_init_amd7409 (struct pci_dev *dev, const char *name)
+{
+ unsigned long fixdma_base = dev->resource[4].start;
+
+ if (!fixdma_base || fixdma_base == PCI_BASE_ADDRESS_IO_MASK) {
+ /*
+ *
+ */
+ } else {
+ /*
+ * enable DMA capable bit, and "not" simplex only
+ */
+ outb(inb(fixdma_base+2) & 0x60, fixdma_base+2);
+
+ if (inb(fixdma_base+2) & 0x80)
+ printk("%s: simplex device: DMA will fail!!\n", name);
+ }
+#if defined(DISPLAY_VIPER_TIMINGS) && defined(CONFIG_PROC_FS)
+ amd7409_proc = 1;
+ bmide_dev = dev;
+ amd7409_display_info = &amd7409_get_info;
+#endif /* DISPLAY_VIPER_TIMINGS && CONFIG_PROC_FS */
+
+ return 0;
+}
+
+unsigned int __init ata66_amd7409 (ide_hwif_t *hwif)
+{
+#ifdef CONFIG_AMD7409_OVERRIDE
+ byte ata66 = 1;
+#else
+ byte ata66 = 0;
+#endif /* CONFIG_AMD7409_OVERRIDE */
+
+#if 0
+ pci_read_config_byte(hwif->pci_dev, 0x48, &ata66);
+ return ((ata66 & 0x02) ? 0 : 1);
+#endif
+ return ata66;
+}
+
+void __init ide_init_amd7409 (ide_hwif_t *hwif)
+{
+ hwif->tuneproc = &amd7409_tune_drive;
+ if (hwif->dma_base) {
+ hwif->dmaproc = &amd7409_dmaproc;
+ } else {
+ hwif->autodma = 0;
+ hwif->drives[0].autotune = 1;
+ hwif->drives[1].autotune = 1;
+ }
+}
+
+void ide_dmacapable_amd7409 (ide_hwif_t *hwif, unsigned long dmabase)
+{
+ ide_setup_dma(hwif, dmabase, 8);
+}
diff --git a/drivers/ide/buddha.c b/drivers/ide/buddha.c
new file mode 100644
index 000000000..3e1cfcd33
--- /dev/null
+++ b/drivers/ide/buddha.c
@@ -0,0 +1,165 @@
+/*
+ * linux/drivers/block/buddha.c -- Amiga Buddha and Catweasel IDE Driver
+ *
+ * Copyright (C) 1997 by Geert Uytterhoeven
+ *
+ * This driver was written by based on the specifications in README.buddha.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ *
+ * TODO:
+ * - test it :-)
+ * - tune the timings using the speed-register
+ */
+
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/zorro.h>
+#include <linux/ide.h>
+
+#include <asm/amigahw.h>
+#include <asm/amigaints.h>
+
+
+ /*
+ * The Buddha has 2 IDE interfaces, the Catweasel has 3
+ */
+
+#define BUDDHA_NUM_HWIFS 2
+#define CATWEASEL_NUM_HWIFS 3
+
+
+ /*
+ * Bases of the IDE interfaces (relative to the board address)
+ */
+
+#define BUDDHA_BASE1 0x800
+#define BUDDHA_BASE2 0xa00
+#define BUDDHA_BASE3 0xc00
+
+static const u_int __init buddha_bases[CATWEASEL_NUM_HWIFS] = {
+ BUDDHA_BASE1, BUDDHA_BASE2, BUDDHA_BASE3
+};
+
+
+ /*
+ * Offsets from one of the above bases
+ */
+
+#define BUDDHA_DATA 0x00
+#define BUDDHA_ERROR 0x06 /* see err-bits */
+#define BUDDHA_NSECTOR 0x0a /* nr of sectors to read/write */
+#define BUDDHA_SECTOR 0x0e /* starting sector */
+#define BUDDHA_LCYL 0x12 /* starting cylinder */
+#define BUDDHA_HCYL 0x16 /* high byte of starting cyl */
+#define BUDDHA_SELECT 0x1a /* 101dhhhh , d=drive, hhhh=head */
+#define BUDDHA_STATUS 0x1e /* see status-bits */
+#define BUDDHA_CONTROL 0x11a
+
+static int __init buddha_offsets[IDE_NR_PORTS] = {
+ BUDDHA_DATA, BUDDHA_ERROR, BUDDHA_NSECTOR, BUDDHA_SECTOR, BUDDHA_LCYL,
+ BUDDHA_HCYL, BUDDHA_SELECT, BUDDHA_STATUS, BUDDHA_CONTROL
+};
+
+
+ /*
+ * Other registers
+ */
+
+#define BUDDHA_IRQ1 0xf00 /* MSB = 1, Harddisk is source of */
+#define BUDDHA_IRQ2 0xf40 /* interrupt */
+#define BUDDHA_IRQ3 0xf80
+
+static const int __init buddha_irqports[CATWEASEL_NUM_HWIFS] = {
+ BUDDHA_IRQ1, BUDDHA_IRQ2, BUDDHA_IRQ3
+};
+
+#define BUDDHA_IRQ_MR 0xfc0 /* master interrupt enable */
+
+
+ /*
+ * Board information
+ */
+
+static u_long buddha_board = 0;
+static int buddha_num_hwifs = -1;
+
+
+ /*
+ * Check and acknowledge the interrupt status
+ */
+
+static int buddha_ack_intr(ide_hwif_t *hwif)
+{
+ unsigned char ch;
+
+ ch = inb(hwif->io_ports[IDE_IRQ_OFFSET]);
+ if (!(ch & 0x80))
+ return 0;
+ return 1;
+}
+
+
+ /*
+ * Any Buddha or Catweasel boards present?
+ */
+
+static int __init find_buddha(void)
+{
+ struct zorro_dev *z = NULL;
+
+ buddha_num_hwifs = 0;
+ while ((z = zorro_find_device(ZORRO_WILDCARD, z))) {
+ unsigned long board;
+ const char *name;
+ if (z->id == ZORRO_PROD_INDIVIDUAL_COMPUTERS_BUDDHA) {
+ buddha_num_hwifs = BUDDHA_NUM_HWIFS;
+ name = "Buddha IDE Interface";
+ } else if (z->id == ZORRO_PROD_INDIVIDUAL_COMPUTERS_CATWEASEL) {
+ buddha_num_hwifs = CATWEASEL_NUM_HWIFS;
+ name = "Catweasel IDE Interface and Floppy Controller";
+ } else
+ continue;
+ board = z->resource.start;
+ if (!request_mem_region(board+BUDDHA_BASE1, 0x800, "IDE"))
+ continue;
+ strcpy(z->name, name);
+ buddha_board = ZTWO_VADDR(board);
+ /* write to BUDDHA_IRQ_MR to enable the board IRQ */
+ *(char *)(buddha_board+BUDDHA_IRQ_MR) = 0;
+ break;
+ }
+ return buddha_num_hwifs;
+}
+
+
+ /*
+ * Probe for a Buddha or Catweasel IDE interface
+ * We support only _one_ of them, no multiple boards!
+ */
+
+void __init buddha_init(void)
+{
+ hw_regs_t hw;
+ int i, index;
+
+ if (buddha_num_hwifs < 0 && !find_buddha())
+ return;
+
+ for (i = 0; i < buddha_num_hwifs; i++) {
+ ide_setup_ports(&hw, (ide_ioreg_t)(buddha_board+buddha_bases[i]),
+ buddha_offsets, 0,
+ (ide_ioreg_t)(buddha_board+buddha_irqports[i]),
+ buddha_ack_intr, IRQ_AMIGA_PORTS);
+ index = ide_register_hw(&hw, NULL);
+ if (index != -1)
+ printk("ide%d: %s IDE interface\n", index,
+ buddha_num_hwifs == BUDDHA_NUM_HWIFS ? "Buddha" :
+ "Catweasel");
+ }
+}
diff --git a/drivers/ide/cmd640.c b/drivers/ide/cmd640.c
new file mode 100644
index 000000000..b2077df6d
--- /dev/null
+++ b/drivers/ide/cmd640.c
@@ -0,0 +1,855 @@
+/*
+ * linux/drivers/block/cmd640.c Version 1.02 Sep 01, 1996
+ *
+ * Copyright (C) 1995-1996 Linus Torvalds & authors (see below)
+ */
+
+/*
+ * Original authors: abramov@cecmow.enet.dec.com (Igor Abramov)
+ * mlord@pobox.com (Mark Lord)
+ *
+ * See linux/MAINTAINERS for address of current maintainer.
+ *
+ * This file provides support for the advanced features and bugs
+ * of IDE interfaces using the CMD Technologies 0640 IDE interface chip.
+ *
+ * These chips are basically fucked by design, and getting this driver
+ * to work on every motherboard design that uses this screwed chip seems
+ * bloody well impossible. However, we're still trying.
+ *
+ * Version 0.97 worked for everybody.
+ *
+ * User feedback is essential. Many thanks to the beta test team:
+ *
+ * A.Hartgers@stud.tue.nl, JZDQC@CUNYVM.CUNY.edu, abramov@cecmow.enet.dec.com,
+ * bardj@utopia.ppp.sn.no, bart@gaga.tue.nl, bbol001@cs.auckland.ac.nz,
+ * chrisc@dbass.demon.co.uk, dalecki@namu26.Num.Math.Uni-Goettingen.de,
+ * derekn@vw.ece.cmu.edu, florian@btp2x3.phy.uni-bayreuth.de,
+ * flynn@dei.unipd.it, gadio@netvision.net.il, godzilla@futuris.net,
+ * j@pobox.com, jkemp1@mises.uni-paderborn.de, jtoppe@hiwaay.net,
+ * kerouac@ssnet.com, meskes@informatik.rwth-aachen.de, hzoli@cs.elte.hu,
+ * peter@udgaard.isgtec.com, phil@tazenda.demon.co.uk, roadcapw@cfw.com,
+ * s0033las@sun10.vsz.bme.hu, schaffer@tam.cornell.edu, sjd@slip.net,
+ * steve@ei.org, ulrpeg@bigcomm.gun.de, ism@tardis.ed.ac.uk, mack@cray.com
+ * liug@mama.indstate.edu, and others.
+ *
+ * Version 0.01 Initial version, hacked out of ide.c,
+ * and #include'd rather than compiled separately.
+ * This will get cleaned up in a subsequent release.
+ *
+ * Version 0.02 Fixes for vlb initialization code, enable prefetch
+ * for versions 'B' and 'C' of chip by default,
+ * some code cleanup.
+ *
+ * Version 0.03 Added reset of secondary interface,
+ * and black list for devices which are not compatible
+ * with prefetch mode. Separate function for setting
+ * prefetch is added, possibly it will be called some
+ * day from ioctl processing code.
+ *
+ * Version 0.04 Now configs/compiles separate from ide.c
+ *
+ * Version 0.05 Major rewrite of interface timing code.
+ * Added new function cmd640_set_mode to set PIO mode
+ * from ioctl call. New drives added to black list.
+ *
+ * Version 0.06 More code cleanup. Prefetch is enabled only for
+ * detected hard drives, not included in prefetch
+ * black list.
+ *
+ * Version 0.07 Changed to more conservative drive tuning policy.
+ * Unknown drives, which report PIO < 4 are set to
+ * (reported_PIO - 1) if it is supported, or to PIO0.
+ * List of known drives extended by info provided by
+ * CMD at their ftp site.
+ *
+ * Version 0.08 Added autotune/noautotune support.
+ *
+ * Version 0.09 Try to be smarter about 2nd port enabling.
+ * Version 0.10 Be nice and don't reset 2nd port.
+ * Version 0.11 Try to handle more wierd situations.
+ *
+ * Version 0.12 Lots of bug fixes from Laszlo Peter
+ * irq unmasking disabled for reliability.
+ * try to be even smarter about the second port.
+ * tidy up source code formatting.
+ * Version 0.13 permit irq unmasking again.
+ * Version 0.90 massive code cleanup, some bugs fixed.
+ * defaults all drives to PIO mode0, prefetch off.
+ * autotune is OFF by default, with compile time flag.
+ * prefetch can be turned OFF/ON using "hdparm -p8/-p9"
+ * (requires hdparm-3.1 or newer)
+ * Version 0.91 first release to linux-kernel list.
+ * Version 0.92 move initial reg dump to separate callable function
+ * change "readahead" to "prefetch" to avoid confusion
+ * Version 0.95 respect original BIOS timings unless autotuning.
+ * tons of code cleanup and rearrangement.
+ * added CONFIG_BLK_DEV_CMD640_ENHANCED option
+ * prevent use of unmask when prefetch is on
+ * Version 0.96 prevent use of io_32bit when prefetch is off
+ * Version 0.97 fix VLB secondary interface for sjd@slip.net
+ * other minor tune-ups: 0.96 was very good.
+ * Version 0.98 ignore PCI version when disabled by BIOS
+ * Version 0.99 display setup/active/recovery clocks with PIO mode
+ * Version 1.00 Mmm.. cannot depend on PCMD_ENA in all systems
+ * Version 1.01 slow/fast devsel can be selected with "hdparm -p6/-p7"
+ * ("fast" is necessary for 32bit I/O in some systems)
+ * Version 1.02 fix bug that resulted in slow "setup times"
+ * (patch courtesy of Zoltan Hidvegi)
+ */
+
+#undef REALLY_SLOW_IO /* most systems can safely undef this */
+#define CMD640_PREFETCH_MASKS 1
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+
+#include "ide_modes.h"
+
+/*
+ * This flag is set in ide.c by the parameter: ide0=cmd640_vlb
+ */
+int cmd640_vlb = 0;
+
+/*
+ * CMD640 specific registers definition.
+ */
+
+#define VID 0x00
+#define DID 0x02
+#define PCMD 0x04
+#define PCMD_ENA 0x01
+#define PSTTS 0x06
+#define REVID 0x08
+#define PROGIF 0x09
+#define SUBCL 0x0a
+#define BASCL 0x0b
+#define BaseA0 0x10
+#define BaseA1 0x14
+#define BaseA2 0x18
+#define BaseA3 0x1c
+#define INTLINE 0x3c
+#define INPINE 0x3d
+
+#define CFR 0x50
+#define CFR_DEVREV 0x03
+#define CFR_IDE01INTR 0x04
+#define CFR_DEVID 0x18
+#define CFR_AT_VESA_078h 0x20
+#define CFR_DSA1 0x40
+#define CFR_DSA0 0x80
+
+#define CNTRL 0x51
+#define CNTRL_DIS_RA0 0x40
+#define CNTRL_DIS_RA1 0x80
+#define CNTRL_ENA_2ND 0x08
+
+#define CMDTIM 0x52
+#define ARTTIM0 0x53
+#define DRWTIM0 0x54
+#define ARTTIM1 0x55
+#define DRWTIM1 0x56
+#define ARTTIM23 0x57
+#define ARTTIM23_DIS_RA2 0x04
+#define ARTTIM23_DIS_RA3 0x08
+#define DRWTIM23 0x58
+#define BRST 0x59
+
+/*
+ * Registers and masks for easy access by drive index:
+ */
+static byte prefetch_regs[4] = {CNTRL, CNTRL, ARTTIM23, ARTTIM23};
+static byte prefetch_masks[4] = {CNTRL_DIS_RA0, CNTRL_DIS_RA1, ARTTIM23_DIS_RA2, ARTTIM23_DIS_RA3};
+
+#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
+
+static byte arttim_regs[4] = {ARTTIM0, ARTTIM1, ARTTIM23, ARTTIM23};
+static byte drwtim_regs[4] = {DRWTIM0, DRWTIM1, DRWTIM23, DRWTIM23};
+
+/*
+ * Current cmd640 timing values for each drive.
+ * The defaults for each are the slowest possible timings.
+ */
+static byte setup_counts[4] = {4, 4, 4, 4}; /* Address setup count (in clocks) */
+static byte active_counts[4] = {16, 16, 16, 16}; /* Active count (encoded) */
+static byte recovery_counts[4] = {16, 16, 16, 16}; /* Recovery count (encoded) */
+
+#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
+
+/*
+ * These are initialized to point at the devices we control
+ */
+static ide_hwif_t *cmd_hwif0, *cmd_hwif1;
+static ide_drive_t *cmd_drives[4];
+
+/*
+ * Interface to access cmd640x registers
+ */
+static unsigned int cmd640_key;
+static void (*put_cmd640_reg)(unsigned short reg, byte val);
+static byte (*get_cmd640_reg)(unsigned short reg);
+
+/*
+ * This is read from the CFR reg, and is used in several places.
+ */
+static unsigned int cmd640_chip_version;
+
+/*
+ * The CMD640x chip does not support DWORD config write cycles, but some
+ * of the BIOSes use them to implement the config services.
+ * Therefore, we must use direct IO instead.
+ */
+
+/* PCI method 1 access */
+
+static void put_cmd640_reg_pci1 (unsigned short reg, byte val)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ outl_p((reg & 0xfc) | cmd640_key, 0xcf8);
+ outb_p(val, (reg & 3) | 0xcfc);
+ restore_flags(flags);
+}
+
+static byte get_cmd640_reg_pci1 (unsigned short reg)
+{
+ byte b;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ outl_p((reg & 0xfc) | cmd640_key, 0xcf8);
+ b = inb_p((reg & 3) | 0xcfc);
+ restore_flags(flags);
+ return b;
+}
+
+/* PCI method 2 access (from CMD datasheet) */
+
+static void put_cmd640_reg_pci2 (unsigned short reg, byte val)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ outb_p(0x10, 0xcf8);
+ outb_p(val, cmd640_key + reg);
+ outb_p(0, 0xcf8);
+ restore_flags(flags);
+}
+
+static byte get_cmd640_reg_pci2 (unsigned short reg)
+{
+ byte b;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ outb_p(0x10, 0xcf8);
+ b = inb_p(cmd640_key + reg);
+ outb_p(0, 0xcf8);
+ restore_flags(flags);
+ return b;
+}
+
+/* VLB access */
+
+static void put_cmd640_reg_vlb (unsigned short reg, byte val)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ outb_p(reg, cmd640_key);
+ outb_p(val, cmd640_key + 4);
+ restore_flags(flags);
+}
+
+static byte get_cmd640_reg_vlb (unsigned short reg)
+{
+ byte b;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ outb_p(reg, cmd640_key);
+ b = inb_p(cmd640_key + 4);
+ restore_flags(flags);
+ return b;
+}
+
+static int __init match_pci_cmd640_device (void)
+{
+ const byte ven_dev[4] = {0x95, 0x10, 0x40, 0x06};
+ unsigned int i;
+ for (i = 0; i < 4; i++) {
+ if (get_cmd640_reg(i) != ven_dev[i])
+ return 0;
+ }
+#ifdef STUPIDLY_TRUST_BROKEN_PCMD_ENA_BIT
+ if ((get_cmd640_reg(PCMD) & PCMD_ENA) == 0) {
+ printk("ide: cmd640 on PCI disabled by BIOS\n");
+ return 0;
+ }
+#endif /* STUPIDLY_TRUST_BROKEN_PCMD_ENA_BIT */
+ return 1; /* success */
+}
+
+/*
+ * Probe for CMD640x -- pci method 1
+ */
+static int __init probe_for_cmd640_pci1 (void)
+{
+ get_cmd640_reg = get_cmd640_reg_pci1;
+ put_cmd640_reg = put_cmd640_reg_pci1;
+ for (cmd640_key = 0x80000000; cmd640_key <= 0x8000f800; cmd640_key += 0x800) {
+ if (match_pci_cmd640_device())
+ return 1; /* success */
+ }
+ return 0;
+}
+
+/*
+ * Probe for CMD640x -- pci method 2
+ */
+static int __init probe_for_cmd640_pci2 (void)
+{
+ get_cmd640_reg = get_cmd640_reg_pci2;
+ put_cmd640_reg = put_cmd640_reg_pci2;
+ for (cmd640_key = 0xc000; cmd640_key <= 0xcf00; cmd640_key += 0x100) {
+ if (match_pci_cmd640_device())
+ return 1; /* success */
+ }
+ return 0;
+}
+
+/*
+ * Probe for CMD640x -- vlb
+ */
+static int __init probe_for_cmd640_vlb (void)
+{
+ byte b;
+
+ get_cmd640_reg = get_cmd640_reg_vlb;
+ put_cmd640_reg = put_cmd640_reg_vlb;
+ cmd640_key = 0x178;
+ b = get_cmd640_reg(CFR);
+ if (b == 0xff || b == 0x00 || (b & CFR_AT_VESA_078h)) {
+ cmd640_key = 0x78;
+ b = get_cmd640_reg(CFR);
+ if (b == 0xff || b == 0x00 || !(b & CFR_AT_VESA_078h))
+ return 0;
+ }
+ return 1; /* success */
+}
+
+/*
+ * Returns 1 if an IDE interface/drive exists at 0x170,
+ * Returns 0 otherwise.
+ */
+static int __init secondary_port_responding (void)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ outb_p(0x0a, 0x170 + IDE_SELECT_OFFSET); /* select drive0 */
+ udelay(100);
+ if ((inb_p(0x170 + IDE_SELECT_OFFSET) & 0x1f) != 0x0a) {
+ outb_p(0x1a, 0x170 + IDE_SELECT_OFFSET); /* select drive1 */
+ udelay(100);
+ if ((inb_p(0x170 + IDE_SELECT_OFFSET) & 0x1f) != 0x1a) {
+ restore_flags(flags);
+ return 0; /* nothing responded */
+ }
+ }
+ restore_flags(flags);
+ return 1; /* success */
+}
+
+#ifdef CMD640_DUMP_REGS
+/*
+ * Dump out all cmd640 registers. May be called from ide.c
+ */
+void cmd640_dump_regs (void)
+{
+ unsigned int reg = cmd640_vlb ? 0x50 : 0x00;
+
+ /* Dump current state of chip registers */
+ printk("ide: cmd640 internal register dump:");
+ for (; reg <= 0x59; reg++) {
+ if (!(reg & 0x0f))
+ printk("\n%04x:", reg);
+ printk(" %02x", get_cmd640_reg(reg));
+ }
+ printk("\n");
+}
+#endif
+
+/*
+ * Check whether prefetch is on for a drive,
+ * and initialize the unmask flags for safe operation.
+ */
+static void __init check_prefetch (unsigned int index)
+{
+ ide_drive_t *drive = cmd_drives[index];
+ byte b = get_cmd640_reg(prefetch_regs[index]);
+
+ if (b & prefetch_masks[index]) { /* is prefetch off? */
+ drive->no_unmask = 0;
+ drive->no_io_32bit = 1;
+ drive->io_32bit = 0;
+ } else {
+#if CMD640_PREFETCH_MASKS
+ drive->no_unmask = 1;
+ drive->unmask = 0;
+#endif
+ drive->no_io_32bit = 0;
+ }
+}
+
+/*
+ * Figure out which devices we control
+ */
+static void __init setup_device_ptrs (void)
+{
+ unsigned int i;
+
+ cmd_hwif0 = &ide_hwifs[0]; /* default, if not found below */
+ cmd_hwif1 = &ide_hwifs[1]; /* default, if not found below */
+ for (i = 0; i < MAX_HWIFS; i++) {
+ ide_hwif_t *hwif = &ide_hwifs[i];
+ if (hwif->chipset == ide_unknown || hwif->chipset == ide_generic) {
+ if (hwif->io_ports[IDE_DATA_OFFSET] == 0x1f0)
+ cmd_hwif0 = hwif;
+ else if (hwif->io_ports[IDE_DATA_OFFSET] == 0x170)
+ cmd_hwif1 = hwif;
+ }
+ }
+ cmd_drives[0] = &cmd_hwif0->drives[0];
+ cmd_drives[1] = &cmd_hwif0->drives[1];
+ cmd_drives[2] = &cmd_hwif1->drives[0];
+ cmd_drives[3] = &cmd_hwif1->drives[1];
+}
+
+#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
+
+/*
+ * Sets prefetch mode for a drive.
+ */
+static void set_prefetch_mode (unsigned int index, int mode)
+{
+ ide_drive_t *drive = cmd_drives[index];
+ int reg = prefetch_regs[index];
+ byte b;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ b = get_cmd640_reg(reg);
+ if (mode) { /* want prefetch on? */
+#if CMD640_PREFETCH_MASKS
+ drive->no_unmask = 1;
+ drive->unmask = 0;
+#endif
+ drive->no_io_32bit = 0;
+ b &= ~prefetch_masks[index]; /* enable prefetch */
+ } else {
+ drive->no_unmask = 0;
+ drive->no_io_32bit = 1;
+ drive->io_32bit = 0;
+ b |= prefetch_masks[index]; /* disable prefetch */
+ }
+ put_cmd640_reg(reg, b);
+ restore_flags(flags);
+}
+
+/*
+ * Dump out current drive clocks settings
+ */
+static void display_clocks (unsigned int index)
+{
+ byte active_count, recovery_count;
+
+ active_count = active_counts[index];
+ if (active_count == 1)
+ ++active_count;
+ recovery_count = recovery_counts[index];
+ if (active_count > 3 && recovery_count == 1)
+ ++recovery_count;
+ if (cmd640_chip_version > 1)
+ recovery_count += 1; /* cmd640b uses (count + 1)*/
+ printk(", clocks=%d/%d/%d\n", setup_counts[index], active_count, recovery_count);
+}
+
+/*
+ * Pack active and recovery counts into single byte representation
+ * used by controller
+ */
+inline static byte pack_nibbles (byte upper, byte lower)
+{
+ return ((upper & 0x0f) << 4) | (lower & 0x0f);
+}
+
+/*
+ * This routine retrieves the initial drive timings from the chipset.
+ */
+static void __init retrieve_drive_counts (unsigned int index)
+{
+ byte b;
+
+ /*
+ * Get the internal setup timing, and convert to clock count
+ */
+ b = get_cmd640_reg(arttim_regs[index]) & ~0x3f;
+ switch (b) {
+ case 0x00: b = 4; break;
+ case 0x80: b = 3; break;
+ case 0x40: b = 2; break;
+ default: b = 5; break;
+ }
+ setup_counts[index] = b;
+
+ /*
+ * Get the active/recovery counts
+ */
+ b = get_cmd640_reg(drwtim_regs[index]);
+ active_counts[index] = (b >> 4) ? (b >> 4) : 0x10;
+ recovery_counts[index] = (b & 0x0f) ? (b & 0x0f) : 0x10;
+}
+
+
+/*
+ * This routine writes the prepared setup/active/recovery counts
+ * for a drive into the cmd640 chipset registers to active them.
+ */
+static void program_drive_counts (unsigned int index)
+{
+ unsigned long flags;
+ byte setup_count = setup_counts[index];
+ byte active_count = active_counts[index];
+ byte recovery_count = recovery_counts[index];
+
+ /*
+ * Set up address setup count and drive read/write timing registers.
+ * Primary interface has individual count/timing registers for
+ * each drive. Secondary interface has one common set of registers,
+ * so we merge the timings, using the slowest value for each timing.
+ */
+ if (index > 1) {
+ unsigned int mate;
+ if (cmd_drives[mate = index ^ 1]->present) {
+ if (setup_count < setup_counts[mate])
+ setup_count = setup_counts[mate];
+ if (active_count < active_counts[mate])
+ active_count = active_counts[mate];
+ if (recovery_count < recovery_counts[mate])
+ recovery_count = recovery_counts[mate];
+ }
+ }
+
+ /*
+ * Convert setup_count to internal chipset representation
+ */
+ switch (setup_count) {
+ case 4: setup_count = 0x00; break;
+ case 3: setup_count = 0x80; break;
+ case 1:
+ case 2: setup_count = 0x40; break;
+ default: setup_count = 0xc0; /* case 5 */
+ }
+
+ /*
+ * Now that everything is ready, program the new timings
+ */
+ save_flags (flags);
+ cli();
+ /*
+ * Program the address_setup clocks into ARTTIM reg,
+ * and then the active/recovery counts into the DRWTIM reg
+ * (this converts counts of 16 into counts of zero -- okay).
+ */
+ setup_count |= get_cmd640_reg(arttim_regs[index]) & 0x3f;
+ put_cmd640_reg(arttim_regs[index], setup_count);
+ put_cmd640_reg(drwtim_regs[index], pack_nibbles(active_count, recovery_count));
+ restore_flags(flags);
+}
+
+/*
+ * Set a specific pio_mode for a drive
+ */
+static void cmd640_set_mode (unsigned int index, byte pio_mode, unsigned int cycle_time)
+{
+ int setup_time, active_time, recovery_time, clock_time;
+ byte setup_count, active_count, recovery_count, recovery_count2, cycle_count;
+ int bus_speed = ide_system_bus_speed();
+
+ if (pio_mode > 5)
+ pio_mode = 5;
+ setup_time = ide_pio_timings[pio_mode].setup_time;
+ active_time = ide_pio_timings[pio_mode].active_time;
+ recovery_time = cycle_time - (setup_time + active_time);
+ clock_time = 1000 / bus_speed;
+ cycle_count = (cycle_time + clock_time - 1) / clock_time;
+
+ setup_count = (setup_time + clock_time - 1) / clock_time;
+
+ active_count = (active_time + clock_time - 1) / clock_time;
+ if (active_count < 2)
+ active_count = 2; /* minimum allowed by cmd640 */
+
+ recovery_count = (recovery_time + clock_time - 1) / clock_time;
+ recovery_count2 = cycle_count - (setup_count + active_count);
+ if (recovery_count2 > recovery_count)
+ recovery_count = recovery_count2;
+ if (recovery_count < 2)
+ recovery_count = 2; /* minimum allowed by cmd640 */
+ if (recovery_count > 17) {
+ active_count += recovery_count - 17;
+ recovery_count = 17;
+ }
+ if (active_count > 16)
+ active_count = 16; /* maximum allowed by cmd640 */
+ if (cmd640_chip_version > 1)
+ recovery_count -= 1; /* cmd640b uses (count + 1)*/
+ if (recovery_count > 16)
+ recovery_count = 16; /* maximum allowed by cmd640 */
+
+ setup_counts[index] = setup_count;
+ active_counts[index] = active_count;
+ recovery_counts[index] = recovery_count;
+
+ /*
+ * In a perfect world, we might set the drive pio mode here
+ * (using WIN_SETFEATURE) before continuing.
+ *
+ * But we do not, because:
+ * 1) this is the wrong place to do it (proper is do_special() in ide.c)
+ * 2) in practice this is rarely, if ever, necessary
+ */
+ program_drive_counts (index);
+}
+
+/*
+ * Drive PIO mode selection:
+ */
+static void cmd640_tune_drive (ide_drive_t *drive, byte mode_wanted)
+{
+ byte b;
+ ide_pio_data_t d;
+ unsigned int index = 0;
+
+ while (drive != cmd_drives[index]) {
+ if (++index > 3) {
+ printk("%s: bad news in cmd640_tune_drive\n", drive->name);
+ return;
+ }
+ }
+ switch (mode_wanted) {
+ case 6: /* set fast-devsel off */
+ case 7: /* set fast-devsel on */
+ mode_wanted &= 1;
+ b = get_cmd640_reg(CNTRL) & ~0x27;
+ if (mode_wanted)
+ b |= 0x27;
+ put_cmd640_reg(CNTRL, b);
+ printk("%s: %sabled cmd640 fast host timing (devsel)\n", drive->name, mode_wanted ? "en" : "dis");
+ return;
+
+ case 8: /* set prefetch off */
+ case 9: /* set prefetch on */
+ mode_wanted &= 1;
+ set_prefetch_mode(index, mode_wanted);
+ printk("%s: %sabled cmd640 prefetch\n", drive->name, mode_wanted ? "en" : "dis");
+ return;
+ }
+
+ (void) ide_get_best_pio_mode (drive, mode_wanted, 5, &d);
+ cmd640_set_mode (index, d.pio_mode, d.cycle_time);
+
+ printk ("%s: selected cmd640 PIO mode%d (%dns)%s",
+ drive->name,
+ d.pio_mode,
+ d.cycle_time,
+ d.overridden ? " (overriding vendor mode)" : "");
+ display_clocks(index);
+ return;
+}
+
+#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
+
+/*
+ * Probe for a cmd640 chipset, and initialize it if found. Called from ide.c
+ */
+int __init ide_probe_for_cmd640x (void)
+{
+#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
+ int second_port_toggled = 0;
+#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
+ int second_port_cmd640 = 0;
+ const char *bus_type, *port2;
+ unsigned int index;
+ byte b, cfr;
+
+ if (cmd640_vlb && probe_for_cmd640_vlb()) {
+ bus_type = "VLB";
+ } else {
+ cmd640_vlb = 0;
+ if (probe_for_cmd640_pci1())
+ bus_type = "PCI (type1)";
+ else if (probe_for_cmd640_pci2())
+ bus_type = "PCI (type2)";
+ else
+ return 0;
+ }
+ /*
+ * Undocumented magic (there is no 0x5b reg in specs)
+ */
+ put_cmd640_reg(0x5b, 0xbd);
+ if (get_cmd640_reg(0x5b) != 0xbd) {
+ printk("ide: cmd640 init failed: wrong value in reg 0x5b\n");
+ return 0;
+ }
+ put_cmd640_reg(0x5b, 0);
+
+#ifdef CMD640_DUMP_REGS
+ CMD640_DUMP_REGS;
+#endif
+
+ /*
+ * Documented magic begins here
+ */
+ cfr = get_cmd640_reg(CFR);
+ cmd640_chip_version = cfr & CFR_DEVREV;
+ if (cmd640_chip_version == 0) {
+ printk ("ide: bad cmd640 revision: %d\n", cmd640_chip_version);
+ return 0;
+ }
+
+ /*
+ * Initialize data for primary port
+ */
+ setup_device_ptrs ();
+ printk("%s: buggy cmd640%c interface on %s, config=0x%02x\n",
+ cmd_hwif0->name, 'a' + cmd640_chip_version - 1, bus_type, cfr);
+ cmd_hwif0->chipset = ide_cmd640;
+#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
+ cmd_hwif0->tuneproc = &cmd640_tune_drive;
+#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
+
+ /*
+ * Ensure compatibility by always using the slowest timings
+ * for access to the drive's command register block,
+ * and reset the prefetch burstsize to default (512 bytes).
+ *
+ * Maybe we need a way to NOT do these on *some* systems?
+ */
+ put_cmd640_reg(CMDTIM, 0);
+ put_cmd640_reg(BRST, 0x40);
+
+ /*
+ * Try to enable the secondary interface, if not already enabled
+ */
+ if (cmd_hwif1->noprobe) {
+ port2 = "not probed";
+ } else {
+ b = get_cmd640_reg(CNTRL);
+ if (secondary_port_responding()) {
+ if ((b & CNTRL_ENA_2ND)) {
+ second_port_cmd640 = 1;
+ port2 = "okay";
+ } else if (cmd640_vlb) {
+ second_port_cmd640 = 1;
+ port2 = "alive";
+ } else
+ port2 = "not cmd640";
+ } else {
+ put_cmd640_reg(CNTRL, b ^ CNTRL_ENA_2ND); /* toggle the bit */
+ if (secondary_port_responding()) {
+ second_port_cmd640 = 1;
+#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
+ second_port_toggled = 1;
+#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
+ port2 = "enabled";
+ } else {
+ put_cmd640_reg(CNTRL, b); /* restore original setting */
+ port2 = "not responding";
+ }
+ }
+ }
+
+ /*
+ * Initialize data for secondary cmd640 port, if enabled
+ */
+ if (second_port_cmd640) {
+ cmd_hwif0->serialized = 1;
+ cmd_hwif1->serialized = 1;
+ cmd_hwif1->chipset = ide_cmd640;
+ cmd_hwif0->mate = cmd_hwif1;
+ cmd_hwif1->mate = cmd_hwif0;
+ cmd_hwif1->channel = 1;
+#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
+ cmd_hwif1->tuneproc = &cmd640_tune_drive;
+#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
+ }
+ printk("%s: %sserialized, secondary interface %s\n", cmd_hwif1->name,
+ cmd_hwif0->serialized ? "" : "not ", port2);
+
+ /*
+ * Establish initial timings/prefetch for all drives.
+ * Do not unnecessarily disturb any prior BIOS setup of these.
+ */
+ for (index = 0; index < (2 + (second_port_cmd640 << 1)); index++) {
+ ide_drive_t *drive = cmd_drives[index];
+#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
+ if (drive->autotune || ((index > 1) && second_port_toggled)) {
+ /*
+ * Reset timing to the slowest speed and turn off prefetch.
+ * This way, the drive identify code has a better chance.
+ */
+ setup_counts [index] = 4; /* max possible */
+ active_counts [index] = 16; /* max possible */
+ recovery_counts [index] = 16; /* max possible */
+ program_drive_counts (index);
+ set_prefetch_mode (index, 0);
+ printk("cmd640: drive%d timings/prefetch cleared\n", index);
+ } else {
+ /*
+ * Record timings/prefetch without changing them.
+ * This preserves any prior BIOS setup.
+ */
+ retrieve_drive_counts (index);
+ check_prefetch (index);
+ printk("cmd640: drive%d timings/prefetch(%s) preserved",
+ index, drive->no_io_32bit ? "off" : "on");
+ display_clocks(index);
+ }
+#else
+ /*
+ * Set the drive unmask flags to match the prefetch setting
+ */
+ check_prefetch (index);
+ printk("cmd640: drive%d timings/prefetch(%s) preserved\n",
+ index, drive->no_io_32bit ? "off" : "on");
+#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
+ }
+
+#ifdef CMD640_DUMP_REGS
+ CMD640_DUMP_REGS;
+#endif
+ return 1;
+}
+
diff --git a/drivers/ide/cmd64x.c b/drivers/ide/cmd64x.c
new file mode 100644
index 000000000..542ad44a1
--- /dev/null
+++ b/drivers/ide/cmd64x.c
@@ -0,0 +1,721 @@
+/* $Id: cmd64x.c,v 1.21 2000/01/30 23:23:16
+ *
+ * cmd64x.c: Enable interrupts at initialization time on Ultra/PCI machines.
+ * Note, this driver is not used at all on other systems because
+ * there the "BIOS" has done all of the following already.
+ * Due to massive hardware bugs, UltraDMA is only supported
+ * on the 646U2 and not on the 646U.
+ *
+ * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be)
+ * Copyright (C) 1998 David S. Miller (davem@redhat.com)
+ * Copyright (C) 1999-2000 Andre Hedrick (andre@suse.com)
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/hdreg.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+#include "ide_modes.h"
+
+#ifndef SPLIT_BYTE
+#define SPLIT_BYTE(B,H,L) ((H)=(B>>4), (L)=(B-((B>>4)<<4)))
+#endif
+
+#define CMD_DEBUG 0
+
+#if CMD_DEBUG
+#define cmdprintk(x...) printk(##x)
+#else
+#define cmdprintk(x...)
+#endif
+
+/*
+ * CMD64x specific registers definition.
+ */
+
+#define CNTRL 0x51
+#define CNTRL_DIS_RA0 0x40
+#define CNTRL_DIS_RA1 0x80
+#define CNTRL_ENA_2ND 0x08
+
+#define CMDTIM 0x52
+#define ARTTIM0 0x53
+#define DRWTIM0 0x54
+#define ARTTIM1 0x55
+#define DRWTIM1 0x56
+#define ARTTIM23 0x57
+#define ARTTIM23_DIS_RA2 0x04
+#define ARTTIM23_DIS_RA3 0x08
+#define ARTTIM2 0x57
+#define ARTTIM3 0x57
+#define DRWTIM23 0x58
+#define DRWTIM2 0x58
+#define BRST 0x59
+#define DRWTIM3 0x5b
+
+#define BMIDECR0 0x70
+#define MRDMODE 0x71
+#define BMIDESR0 0x72
+#define UDIDETCR0 0x73
+#define DTPR0 0x74
+#define BMIDECR1 0x78
+#define BMIDECSR 0x79
+#define BMIDESR1 0x7A
+#define UDIDETCR1 0x7B
+#define DTPR1 0x7C
+
+#undef DISPLAY_CMD64X_TIMINGS
+
+#if defined(DISPLAY_CMD64X_TIMINGS) && defined(CONFIG_PROC_FS)
+#include <linux/stat.h>
+#include <linux/proc_fs.h>
+
+static int cmd64x_get_info(char *, char **, off_t, int);
+extern int (*cmd64x_display_info)(char *, char **, off_t, int); /* ide-proc.c */
+extern char *ide_media_verbose(ide_drive_t *);
+static struct pci_dev *bmide_dev;
+
+static int cmd64x_get_info (char *buffer, char **addr, off_t offset, int count)
+{
+ char *p = buffer;
+ u8 reg53 = 0, reg54 = 0, reg55 = 0, reg56 = 0; /* primary */
+ u8 reg57 = 0, reg58 = 0, reg5b; /* secondary */
+ u8 reg72 = 0, reg73 = 0; /* primary */
+ u8 reg7a = 0, reg7b = 0; /* secondary */
+ u8 hi_byte = 0, lo_byte = 0;
+
+ switch(bmide_dev->device) {
+ case PCI_DEVICE_ID_CMD_648:
+ p += sprintf(p, "\n CMD648 Chipset.\n");
+ break;
+ case PCI_DEVICE_ID_CMD_646:
+ p += sprintf(p, "\n CMD646 Chipset.\n");
+ break;
+ case PCI_DEVICE_ID_CMD_643:
+ p += sprintf(p, "\n CMD643 Chipset.\n");
+ break;
+ default:
+ p += sprintf(p, "\n CMD64? Chipse.\n");
+ break;
+ }
+ (void) pci_read_config_byte(bmide_dev, ARTTIM0, &reg53);
+ (void) pci_read_config_byte(bmide_dev, DRWTIM0, &reg54);
+ (void) pci_read_config_byte(bmide_dev, ARTTIM1, &reg55);
+ (void) pci_read_config_byte(bmide_dev, DRWTIM1, &reg56);
+ (void) pci_read_config_byte(bmide_dev, ARTTIM2, &reg57);
+ (void) pci_read_config_byte(bmide_dev, DRWTIM2, &reg58);
+ (void) pci_read_config_byte(bmide_dev, DRWTIM3, &reg5b);
+ (void) pci_read_config_byte(bmide_dev, BMIDESR0, &reg72);
+ (void) pci_read_config_byte(bmide_dev, UDIDETCR0, &reg73);
+ (void) pci_read_config_byte(bmide_dev, BMIDESR1, &reg7a);
+ (void) pci_read_config_byte(bmide_dev, UDIDETCR1, &reg7b);
+
+ p += sprintf(p, "--------------- Primary Channel ---------------- Secondary Channel -------------\n");
+ p += sprintf(p, " %sabled %sabled\n",
+ (reg72&0x80) ? "dis" : " en", (reg7a&0x80) ? "dis" : " en");
+ p += sprintf(p, "--------------- drive0 --------- drive1 -------- drive0 ---------- drive1 ------\n");
+ p += sprintf(p, "DMA enabled: %s %s %s %s\n",
+ (reg72&0x20) ? "yes" : "no ", (reg72&0x40) ? "yes" : "no ", (reg7a&0x20) ? "yes" : "no ", (reg7a&0x40) ? "yes" : "no " );
+ p += sprintf(p, "UDMA enabled: %s %s %s %s\n",
+ (reg73&0x01) ? "yes" : "no ", (reg73&0x02) ? "yes" : "no ", (reg7b&0x01) ? "yes" : "no ", (reg7b&0x02) ? "yes" : "no " );
+ p += sprintf(p, "UDMA enabled: %s %s %s %s\n",
+ (reg73&0x15) ? "4" : (reg73&0x25) ? "3" : (reg73&0x11) ? "2" : (reg73&0x21) ? "1" : (reg73&0x31) ? "0" : "X",
+ (reg73&0x4A) ? "4" : (reg73&0x8A) ? "3" : (reg73&0x42) ? "2" : (reg73&0x82) ? "1" : (reg73&0xC2) ? "0" : "X",
+ (reg7b&0x15) ? "4" : (reg7b&0x25) ? "3" : (reg7b&0x11) ? "2" : (reg7b&0x21) ? "1" : (reg7b&0x31) ? "0" : "X",
+ (reg7b&0x4A) ? "4" : (reg7b&0x8A) ? "3" : (reg7b&0x42) ? "2" : (reg7b&0x82) ? "1" : (reg7b&0xC2) ? "0" : "X" );
+ p += sprintf(p, "DMA enabled: %s %s %s %s\n",
+ (reg73&0x10) ? "2" : (reg73&0x20) ? "1" : (reg73&0x30) ? "0" : "X",
+ (reg73&0x40) ? "2" : (reg73&0x80) ? "1" : (reg73&0xC0) ? "0" : "X",
+ (reg7b&0x10) ? "2" : (reg7b&0x20) ? "1" : (reg7b&0x30) ? "0" : "X",
+ (reg7b&0x40) ? "2" : (reg7b&0x80) ? "1" : (reg7b&0xC0) ? "0" : "X" );
+ p += sprintf(p, "PIO\n");
+
+ SPLIT_BYTE(reg53, hi_byte, lo_byte);
+ p += sprintf(p, "ARTTIM0 = 0x%02x, HI = 0x%02x, LOW = 0x%02x\n", reg53, hi_byte, lo_byte);
+ SPLIT_BYTE(reg54, hi_byte, lo_byte);
+ p += sprintf(p, "DRWTIM0 = 0x%02x, HI = 0x%02x, LOW = 0x%02x\n", reg54, hi_byte, lo_byte);
+ SPLIT_BYTE(reg55, hi_byte, lo_byte);
+ p += sprintf(p, "ARTTIM1 = 0x%02x, HI = 0x%02x, LOW = 0x%02x\n", reg55, hi_byte, lo_byte);
+ SPLIT_BYTE(reg56, hi_byte, lo_byte);
+ p += sprintf(p, "DRWTIM1 = 0x%02x, HI = 0x%02x, LOW = 0x%02x\n", reg56, hi_byte, lo_byte);
+ SPLIT_BYTE(reg57, hi_byte, lo_byte);
+ p += sprintf(p, "ARTTIM23 = 0x%02x, HI = 0x%02x, LOW = 0x%02x\n", reg57, hi_byte, lo_byte);
+ SPLIT_BYTE(reg58, hi_byte, lo_byte);
+ p += sprintf(p, "DRWTIM2 = 0x%02x, HI = 0x%02x, LOW = 0x%02x\n", reg58, hi_byte, lo_byte);
+ SPLIT_BYTE(reg5b, hi_byte, lo_byte);
+ p += sprintf(p, "DRWTIM3 = 0x%02x, HI = 0x%02x, LOW = 0x%02x\n", reg5b, hi_byte, lo_byte);
+ SPLIT_BYTE(reg73, hi_byte, lo_byte);
+ p += sprintf(p, "UDIDETCR0 = 0x%02x, HI = 0x%02x, LOW = 0x%02x\n", reg73, hi_byte, lo_byte);
+ SPLIT_BYTE(reg7b, hi_byte, lo_byte);
+ p += sprintf(p, "UDIDETCR1 = 0x%02x, HI = 0x%02x, LOW = 0x%02x\n", reg7b, hi_byte, lo_byte);
+
+ return p-buffer; /* => must be less than 4k! */
+}
+#endif /* defined(DISPLAY_CMD64X_TIMINGS) && defined(CONFIG_PROC_FS) */
+
+byte cmd64x_proc = 0;
+
+/*
+ * Registers and masks for easy access by drive index:
+ */
+#if 0
+static byte prefetch_regs[4] = {CNTRL, CNTRL, ARTTIM23, ARTTIM23};
+static byte prefetch_masks[4] = {CNTRL_DIS_RA0, CNTRL_DIS_RA1, ARTTIM23_DIS_RA2, ARTTIM23_DIS_RA3};
+#endif
+
+/*
+ * This routine writes the prepared setup/active/recovery counts
+ * for a drive into the cmd646 chipset registers to active them.
+ */
+static void program_drive_counts (ide_drive_t *drive, int setup_count, int active_count, int recovery_count)
+{
+ unsigned long flags;
+ ide_drive_t *drives = HWIF(drive)->drives;
+ byte temp_b;
+ static const byte setup_counts[] = {0x40, 0x40, 0x40, 0x80, 0, 0xc0};
+ static const byte recovery_counts[] =
+ {15, 15, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 0};
+ static const byte arttim_regs[2][2] = {
+ { ARTTIM0, ARTTIM1 },
+ { ARTTIM23, ARTTIM23 }
+ };
+ static const byte drwtim_regs[2][2] = {
+ { DRWTIM0, DRWTIM1 },
+ { DRWTIM2, DRWTIM3 }
+ };
+ int channel = (int) HWIF(drive)->channel;
+ int slave = (drives != drive); /* Is this really the best way to determine this?? */
+
+ cmdprintk("program_drive_count parameters = s(%d),a(%d),r(%d),p(%d)\n", setup_count,
+ active_count, recovery_count, drive->present);
+ /*
+ * Set up address setup count registers.
+ * Primary interface has individual count/timing registers for
+ * each drive. Secondary interface has one common set of registers,
+ * for address setup so we merge these timings, using the slowest
+ * value.
+ */
+ if (channel) {
+ drive->drive_data = setup_count;
+ setup_count = IDE_MAX(drives[0].drive_data, drives[1].drive_data);
+ cmdprintk("Secondary interface, setup_count = %d\n", setup_count);
+ }
+
+ /*
+ * Convert values to internal chipset representation
+ */
+ setup_count = (setup_count > 5) ? 0xc0 : (int) setup_counts[setup_count];
+ active_count &= 0xf; /* Remember, max value is 16 */
+ recovery_count = (int) recovery_counts[recovery_count];
+
+ cmdprintk("Final values = %d,%d,%d\n", setup_count, active_count, recovery_count);
+
+ /*
+ * Now that everything is ready, program the new timings
+ */
+ __save_flags (flags);
+ __cli();
+ /*
+ * Program the address_setup clocks into ARTTIM reg,
+ * and then the active/recovery counts into the DRWTIM reg
+ */
+ (void) pci_read_config_byte(HWIF(drive)->pci_dev, arttim_regs[channel][slave], &temp_b);
+ (void) pci_write_config_byte(HWIF(drive)->pci_dev, arttim_regs[channel][slave],
+ ((byte) setup_count) | (temp_b & 0x3f));
+ (void) pci_write_config_byte(HWIF(drive)->pci_dev, drwtim_regs[channel][slave],
+ (byte) ((active_count << 4) | recovery_count));
+ cmdprintk ("Write %x to %x\n", ((byte) setup_count) | (temp_b & 0x3f), arttim_regs[channel][slave]);
+ cmdprintk ("Write %x to %x\n", (byte) ((active_count << 4) | recovery_count), drwtim_regs[channel][slave]);
+ __restore_flags(flags);
+}
+
+/*
+ * Attempts to set the interface PIO mode.
+ * The preferred method of selecting PIO modes (e.g. mode 4) is
+ * "echo 'piomode:4' > /proc/ide/hdx/settings". Special cases are
+ * 8: prefetch off, 9: prefetch on, 255: auto-select best mode.
+ * Called with 255 at boot time.
+ */
+static void cmd64x_tuneproc (ide_drive_t *drive, byte mode_wanted)
+{
+ int setup_time, active_time, recovery_time, clock_time, pio_mode, cycle_time;
+ byte recovery_count2, cycle_count;
+ int setup_count, active_count, recovery_count;
+ int bus_speed = ide_system_bus_speed();
+ /*byte b;*/
+ ide_pio_data_t d;
+
+ switch (mode_wanted) {
+ case 8: /* set prefetch off */
+ case 9: /* set prefetch on */
+ mode_wanted &= 1;
+ /*set_prefetch_mode(index, mode_wanted);*/
+ cmdprintk("%s: %sabled cmd640 prefetch\n", drive->name, mode_wanted ? "en" : "dis");
+ return;
+ }
+
+ mode_wanted = ide_get_best_pio_mode (drive, mode_wanted, 5, &d);
+ pio_mode = d.pio_mode;
+ cycle_time = d.cycle_time;
+
+ /*
+ * I copied all this complicated stuff from cmd640.c and made a few minor changes.
+ * For now I am just going to pray that it is correct.
+ */
+ if (pio_mode > 5)
+ pio_mode = 5;
+ setup_time = ide_pio_timings[pio_mode].setup_time;
+ active_time = ide_pio_timings[pio_mode].active_time;
+ recovery_time = cycle_time - (setup_time + active_time);
+ clock_time = 1000 / bus_speed;
+ cycle_count = (cycle_time + clock_time - 1) / clock_time;
+
+ setup_count = (setup_time + clock_time - 1) / clock_time;
+
+ active_count = (active_time + clock_time - 1) / clock_time;
+
+ recovery_count = (recovery_time + clock_time - 1) / clock_time;
+ recovery_count2 = cycle_count - (setup_count + active_count);
+ if (recovery_count2 > recovery_count)
+ recovery_count = recovery_count2;
+ if (recovery_count > 16) {
+ active_count += recovery_count - 16;
+ recovery_count = 16;
+ }
+ if (active_count > 16)
+ active_count = 16; /* maximum allowed by cmd646 */
+
+ /*
+ * In a perfect world, we might set the drive pio mode here
+ * (using WIN_SETFEATURE) before continuing.
+ *
+ * But we do not, because:
+ * 1) this is the wrong place to do it (proper is do_special() in ide.c)
+ * 2) in practice this is rarely, if ever, necessary
+ */
+ program_drive_counts (drive, setup_count, active_count, recovery_count);
+
+ cmdprintk("%s: selected cmd646 PIO mode%d : %d (%dns)%s, clocks=%d/%d/%d\n",
+ drive->name, pio_mode, mode_wanted, cycle_time,
+ d.overridden ? " (overriding vendor mode)" : "",
+ setup_count, active_count, recovery_count);
+}
+
+static int tune_chipset_for_dma (ide_drive_t *drive, byte speed)
+{
+#if 0
+ struct hd_driveid *id = drive->id;
+ ide_hwif_t *hwif = HWIF(drive);
+ struct pci_dev *dev = hwif->pci_dev;
+ unsigned long dma_base = hwif->dma_base;
+ byte unit = (drive->select.b.unit & 0x01);
+
+ u8 reg72 = 0, reg73 = 0; /* primary */
+ u8 reg7a = 0, reg7b = 0; /* secondary */
+ u8 pciU = (hwif->channel) ? UDIDETCR1 : UDIDETCR0;
+ u8 pciD = (hwif->channel) ? BMIDESR1 : BMIDESR0;
+ u8 regU = (hwif->channel) ? 2 : 0;
+ u8 regD = (hwif->channel) ? 2 : 0;
+
+ (void) pci_read_config_byte(dev, BMIDESR0, &reg72);
+ (void) pci_read_config_byte(dev, UDIDETCR0, &reg73);
+ (void) pci_read_config_byte(dev, BMIDESR1, &reg7a);
+ (void) pci_read_config_byte(dev, UDIDETCR1, &reg7b);
+
+ switch(speed) {
+ case XFER_UDMA_4:
+ pciU = unit ? 0x4A : 0x15;
+ case XFER_UDMA_3:
+ pciU = unit ? 0x8A : 0x25;
+ case XFER_UDMA_2:
+ pciU = unit ? 0x42 : 0x11;
+ case XFER_UDMA_1:
+ pciU = unit ? 0x82 : 0x21;
+ case XFER_UDMA_0:
+ pciU = unit ? 0xC2 : 0x31
+(reg73&0x15)?"4":(reg73&0x25)?"3":(reg73&0x11)?"2":(reg73&0x21)?"1":(reg73&0x31)?"0":"X",
+(reg73&0x4A)?"4":(reg73&0x8A)?"3":(reg73&0x42)?"2":(reg73&0x82)?"1":(reg73&0xC2)?"0":"X",
+(reg7b&0x15)?"4":(reg7b&0x25)?"3":(reg7b&0x11)?"2":(reg7b&0x21)?"1":(reg7b&0x31)?"0":"X",
+(reg7b&0x4A)?"4":(reg7b&0x8A)?"3":(reg7b&0x42)?"2":(reg7b&0x82)?"1":(reg7b&0xC2)?"0":"X",
+
+ case XFER_MW_DMA_2:
+ pciD = unit ? 0x40 : 0x10;
+ case XFER_MW_DMA_1:
+ pciD = unit ? 0x80 : 0x20;
+ case XFER_MW_DMA_0:
+ pciD = unit ? 0xC0 : 0x30;
+ case XFER_SW_DMA_2:
+ case XFER_SW_DMA_1:
+ case XFER_SW_DMA_0:
+(reg73&0x10)?"2":(reg73&0x20)?"1":(reg73&0x30)?"0":"X",
+(reg73&0x40)?"2":(reg73&0x80)?"1":(reg73&0xC0)?"0":"X",
+(reg7b&0x10)?"2":(reg7b&0x20)?"1":(reg7b&0x30)?"0":"X",
+(reg7b&0x40)?"2":(reg7b&0x80)?"1":(reg7b&0xC0)?"0":"X" );
+
+ default:
+ return 1;
+ }
+
+ (void) ide_config_drive_speed(drive, speed);
+ outb(inb(dma_base+2)|(1<<(5+unit)), dma_base+2);
+#endif
+ return 0;
+}
+
+static void config_chipset_for_pio (ide_drive_t *drive, byte set_speed)
+{
+ byte speed = 0x00;
+ byte set_pio = ide_get_best_pio_mode(drive, 4, 5, NULL);
+
+ cmd64x_tuneproc(drive, set_pio);
+ speed = XFER_PIO_0 + set_pio;
+ if (set_speed)
+ (void) ide_config_drive_speed(drive, speed);
+}
+
+static int config_chipset_for_dma (ide_drive_t *drive, unsigned int rev, byte ultra_66)
+{
+ struct hd_driveid *id = drive->id;
+ ide_hwif_t *hwif = HWIF(drive);
+ struct pci_dev *dev = hwif->pci_dev;
+ unsigned long dma_base = hwif->dma_base;
+
+ byte unit = (drive->select.b.unit & 0x01);
+ byte speed = 0x00;
+ byte set_pio = 0x00;
+ byte udma_timing_bits = 0x00;
+ byte udma_33 = ((rev >= 0x05) || (ultra_66)) ? 1 : 0;
+ byte udma_66 = ((id->hw_config & 0x2000) && (hwif->udma_four)) ? 1 : 0;
+ /* int drive_number = ((hwif->channel ? 2 : 0) + unit); */
+ int rval;
+
+ switch(dev->device) {
+ case PCI_DEVICE_ID_CMD_643:
+ case PCI_DEVICE_ID_CMD_646:
+ case PCI_DEVICE_ID_CMD_648:
+ default:
+ break;
+ }
+
+ if (drive->media != ide_disk) {
+ cmdprintk("CMD64X: drive->media != ide_disk at double check, inital check failed!!\n");
+ return ((int) ide_dma_off);
+ }
+
+ /* UltraDMA only supported on PCI646U and PCI646U2,
+ * which correspond to revisions 0x03, 0x05 and 0x07 respectively.
+ * Actually, although the CMD tech support people won't
+ * tell me the details, the 0x03 revision cannot support
+ * UDMA correctly without hardware modifications, and even
+ * then it only works with Quantum disks due to some
+ * hold time assumptions in the 646U part which are fixed
+ * in the 646U2.
+ * So we only do UltraDMA on revision 0x05 and 0x07 chipsets.
+ */
+
+ if ((id->dma_ultra & 0x0010) && (udma_66) && (udma_33)) {
+ speed = XFER_UDMA_4;
+ udma_timing_bits = 0x10; /* 2 clock */
+ } else if ((id->dma_ultra & 0x0008) && (udma_66) && (udma_33)) {
+ speed = XFER_UDMA_3;
+ udma_timing_bits = 0x20; /* 3 clock */
+ } else if ((id->dma_ultra & 0x0004) && (udma_33)) {
+ speed = XFER_UDMA_2;
+ udma_timing_bits = 0x10; /* 2 clock */
+ } else if ((id->dma_ultra & 0x0002) && (udma_33)) {
+ speed = XFER_UDMA_1;
+ udma_timing_bits = 0x20; /* 3 clock */
+ } else if ((id->dma_ultra & 0x0001) && (udma_33)) {
+ speed = XFER_UDMA_0;
+ udma_timing_bits = 0x30; /* 4 clock */
+ } else if (id->dma_mword & 0x0004) {
+ speed = XFER_MW_DMA_2;
+ } else if (id->dma_mword & 0x0002) {
+ speed = XFER_MW_DMA_1;
+ } else if (id->dma_mword & 0x0001) {
+ speed = XFER_MW_DMA_0;
+ } else if (id->dma_1word & 0x0004) {
+ speed = XFER_SW_DMA_2;
+ } else if (id->dma_1word & 0x0002) {
+ speed = XFER_SW_DMA_1;
+ } else if (id->dma_1word & 0x0001) {
+ speed = XFER_SW_DMA_0;
+ } else {
+ set_pio = 1;
+ }
+
+ config_chipset_for_pio(drive, set_pio);
+
+ if (set_pio)
+ return ((int) ide_dma_off_quietly);
+
+#if 1
+ /*
+ * This the alternate access method. :-(
+ * The correct method is to directly setup the pci-config space.
+ */
+ (void) ide_config_drive_speed(drive, speed);
+ outb(inb(dma_base+2)|(1<<(5+unit)), dma_base+2);
+
+ if (speed >= XFER_UDMA_0) {
+ byte udma_ctrl = inb(dma_base + 3);
+ /* Put this channel into UDMA mode. */
+ udma_ctrl |= (1 << unit);
+ udma_ctrl &= ~(0x04 << unit);
+ if (udma_66)
+ udma_ctrl |= (0x04 << unit);
+ udma_ctrl &= ~(0x30 << (unit * 2));
+ udma_ctrl |= (udma_timing_bits << (unit * 2));
+ outb(udma_ctrl, dma_base+3);
+ }
+#endif
+
+ if (tune_chipset_for_dma(drive, speed))
+ return ((int) ide_dma_off);
+
+ rval = (int)( ((id->dma_ultra >> 11) & 3) ? ide_dma_on :
+ ((id->dma_ultra >> 8) & 7) ? ide_dma_on :
+ ((id->dma_mword >> 8) & 7) ? ide_dma_on :
+ ((id->dma_1word >> 8) & 7) ? ide_dma_on :
+ ide_dma_off_quietly);
+
+ return rval;
+}
+
+static int cmd64x_config_drive_for_dma (ide_drive_t *drive)
+{
+ struct hd_driveid *id = drive->id;
+ ide_hwif_t *hwif = HWIF(drive);
+ struct pci_dev *dev = hwif->pci_dev;
+ unsigned int class_rev = 0;
+ byte can_ultra_33 = 0;
+ byte can_ultra_66 = 0;
+ ide_dma_action_t dma_func = ide_dma_on;
+
+ pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev);
+ class_rev &= 0xff;
+
+ switch(dev->device) {
+ case PCI_DEVICE_ID_CMD_643:
+ can_ultra_33 = 1;
+ can_ultra_66 = 0;
+ break;
+ case PCI_DEVICE_ID_CMD_646:
+ can_ultra_33 = (class_rev >= 0x05) ? 1 : 0;
+ can_ultra_66 = 0;
+ break;
+ case PCI_DEVICE_ID_CMD_648:
+ can_ultra_33 = 1;
+ can_ultra_66 = 1;
+ break;
+ default:
+ return hwif->dmaproc(ide_dma_off, drive);
+ }
+
+ if ((id != NULL) && ((id->capability & 1) != 0) &&
+ hwif->autodma && (drive->media == ide_disk)) {
+ /* Consult the list of known "bad" drives */
+ if (ide_dmaproc(ide_dma_bad_drive, drive)) {
+ dma_func = ide_dma_off;
+ goto fast_ata_pio;
+ }
+ dma_func = ide_dma_off_quietly;
+ if ((id->field_valid & 4) && (can_ultra_33)) {
+ if (id->dma_ultra & 0x001F) {
+ /* Force if Capable UltraDMA */
+ dma_func = config_chipset_for_dma(drive, class_rev, can_ultra_66);
+ if ((id->field_valid & 2) &&
+ (dma_func != ide_dma_on))
+ goto try_dma_modes;
+ }
+ } else if (id->field_valid & 2) {
+try_dma_modes:
+ if ((id->dma_mword & 0x0007) ||
+ (id->dma_1word & 0x0007)) {
+ /* Force if Capable regular DMA modes */
+ dma_func = config_chipset_for_dma(drive, class_rev, 0);
+ if (dma_func != ide_dma_on)
+ goto no_dma_set;
+ }
+ } else if (ide_dmaproc(ide_dma_good_drive, drive)) {
+ if (id->eide_dma_time > 150) {
+ goto no_dma_set;
+ }
+ /* Consult the list of known "good" drives */
+ dma_func = config_chipset_for_dma(drive, class_rev, 0);
+ if (dma_func != ide_dma_on)
+ goto no_dma_set;
+ } else {
+ goto fast_ata_pio;
+ }
+ } else if ((id->capability & 8) || (id->field_valid & 2)) {
+fast_ata_pio:
+ dma_func = ide_dma_off_quietly;
+no_dma_set:
+ config_chipset_for_pio(drive, 1);
+ }
+ return HWIF(drive)->dmaproc(dma_func, drive);
+}
+
+static int cmd64x_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
+{
+ switch (func) {
+ case ide_dma_check:
+ return cmd64x_config_drive_for_dma(drive);
+ default:
+ break;
+ }
+ /* Other cases are done by generic IDE-DMA code. */
+ return ide_dmaproc(func, drive);
+}
+
+/*
+ * ASUS P55T2P4D with CMD646 chipset revision 0x01 requires the old
+ * event order for DMA transfers.
+ */
+static int cmd646_1_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
+{
+ ide_hwif_t *hwif = HWIF(drive);
+ unsigned long dma_base = hwif->dma_base;
+ byte dma_stat;
+
+ if (func == ide_dma_end) {
+ drive->waiting_for_dma = 0;
+ dma_stat = inb(dma_base+2); /* get DMA status */
+ outb(inb(dma_base)&~1, dma_base); /* stop DMA */
+ outb(dma_stat|6, dma_base+2); /* clear the INTR & ERROR bits */
+ ide_destroy_dmatable(drive); /* and free any DMA resources */
+ return (dma_stat & 7) != 4; /* verify good DMA status */
+ }
+
+ /* Other cases are done by generic IDE-DMA code. */
+ return cmd64x_dmaproc(func, drive);
+}
+
+unsigned int __init pci_init_cmd64x (struct pci_dev *dev, const char *name)
+{
+ unsigned char mrdmode;
+ unsigned int class_rev;
+
+ pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev);
+ class_rev &= 0xff;
+
+#if 0
+ if (dev->resource[PCI_ROM_RESOURCE].start)
+ pci_write_config_byte(dev, PCI_ROM_ADDRESS, dev->resource[PCI_ROM_RESOURCE].start | PCI_ROM_ADDRESS_ENABLE);
+#endif
+
+ switch(dev->device) {
+ case PCI_DEVICE_ID_CMD_643:
+ break;
+ case PCI_DEVICE_ID_CMD_646:
+ printk("%s: chipset revision 0x%02X, ", name, class_rev);
+ switch(class_rev) {
+ case 0x07:
+ case 0x05:
+ printk("UltraDMA Capable");
+ break;
+ case 0x03:
+ printk("MultiWord DMA Force Limited");
+ break;
+ case 0x01:
+ default:
+ printk("MultiWord DMA Limited, IRQ workaround enabled");
+ break;
+ }
+ printk("\n");
+ break;
+ case PCI_DEVICE_ID_CMD_648:
+ break;
+ default:
+ break;
+ }
+
+ /* Set a good latency timer and cache line size value. */
+ (void) pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64);
+#ifdef __sparc_v9__
+ (void) pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 0x10);
+#endif
+
+ /* Setup interrupts. */
+ (void) pci_read_config_byte(dev, MRDMODE, &mrdmode);
+ mrdmode &= ~(0x30);
+ (void) pci_write_config_byte(dev, MRDMODE, mrdmode);
+
+ /* Use MEMORY READ LINE for reads.
+ * NOTE: Although not mentioned in the PCI0646U specs,
+ * these bits are write only and won't be read
+ * back as set or not. The PCI0646U2 specs clarify
+ * this point.
+ */
+ (void) pci_write_config_byte(dev, MRDMODE, mrdmode | 0x02);
+
+ /* Set reasonable active/recovery/address-setup values. */
+ (void) pci_write_config_byte(dev, ARTTIM0, 0x40);
+ (void) pci_write_config_byte(dev, DRWTIM0, 0x3f);
+ (void) pci_write_config_byte(dev, ARTTIM1, 0x40);
+ (void) pci_write_config_byte(dev, DRWTIM1, 0x3f);
+#ifdef __i386__
+ (void) pci_write_config_byte(dev, ARTTIM23, 0x1c);
+#else
+ (void) pci_write_config_byte(dev, ARTTIM23, 0x5c);
+#endif
+ (void) pci_write_config_byte(dev, DRWTIM23, 0x3f);
+ (void) pci_write_config_byte(dev, DRWTIM3, 0x3f);
+
+#if defined(DISPLAY_CMD64X_TIMINGS) && defined(CONFIG_PROC_FS)
+ cmd64x_proc = 1;
+ bmide_dev = dev;
+ cmd64x_display_info = &cmd64x_get_info;
+#endif /* DISPLAY_CMD64X_TIMINGS && CONFIG_PROC_FS */
+
+ return 0;
+}
+
+unsigned int __init ata66_cmd64x (ide_hwif_t *hwif)
+{
+ byte ata66 = 0;
+ byte mask = (hwif->channel) ? 0x02 : 0x01;
+
+ pci_read_config_byte(hwif->pci_dev, BMIDECSR, &ata66);
+ return (ata66 & mask) ? 1 : 0;
+}
+
+void __init ide_init_cmd64x (ide_hwif_t *hwif)
+{
+ struct pci_dev *dev = hwif->pci_dev;
+ unsigned int class_rev;
+
+ pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev);
+ class_rev &= 0xff;
+
+ hwif->tuneproc = &cmd64x_tuneproc;
+ hwif->drives[0].autotune = 1;
+ hwif->drives[0].autotune = 1;
+
+ if (!hwif->dma_base)
+ return;
+
+ switch(dev->device) {
+ case PCI_DEVICE_ID_CMD_643:
+ hwif->dmaproc = &cmd64x_dmaproc;
+ break;
+ case PCI_DEVICE_ID_CMD_646:
+ hwif->chipset = ide_cmd646;
+ if (class_rev == 0x01) {
+ hwif->dmaproc = &cmd646_1_dmaproc;
+ } else {
+ hwif->dmaproc = &cmd64x_dmaproc;
+ }
+ break;
+ case PCI_DEVICE_ID_CMD_648:
+ hwif->dmaproc = &cmd64x_dmaproc;
+ break;
+ default:
+ break;
+ }
+}
diff --git a/drivers/ide/cs5530.c b/drivers/ide/cs5530.c
new file mode 100644
index 000000000..bb68f7b2e
--- /dev/null
+++ b/drivers/ide/cs5530.c
@@ -0,0 +1,361 @@
+/*
+ * linux/drivers/block/cs5530.c Version 0.5 Feb 13, 2000
+ *
+ * Copyright (C) 2000 Mark Lord <mlord@pobox.com>
+ * May be copied or modified under the terms of the GNU General Public License
+ *
+ * Development of this chipset driver was funded
+ * by the nice folks at National Semiconductor.
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/ide.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include "ide_modes.h"
+
+#define DISPLAY_CS5530_TIMINGS
+
+#if defined(DISPLAY_CS5530_TIMINGS) && defined(CONFIG_PROC_FS)
+#include <linux/stat.h>
+#include <linux/proc_fs.h>
+
+static int cs5530_get_info(char *, char **, off_t, int);
+extern int (*cs5530_display_info)(char *, char **, off_t, int); /* ide-proc.c */
+extern char *ide_media_verbose(ide_drive_t *);
+static struct pci_dev *bmide_dev;
+
+static int cs5530_get_info (char *buffer, char **addr, off_t offset, int count)
+{
+ char *p = buffer;
+ u32 bibma = bmide_dev->resource[4].start;
+ u8 c0 = 0, c1 = 0;
+
+ /*
+ * at that point bibma+0x2 et bibma+0xa are byte registers
+ * to investigate:
+ */
+
+ c0 = inb_p((unsigned short)bibma + 0x02);
+ c1 = inb_p((unsigned short)bibma + 0x0a);
+
+ p += sprintf(p, "\n Cyrix 5530 Chipset.\n");
+ p += sprintf(p, "--------------- Primary Channel ---------------- Secondary Channel -------------\n");
+ p += sprintf(p, " %sabled %sabled\n",
+ (c0&0x80) ? "dis" : " en",
+ (c1&0x80) ? "dis" : " en");
+ p += sprintf(p, "--------------- drive0 --------- drive1 -------- drive0 ---------- drive1 ------\n");
+ p += sprintf(p, "DMA enabled: %s %s %s %s\n",
+ (c0&0x20) ? "yes" : "no ", (c0&0x40) ? "yes" : "no ",
+ (c1&0x20) ? "yes" : "no ", (c1&0x40) ? "yes" : "no " );
+
+ p += sprintf(p, "UDMA\n");
+ p += sprintf(p, "DMA\n");
+ p += sprintf(p, "PIO\n");
+
+ return p-buffer;
+}
+#endif /* DISPLAY_CS5530_TIMINGS && CONFIG_PROC_FS */
+
+byte cs5530_proc = 0;
+
+extern char *ide_xfer_verbose (byte xfer_rate);
+
+/*
+ * Set a new transfer mode at the drive
+ */
+int cs5530_set_xfer_mode (ide_drive_t *drive, byte mode)
+{
+ int error = 0;
+
+ printk("%s: cs5530_set_xfer_mode(%s)\n", drive->name, ide_xfer_verbose(mode));
+ error = ide_config_drive_speed(drive, mode);
+
+ return error;
+}
+
+/*
+ * Here are the standard PIO mode 0-4 timings for each "format".
+ * Format-0 uses fast data reg timings, with slower command reg timings.
+ * Format-1 uses fast timings for all registers, but won't work with all drives.
+ */
+static unsigned int cs5530_pio_timings[2][5] =
+ {{0x00009172, 0x00012171, 0x00020080, 0x00032010, 0x00040010},
+ {0xd1329172, 0x71212171, 0x30200080, 0x20102010, 0x00100010}};
+
+/*
+ * After chip reset, the PIO timings are set to 0x0000e132, which is not valid.
+ */
+#define CS5530_BAD_PIO(timings) (((timings)&~0x80000000)==0x0000e132)
+#define CS5530_BASEREG(hwif) (((hwif)->dma_base & ~0xf) + ((hwif)->channel ? 0x30 : 0x20))
+
+/*
+ * cs5530_tuneproc() handles selection/setting of PIO modes
+ * for both the chipset and drive.
+ *
+ * The ide_init_cs5530() routine guarantees that all drives
+ * will have valid default PIO timings set up before we get here.
+ */
+static void cs5530_tuneproc (ide_drive_t *drive, byte pio) /* pio=255 means "autotune" */
+{
+ ide_hwif_t *hwif = HWIF(drive);
+ unsigned int format, basereg = CS5530_BASEREG(hwif);
+ static byte modes[5] = {XFER_PIO_0, XFER_PIO_1, XFER_PIO_2, XFER_PIO_3, XFER_PIO_4};
+
+ pio = ide_get_best_pio_mode(drive, pio, 4, NULL);
+ if (!cs5530_set_xfer_mode(drive, modes[pio])) {
+ format = (inl(basereg+4) >> 31) & 1;
+ outl(cs5530_pio_timings[format][pio], basereg+(drive->select.b.unit<<3));
+ }
+}
+
+/*
+ * cs5530_config_dma() handles selection/setting of DMA/UDMA modes
+ * for both the chipset and drive.
+ */
+static int cs5530_config_dma (ide_drive_t *drive)
+{
+ int udma_ok = 1, mode = 0;
+ ide_hwif_t *hwif = HWIF(drive);
+ int unit = drive->select.b.unit;
+ ide_drive_t *mate = &hwif->drives[unit^1];
+ struct hd_driveid *id = drive->id;
+ unsigned int basereg, reg, timings;
+
+
+ /*
+ * Default to DMA-off in case we run into trouble here.
+ */
+ (void)hwif->dmaproc(ide_dma_off_quietly, drive); /* turn off DMA while we fiddle */
+ outb(inb(hwif->dma_base+2)&~(unit?0x40:0x20), hwif->dma_base+2); /* clear DMA_capable bit */
+
+ /*
+ * The CS5530 specifies that two drives sharing a cable cannot
+ * mix UDMA/MDMA. It has to be one or the other, for the pair,
+ * though different timings can still be chosen for each drive.
+ * We could set the appropriate timing bits on the fly,
+ * but that might be a bit confusing. So, for now we statically
+ * handle this requirement by looking at our mate drive to see
+ * what it is capable of, before choosing a mode for our own drive.
+ */
+ if (mate->present) {
+ struct hd_driveid *mateid = mate->id;
+ if (mateid && (mateid->capability & 1) && !hwif->dmaproc(ide_dma_bad_drive, mate)) {
+ if ((mateid->field_valid & 4) && (mateid->dma_ultra & 7))
+ udma_ok = 1;
+ else if ((mateid->field_valid & 2) && (mateid->dma_mword & 7))
+ udma_ok = 0;
+ else
+ udma_ok = 1;
+ }
+ }
+
+ /*
+ * Now see what the current drive is capable of,
+ * selecting UDMA only if the mate said it was ok.
+ */
+ if (id && (id->capability & 1) && hwif->autodma && !hwif->dmaproc(ide_dma_bad_drive, drive)) {
+ if (udma_ok && (id->field_valid & 4) && (id->dma_ultra & 7)) {
+ if (id->dma_ultra & 4)
+ mode = XFER_UDMA_2;
+ else if (id->dma_ultra & 2)
+ mode = XFER_UDMA_1;
+ else if (id->dma_ultra & 1)
+ mode = XFER_UDMA_0;
+ }
+ if (!mode && (id->field_valid & 2) && (id->dma_mword & 7)) {
+ if (id->dma_mword & 4)
+ mode = XFER_MW_DMA_2;
+ else if (id->dma_mword & 2)
+ mode = XFER_MW_DMA_1;
+ else if (id->dma_mword & 1)
+ mode = XFER_MW_DMA_0;
+ }
+ }
+
+ /*
+ * Tell the drive to switch to the new mode; abort on failure.
+ */
+ if (!mode || cs5530_set_xfer_mode(drive, mode))
+ return 1; /* failure */
+
+ /*
+ * Now tune the chipset to match the drive:
+ */
+ switch (mode) {
+ case XFER_UDMA_0: timings = 0x00921250; break;
+ case XFER_UDMA_1: timings = 0x00911140; break;
+ case XFER_UDMA_2: timings = 0x00911030; break;
+ case XFER_MW_DMA_0: timings = 0x00077771; break;
+ case XFER_MW_DMA_1: timings = 0x00012121; break;
+ case XFER_MW_DMA_2: timings = 0x00002020; break;
+ default:
+ printk("%s: cs5530_config_dma: huh? mode=%02x\n", drive->name, mode);
+ return 1; /* failure */
+ }
+ basereg = CS5530_BASEREG(hwif);
+ reg = inl(basereg+4); /* get drive0 config register */
+ timings |= reg & 0x80000000; /* preserve PIO format bit */
+ if (unit == 0) { /* are we configuring drive0? */
+ outl(timings, basereg+4); /* write drive0 config register */
+ } else {
+ if (timings & 0x00100000)
+ reg |= 0x00100000; /* enable UDMA timings for both drives */
+ else
+ reg &= ~0x00100000; /* disable UDMA timings for both drives */
+ outl(reg, basereg+4); /* write drive0 config register */
+ outl(timings, basereg+12); /* write drive1 config register */
+ }
+ outb(inb(hwif->dma_base+2)|(unit?0x40:0x20), hwif->dma_base+2); /* set DMA_capable bit */
+
+ /*
+ * Finally, turn DMA on in software, and exit.
+ */
+ return hwif->dmaproc(ide_dma_on, drive); /* success */
+}
+
+/*
+ * This is a CS5530-specific wrapper for the standard ide_dmaproc().
+ * We need it for our custom "ide_dma_check" function.
+ * All other requests are forwarded to the standard ide_dmaproc().
+ */
+int cs5530_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
+{
+ switch (func) {
+ case ide_dma_check:
+ return cs5530_config_dma(drive);
+ default:
+ break;
+ }
+ /* Other cases are done by generic IDE-DMA code. */
+ return ide_dmaproc(func, drive);
+}
+
+/*
+ * Initialize the cs5530 bridge for reliable IDE DMA operation.
+ */
+unsigned int __init pci_init_cs5530 (struct pci_dev *dev, const char *name)
+{
+ struct pci_dev *master_0 = NULL, *cs5530_0 = NULL;
+ unsigned short pcicmd = 0;
+ unsigned long flags;
+
+ pci_for_each_dev (dev) {
+ if (dev->vendor == PCI_VENDOR_ID_CYRIX) {
+ switch (dev->device) {
+ case PCI_DEVICE_ID_CYRIX_PCI_MASTER:
+ master_0 = dev;
+ break;
+ case PCI_DEVICE_ID_CYRIX_5530_LEGACY:
+ cs5530_0 = dev;
+ break;
+ }
+ }
+ }
+ if (!master_0) {
+ printk("%s: unable to locate PCI MASTER function\n", name);
+ return 0;
+ }
+ if (!cs5530_0) {
+ printk("%s: unable to locate CS5530 LEGACY function\n", name);
+ return 0;
+ }
+
+ save_flags(flags);
+ cli(); /* all CPUs (there should only be one CPU with this chipset) */
+
+ /*
+ * Enable BusMaster and MemoryWriteAndInvalidate for the cs5530:
+ * --> OR 0x14 into 16-bit PCI COMMAND reg of function 0 of the cs5530
+ */
+ pci_read_config_word (cs5530_0, PCI_COMMAND, &pcicmd);
+ pci_write_config_word(cs5530_0, PCI_COMMAND, pcicmd | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE);
+
+ /*
+ * Set PCI CacheLineSize to 16-bytes:
+ * --> Write 0x04 into 8-bit PCI CACHELINESIZE reg of function 0 of the cs5530
+ */
+ pci_write_config_byte(cs5530_0, PCI_CACHE_LINE_SIZE, 0x04);
+
+ /*
+ * Disable trapping of UDMA register accesses (Win98 hack):
+ * --> Write 0x5006 into 16-bit reg at offset 0xd0 of function 0 of the cs5530
+ */
+ pci_write_config_word(cs5530_0, 0xd0, 0x5006);
+
+ /*
+ * Bit-1 at 0x40 enables MemoryWriteAndInvalidate on internal X-bus:
+ * The other settings are what is necessary to get the register
+ * into a sane state for IDE DMA operation.
+ */
+ pci_write_config_byte(master_0, 0x40, 0x1e);
+
+ /*
+ * Set max PCI burst size (16-bytes seems to work best):
+ * 16bytes: set bit-1 at 0x41 (reg value of 0x16)
+ * all others: clear bit-1 at 0x41, and do:
+ * 128bytes: OR 0x00 at 0x41
+ * 256bytes: OR 0x04 at 0x41
+ * 512bytes: OR 0x08 at 0x41
+ * 1024bytes: OR 0x0c at 0x41
+ */
+ pci_write_config_byte(master_0, 0x41, 0x14);
+
+ /*
+ * These settings are necessary to get the chip
+ * into a sane state for IDE DMA operation.
+ */
+ pci_write_config_byte(master_0, 0x42, 0x00);
+ pci_write_config_byte(master_0, 0x43, 0xc1);
+
+ restore_flags(flags);
+
+#if defined(DISPLAY_CS5530_TIMINGS) && defined(CONFIG_PROC_FS)
+ cs5530_proc = 1;
+ bmide_dev = dev;
+ cs5530_display_info = &cs5530_get_info;
+#endif /* DISPLAY_CS5530_TIMINGS && CONFIG_PROC_FS */
+
+ return 0;
+}
+
+/*
+ * This gets invoked by the IDE driver once for each channel,
+ * and performs channel-specific pre-initialization before drive probing.
+ */
+void __init ide_init_cs5530 (ide_hwif_t *hwif)
+{
+ if (hwif->mate)
+ hwif->serialized = hwif->mate->serialized = 1;
+ if (!hwif->dma_base) {
+ hwif->autodma = 0;
+ } else {
+ unsigned int basereg, d0_timings;
+
+ hwif->dmaproc = &cs5530_dmaproc;
+ hwif->tuneproc = &cs5530_tuneproc;
+ basereg = CS5530_BASEREG(hwif);
+ d0_timings = inl(basereg+0);
+ if (CS5530_BAD_PIO(d0_timings)) { /* PIO timings not initialized? */
+ outl(cs5530_pio_timings[(d0_timings>>31)&1][0], basereg+0);
+ if (!hwif->drives[0].autotune)
+ hwif->drives[0].autotune = 1; /* needs autotuning later */
+ }
+ if (CS5530_BAD_PIO(inl(basereg+8))) { /* PIO timings not initialized? */
+ outl(cs5530_pio_timings[(d0_timings>>31)&1][0], basereg+8);
+ if (!hwif->drives[1].autotune)
+ hwif->drives[1].autotune = 1; /* needs autotuning later */
+ }
+ }
+}
diff --git a/drivers/ide/cy82c693.c b/drivers/ide/cy82c693.c
new file mode 100644
index 000000000..cfff0381c
--- /dev/null
+++ b/drivers/ide/cy82c693.c
@@ -0,0 +1,441 @@
+/*
+ * linux/drivers/block/cy82c693.c Version 0.34 Dec. 13, 1999
+ *
+ * Copyright (C) 1998-99 Andreas S. Krebs (akrebs@altavista.net), Maintainer
+ * Copyright (C) 1998-99 Andre Hedrick, Integrater
+ *
+ * CYPRESS CY82C693 chipset IDE controller
+ *
+ * The CY82C693 chipset is used on Digital's PC-Alpha 164SX boards.
+ * Writting the driver was quite simple, since most of the job is
+ * done by the generic pci-ide support.
+ * The hard part was finding the CY82C693's datasheet on Cypress's
+ * web page :-(. But Altavista solved this problem :-).
+ *
+ *
+ * Notes:
+ * - I recently got a 16.8G IBM DTTA, so I was able to test it with
+ * a large and fast disk - the results look great, so I'd say the
+ * driver is working fine :-)
+ * hdparm -t reports 8.17 MB/sec at about 6% CPU usage for the DTTA
+ * - this is my first linux driver, so there's probably a lot of room
+ * for optimizations and bug fixing, so feel free to do it.
+ * - use idebus=xx parameter to set PCI bus speed - needed to calc
+ * timings for PIO modes (default will be 40)
+ * - if using PIO mode it's a good idea to set the PIO mode and
+ * 32-bit I/O support (if possible), e.g. hdparm -p2 -c1 /dev/hda
+ * - I had some problems with my IBM DHEA with PIO modes < 2
+ * (lost interrupts) ?????
+ * - first tests with DMA look okay, they seem to work, but there is a
+ * problem with sound - the BusMaster IDE TimeOut should fixed this
+ *
+ *
+ * History:
+ * AMH@1999-08-24: v0.34 init_cy82c693_chip moved to pci_init_cy82c693
+ * ASK@1999-01-23: v0.33 made a few minor code clean ups
+ * removed DMA clock speed setting by default
+ * added boot message
+ * ASK@1998-11-01: v0.32 added support to set BusMaster IDE TimeOut
+ * added support to set DMA Controller Clock Speed
+ * ASK@1998-10-31: v0.31 fixed problem with setting to high DMA modes on some drive
+ * ASK@1998-10-29: v0.3 added support to set DMA modes
+ * ASK@1998-10-28: v0.2 added support to set PIO modes
+ * ASK@1998-10-27: v0.1 first version - chipset detection
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+
+#include "ide_modes.h"
+
+/* the current version */
+#define CY82_VERSION "CY82C693U driver v0.34 99-13-12 Andreas S. Krebs (akrebs@altavista.net)"
+
+/*
+ * The following are used to debug the driver.
+ */
+#define CY82C693_DEBUG_LOGS 0
+#define CY82C693_DEBUG_INFO 0
+
+/* define CY82C693_SETDMA_CLOCK to set DMA Controller Clock Speed to ATCLK */
+#undef CY82C693_SETDMA_CLOCK
+
+/*
+ * note: the value for busmaster timeout is tricky and i got it by trial and error !
+ * using a to low value will cause DMA timeouts and drop IDE performance
+ * using a to high value will cause audio playback to scatter
+ * if you know a better value or how to calc it, please let me know
+ */
+#define BUSMASTER_TIMEOUT 0x50 /* twice the value written in cy82c693ub datasheet */
+/*
+ * the value above was tested on my machine and it seems to work okay
+ */
+
+/* here are the offset definitions for the registers */
+#define CY82_IDE_CMDREG 0x04
+#define CY82_IDE_ADDRSETUP 0x48
+#define CY82_IDE_MASTER_IOR 0x4C
+#define CY82_IDE_MASTER_IOW 0x4D
+#define CY82_IDE_SLAVE_IOR 0x4E
+#define CY82_IDE_SLAVE_IOW 0x4F
+#define CY82_IDE_MASTER_8BIT 0x50
+#define CY82_IDE_SLAVE_8BIT 0x51
+
+#define CY82_INDEX_PORT 0x22
+#define CY82_DATA_PORT 0x23
+
+#define CY82_INDEX_CTRLREG1 0x01
+#define CY82_INDEX_CHANNEL0 0x30
+#define CY82_INDEX_CHANNEL1 0x31
+#define CY82_INDEX_TIMEOUT 0x32
+
+/* the max PIO mode - from datasheet */
+#define CY82C693_MAX_PIO 4
+
+/* the min and max PCI bus speed in MHz - from datasheet */
+#define CY82C963_MIN_BUS_SPEED 25
+#define CY82C963_MAX_BUS_SPEED 33
+
+/* the struct for the PIO mode timings */
+typedef struct pio_clocks_s {
+ byte address_time; /* Address setup (clocks) */
+ byte time_16r; /* clocks for 16bit IOR (0xF0=Active/data, 0x0F=Recovery) */
+ byte time_16w; /* clocks for 16bit IOW (0xF0=Active/data, 0x0F=Recovery) */
+ byte time_8; /* clocks for 8bit (0xF0=Active/data, 0x0F=Recovery) */
+} pio_clocks_t;
+
+/*
+ * calc clocks using bus_speed
+ * returns (rounded up) time in bus clocks for time in ns
+ */
+static int calc_clk (int time, int bus_speed)
+{
+ int clocks;
+
+ clocks = (time*bus_speed+999)/1000 -1;
+
+ if (clocks < 0)
+ clocks = 0;
+
+ if (clocks > 0x0F)
+ clocks = 0x0F;
+
+ return clocks;
+}
+
+/*
+ * compute the values for the clock registers for PIO
+ * mode and pci_clk [MHz] speed
+ *
+ * NOTE: for mode 0,1 and 2 drives 8-bit IDE command control registers are used
+ * for mode 3 and 4 drives 8 and 16-bit timings are the same
+ *
+ */
+static void compute_clocks (byte pio, pio_clocks_t *p_pclk)
+{
+ int clk1, clk2;
+ int bus_speed;
+
+ bus_speed = ide_system_bus_speed(); /* get speed of PCI bus */
+ /* we don't check against CY82C693's min and max speed,
+ * so you can play with the idebus=xx parameter
+ */
+
+ if (pio > CY82C693_MAX_PIO)
+ pio = CY82C693_MAX_PIO;
+
+ /* let's calc the address setup time clocks */
+ p_pclk->address_time = (byte)calc_clk(ide_pio_timings[pio].setup_time, bus_speed);
+
+ /* let's calc the active and recovery time clocks */
+ clk1 = calc_clk(ide_pio_timings[pio].active_time, bus_speed);
+
+ /* calc recovery timing */
+ clk2 = ide_pio_timings[pio].cycle_time -
+ ide_pio_timings[pio].active_time -
+ ide_pio_timings[pio].setup_time;
+
+ clk2 = calc_clk(clk2, bus_speed);
+
+ clk1 = (clk1<<4)|clk2; /* combine active and recovery clocks */
+
+ /* note: we use the same values for 16bit IOR and IOW
+ * those are all the same, since I don't have other
+ * timings than those from ide_modes.h
+ */
+
+ p_pclk->time_16r = (byte)clk1;
+ p_pclk->time_16w = (byte)clk1;
+
+ /* what are good values for 8bit ?? */
+ p_pclk->time_8 = (byte)clk1;
+}
+
+/*
+ * set DMA mode a specific channel for CY82C693
+ */
+static void cy82c693_dma_enable (ide_drive_t *drive, int mode, int single)
+{
+ byte index;
+ byte data;
+
+ if (mode>2) /* make sure we set a valid mode */
+ mode = 2;
+
+ if (mode > drive->id->tDMA) /* to be absolutly sure we have a valid mode */
+ mode = drive->id->tDMA;
+
+ index = (HWIF(drive)->channel==0) ? CY82_INDEX_CHANNEL0 : CY82_INDEX_CHANNEL1;
+
+#if CY82C693_DEBUG_LOGS
+ /* for debug let's show the previous values */
+
+ OUT_BYTE(index, CY82_INDEX_PORT);
+ data = IN_BYTE(CY82_DATA_PORT);
+
+ printk (KERN_INFO "%s (ch=%d, dev=%d): DMA mode is %d (single=%d)\n", drive->name, HWIF(drive)->channel, drive->select.b.unit, (data&0x3), ((data>>2)&1));
+#endif /* CY82C693_DEBUG_LOGS */
+
+ data = (byte)mode|(byte)(single<<2);
+
+ OUT_BYTE(index, CY82_INDEX_PORT);
+ OUT_BYTE(data, CY82_DATA_PORT);
+
+#if CY82C693_DEBUG_INFO
+ printk (KERN_INFO "%s (ch=%d, dev=%d): set DMA mode to %d (single=%d)\n", drive->name, HWIF(drive)->channel, drive->select.b.unit, mode, single);
+#endif /* CY82C693_DEBUG_INFO */
+
+ /*
+ * note: below we set the value for Bus Master IDE TimeOut Register
+ * I'm not absolutly sure what this does, but it solved my problem
+ * with IDE DMA and sound, so I now can play sound and work with
+ * my IDE driver at the same time :-)
+ *
+ * If you know the correct (best) value for this register please
+ * let me know - ASK
+ */
+
+ data = BUSMASTER_TIMEOUT;
+ OUT_BYTE(CY82_INDEX_TIMEOUT, CY82_INDEX_PORT);
+ OUT_BYTE(data, CY82_DATA_PORT);
+
+#if CY82C693_DEBUG_INFO
+ printk (KERN_INFO "%s: Set IDE Bus Master TimeOut Register to 0x%X\n", drive->name, data);
+#endif /* CY82C693_DEBUG_INFO */
+}
+
+/*
+ * used to set DMA mode for CY82C693 (single and multi modes)
+ */
+static int cy82c693_dmaproc(ide_dma_action_t func, ide_drive_t *drive)
+{
+ /*
+ * if the function is dma on, set dma mode for drive everything
+ * else is done by the defaul func
+ */
+ if (func == ide_dma_on) {
+ struct hd_driveid *id = drive->id;
+
+#if CY82C693_DEBUG_INFO
+ printk (KERN_INFO "dma_on: %s\n", drive->name);
+#endif /* CY82C693_DEBUG_INFO */
+
+ if (id != NULL) {
+ /* Enable DMA on any drive that has DMA (multi or single) enabled */
+ if (id->field_valid & 2) { /* regular DMA */
+ int mmode, smode;
+
+ mmode = id->dma_mword & (id->dma_mword >> 8);
+ smode = id->dma_1word & (id->dma_1word >> 8);
+
+ if (mmode != 0)
+ cy82c693_dma_enable(drive, (mmode >> 1), 0); /* enable multi */
+ else if (smode != 0)
+ cy82c693_dma_enable(drive, (smode >> 1), 1); /* enable single */
+ }
+ }
+ }
+ return ide_dmaproc(func, drive);
+}
+
+/*
+ * tune ide drive - set PIO mode
+ */
+static void cy82c693_tune_drive (ide_drive_t *drive, byte pio)
+{
+ ide_hwif_t *hwif = HWIF(drive);
+ struct pci_dev *dev = hwif->pci_dev;
+ pio_clocks_t pclk;
+ unsigned int addrCtrl;
+
+ /* select primary or secondary channel */
+ if (hwif->index > 0) { /* drive is on the secondary channel */
+ dev = pci_find_slot(dev->bus->number, dev->devfn+1);
+ if (!dev) {
+ printk(KERN_ERR "%s: tune_drive: Cannot find secondary interface!\n", drive->name);
+ return;
+ }
+ }
+
+#if CY82C693_DEBUG_LOGS
+ /* for debug let's show the register values */
+
+ if (drive->select.b.unit == 0) {
+ /*
+ * get master drive registers
+ * address setup control register
+ * is 32 bit !!!
+ */
+ pci_read_config_dword(dev, CY82_IDE_ADDRSETUP, &addrCtrl);
+ addrCtrl &= 0x0F;
+
+ /* now let's get the remaining registers */
+ pci_read_config_byte(dev, CY82_IDE_MASTER_IOR, &pclk.time_16r);
+ pci_read_config_byte(dev, CY82_IDE_MASTER_IOW, &pclk.time_16w);
+ pci_read_config_byte(dev, CY82_IDE_MASTER_8BIT, &pclk.time_8);
+ } else {
+ /*
+ * set slave drive registers
+ * address setup control register
+ * is 32 bit !!!
+ */
+ pci_read_config_dword(dev, CY82_IDE_ADDRSETUP, &addrCtrl);
+
+ addrCtrl &= 0xF0;
+ addrCtrl >>= 4;
+
+ /* now let's get the remaining registers */
+ pci_read_config_byte(dev, CY82_IDE_SLAVE_IOR, &pclk.time_16r);
+ pci_read_config_byte(dev, CY82_IDE_SLAVE_IOW, &pclk.time_16w);
+ pci_read_config_byte(dev, CY82_IDE_SLAVE_8BIT, &pclk.time_8);
+ }
+
+ printk (KERN_INFO "%s (ch=%d, dev=%d): PIO timing is (addr=0x%X, ior=0x%X, iow=0x%X, 8bit=0x%X)\n", drive->name, hwif->channel, drive->select.b.unit, addrCtrl, pclk.time_16r, pclk.time_16w, pclk.time_8);
+#endif /* CY82C693_DEBUG_LOGS */
+
+ /* first let's calc the pio modes */
+ pio = ide_get_best_pio_mode(drive, pio, CY82C693_MAX_PIO, NULL);
+
+#if CY82C693_DEBUG_INFO
+ printk (KERN_INFO "%s: Selected PIO mode %d\n", drive->name, pio);
+#endif /* CY82C693_DEBUG_INFO */
+
+ compute_clocks(pio, &pclk); /* let's calc the values for this PIO mode */
+
+ /* now let's write the clocks registers */
+ if (drive->select.b.unit == 0) {
+ /*
+ * set master drive
+ * address setup control register
+ * is 32 bit !!!
+ */
+ pci_read_config_dword(dev, CY82_IDE_ADDRSETUP, &addrCtrl);
+
+ addrCtrl &= (~0xF);
+ addrCtrl |= (unsigned int)pclk.address_time;
+ pci_write_config_dword(dev, CY82_IDE_ADDRSETUP, addrCtrl);
+
+ /* now let's set the remaining registers */
+ pci_write_config_byte(dev, CY82_IDE_MASTER_IOR, pclk.time_16r);
+ pci_write_config_byte(dev, CY82_IDE_MASTER_IOW, pclk.time_16w);
+ pci_write_config_byte(dev, CY82_IDE_MASTER_8BIT, pclk.time_8);
+
+ addrCtrl &= 0xF;
+ } else {
+ /*
+ * set slave drive
+ * address setup control register
+ * is 32 bit !!!
+ */
+ pci_read_config_dword(dev, CY82_IDE_ADDRSETUP, &addrCtrl);
+
+ addrCtrl &= (~0xF0);
+ addrCtrl |= ((unsigned int)pclk.address_time<<4);
+ pci_write_config_dword(dev, CY82_IDE_ADDRSETUP, addrCtrl);
+
+ /* now let's set the remaining registers */
+ pci_write_config_byte(dev, CY82_IDE_SLAVE_IOR, pclk.time_16r);
+ pci_write_config_byte(dev, CY82_IDE_SLAVE_IOW, pclk.time_16w);
+ pci_write_config_byte(dev, CY82_IDE_SLAVE_8BIT, pclk.time_8);
+
+ addrCtrl >>= 4;
+ addrCtrl &= 0xF;
+ }
+
+#if CY82C693_DEBUG_INFO
+ printk (KERN_INFO "%s (ch=%d, dev=%d): set PIO timing to (addr=0x%X, ior=0x%X, iow=0x%X, 8bit=0x%X)\n", drive->name, hwif->channel, drive->select.b.unit, addrCtrl, pclk.time_16r, pclk.time_16w, pclk.time_8);
+#endif /* CY82C693_DEBUG_INFO */
+}
+
+/*
+ * this function is called during init and is used to setup the cy82c693 chip
+ */
+/*
+ * FIXME! "pci_init_cy82c693" really should replace
+ * the "init_cy82c693_chip", it is the correct location to tinker/setup
+ * the device prior to INIT.
+ */
+
+unsigned int __init pci_init_cy82c693(struct pci_dev *dev, const char *name)
+{
+#ifdef CY82C693_SETDMA_CLOCK
+ byte data;
+#endif /* CY82C693_SETDMA_CLOCK */
+
+ /* write info about this verion of the driver */
+ printk (KERN_INFO CY82_VERSION "\n");
+
+#ifdef CY82C693_SETDMA_CLOCK
+ /* okay let's set the DMA clock speed */
+
+ OUT_BYTE(CY82_INDEX_CTRLREG1, CY82_INDEX_PORT);
+ data = IN_BYTE(CY82_DATA_PORT);
+
+#if CY82C693_DEBUG_INFO
+ printk (KERN_INFO "%s: Peripheral Configuration Register: 0x%X\n", name, data);
+#endif /* CY82C693_DEBUG_INFO */
+
+ /*
+ * for some reason sometimes the DMA controller
+ * speed is set to ATCLK/2 ???? - we fix this here
+ *
+ * note: i don't know what causes this strange behaviour,
+ * but even changing the dma speed doesn't solve it :-(
+ * the ide performance is still only half the normal speed
+ *
+ * if anybody knows what goes wrong with my machine, please
+ * let me know - ASK
+ */
+
+ data |= 0x03;
+
+ OUT_BYTE(CY82_INDEX_CTRLREG1, CY82_INDEX_PORT);
+ OUT_BYTE(data, CY82_DATA_PORT);
+
+#if CY82C693_DEBUG_INFO
+ printk (KERN_INFO "%s: New Peripheral Configuration Register: 0x%X\n", name, data);
+#endif /* CY82C693_DEBUG_INFO */
+
+#endif /* CY82C693_SETDMA_CLOCK */
+ return 0;
+}
+
+/*
+ * the init function - called for each ide channel once
+ */
+void __init ide_init_cy82c693(ide_hwif_t *hwif)
+{
+ hwif->chipset = ide_cy82c693;
+ hwif->tuneproc = &cy82c693_tune_drive;
+ if (hwif->dma_base) {
+ hwif->dmaproc = &cy82c693_dmaproc;
+ } else {
+ hwif->drives[0].autotune = 1;
+ hwif->drives[1].autotune = 1;
+ }
+}
diff --git a/drivers/ide/dtc2278.c b/drivers/ide/dtc2278.c
new file mode 100644
index 000000000..d8838e111
--- /dev/null
+++ b/drivers/ide/dtc2278.c
@@ -0,0 +1,133 @@
+/*
+ * linux/drivers/block/dtc2278.c Version 0.02 Feb 10, 1996
+ *
+ * Copyright (C) 1996 Linus Torvalds & author (see below)
+ */
+
+#undef REALLY_SLOW_IO /* most systems can safely undef this */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+
+#include "ide_modes.h"
+
+/*
+ * Changing this #undef to #define may solve start up problems in some systems.
+ */
+#undef ALWAYS_SET_DTC2278_PIO_MODE
+
+/*
+ * From: andy@cercle.cts.com (Dyan Wile)
+ *
+ * Below is a patch for DTC-2278 - alike software-programmable controllers
+ * The code enables the secondary IDE controller and the PIO4 (3?) timings on
+ * the primary (EIDE). You may probably have to enable the 32-bit support to
+ * get the full speed. You better get the disk interrupts disabled ( hdparm -u0
+ * /dev/hd.. ) for the drives connected to the EIDE interface. (I get my
+ * filesystem corrupted with -u1, but under heavy disk load only :-)
+ *
+ * This card is now forced to use the "serialize" feature,
+ * and irq-unmasking is disallowed. If io_32bit is enabled,
+ * it must be done for BOTH drives on each interface.
+ *
+ * This code was written for the DTC2278E, but might work with any of these:
+ *
+ * DTC2278S has only a single IDE interface.
+ * DTC2278D has two IDE interfaces and is otherwise identical to the S version.
+ * DTC2278E also has serial ports and a printer port
+ * DTC2278EB: has onboard BIOS, and "works like a charm" -- Kent Bradford <kent@theory.caltech.edu>
+ *
+ * There may be a fourth controller type. The S and D versions use the
+ * Winbond chip, and I think the E version does also.
+ *
+ */
+
+static void sub22 (char b, char c)
+{
+ int i;
+
+ for(i = 0; i < 3; ++i) {
+ inb(0x3f6);
+ outb_p(b,0xb0);
+ inb(0x3f6);
+ outb_p(c,0xb4);
+ inb(0x3f6);
+ if(inb(0xb4) == c) {
+ outb_p(7,0xb0);
+ inb(0x3f6);
+ return; /* success */
+ }
+ }
+}
+
+static void tune_dtc2278 (ide_drive_t *drive, byte pio)
+{
+ unsigned long flags;
+
+ pio = ide_get_best_pio_mode(drive, pio, 4, NULL);
+
+ if (pio >= 3) {
+ save_flags(flags); /* all CPUs */
+ cli(); /* all CPUs */
+ /*
+ * This enables PIO mode4 (3?) on the first interface
+ */
+ sub22(1,0xc3);
+ sub22(0,0xa0);
+ restore_flags(flags); /* all CPUs */
+ } else {
+ /* we don't know how to set it back again.. */
+ }
+
+ /*
+ * 32bit I/O has to be enabled for *both* drives at the same time.
+ */
+ drive->io_32bit = 1;
+ HWIF(drive)->drives[!drive->select.b.unit].io_32bit = 1;
+}
+
+void __init init_dtc2278 (void)
+{
+ unsigned long flags;
+
+ __save_flags(flags); /* local CPU only */
+ __cli(); /* local CPU only */
+ /*
+ * This enables the second interface
+ */
+ outb_p(4,0xb0);
+ inb(0x3f6);
+ outb_p(0x20,0xb4);
+ inb(0x3f6);
+#ifdef ALWAYS_SET_DTC2278_PIO_MODE
+ /*
+ * This enables PIO mode4 (3?) on the first interface
+ * and may solve start-up problems for some people.
+ */
+ sub22(1,0xc3);
+ sub22(0,0xa0);
+#endif
+ __restore_flags(flags); /* local CPU only */
+
+ ide_hwifs[0].serialized = 1;
+ ide_hwifs[1].serialized = 1;
+ ide_hwifs[0].chipset = ide_dtc2278;
+ ide_hwifs[1].chipset = ide_dtc2278;
+ ide_hwifs[0].tuneproc = &tune_dtc2278;
+ ide_hwifs[0].drives[0].no_unmask = 1;
+ ide_hwifs[0].drives[1].no_unmask = 1;
+ ide_hwifs[1].drives[0].no_unmask = 1;
+ ide_hwifs[1].drives[1].no_unmask = 1;
+ ide_hwifs[0].mate = &ide_hwifs[1];
+ ide_hwifs[1].mate = &ide_hwifs[0];
+ ide_hwifs[1].channel = 1;
+}
diff --git a/drivers/ide/falconide.c b/drivers/ide/falconide.c
new file mode 100644
index 000000000..7bce07517
--- /dev/null
+++ b/drivers/ide/falconide.c
@@ -0,0 +1,66 @@
+/*
+ * linux/drivers/block/falconide.c -- Atari Falcon IDE Driver
+ *
+ * Created 12 Jul 1997 by Geert Uytterhoeven
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/ide.h>
+
+#include <asm/atarihw.h>
+#include <asm/atariints.h>
+#include <asm/atari_stdma.h>
+
+
+ /*
+ * Base of the IDE interface
+ */
+
+#define ATA_HD_BASE 0xfff00000
+
+ /*
+ * Offsets from the above base
+ */
+
+#define ATA_HD_DATA 0x00
+#define ATA_HD_ERROR 0x05 /* see err-bits */
+#define ATA_HD_NSECTOR 0x09 /* nr of sectors to read/write */
+#define ATA_HD_SECTOR 0x0d /* starting sector */
+#define ATA_HD_LCYL 0x11 /* starting cylinder */
+#define ATA_HD_HCYL 0x15 /* high byte of starting cyl */
+#define ATA_HD_SELECT 0x19 /* 101dhhhh , d=drive, hhhh=head */
+#define ATA_HD_STATUS 0x1d /* see status-bits */
+#define ATA_HD_CONTROL 0x39
+
+static int __init falconide_offsets[IDE_NR_PORTS] = {
+ ATA_HD_DATA, ATA_HD_ERROR, ATA_HD_NSECTOR, ATA_HD_SECTOR, ATA_HD_LCYL,
+ ATA_HD_HCYL, ATA_HD_SELECT, ATA_HD_STATUS, ATA_HD_CONTROL, -1
+};
+
+
+ /*
+ * Probe for a Falcon IDE interface
+ */
+
+void __init falconide_init(void)
+{
+ if (MACH_IS_ATARI && ATARIHW_PRESENT(IDE)) {
+ hw_regs_t hw;
+ int index;
+
+ ide_setup_ports(&hw, (ide_ioreg_t)ATA_HD_BASE, falconide_offsets,
+ 0, 0, NULL, IRQ_MFP_IDE);
+ index = ide_register_hw(&hw, NULL);
+
+ if (index != -1)
+ printk("ide%d: Falcon IDE interface\n", index);
+ }
+}
diff --git a/drivers/ide/gayle.c b/drivers/ide/gayle.c
new file mode 100644
index 000000000..29cceb20e
--- /dev/null
+++ b/drivers/ide/gayle.c
@@ -0,0 +1,169 @@
+/*
+ * linux/drivers/block/gayle.c -- Amiga Gayle IDE Driver
+ *
+ * Created 9 Jul 1997 by Geert Uytterhoeven
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/ide.h>
+
+#include <asm/amigahw.h>
+#include <asm/amigaints.h>
+
+
+ /*
+ * Bases of the IDE interfaces
+ */
+
+#define GAYLE_BASE_4000 0xdd2020 /* A4000/A4000T */
+#define GAYLE_BASE_1200 0xda0000 /* A1200/A600 */
+
+ /*
+ * Offsets from one of the above bases
+ */
+
+#define GAYLE_DATA 0x00
+#define GAYLE_ERROR 0x06 /* see err-bits */
+#define GAYLE_NSECTOR 0x0a /* nr of sectors to read/write */
+#define GAYLE_SECTOR 0x0e /* starting sector */
+#define GAYLE_LCYL 0x12 /* starting cylinder */
+#define GAYLE_HCYL 0x16 /* high byte of starting cyl */
+#define GAYLE_SELECT 0x1a /* 101dhhhh , d=drive, hhhh=head */
+#define GAYLE_STATUS 0x1e /* see status-bits */
+#define GAYLE_CONTROL 0x101a
+
+static int __init gayle_offsets[IDE_NR_PORTS] = {
+ GAYLE_DATA, GAYLE_ERROR, GAYLE_NSECTOR, GAYLE_SECTOR, GAYLE_LCYL,
+ GAYLE_HCYL, GAYLE_SELECT, GAYLE_STATUS, -1, -1
+};
+
+
+ /*
+ * These are at different offsets from the base
+ */
+
+#define GAYLE_IRQ_4000 0xdd3020 /* MSB = 1, Harddisk is source of */
+#define GAYLE_IRQ_1200 0xda9000 /* interrupt */
+
+
+ /*
+ * Offset of the secondary port for IDE doublers
+ * Note that GAYLE_CONTROL is NOT available then!
+ */
+
+#define GAYLE_NEXT_PORT 0x1000
+
+#ifndef CONFIG_BLK_DEV_IDEDOUBLER
+#define GAYLE_NUM_HWIFS 1
+#define GAYLE_NUM_PROBE_HWIFS GAYLE_NUM_HWIFS
+#define GAYLE_HAS_CONTROL_REG 1
+#else /* CONFIG_BLK_DEV_IDEDOUBLER */
+#define GAYLE_NUM_HWIFS 2
+#define GAYLE_NUM_PROBE_HWIFS (ide_doubler ? GAYLE_NUM_HWIFS : \
+ GAYLE_NUM_HWIFS-1)
+#define GAYLE_HAS_CONTROL_REG (!ide_doubler)
+int ide_doubler = 0; /* support IDE doublers? */
+#endif /* CONFIG_BLK_DEV_IDEDOUBLER */
+
+
+ /*
+ * Check and acknowledge the interrupt status
+ */
+
+static int gayle_ack_intr_a4000(ide_hwif_t *hwif)
+{
+ unsigned char ch;
+
+ ch = inb(hwif->io_ports[IDE_IRQ_OFFSET]);
+ if (!(ch & 0x80))
+ return 0;
+ return 1;
+}
+
+static int gayle_ack_intr_a1200(ide_hwif_t *hwif)
+{
+ unsigned char ch;
+
+ ch = inb(hwif->io_ports[IDE_IRQ_OFFSET]);
+ if (!(ch & 0x80))
+ return 0;
+ (void)inb(hwif->io_ports[IDE_STATUS_OFFSET]);
+ outb(0x7c | (ch & 0x03), hwif->io_ports[IDE_IRQ_OFFSET]);
+ return 1;
+}
+
+ /*
+ * Probe for a Gayle IDE interface (and optionally for an IDE doubler)
+ */
+
+void __init gayle_init(void)
+{
+ int a4000, i;
+
+ if (!MACH_IS_AMIGA)
+ return;
+
+ if (!(a4000 = AMIGAHW_PRESENT(A4000_IDE)) && !AMIGAHW_PRESENT(A1200_IDE))
+ return;
+
+ for (i = 0; i < GAYLE_NUM_PROBE_HWIFS; i++) {
+ ide_ioreg_t base, ctrlport, irqport;
+ ide_ack_intr_t *ack_intr;
+ hw_regs_t hw;
+ int index;
+
+ if (a4000) {
+ base = (ide_ioreg_t)ZTWO_VADDR(GAYLE_BASE_4000);
+ irqport = (ide_ioreg_t)ZTWO_VADDR(GAYLE_IRQ_4000);
+ ack_intr = gayle_ack_intr_a4000;
+ } else {
+ base = (ide_ioreg_t)ZTWO_VADDR(GAYLE_BASE_1200);
+ irqport = (ide_ioreg_t)ZTWO_VADDR(GAYLE_IRQ_1200);
+ ack_intr = gayle_ack_intr_a1200;
+ }
+
+ if (GAYLE_HAS_CONTROL_REG)
+ ctrlport = base + GAYLE_CONTROL;
+ else
+ ctrlport = 0;
+
+ base += i*GAYLE_NEXT_PORT;
+
+ ide_setup_ports(&hw, base, gayle_offsets,
+ ctrlport, irqport, ack_intr, IRQ_AMIGA_PORTS);
+
+ index = ide_register_hw(&hw, NULL);
+ if (index != -1) {
+ switch (i) {
+ case 0:
+ printk("ide%d: Gayle IDE interface (A%d style)\n", index,
+ a4000 ? 4000 : 1200);
+ break;
+#ifdef CONFIG_BLK_DEV_IDEDOUBLER
+ case 1:
+ printk("ide%d: IDE doubler\n", index);
+ break;
+#endif /* CONFIG_BLK_DEV_IDEDOUBLER */
+ }
+ }
+#if 1 /* TESTING */
+ if (i == 1) {
+ volatile u_short *addr = (u_short *)base;
+ u_short data;
+ printk("+++ Probing for IDE doubler... ");
+ *addr = 0xffff;
+ data = *addr;
+ printk("probe returned 0x%02x (PLEASE REPORT THIS!!)\n", data);
+ }
+#endif /* TESTING */
+ }
+}
diff --git a/drivers/ide/hd.c b/drivers/ide/hd.c
new file mode 100644
index 000000000..5520c17b0
--- /dev/null
+++ b/drivers/ide/hd.c
@@ -0,0 +1,883 @@
+/*
+ * linux/drivers/block/hd.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+/*
+ * This is the low-level hd interrupt support. It traverses the
+ * request-list, using interrupts to jump between functions. As
+ * all the functions are called within interrupts, we may not
+ * sleep. Special care is recommended.
+ *
+ * modified by Drew Eckhardt to check nr of hd's from the CMOS.
+ *
+ * Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug
+ * in the early extended-partition checks and added DM partitions
+ *
+ * IRQ-unmask, drive-id, multiple-mode, support for ">16 heads",
+ * and general streamlining by Mark Lord.
+ *
+ * Removed 99% of above. Use Mark's ide driver for those options.
+ * This is now a lightweight ST-506 driver. (Paul Gortmaker)
+ *
+ * Modified 1995 Russell King for ARM processor.
+ */
+
+/* Uncomment the following if you want verbose error reports. */
+/* #define VERBOSE_ERRORS */
+
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/fs.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/kernel.h>
+#include <linux/hdreg.h>
+#include <linux/genhd.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/mc146818rtc.h> /* CMOS defines */
+#include <linux/init.h>
+#include <linux/blkpg.h>
+
+#define REALLY_SLOW_IO
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+#define MAJOR_NR HD_MAJOR
+#include <linux/blk.h>
+
+#ifdef __arm__
+#undef HD_IRQ
+#endif
+#include <asm/irq.h>
+#ifdef __arm__
+#define HD_IRQ IRQ_HARDDISK
+#endif
+
+static int revalidate_hddisk(kdev_t, int);
+
+#define HD_DELAY 0
+
+#define MAX_ERRORS 16 /* Max read/write errors/sector */
+#define RESET_FREQ 8 /* Reset controller every 8th retry */
+#define RECAL_FREQ 4 /* Recalibrate every 4th retry */
+#define MAX_HD 2
+
+#define STAT_OK (READY_STAT|SEEK_STAT)
+#define OK_STATUS(s) (((s)&(STAT_OK|(BUSY_STAT|WRERR_STAT|ERR_STAT)))==STAT_OK)
+
+static void recal_intr(void);
+static void bad_rw_intr(void);
+
+static char recalibrate[MAX_HD];
+static char special_op[MAX_HD];
+static int access_count[MAX_HD];
+static char busy[MAX_HD];
+static DECLARE_WAIT_QUEUE_HEAD(busy_wait);
+
+static int reset;
+static int hd_error;
+
+#define SUBSECTOR(block) (CURRENT->current_nr_sectors > 0)
+
+/*
+ * This struct defines the HD's and their types.
+ */
+struct hd_i_struct {
+ unsigned int head,sect,cyl,wpcom,lzone,ctl;
+};
+
+#ifdef HD_TYPE
+static struct hd_i_struct hd_info[] = { HD_TYPE };
+static int NR_HD = ((sizeof (hd_info))/(sizeof (struct hd_i_struct)));
+#else
+static struct hd_i_struct hd_info[MAX_HD];
+static int NR_HD;
+#endif
+
+static struct hd_struct hd[MAX_HD<<6];
+static int hd_sizes[MAX_HD<<6];
+static int hd_blocksizes[MAX_HD<<6];
+static int hd_hardsectsizes[MAX_HD<<6];
+
+#if (HD_DELAY > 0)
+unsigned long last_req;
+
+unsigned long read_timer(void)
+{
+ unsigned long t, flags;
+ int i;
+
+ save_flags(flags);
+ cli();
+ t = jiffies * 11932;
+ outb_p(0, 0x43);
+ i = inb_p(0x40);
+ i |= inb(0x40) << 8;
+ restore_flags(flags);
+ return(t - i);
+}
+#endif
+
+void __init hd_setup(char *str, int *ints)
+{
+ int hdind = 0;
+
+ if (ints[0] != 3)
+ return;
+ if (hd_info[0].head != 0)
+ hdind=1;
+ hd_info[hdind].head = ints[2];
+ hd_info[hdind].sect = ints[3];
+ hd_info[hdind].cyl = ints[1];
+ hd_info[hdind].wpcom = 0;
+ hd_info[hdind].lzone = ints[1];
+ hd_info[hdind].ctl = (ints[2] > 8 ? 8 : 0);
+ NR_HD = hdind+1;
+}
+
+static void dump_status (const char *msg, unsigned int stat)
+{
+ unsigned long flags;
+ char devc;
+
+ devc = !QUEUE_EMPTY ? 'a' + DEVICE_NR(CURRENT->rq_dev) : '?';
+ save_flags (flags);
+ sti();
+#ifdef VERBOSE_ERRORS
+ printk("hd%c: %s: status=0x%02x { ", devc, msg, stat & 0xff);
+ if (stat & BUSY_STAT) printk("Busy ");
+ if (stat & READY_STAT) printk("DriveReady ");
+ if (stat & WRERR_STAT) printk("WriteFault ");
+ if (stat & SEEK_STAT) printk("SeekComplete ");
+ if (stat & DRQ_STAT) printk("DataRequest ");
+ if (stat & ECC_STAT) printk("CorrectedError ");
+ if (stat & INDEX_STAT) printk("Index ");
+ if (stat & ERR_STAT) printk("Error ");
+ printk("}\n");
+ if ((stat & ERR_STAT) == 0) {
+ hd_error = 0;
+ } else {
+ hd_error = inb(HD_ERROR);
+ printk("hd%c: %s: error=0x%02x { ", devc, msg, hd_error & 0xff);
+ if (hd_error & BBD_ERR) printk("BadSector ");
+ if (hd_error & ECC_ERR) printk("UncorrectableError ");
+ if (hd_error & ID_ERR) printk("SectorIdNotFound ");
+ if (hd_error & ABRT_ERR) printk("DriveStatusError ");
+ if (hd_error & TRK0_ERR) printk("TrackZeroNotFound ");
+ if (hd_error & MARK_ERR) printk("AddrMarkNotFound ");
+ printk("}");
+ if (hd_error & (BBD_ERR|ECC_ERR|ID_ERR|MARK_ERR)) {
+ printk(", CHS=%d/%d/%d", (inb(HD_HCYL)<<8) + inb(HD_LCYL),
+ inb(HD_CURRENT) & 0xf, inb(HD_SECTOR));
+ if (!QUEUE_EMPTY)
+ printk(", sector=%ld", CURRENT->sector);
+ }
+ printk("\n");
+ }
+#else
+ printk("hd%c: %s: status=0x%02x.\n", devc, msg, stat & 0xff);
+ if ((stat & ERR_STAT) == 0) {
+ hd_error = 0;
+ } else {
+ hd_error = inb(HD_ERROR);
+ printk("hd%c: %s: error=0x%02x.\n", devc, msg, hd_error & 0xff);
+ }
+#endif /* verbose errors */
+ restore_flags (flags);
+}
+
+void check_status(void)
+{
+ int i = inb_p(HD_STATUS);
+
+ if (!OK_STATUS(i)) {
+ dump_status("check_status", i);
+ bad_rw_intr();
+ }
+}
+
+static int controller_busy(void)
+{
+ int retries = 100000;
+ unsigned char status;
+
+ do {
+ status = inb_p(HD_STATUS);
+ } while ((status & BUSY_STAT) && --retries);
+ return status;
+}
+
+static int status_ok(void)
+{
+ unsigned char status = inb_p(HD_STATUS);
+
+ if (status & BUSY_STAT)
+ return 1; /* Ancient, but does it make sense??? */
+ if (status & WRERR_STAT)
+ return 0;
+ if (!(status & READY_STAT))
+ return 0;
+ if (!(status & SEEK_STAT))
+ return 0;
+ return 1;
+}
+
+static int controller_ready(unsigned int drive, unsigned int head)
+{
+ int retry = 100;
+
+ do {
+ if (controller_busy() & BUSY_STAT)
+ return 0;
+ outb_p(0xA0 | (drive<<4) | head, HD_CURRENT);
+ if (status_ok())
+ return 1;
+ } while (--retry);
+ return 0;
+}
+
+static void hd_out(unsigned int drive,unsigned int nsect,unsigned int sect,
+ unsigned int head,unsigned int cyl,unsigned int cmd,
+ void (*intr_addr)(void))
+{
+ unsigned short port;
+
+#if (HD_DELAY > 0)
+ while (read_timer() - last_req < HD_DELAY)
+ /* nothing */;
+#endif
+ if (reset)
+ return;
+ if (!controller_ready(drive, head)) {
+ reset = 1;
+ return;
+ }
+ SET_INTR(intr_addr);
+ outb_p(hd_info[drive].ctl,HD_CMD);
+ port=HD_DATA;
+ outb_p(hd_info[drive].wpcom>>2,++port);
+ outb_p(nsect,++port);
+ outb_p(sect,++port);
+ outb_p(cyl,++port);
+ outb_p(cyl>>8,++port);
+ outb_p(0xA0|(drive<<4)|head,++port);
+ outb_p(cmd,++port);
+}
+
+static void hd_request (void);
+
+static int drive_busy(void)
+{
+ unsigned int i;
+ unsigned char c;
+
+ for (i = 0; i < 500000 ; i++) {
+ c = inb_p(HD_STATUS);
+ if ((c & (BUSY_STAT | READY_STAT | SEEK_STAT)) == STAT_OK)
+ return 0;
+ }
+ dump_status("reset timed out", c);
+ return 1;
+}
+
+static void reset_controller(void)
+{
+ int i;
+
+ outb_p(4,HD_CMD);
+ for(i = 0; i < 1000; i++) barrier();
+ outb_p(hd_info[0].ctl & 0x0f,HD_CMD);
+ for(i = 0; i < 1000; i++) barrier();
+ if (drive_busy())
+ printk("hd: controller still busy\n");
+ else if ((hd_error = inb(HD_ERROR)) != 1)
+ printk("hd: controller reset failed: %02x\n",hd_error);
+}
+
+static void reset_hd(void)
+{
+ static int i;
+
+repeat:
+ if (reset) {
+ reset = 0;
+ i = -1;
+ reset_controller();
+ } else {
+ check_status();
+ if (reset)
+ goto repeat;
+ }
+ if (++i < NR_HD) {
+ special_op[i] = recalibrate[i] = 1;
+ hd_out(i,hd_info[i].sect,hd_info[i].sect,hd_info[i].head-1,
+ hd_info[i].cyl,WIN_SPECIFY,&reset_hd);
+ if (reset)
+ goto repeat;
+ } else
+ hd_request();
+}
+
+/*
+ * Ok, don't know what to do with the unexpected interrupts: on some machines
+ * doing a reset and a retry seems to result in an eternal loop. Right now I
+ * ignore it, and just set the timeout.
+ *
+ * On laptops (and "green" PCs), an unexpected interrupt occurs whenever the
+ * drive enters "idle", "standby", or "sleep" mode, so if the status looks
+ * "good", we just ignore the interrupt completely.
+ */
+void unexpected_hd_interrupt(void)
+{
+ unsigned int stat = inb_p(HD_STATUS);
+
+ if (stat & (BUSY_STAT|DRQ_STAT|ECC_STAT|ERR_STAT)) {
+ dump_status ("unexpected interrupt", stat);
+ SET_TIMER;
+ }
+}
+
+/*
+ * bad_rw_intr() now tries to be a bit smarter and does things
+ * according to the error returned by the controller.
+ * -Mika Liljeberg (liljeber@cs.Helsinki.FI)
+ */
+static void bad_rw_intr(void)
+{
+ int dev;
+
+ if (QUEUE_EMPTY)
+ return;
+ dev = DEVICE_NR(CURRENT->rq_dev);
+ if (++CURRENT->errors >= MAX_ERRORS || (hd_error & BBD_ERR)) {
+ end_request(0);
+ special_op[dev] = recalibrate[dev] = 1;
+ } else if (CURRENT->errors % RESET_FREQ == 0)
+ reset = 1;
+ else if ((hd_error & TRK0_ERR) || CURRENT->errors % RECAL_FREQ == 0)
+ special_op[dev] = recalibrate[dev] = 1;
+ /* Otherwise just retry */
+}
+
+static inline int wait_DRQ(void)
+{
+ int retries = 100000, stat;
+
+ while (--retries > 0)
+ if ((stat = inb_p(HD_STATUS)) & DRQ_STAT)
+ return 0;
+ dump_status("wait_DRQ", stat);
+ return -1;
+}
+
+static void read_intr(void)
+{
+ int i, retries = 100000;
+
+ do {
+ i = (unsigned) inb_p(HD_STATUS);
+ if (i & BUSY_STAT)
+ continue;
+ if (!OK_STATUS(i))
+ break;
+ if (i & DRQ_STAT)
+ goto ok_to_read;
+ } while (--retries > 0);
+ dump_status("read_intr", i);
+ bad_rw_intr();
+ hd_request();
+ return;
+ok_to_read:
+ insw(HD_DATA,CURRENT->buffer,256);
+ CURRENT->sector++;
+ CURRENT->buffer += 512;
+ CURRENT->errors = 0;
+ i = --CURRENT->nr_sectors;
+ --CURRENT->current_nr_sectors;
+#ifdef DEBUG
+ printk("hd%c: read: sector %ld, remaining = %ld, buffer=0x%08lx\n",
+ dev+'a', CURRENT->sector, CURRENT->nr_sectors,
+ (unsigned long) CURRENT->buffer+512));
+#endif
+ if (CURRENT->current_nr_sectors <= 0)
+ end_request(1);
+ if (i > 0) {
+ SET_INTR(&read_intr);
+ return;
+ }
+ (void) inb_p(HD_STATUS);
+#if (HD_DELAY > 0)
+ last_req = read_timer();
+#endif
+ if (!QUEUE_EMPTY)
+ hd_request();
+ return;
+}
+
+static void write_intr(void)
+{
+ int i;
+ int retries = 100000;
+
+ do {
+ i = (unsigned) inb_p(HD_STATUS);
+ if (i & BUSY_STAT)
+ continue;
+ if (!OK_STATUS(i))
+ break;
+ if ((CURRENT->nr_sectors <= 1) || (i & DRQ_STAT))
+ goto ok_to_write;
+ } while (--retries > 0);
+ dump_status("write_intr", i);
+ bad_rw_intr();
+ hd_request();
+ return;
+ok_to_write:
+ CURRENT->sector++;
+ i = --CURRENT->nr_sectors;
+ --CURRENT->current_nr_sectors;
+ CURRENT->buffer += 512;
+ if (!i || (CURRENT->bh && !SUBSECTOR(i)))
+ end_request(1);
+ if (i > 0) {
+ SET_INTR(&write_intr);
+ outsw(HD_DATA,CURRENT->buffer,256);
+ sti();
+ } else {
+#if (HD_DELAY > 0)
+ last_req = read_timer();
+#endif
+ hd_request();
+ }
+ return;
+}
+
+static void recal_intr(void)
+{
+ check_status();
+#if (HD_DELAY > 0)
+ last_req = read_timer();
+#endif
+ hd_request();
+}
+
+/*
+ * This is another of the error-routines I don't know what to do with. The
+ * best idea seems to just set reset, and start all over again.
+ */
+static void hd_times_out(void)
+{
+ unsigned int dev;
+
+ DEVICE_INTR = NULL;
+ if (QUEUE_EMPTY)
+ return;
+ disable_irq(HD_IRQ);
+ sti();
+ reset = 1;
+ dev = DEVICE_NR(CURRENT->rq_dev);
+ printk("hd%c: timeout\n", dev+'a');
+ if (++CURRENT->errors >= MAX_ERRORS) {
+#ifdef DEBUG
+ printk("hd%c: too many errors\n", dev+'a');
+#endif
+ end_request(0);
+ }
+ cli();
+ hd_request();
+ enable_irq(HD_IRQ);
+}
+
+int do_special_op (unsigned int dev)
+{
+ if (recalibrate[dev]) {
+ recalibrate[dev] = 0;
+ hd_out(dev,hd_info[dev].sect,0,0,0,WIN_RESTORE,&recal_intr);
+ return reset;
+ }
+ if (hd_info[dev].head > 16) {
+ printk ("hd%c: cannot handle device with more than 16 heads - giving up\n", dev+'a');
+ end_request(0);
+ }
+ special_op[dev] = 0;
+ return 1;
+}
+
+/*
+ * The driver enables interrupts as much as possible. In order to do this,
+ * (a) the device-interrupt is disabled before entering hd_request(),
+ * and (b) the timeout-interrupt is disabled before the sti().
+ *
+ * Interrupts are still masked (by default) whenever we are exchanging
+ * data/cmds with a drive, because some drives seem to have very poor
+ * tolerance for latency during I/O. The IDE driver has support to unmask
+ * interrupts for non-broken hardware, so use that driver if required.
+ */
+static void hd_request(void)
+{
+ unsigned int dev, block, nsect, sec, track, head, cyl;
+
+ if (!QUEUE_EMPTY && CURRENT->rq_status == RQ_INACTIVE) return;
+ if (DEVICE_INTR)
+ return;
+repeat:
+ timer_active &= ~(1<<HD_TIMER);
+ sti();
+ INIT_REQUEST;
+ if (reset) {
+ cli();
+ reset_hd();
+ return;
+ }
+ dev = MINOR(CURRENT->rq_dev);
+ block = CURRENT->sector;
+ nsect = CURRENT->nr_sectors;
+ if (dev >= (NR_HD<<6) || block >= hd[dev].nr_sects || ((block+nsect) > hd[dev].nr_sects)) {
+#ifdef DEBUG
+ if (dev >= (NR_HD<<6))
+ printk("hd: bad minor number: device=%s\n",
+ kdevname(CURRENT->rq_dev));
+ else
+ printk("hd%c: bad access: block=%d, count=%d\n",
+ (MINOR(CURRENT->rq_dev)>>6)+'a', block, nsect);
+#endif
+ end_request(0);
+ goto repeat;
+ }
+ block += hd[dev].start_sect;
+ dev >>= 6;
+ if (special_op[dev]) {
+ if (do_special_op(dev))
+ goto repeat;
+ return;
+ }
+ sec = block % hd_info[dev].sect + 1;
+ track = block / hd_info[dev].sect;
+ head = track % hd_info[dev].head;
+ cyl = track / hd_info[dev].head;
+#ifdef DEBUG
+ printk("hd%c: %sing: CHS=%d/%d/%d, sectors=%d, buffer=0x%08lx\n",
+ dev+'a', (CURRENT->cmd == READ)?"read":"writ",
+ cyl, head, sec, nsect, (unsigned long) CURRENT->buffer);
+#endif
+ if (CURRENT->cmd == READ) {
+ hd_out(dev,nsect,sec,head,cyl,WIN_READ,&read_intr);
+ if (reset)
+ goto repeat;
+ return;
+ }
+ if (CURRENT->cmd == WRITE) {
+ hd_out(dev,nsect,sec,head,cyl,WIN_WRITE,&write_intr);
+ if (reset)
+ goto repeat;
+ if (wait_DRQ()) {
+ bad_rw_intr();
+ goto repeat;
+ }
+ outsw(HD_DATA,CURRENT->buffer,256);
+ return;
+ }
+ panic("unknown hd-command");
+}
+
+static void do_hd_request (request_queue_t * q)
+{
+ disable_irq(HD_IRQ);
+ hd_request();
+ enable_irq(HD_IRQ);
+}
+
+static int hd_ioctl(struct inode * inode, struct file * file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct hd_geometry *loc = (struct hd_geometry *) arg;
+ int dev;
+
+ if ((!inode) || !(inode->i_rdev))
+ return -EINVAL;
+ dev = DEVICE_NR(inode->i_rdev);
+ if (dev >= NR_HD)
+ return -EINVAL;
+ switch (cmd) {
+ case HDIO_GETGEO:
+ {
+ struct hd_geometry g;
+ if (!loc) return -EINVAL;
+ g.heads = hd_info[dev].head;
+ g.sectors = hd_info[dev].sect;
+ g.cylinders = hd_info[dev].cyl;
+ g.start = hd[MINOR(inode->i_rdev)].start_sect;
+ return copy_to_user(loc, &g, sizeof g) ? -EFAULT : 0;
+ }
+
+ case BLKGETSIZE: /* Return device size */
+ if (!arg) return -EINVAL;
+ return put_user(hd[MINOR(inode->i_rdev)].nr_sects,
+ (long *) arg);
+
+ case BLKRRPART: /* Re-read partition tables */
+ if (!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+ return revalidate_hddisk(inode->i_rdev, 1);
+
+ case BLKROSET:
+ case BLKROGET:
+ case BLKRASET:
+ case BLKRAGET:
+ case BLKFLSBUF:
+ case BLKPG:
+ return blk_ioctl(inode->i_rdev, cmd, arg);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int hd_open(struct inode * inode, struct file * filp)
+{
+ int target;
+ target = DEVICE_NR(inode->i_rdev);
+
+ if (target >= NR_HD)
+ return -ENODEV;
+ while (busy[target])
+ sleep_on(&busy_wait);
+ access_count[target]++;
+ return 0;
+}
+
+/*
+ * Releasing a block device means we sync() it, so that it can safely
+ * be forgotten about...
+ */
+static int hd_release(struct inode * inode, struct file * file)
+{
+ int target = DEVICE_NR(inode->i_rdev);
+ access_count[target]--;
+ return 0;
+}
+
+extern struct block_device_operations hd_fops;
+
+static struct gendisk hd_gendisk = {
+ MAJOR_NR, /* Major number */
+ "hd", /* Major name */
+ 6, /* Bits to shift to get real from partition */
+ 1 << 6, /* Number of partitions per real */
+ hd, /* hd struct */
+ hd_sizes, /* block sizes */
+ 0, /* number */
+ NULL, /* internal use, not presently used */
+ NULL, /* next */
+ &hd_fops, /* file operations */
+};
+
+static void hd_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ void (*handler)(void) = DEVICE_INTR;
+
+ DEVICE_INTR = NULL;
+ timer_active &= ~(1<<HD_TIMER);
+ if (!handler)
+ handler = unexpected_hd_interrupt;
+ handler();
+ sti();
+}
+
+static struct block_device_operations hd_fops = {
+ open: hd_open,
+ release: hd_release,
+ ioctl: hd_ioctl,
+};
+
+/*
+ * This is the hard disk IRQ description. The SA_INTERRUPT in sa_flags
+ * means we run the IRQ-handler with interrupts disabled: this is bad for
+ * interrupt latency, but anything else has led to problems on some
+ * machines.
+ *
+ * We enable interrupts in some of the routines after making sure it's
+ * safe.
+ */
+static void hd_geninit(void)
+{
+ int drive;
+
+ for(drive=0; drive < (MAX_HD << 6); drive++) {
+ hd_blocksizes[drive] = 1024;
+ hd_hardsectsizes[drive] = 512;
+ }
+ blksize_size[MAJOR_NR] = hd_blocksizes;
+ hardsect_size[MAJOR_NR] = hd_hardsectsizes;
+
+#ifdef __i386__
+ if (!NR_HD) {
+ extern struct drive_info drive_info;
+ unsigned char *BIOS = (unsigned char *) &drive_info;
+ int cmos_disks;
+
+ for (drive=0 ; drive<2 ; drive++) {
+ hd_info[drive].cyl = *(unsigned short *) BIOS;
+ hd_info[drive].head = *(2+BIOS);
+ hd_info[drive].wpcom = *(unsigned short *) (5+BIOS);
+ hd_info[drive].ctl = *(8+BIOS);
+ hd_info[drive].lzone = *(unsigned short *) (12+BIOS);
+ hd_info[drive].sect = *(14+BIOS);
+#ifdef does_not_work_for_everybody_with_scsi_but_helps_ibm_vp
+ if (hd_info[drive].cyl && NR_HD == drive)
+ NR_HD++;
+#endif
+ BIOS += 16;
+ }
+
+ /*
+ We query CMOS about hard disks : it could be that
+ we have a SCSI/ESDI/etc controller that is BIOS
+ compatible with ST-506, and thus showing up in our
+ BIOS table, but not register compatible, and therefore
+ not present in CMOS.
+
+ Furthermore, we will assume that our ST-506 drives
+ <if any> are the primary drives in the system, and
+ the ones reflected as drive 1 or 2.
+
+ The first drive is stored in the high nibble of CMOS
+ byte 0x12, the second in the low nibble. This will be
+ either a 4 bit drive type or 0xf indicating use byte 0x19
+ for an 8 bit type, drive 1, 0x1a for drive 2 in CMOS.
+
+ Needless to say, a non-zero value means we have
+ an AT controller hard disk for that drive.
+
+
+ */
+
+ if ((cmos_disks = CMOS_READ(0x12)) & 0xf0) {
+ if (cmos_disks & 0x0f)
+ NR_HD = 2;
+ else
+ NR_HD = 1;
+ }
+ }
+#endif /* __i386__ */
+#ifdef __arm__
+ if (!NR_HD) {
+ /* We don't know anything about the drive. This means
+ * that you *MUST* specify the drive parameters to the
+ * kernel yourself.
+ */
+ printk("hd: no drives specified - use hd=cyl,head,sectors"
+ " on kernel command line\n");
+ }
+#endif
+
+ for (drive=0 ; drive < NR_HD ; drive++) {
+ printk ("hd%c: %ldMB, CHS=%d/%d/%d\n", drive+'a',
+ hd[drive<<6].nr_sects / 2048, hd_info[drive].cyl,
+ hd_info[drive].head, hd_info[drive].sect);
+ }
+ if (!NR_HD)
+ return;
+
+ if (request_irq(HD_IRQ, hd_interrupt, SA_INTERRUPT, "hd", NULL)) {
+ printk("hd: unable to get IRQ%d for the hard disk driver\n",
+ HD_IRQ);
+ NR_HD = 0;
+ return;
+ }
+ request_region(HD_DATA, 8, "hd");
+ request_region(HD_CMD, 1, "hd(cmd)");
+
+ hd_gendisk.nr_real = NR_HD;
+
+ for(drive=0; drive < NR_HD; drive++)
+ register_disk(&hd_gendisk, MKDEV(MAJOR_NR,drive<<6), 1<<6,
+ &hd_fops, hd_info[drive].head * hd_info[drive].sect *
+ hd_info[drive].cyl);
+}
+
+int __init hd_init(void)
+{
+ if (devfs_register_blkdev(MAJOR_NR,"hd",&hd_fops)) {
+ printk("hd: unable to get major %d for hard disk\n",MAJOR_NR);
+ return -1;
+ }
+ blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST);
+ read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read-ahead */
+ hd_gendisk.next = gendisk_head;
+ gendisk_head = &hd_gendisk;
+ timer_table[HD_TIMER].fn = hd_times_out;
+ hd_geninit();
+ return 0;
+}
+
+#define DEVICE_BUSY busy[target]
+#define USAGE access_count[target]
+#define CAPACITY (hd_info[target].head*hd_info[target].sect*hd_info[target].cyl)
+/* We assume that the BIOS parameters do not change, so the disk capacity
+ will not change */
+#undef MAYBE_REINIT
+#define GENDISK_STRUCT hd_gendisk
+
+/*
+ * This routine is called to flush all partitions and partition tables
+ * for a changed disk, and then re-read the new partition table.
+ * If we are revalidating a disk because of a media change, then we
+ * enter with usage == 0. If we are using an ioctl, we automatically have
+ * usage == 1 (we need an open channel to use an ioctl :-), so this
+ * is our limit.
+ */
+static int revalidate_hddisk(kdev_t dev, int maxusage)
+{
+ int target;
+ struct gendisk * gdev;
+ int max_p;
+ int start;
+ int i;
+ long flags;
+
+ target = DEVICE_NR(dev);
+ gdev = &GENDISK_STRUCT;
+
+ save_flags(flags);
+ cli();
+ if (DEVICE_BUSY || USAGE > maxusage) {
+ restore_flags(flags);
+ return -EBUSY;
+ }
+ DEVICE_BUSY = 1;
+ restore_flags(flags);
+
+ max_p = gdev->max_p;
+ start = target << gdev->minor_shift;
+
+ for (i=max_p - 1; i >=0 ; i--) {
+ int minor = start + i;
+ kdev_t devi = MKDEV(MAJOR_NR, minor);
+ struct super_block *sb = get_super(devi);
+
+ sync_dev(devi);
+ if (sb)
+ invalidate_inodes(sb);
+ invalidate_buffers(devi);
+ gdev->part[minor].start_sect = 0;
+ gdev->part[minor].nr_sects = 0;
+ }
+
+#ifdef MAYBE_REINIT
+ MAYBE_REINIT;
+#endif
+
+ grok_partitions(gdev, target, 1<<6, CAPACITY);
+
+ DEVICE_BUSY = 0;
+ wake_up(&busy_wait);
+ return 0;
+}
+
diff --git a/drivers/ide/hpt34x.c b/drivers/ide/hpt34x.c
new file mode 100644
index 000000000..425ce35a4
--- /dev/null
+++ b/drivers/ide/hpt34x.c
@@ -0,0 +1,419 @@
+/*
+ * linux/drivers/block/hpt34x.c Version 0.29 Feb. 10, 2000
+ *
+ * Copyright (C) 1998-2000 Andre Hedrick (andre@suse.com)
+ * May be copied or modified under the terms of the GNU General Public License
+ *
+ *
+ * 00:12.0 Unknown mass storage controller:
+ * Triones Technologies, Inc.
+ * Unknown device 0003 (rev 01)
+ *
+ * hde: UDMA 2 (0x0000 0x0002) (0x0000 0x0010)
+ * hdf: UDMA 2 (0x0002 0x0012) (0x0010 0x0030)
+ * hde: DMA 2 (0x0000 0x0002) (0x0000 0x0010)
+ * hdf: DMA 2 (0x0002 0x0012) (0x0010 0x0030)
+ * hdg: DMA 1 (0x0012 0x0052) (0x0030 0x0070)
+ * hdh: DMA 1 (0x0052 0x0252) (0x0070 0x00f0)
+ *
+ * ide-pci.c reference
+ *
+ * Since there are two cards that report almost identically,
+ * the only discernable difference is the values reported in pcicmd.
+ * Booting-BIOS card or HPT363 :: pcicmd == 0x07
+ * Non-bootable card or HPT343 :: pcicmd == 0x05
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include "ide_modes.h"
+
+#ifndef SPLIT_BYTE
+#define SPLIT_BYTE(B,H,L) ((H)=(B>>4), (L)=(B-((B>>4)<<4)))
+#endif
+
+#define HPT343_DEBUG_DRIVE_INFO 0
+
+#undef DISPLAY_HPT34X_TIMINGS
+
+#if defined(DISPLAY_HPT34X_TIMINGS) && defined(CONFIG_PROC_FS)
+#include <linux/stat.h>
+#include <linux/proc_fs.h>
+
+static int hpt34x_get_info(char *, char **, off_t, int);
+extern int (*hpt34x_display_info)(char *, char **, off_t, int); /* ide-proc.c */
+extern char *ide_media_verbose(ide_drive_t *);
+static struct pci_dev *bmide_dev;
+
+static int hpt34x_get_info (char *buffer, char **addr, off_t offset, int count)
+{
+ char *p = buffer;
+ u32 bibma = bmide_dev->resource[4].start;
+ u8 c0 = 0, c1 = 0;
+
+ /*
+ * at that point bibma+0x2 et bibma+0xa are byte registers
+ * to investigate:
+ */
+ c0 = inb_p((unsigned short)bibma + 0x02);
+ c1 = inb_p((unsigned short)bibma + 0x0a);
+
+ p += sprintf(p, "\n HPT34X Chipset.\n");
+ p += sprintf(p, "--------------- Primary Channel ---------------- Secondary Channel -------------\n");
+ p += sprintf(p, " %sabled %sabled\n",
+ (c0&0x80) ? "dis" : " en",
+ (c1&0x80) ? "dis" : " en");
+ p += sprintf(p, "--------------- drive0 --------- drive1 -------- drive0 ---------- drive1 ------\n");
+ p += sprintf(p, "DMA enabled: %s %s %s %s\n",
+ (c0&0x20) ? "yes" : "no ", (c0&0x40) ? "yes" : "no ",
+ (c1&0x20) ? "yes" : "no ", (c1&0x40) ? "yes" : "no " );
+
+ p += sprintf(p, "UDMA\n");
+ p += sprintf(p, "DMA\n");
+ p += sprintf(p, "PIO\n");
+
+ return p-buffer; /* => must be less than 4k! */
+}
+#endif /* defined(DISPLAY_HPT34X_TIMINGS) && defined(CONFIG_PROC_FS) */
+
+byte hpt34x_proc = 0;
+
+extern char *ide_xfer_verbose (byte xfer_rate);
+
+static void hpt34x_clear_chipset (ide_drive_t *drive)
+{
+ int drive_number = ((HWIF(drive)->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
+ unsigned int reg1 = 0, tmp1 = 0;
+ unsigned int reg2 = 0, tmp2 = 0;
+
+ pci_read_config_dword(HWIF(drive)->pci_dev, 0x44, &reg1);
+ pci_read_config_dword(HWIF(drive)->pci_dev, 0x48, &reg2);
+ tmp1 = ((0x00 << (3*drive_number)) | (reg1 & ~(7 << (3*drive_number))));
+ tmp2 = (reg2 & ~(0x11 << drive_number));
+ pci_write_config_dword(HWIF(drive)->pci_dev, 0x44, tmp1);
+ pci_write_config_dword(HWIF(drive)->pci_dev, 0x48, tmp2);
+}
+
+static int hpt34x_tune_chipset (ide_drive_t *drive, byte speed)
+{
+ int err;
+ byte hi_speed, lo_speed;
+ int drive_number = ((HWIF(drive)->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
+ unsigned int reg1 = 0, tmp1 = 0;
+ unsigned int reg2 = 0, tmp2 = 0;
+
+ SPLIT_BYTE(speed, hi_speed, lo_speed);
+
+ if (hi_speed & 7) {
+ hi_speed = (hi_speed & 4) ? 0x01 : 0x10;
+ } else {
+ lo_speed <<= 5;
+ lo_speed >>= 5;
+ }
+
+ pci_read_config_dword(HWIF(drive)->pci_dev, 0x44, &reg1);
+ pci_read_config_dword(HWIF(drive)->pci_dev, 0x48, &reg2);
+ tmp1 = ((lo_speed << (3*drive_number)) | (reg1 & ~(7 << (3*drive_number))));
+ tmp2 = ((hi_speed << drive_number) | reg2);
+ err = ide_config_drive_speed(drive, speed);
+ pci_write_config_dword(HWIF(drive)->pci_dev, 0x44, tmp1);
+ pci_write_config_dword(HWIF(drive)->pci_dev, 0x48, tmp2);
+
+#if HPT343_DEBUG_DRIVE_INFO
+ printk("%s: %s drive%d (0x%04x 0x%04x) (0x%04x 0x%04x)" \
+ " (0x%02x 0x%02x) 0x%04x\n",
+ drive->name, ide_xfer_verbose(speed),
+ drive_number, reg1, tmp1, reg2, tmp2,
+ hi_speed, lo_speed, err);
+#endif /* HPT343_DEBUG_DRIVE_INFO */
+
+ return(err);
+}
+
+/*
+ * This allows the configuration of ide_pci chipset registers
+ * for cards that learn about the drive's UDMA, DMA, PIO capabilities
+ * after the drive is reported by the OS. Initally for designed for
+ * HPT343 UDMA chipset by HighPoint|Triones Technologies, Inc.
+ */
+static int config_chipset_for_dma (ide_drive_t *drive, byte ultra)
+{
+ struct hd_driveid *id = drive->id;
+ byte speed = 0x00;
+
+ if (drive->media != ide_disk)
+ return ((int) ide_dma_off_quietly);
+
+ hpt34x_clear_chipset(drive);
+
+ if ((id->dma_ultra & 0x0010) && ultra) {
+ speed = XFER_UDMA_2;
+ } else if ((id->dma_ultra & 0x0008) && ultra) {
+ speed = XFER_UDMA_2;
+ } else if ((id->dma_ultra & 0x0004) && ultra) {
+ speed = XFER_UDMA_2;
+ } else if ((id->dma_ultra & 0x0002) && ultra) {
+ speed = XFER_UDMA_1;
+ } else if ((id->dma_ultra & 0x0001) && ultra) {
+ speed = XFER_UDMA_0;
+ } else if (id->dma_mword & 0x0004) {
+ speed = XFER_MW_DMA_2;
+ } else if (id->dma_mword & 0x0002) {
+ speed = XFER_MW_DMA_1;
+ } else if (id->dma_mword & 0x0001) {
+ speed = XFER_MW_DMA_0;
+ } else if (id->dma_1word & 0x0004) {
+ speed = XFER_SW_DMA_2;
+ } else if (id->dma_1word & 0x0002) {
+ speed = XFER_SW_DMA_1;
+ } else if (id->dma_1word & 0x0001) {
+ speed = XFER_SW_DMA_0;
+ } else {
+ return ((int) ide_dma_off_quietly);
+ }
+
+ (void) hpt34x_tune_chipset(drive, speed);
+
+ return ((int) ((id->dma_ultra >> 11) & 3) ? ide_dma_off :
+ ((id->dma_ultra >> 8) & 7) ? ide_dma_on :
+ ((id->dma_mword >> 8) & 7) ? ide_dma_on :
+ ((id->dma_1word >> 8) & 7) ? ide_dma_on :
+ ide_dma_off_quietly);
+}
+
+static void config_chipset_for_pio (ide_drive_t *drive)
+{
+ unsigned short eide_pio_timing[6] = {960, 480, 240, 180, 120, 90};
+ unsigned short xfer_pio = drive->id->eide_pio_modes;
+
+ byte timing, speed, pio;
+
+ pio = ide_get_best_pio_mode(drive, 255, 5, NULL);
+
+ if (xfer_pio> 4)
+ xfer_pio = 0;
+
+ if (drive->id->eide_pio_iordy > 0) {
+ for (xfer_pio = 5;
+ xfer_pio>0 &&
+ drive->id->eide_pio_iordy>eide_pio_timing[xfer_pio];
+ xfer_pio--);
+ } else {
+ xfer_pio = (drive->id->eide_pio_modes & 4) ? 0x05 :
+ (drive->id->eide_pio_modes & 2) ? 0x04 :
+ (drive->id->eide_pio_modes & 1) ? 0x03 : xfer_pio;
+ }
+
+ timing = (xfer_pio >= pio) ? xfer_pio : pio;
+
+ switch(timing) {
+ case 4: speed = XFER_PIO_4;break;
+ case 3: speed = XFER_PIO_3;break;
+ case 2: speed = XFER_PIO_2;break;
+ case 1: speed = XFER_PIO_1;break;
+ default:
+ speed = (!drive->id->tPIO) ? XFER_PIO_0 : XFER_PIO_SLOW;
+ break;
+ }
+ (void) hpt34x_tune_chipset(drive, speed);
+}
+
+static void hpt34x_tune_drive (ide_drive_t *drive, byte pio)
+{
+ byte speed;
+
+ switch(pio) {
+ case 4: speed = XFER_PIO_4;break;
+ case 3: speed = XFER_PIO_3;break;
+ case 2: speed = XFER_PIO_2;break;
+ case 1: speed = XFER_PIO_1;break;
+ default: speed = XFER_PIO_0;break;
+ }
+ hpt34x_clear_chipset(drive);
+ (void) hpt34x_tune_chipset(drive, speed);
+}
+
+static int config_drive_xfer_rate (ide_drive_t *drive)
+{
+ struct hd_driveid *id = drive->id;
+ ide_dma_action_t dma_func = ide_dma_on;
+
+ if (id && (id->capability & 1) && HWIF(drive)->autodma) {
+ /* Consult the list of known "bad" drives */
+ if (ide_dmaproc(ide_dma_bad_drive, drive)) {
+ dma_func = ide_dma_off;
+ goto fast_ata_pio;
+ }
+ dma_func = ide_dma_off_quietly;
+ if (id->field_valid & 4) {
+ if (id->dma_ultra & 0x0007) {
+ /* Force if Capable UltraDMA */
+ dma_func = config_chipset_for_dma(drive, 1);
+ if ((id->field_valid & 2) &&
+ (dma_func != ide_dma_on))
+ goto try_dma_modes;
+ }
+ } else if (id->field_valid & 2) {
+try_dma_modes:
+ if ((id->dma_mword & 0x0007) ||
+ (id->dma_1word & 0x0007)) {
+ /* Force if Capable regular DMA modes */
+ dma_func = config_chipset_for_dma(drive, 0);
+ if (dma_func != ide_dma_on)
+ goto no_dma_set;
+ }
+ } else if (ide_dmaproc(ide_dma_good_drive, drive)) {
+ if (id->eide_dma_time > 150) {
+ goto no_dma_set;
+ }
+ /* Consult the list of known "good" drives */
+ dma_func = config_chipset_for_dma(drive, 0);
+ if (dma_func != ide_dma_on)
+ goto no_dma_set;
+ } else {
+ goto fast_ata_pio;
+ }
+ } else if ((id->capability & 8) || (id->field_valid & 2)) {
+fast_ata_pio:
+ dma_func = ide_dma_off_quietly;
+no_dma_set:
+ config_chipset_for_pio(drive);
+ }
+
+#ifndef CONFIG_HPT34X_AUTODMA
+ if (dma_func == ide_dma_on)
+ dma_func = ide_dma_off;
+#endif /* CONFIG_HPT34X_AUTODMA */
+
+ return HWIF(drive)->dmaproc(dma_func, drive);
+}
+
+/*
+ * hpt34x_dmaproc() initiates/aborts (U)DMA read/write operations on a drive.
+ *
+ * This is specific to the HPT343 UDMA bios-less chipset
+ * and HPT345 UDMA bios chipset (stamped HPT363)
+ * by HighPoint|Triones Technologies, Inc.
+ */
+
+int hpt34x_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
+{
+ ide_hwif_t *hwif = HWIF(drive);
+ unsigned long dma_base = hwif->dma_base;
+ unsigned int count, reading = 0;
+ byte dma_stat;
+
+ switch (func) {
+ case ide_dma_check:
+ return config_drive_xfer_rate(drive);
+ case ide_dma_read:
+ reading = 1 << 3;
+ case ide_dma_write:
+ if (!(count = ide_build_dmatable(drive, func)))
+ return 1; /* try PIO instead of DMA */
+ outl(hwif->dmatable_dma, dma_base + 4); /* PRD table */
+ reading |= 0x01;
+ outb(reading, dma_base); /* specify r/w */
+ outb(inb(dma_base+2)|6, dma_base+2); /* clear INTR & ERROR flags */
+ drive->waiting_for_dma = 1;
+ if (drive->media != ide_disk)
+ return 0;
+ ide_set_handler(drive, &ide_dma_intr, WAIT_CMD, NULL); /* issue cmd to drive */
+ OUT_BYTE((reading == 9) ? WIN_READDMA : WIN_WRITEDMA, IDE_COMMAND_REG);
+ return 0;
+ case ide_dma_end: /* returns 1 on error, 0 otherwise */
+ drive->waiting_for_dma = 0;
+ outb(inb(dma_base)&~1, dma_base); /* stop DMA */
+ dma_stat = inb(dma_base+2); /* get DMA status */
+ outb(dma_stat|6, dma_base+2); /* clear the INTR & ERROR bits */
+ ide_destroy_dmatable(drive); /* purge DMA mappings */
+ return (dma_stat & 7) != 4; /* verify good DMA status */
+ default:
+ break;
+ }
+ return ide_dmaproc(func, drive); /* use standard DMA stuff */
+}
+
+/*
+ * If the BIOS does not set the IO base addaress to XX00, 343 will fail.
+ */
+#define HPT34X_PCI_INIT_REG 0x80
+
+unsigned int __init pci_init_hpt34x (struct pci_dev *dev, const char *name)
+{
+ int i = 0;
+ unsigned long hpt34xIoBase = dev->resource[4].start;
+ unsigned short cmd;
+ unsigned long flags;
+
+ __save_flags(flags); /* local CPU only */
+ __cli(); /* local CPU only */
+
+ pci_write_config_byte(dev, HPT34X_PCI_INIT_REG, 0x00);
+ pci_read_config_word(dev, PCI_COMMAND, &cmd);
+
+ if (cmd & PCI_COMMAND_MEMORY) {
+ if (dev->resource[PCI_ROM_RESOURCE].start) {
+ pci_write_config_byte(dev, PCI_ROM_ADDRESS, dev->resource[PCI_ROM_RESOURCE].start | PCI_ROM_ADDRESS_ENABLE);
+ printk(KERN_INFO "HPT345: ROM enabled at 0x%08lx\n", dev->resource[PCI_ROM_RESOURCE].start);
+ }
+ pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0xF0);
+ } else {
+ pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x20);
+ }
+
+ pci_write_config_word(dev, PCI_COMMAND, cmd & ~PCI_COMMAND_IO);
+ dev->resource[0].start = (hpt34xIoBase + 0x20);
+ dev->resource[1].start = (hpt34xIoBase + 0x34);
+ dev->resource[2].start = (hpt34xIoBase + 0x28);
+ dev->resource[3].start = (hpt34xIoBase + 0x3c);
+ for(i=0; i<4; i++)
+ dev->resource[i].flags |= PCI_BASE_ADDRESS_SPACE_IO;
+ /*
+ * Since 20-23 can be assigned and are R/W, we correct them.
+ */
+ pci_write_config_dword(dev, PCI_BASE_ADDRESS_0, dev->resource[0].start);
+ pci_write_config_dword(dev, PCI_BASE_ADDRESS_1, dev->resource[1].start);
+ pci_write_config_dword(dev, PCI_BASE_ADDRESS_2, dev->resource[2].start);
+ pci_write_config_dword(dev, PCI_BASE_ADDRESS_3, dev->resource[3].start);
+ pci_write_config_word(dev, PCI_COMMAND, cmd);
+
+ __restore_flags(flags); /* local CPU only */
+
+#if defined(DISPLAY_HPT34X_TIMINGS) && defined(CONFIG_PROC_FS)
+ hpt34x_proc = 1;
+ bmide_dev = dev;
+ hpt34x_display_info = &hpt34x_get_info;
+#endif /* DISPLAY_HPT34X_TIMINGS && CONFIG_PROC_FS */
+
+ return dev->irq;
+}
+
+void __init ide_init_hpt34x (ide_hwif_t *hwif)
+{
+ hwif->tuneproc = &hpt34x_tune_drive;
+ if (hwif->dma_base) {
+ unsigned short pcicmd = 0;
+
+ pci_read_config_word(hwif->pci_dev, PCI_COMMAND, &pcicmd);
+ hwif->autodma = (pcicmd & PCI_COMMAND_MEMORY) ? 1 : 0;
+ hwif->dmaproc = &hpt34x_dmaproc;
+ } else {
+ hwif->drives[0].autotune = 1;
+ hwif->drives[1].autotune = 1;
+ }
+}
diff --git a/drivers/ide/hpt366.c b/drivers/ide/hpt366.c
new file mode 100644
index 000000000..d2f2fb433
--- /dev/null
+++ b/drivers/ide/hpt366.c
@@ -0,0 +1,564 @@
+/*
+ * linux/drivers/block/hpt366.c Version 0.16 Feb. 10, 2000
+ *
+ * Copyright (C) 1999-2000 Andre Hedrick <andre@suse.com>
+ * May be copied or modified under the terms of the GNU General Public License
+ *
+ * Thanks to HighPoint Technologies for their assistance, and hardware.
+ * Special Thanks to Jon Burchmore in SanDiego for the deep pockets, his
+ * donation of an ABit BP6 mainboard, processor, and memory acellerated
+ * development and support.
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include "ide_modes.h"
+
+#undef DISPLAY_HPT366_TIMINGS
+
+#if defined(DISPLAY_HPT366_TIMINGS) && defined(CONFIG_PROC_FS)
+#include <linux/stat.h>
+#include <linux/proc_fs.h>
+#endif /* defined(DISPLAY_HPT366_TIMINGS) && defined(CONFIG_PROC_FS) */
+
+const char *bad_ata66_4[] = {
+ "WDC AC310200R",
+ NULL
+};
+
+const char *bad_ata66_3[] = {
+ "WDC AC310200R",
+ NULL
+};
+
+const char *bad_ata33[] = {
+ "Maxtor 92720U8", "Maxtor 92040U6", "Maxtor 91360U4", "Maxtor 91020U3", "Maxtor 90845U3", "Maxtor 90650U2",
+ "Maxtor 91360D8", "Maxtor 91190D7", "Maxtor 91020D6", "Maxtor 90845D5", "Maxtor 90680D4", "Maxtor 90510D3", "Maxtor 90340D2",
+ "Maxtor 91152D8", "Maxtor 91008D7", "Maxtor 90845D6", "Maxtor 90840D6", "Maxtor 90720D5", "Maxtor 90648D5", "Maxtor 90576D4",
+ "Maxtor 90510D4",
+ "Maxtor 90432D3", "Maxtor 90288D2", "Maxtor 90256D2",
+ "Maxtor 91000D8", "Maxtor 90910D8", "Maxtor 90875D7", "Maxtor 90840D7", "Maxtor 90750D6", "Maxtor 90625D5", "Maxtor 90500D4",
+ "Maxtor 91728D8", "Maxtor 91512D7", "Maxtor 91303D6", "Maxtor 91080D5", "Maxtor 90845D4", "Maxtor 90680D4", "Maxtor 90648D3", "Maxtor 90432D2",
+ NULL
+};
+
+struct chipset_bus_clock_list_entry {
+ byte xfer_speed;
+ unsigned int chipset_settings;
+};
+
+struct chipset_bus_clock_list_entry forty_base [] = {
+
+ { XFER_UDMA_4 , 0x900fd943 },
+ { XFER_UDMA_3 , 0x900ad943 },
+ { XFER_UDMA_2 , 0x900bd943 },
+ { XFER_UDMA_1 , 0x9008d943 },
+ { XFER_UDMA_0 , 0x9008d943 },
+
+ { XFER_MW_DMA_2 , 0xa008d943 },
+ { XFER_MW_DMA_1 , 0xa010d955 },
+ { XFER_MW_DMA_0 , 0xa010d9fc },
+
+ { XFER_PIO_4 , 0xc008d963 },
+ { XFER_PIO_3 , 0xc010d974 },
+ { XFER_PIO_2 , 0xc010d997 },
+ { XFER_PIO_1 , 0xc010d9c7 },
+ { XFER_PIO_0 , 0xc018d9d9 },
+ { 0 , 0x0120d9d9 }
+};
+
+struct chipset_bus_clock_list_entry thirty_three_base [] = {
+
+ { XFER_UDMA_4 , 0x90c9a731 },
+ { XFER_UDMA_3 , 0x90cfa731 },
+ { XFER_UDMA_2 , 0x90caa731 },
+ { XFER_UDMA_1 , 0x90cba731 },
+ { XFER_UDMA_0 , 0x90c8a731 },
+
+ { XFER_MW_DMA_2 , 0xa0c8a731 },
+ { XFER_MW_DMA_1 , 0xa0c8a732 }, /* 0xa0c8a733 */
+ { XFER_MW_DMA_0 , 0xa0c8a797 },
+
+ { XFER_PIO_4 , 0xc0c8a731 },
+ { XFER_PIO_3 , 0xc0c8a742 },
+ { XFER_PIO_2 , 0xc0d0a753 },
+ { XFER_PIO_1 , 0xc0d0a7a3 }, /* 0xc0d0a793 */
+ { XFER_PIO_0 , 0xc0d0a7aa }, /* 0xc0d0a7a7 */
+ { 0 , 0x0120a7a7 }
+};
+
+struct chipset_bus_clock_list_entry twenty_five_base [] = {
+
+ { XFER_UDMA_4 , 0x90c98521 },
+ { XFER_UDMA_3 , 0x90cf8521 },
+ { XFER_UDMA_2 , 0x90cf8521 },
+ { XFER_UDMA_1 , 0x90cb8521 },
+ { XFER_UDMA_0 , 0x90cb8521 },
+
+ { XFER_MW_DMA_2 , 0xa0ca8521 },
+ { XFER_MW_DMA_1 , 0xa0ca8532 },
+ { XFER_MW_DMA_0 , 0xa0ca8575 },
+
+ { XFER_PIO_4 , 0xc0ca8521 },
+ { XFER_PIO_3 , 0xc0ca8532 },
+ { XFER_PIO_2 , 0xc0ca8542 },
+ { XFER_PIO_1 , 0xc0d08572 },
+ { XFER_PIO_0 , 0xc0d08585 },
+ { 0 , 0x01208585 }
+};
+
+#define HPT366_DEBUG_DRIVE_INFO 0
+#define HPT366_ALLOW_ATA66_4 1
+#define HPT366_ALLOW_ATA66_3 1
+
+#if defined(DISPLAY_HPT366_TIMINGS) && defined(CONFIG_PROC_FS)
+static int hpt366_get_info(char *, char **, off_t, int);
+extern int (*hpt366_display_info)(char *, char **, off_t, int); /* ide-proc.c */
+extern char *ide_media_verbose(ide_drive_t *);
+static struct pci_dev *bmide_dev;
+static struct pci_dev *bmide2_dev;
+
+static int hpt366_get_info (char *buffer, char **addr, off_t offset, int count)
+{
+ char *p = buffer;
+ u32 bibma = bmide_dev->resource[4].start;
+ u32 bibma2 = bmide2_dev->resource[4].start;
+ u8 c0 = 0, c1 = 0;
+
+ /*
+ * at that point bibma+0x2 et bibma+0xa are byte registers
+ * to investigate:
+ */
+ c0 = inb_p((unsigned short)bibma + 0x02);
+ if (bmide2_dev)
+ c1 = inb_p((unsigned short)bibma2 + 0x02);
+
+ p += sprintf(p, "\n HPT366 Chipset.\n");
+ p += sprintf(p, "--------------- Primary Channel ---------------- Secondary Channel -------------\n");
+ p += sprintf(p, " %sabled %sabled\n",
+ (c0&0x80) ? "dis" : " en",
+ (c1&0x80) ? "dis" : " en");
+ p += sprintf(p, "--------------- drive0 --------- drive1 -------- drive0 ---------- drive1 ------\n");
+ p += sprintf(p, "DMA enabled: %s %s %s %s\n",
+ (c0&0x20) ? "yes" : "no ", (c0&0x40) ? "yes" : "no ",
+ (c1&0x20) ? "yes" : "no ", (c1&0x40) ? "yes" : "no " );
+
+ p += sprintf(p, "UDMA\n");
+ p += sprintf(p, "DMA\n");
+ p += sprintf(p, "PIO\n");
+
+ return p-buffer;/* => must be less than 4k! */
+}
+#endif /* defined(DISPLAY_HPT366_TIMINGS) && defined(CONFIG_PROC_FS) */
+
+byte hpt366_proc = 0;
+
+extern char *ide_xfer_verbose (byte xfer_rate);
+byte hpt363_shared_irq = 0;
+byte hpt363_shared_pin = 0;
+
+static int check_in_drive_lists (ide_drive_t *drive, const char **list)
+{
+ struct hd_driveid *id = drive->id;
+#if HPT366_DEBUG_DRIVE_INFO
+ printk("check_in_drive_lists(%s, %p)\n", drive->name, list);
+#endif /* HPT366_DEBUG_DRIVE_INFO */
+
+ while (*list) {
+ if (!strcmp(*list++,id->model)) {
+#ifdef DEBUG
+ printk("%s: Broken ASIC, BackSpeeding (U)DMA for %s\n", drive->name, id->model);
+#endif /* DEBUG */
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static unsigned int pci_bus_clock_list (byte speed, struct chipset_bus_clock_list_entry * chipset_table)
+{
+#if HPT366_DEBUG_DRIVE_INFO
+ printk("pci_bus_clock_list(speed=0x%02x, table=%p)\n", speed, chipset_table);
+#endif /* HPT366_DEBUG_DRIVE_INFO */
+ for ( ; chipset_table->xfer_speed ; chipset_table++)
+ if (chipset_table->xfer_speed == speed) {
+#if HPT366_DEBUG_DRIVE_INFO
+ printk("pci_bus_clock_list: found match: 0x%08x\n", chipset_table->chipset_settings);
+#endif /* HPT366_DEBUG_DRIVE_INFO */
+ return chipset_table->chipset_settings;
+ }
+#if HPT366_DEBUG_DRIVE_INFO
+ printk("pci_bus_clock_list: using default: 0x%08x\n", 0x01208585);
+#endif /* HPT366_DEBUG_DRIVE_INFO */
+ return 0x01208585;
+}
+
+static int hpt366_tune_chipset (ide_drive_t *drive, byte speed)
+{
+ int err;
+#if HPT366_DEBUG_DRIVE_INFO
+ int drive_number = ((HWIF(drive)->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
+#endif /* HPT366_DEBUG_DRIVE_INFO */
+ byte regtime = (drive->select.b.unit & 0x01) ? 0x44 : 0x40;
+ unsigned int reg1 = 0;
+ unsigned int reg2 = 0;
+
+#if HPT366_DEBUG_DRIVE_INFO
+ printk("hpt366_tune_chipset(%s, speed=0x%02x)\n", drive->name, speed);
+#endif /* HPT366_DEBUG_DRIVE_INFO */
+
+ pci_read_config_dword(HWIF(drive)->pci_dev, regtime, &reg1);
+ /* detect bus speed by looking at control reg timing: */
+ switch((reg1 >> 8) & 7) {
+ case 5:
+ reg2 = pci_bus_clock_list(speed, forty_base);
+ break;
+ case 9:
+ reg2 = pci_bus_clock_list(speed, twenty_five_base);
+ break;
+ default:
+ printk("hpt366: assuming 33Mhz PCI bus\n");
+ case 7:
+ reg2 = pci_bus_clock_list(speed, thirty_three_base);
+ break;
+ }
+ /*
+ * Disable on-chip PIO FIFO/buffer (to avoid problems handling I/O errors later)
+ */
+ if (speed >= XFER_MW_DMA_0) {
+ reg2 = (reg2 & ~0xc0000000) | (reg1 & 0xc0000000);
+ } else {
+ reg2 = (reg2 & ~0x30070000) | (reg1 & 0x30070000);
+ }
+ reg2 &= ~0x80000000;
+
+ pci_write_config_dword(HWIF(drive)->pci_dev, regtime, reg2);
+ err = ide_config_drive_speed(drive, speed);
+
+#if HPT366_DEBUG_DRIVE_INFO
+ printk("%s: speed=0x%02x(%s), drive%d, old=0x%08x, new=0x%08x, err=0x%04x\n",
+ drive->name, speed, ide_xfer_verbose(speed),
+ drive_number, reg1, reg2, err);
+#endif /* HPT366_DEBUG_DRIVE_INFO */
+ return(err);
+}
+
+/*
+ * This allows the configuration of ide_pci chipset registers
+ * for cards that learn about the drive's UDMA, DMA, PIO capabilities
+ * after the drive is reported by the OS. Initally for designed for
+ * HPT366 UDMA chipset by HighPoint|Triones Technologies, Inc.
+ *
+ * check_in_drive_lists(drive, bad_ata66_4)
+ * check_in_drive_lists(drive, bad_ata66_3)
+ * check_in_drive_lists(drive, bad_ata33)
+ *
+ */
+static int config_chipset_for_dma (ide_drive_t *drive)
+{
+ struct hd_driveid *id = drive->id;
+ byte speed = 0x00;
+ byte reg51h = 0;
+ int rval;
+
+ if ((id->dma_ultra & 0x0010) &&
+ (!check_in_drive_lists(drive, bad_ata66_4)) &&
+ (HPT366_ALLOW_ATA66_4) &&
+ (HWIF(drive)->udma_four)) {
+ speed = XFER_UDMA_4;
+ } else if ((id->dma_ultra & 0x0008) &&
+ (!check_in_drive_lists(drive, bad_ata66_3)) &&
+ (HPT366_ALLOW_ATA66_3) &&
+ (HWIF(drive)->udma_four)) {
+ speed = XFER_UDMA_3;
+ } else if (id->dma_ultra && (!check_in_drive_lists(drive, bad_ata33))) {
+ if (id->dma_ultra & 0x0004) {
+ speed = XFER_UDMA_2;
+ } else if (id->dma_ultra & 0x0002) {
+ speed = XFER_UDMA_1;
+ } else if (id->dma_ultra & 0x0001) {
+ speed = XFER_UDMA_0;
+ }
+ } else if (id->dma_mword & 0x0004) {
+ speed = XFER_MW_DMA_2;
+ } else if (id->dma_mword & 0x0002) {
+ speed = XFER_MW_DMA_1;
+ } else if (id->dma_mword & 0x0001) {
+ speed = XFER_MW_DMA_0;
+ } else if (id->dma_1word & 0x0004) {
+ speed = XFER_SW_DMA_2;
+ } else if (id->dma_1word & 0x0002) {
+ speed = XFER_SW_DMA_1;
+ } else if (id->dma_1word & 0x0001) {
+ speed = XFER_SW_DMA_0;
+ } else {
+#if HPT366_DEBUG_DRIVE_INFO
+ printk("%s: config_chipset_for_dma: returning 'ide_dma_off_quietly'\n", drive->name);
+#endif /* HPT366_DEBUG_DRIVE_INFO */
+ return ((int) ide_dma_off_quietly);
+ }
+
+ pci_read_config_byte(HWIF(drive)->pci_dev, 0x51, &reg51h);
+
+#ifdef CONFIG_HPT366_FIP
+ /*
+ * Some drives prefer/allow for the method of handling interrupts.
+ */
+ if (!(reg51h & 0x80))
+ pci_write_config_byte(HWIF(drive)->pci_dev, 0x51, reg51h|0x80);
+#else /* ! CONFIG_HPT366_FIP */
+ /*
+ * Disable the "fast interrupt" prediction.
+ * Instead, always wait for the real interrupt from the drive!
+ */
+ if (reg51h & 0x80)
+ pci_write_config_byte(HWIF(drive)->pci_dev, 0x51, reg51h & ~0x80);
+#endif /* CONFIG_HPT366_FIP */
+#if HPT366_DEBUG_DRIVE_INFO
+ printk("%s: config_chipset_for_dma: speed=0x%04x\n", drive->name, speed);
+#endif /* HPT366_DEBUG_DRIVE_INFO */
+ (void) hpt366_tune_chipset(drive, speed);
+
+ rval = (int)( ((id->dma_ultra >> 11) & 3) ? ide_dma_on :
+ ((id->dma_ultra >> 8) & 7) ? ide_dma_on :
+ ((id->dma_mword >> 8) & 7) ? ide_dma_on :
+ ((id->dma_1word >> 8) & 7) ? ide_dma_on :
+ ide_dma_off_quietly);
+
+#if HPT366_DEBUG_DRIVE_INFO
+ printk("%s: config_chipset_for_dma: returning %d (%s)\n", drive->name, rval, rval == ide_dma_on ? "dma_on" : "dma_off");
+#endif /* HPT366_DEBUG_DRIVE_INFO */
+ return rval;
+}
+
+static void config_chipset_for_pio (ide_drive_t *drive)
+{
+ unsigned short eide_pio_timing[6] = {960, 480, 240, 180, 120, 90};
+ unsigned short xfer_pio = drive->id->eide_pio_modes;
+ byte timing, speed, pio;
+
+#if HPT366_DEBUG_DRIVE_INFO
+ printk("%s: config_chipset_for_pio\n", drive->name);
+#endif /* HPT366_DEBUG_DRIVE_INFO */
+ pio = ide_get_best_pio_mode(drive, 255, 5, NULL);
+
+ if (xfer_pio> 4)
+ xfer_pio = 0;
+
+ if (drive->id->eide_pio_iordy > 0) {
+ for (xfer_pio = 5;
+ xfer_pio>0 &&
+ drive->id->eide_pio_iordy>eide_pio_timing[xfer_pio];
+ xfer_pio--);
+ } else {
+ xfer_pio = (drive->id->eide_pio_modes & 4) ? 0x05 :
+ (drive->id->eide_pio_modes & 2) ? 0x04 :
+ (drive->id->eide_pio_modes & 1) ? 0x03 :
+ (drive->id->tPIO & 2) ? 0x02 :
+ (drive->id->tPIO & 1) ? 0x01 : xfer_pio;
+ }
+
+ timing = (xfer_pio >= pio) ? xfer_pio : pio;
+
+ switch(timing) {
+ case 4: speed = XFER_PIO_4;break;
+ case 3: speed = XFER_PIO_3;break;
+ case 2: speed = XFER_PIO_2;break;
+ case 1: speed = XFER_PIO_1;break;
+ default:
+ speed = (!drive->id->tPIO) ? XFER_PIO_0 : XFER_PIO_SLOW;
+ break;
+ }
+#if HPT366_DEBUG_DRIVE_INFO
+ printk("%s: config_chipset_for_pio: speed=0x%04x\n", drive->name, speed);
+#endif /* HPT366_DEBUG_DRIVE_INFO */
+ (void) hpt366_tune_chipset(drive, speed);
+}
+
+static void hpt366_tune_drive (ide_drive_t *drive, byte pio)
+{
+ byte speed;
+ switch(pio) {
+ case 4: speed = XFER_PIO_4;break;
+ case 3: speed = XFER_PIO_3;break;
+ case 2: speed = XFER_PIO_2;break;
+ case 1: speed = XFER_PIO_1;break;
+ default: speed = XFER_PIO_0;break;
+ }
+ (void) hpt366_tune_chipset(drive, speed);
+}
+
+static int config_drive_xfer_rate (ide_drive_t *drive)
+{
+ struct hd_driveid *id = drive->id;
+ ide_dma_action_t dma_func = ide_dma_on;
+
+ if (id && (id->capability & 1) && HWIF(drive)->autodma) {
+ /* Consult the list of known "bad" drives */
+ if (ide_dmaproc(ide_dma_bad_drive, drive)) {
+ dma_func = ide_dma_off;
+ goto fast_ata_pio;
+ }
+ dma_func = ide_dma_off_quietly;
+ if (id->field_valid & 4) {
+ if (id->dma_ultra & 0x001F) {
+ /* Force if Capable UltraDMA */
+ dma_func = config_chipset_for_dma(drive);
+ if ((id->field_valid & 2) &&
+ (dma_func != ide_dma_on))
+ goto try_dma_modes;
+ }
+ } else if (id->field_valid & 2) {
+try_dma_modes:
+ if ((id->dma_mword & 0x0007) ||
+ (id->dma_1word & 0x0007)) {
+ /* Force if Capable regular DMA modes */
+ dma_func = config_chipset_for_dma(drive);
+ if (dma_func != ide_dma_on)
+ goto no_dma_set;
+ }
+ } else if (ide_dmaproc(ide_dma_good_drive, drive)) {
+ if (id->eide_dma_time > 150) {
+ goto no_dma_set;
+ }
+ /* Consult the list of known "good" drives */
+ dma_func = config_chipset_for_dma(drive);
+ if (dma_func != ide_dma_on)
+ goto no_dma_set;
+ } else {
+ goto fast_ata_pio;
+ }
+ } else if ((id->capability & 8) || (id->field_valid & 2)) {
+fast_ata_pio:
+ dma_func = ide_dma_off_quietly;
+no_dma_set:
+
+ config_chipset_for_pio(drive);
+ }
+ return HWIF(drive)->dmaproc(dma_func, drive);
+}
+
+/*
+ * hpt366_dmaproc() initiates/aborts (U)DMA read/write operations on a drive.
+ *
+ * This is specific to the HPT366 UDMA bios chipset
+ * by HighPoint|Triones Technologies, Inc.
+ */
+int hpt366_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
+{
+ byte reg50h = 0;
+
+ switch (func) {
+ case ide_dma_check:
+ return config_drive_xfer_rate(drive);
+ case ide_dma_lostirq:
+ pci_read_config_byte(HWIF(drive)->pci_dev, 0x50, &reg50h);
+ pci_write_config_byte(HWIF(drive)->pci_dev, 0x50, reg50h|0x03);
+ pci_read_config_byte(HWIF(drive)->pci_dev, 0x50, &reg50h);
+ /* ide_set_handler(drive, &ide_dma_intr, WAIT_CMD, NULL); */
+ case ide_dma_timeout:
+ default:
+ break;
+ }
+ return ide_dmaproc(func, drive); /* use standard DMA stuff */
+}
+
+unsigned int __init pci_init_hpt366 (struct pci_dev *dev, const char *name)
+{
+ byte test = 0;
+
+ if (dev->resource[PCI_ROM_RESOURCE].start)
+ pci_write_config_byte(dev, PCI_ROM_ADDRESS, dev->resource[PCI_ROM_RESOURCE].start | PCI_ROM_ADDRESS_ENABLE);
+
+ pci_read_config_byte(dev, PCI_CACHE_LINE_SIZE, &test);
+ if (test != 0x08)
+ pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 0x08);
+
+ pci_read_config_byte(dev, PCI_LATENCY_TIMER, &test);
+ if (test != 0x78)
+ pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x78);
+
+ pci_read_config_byte(dev, PCI_MIN_GNT, &test);
+ if (test != 0x08)
+ pci_write_config_byte(dev, PCI_MIN_GNT, 0x08);
+
+ pci_read_config_byte(dev, PCI_MAX_LAT, &test);
+ if (test != 0x08)
+ pci_write_config_byte(dev, PCI_MAX_LAT, 0x08);
+
+#if defined(DISPLAY_HPT366_TIMINGS) && defined(CONFIG_PROC_FS)
+ if (!hpt366_proc) {
+ hpt366_proc = 1;
+ bmide_dev = dev;
+ hpt366_display_info = &hpt366_get_info;
+ }
+ if ((hpt366_proc) && ((dev->devfn - bmide_dev->devfn) == 1)) {
+ bmide2_dev = dev;
+ }
+#endif /* DISPLAY_HPT366_TIMINGS && CONFIG_PROC_FS */
+
+ return dev->irq;
+}
+
+unsigned int __init ata66_hpt366 (ide_hwif_t *hwif)
+{
+ byte ata66 = 0;
+
+ pci_read_config_byte(hwif->pci_dev, 0x5a, &ata66);
+#ifdef DEBUG
+ printk("HPT366: reg5ah=0x%02x ATA-%s Cable Port%d\n",
+ ata66, (ata66 & 0x02) ? "33" : "66",
+ PCI_FUNC(hwif->pci_dev->devfn));
+#endif /* DEBUG */
+ return ((ata66 & 0x02) ? 0 : 1);
+}
+
+void __init ide_init_hpt366 (ide_hwif_t *hwif)
+{
+ hwif->tuneproc = &hpt366_tune_drive;
+ if (hwif->dma_base) {
+ hwif->dmaproc = &hpt366_dmaproc;
+ } else {
+ hwif->autodma = 0;
+ hwif->drives[0].autotune = 1;
+ hwif->drives[1].autotune = 1;
+ }
+}
+
+void ide_dmacapable_hpt366 (ide_hwif_t *hwif, unsigned long dmabase)
+{
+ byte masterdma = 0, slavedma = 0;
+ byte dma_new = 0, dma_old = inb(dmabase+2);
+ unsigned long flags;
+
+ __save_flags(flags); /* local CPU only */
+ __cli(); /* local CPU only */
+
+ dma_new = dma_old;
+ pci_read_config_byte(hwif->pci_dev, 0x43, &masterdma);
+ pci_read_config_byte(hwif->pci_dev, 0x47, &slavedma);
+
+ if (masterdma & 0x30) dma_new |= 0x20;
+ if (slavedma & 0x30) dma_new |= 0x40;
+ if (dma_new != dma_old) outb(dma_new, dmabase+2);
+
+ __restore_flags(flags); /* local CPU only */
+
+ ide_setup_dma(hwif, dmabase, 8);
+}
diff --git a/drivers/ide/ht6560b.c b/drivers/ide/ht6560b.c
new file mode 100644
index 000000000..6b7d5af72
--- /dev/null
+++ b/drivers/ide/ht6560b.c
@@ -0,0 +1,342 @@
+/*
+ * linux/drivers/block/ht6560b.c Version 0.07 Feb 1, 2000
+ *
+ * Copyright (C) 1995-2000 Linus Torvalds & author (see below)
+ */
+
+/*
+ *
+ * Version 0.01 Initial version hacked out of ide.c
+ *
+ * Version 0.02 Added support for PIO modes, auto-tune
+ *
+ * Version 0.03 Some cleanups
+ *
+ * Version 0.05 PIO mode cycle timings auto-tune using bus-speed
+ *
+ * Version 0.06 Prefetch mode now defaults no OFF. To set
+ * prefetch mode OFF/ON use "hdparm -p8/-p9".
+ * Unmask irq is disabled when prefetch mode
+ * is enabled.
+ *
+ * Version 0.07 Trying to fix CD-ROM detection problem.
+ * "Prefetch" mode bit OFF for ide disks and
+ * ON for anything else.
+ *
+ *
+ * HT-6560B EIDE-controller support
+ * To activate controller support use kernel parameter "ide0=ht6560b".
+ * Use hdparm utility to enable PIO mode support.
+ *
+ * Author: Mikko Ala-Fossi <maf@iki.fi>
+ * Jan Evert van Grootheest <janevert@iae.nl>
+ *
+ * Try: http://www.maf.iki.fi/~maf/ht6560b/
+ */
+
+#define HT6560B_VERSION "v0.07"
+
+#undef REALLY_SLOW_IO /* most systems can safely undef this */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+
+#include "ide_modes.h"
+
+/* #define DEBUG */ /* remove comments for DEBUG messages */
+
+/*
+ * The special i/o-port that HT-6560B uses to configuration:
+ * bit0 (0x01): "1" selects secondary interface
+ * bit2 (0x04): "1" enables FIFO function
+ * bit5 (0x20): "1" enables prefetched data read function (???)
+ *
+ * The special i/o-port that HT-6560A uses to configuration:
+ * bit0 (0x01): "1" selects secondary interface
+ * bit1 (0x02): "1" enables prefetched data read function
+ * bit2 (0x04): "0" enables multi-master system (?)
+ * bit3 (0x08): "1" 3 cycle time, "0" 2 cycle time (?)
+ */
+#define HT_CONFIG_PORT 0x3e6
+#define HT_CONFIG(drivea) (byte)(((drivea)->drive_data & 0xff00) >> 8)
+/*
+ * FIFO + PREFETCH (both a/b-model)
+ */
+#define HT_CONFIG_DEFAULT 0x1c /* no prefetch */
+/* #define HT_CONFIG_DEFAULT 0x3c */ /* with prefetch */
+#define HT_SECONDARY_IF 0x01
+#define HT_PREFETCH_MODE 0x20
+
+/*
+ * ht6560b Timing values:
+ *
+ * I reviewed some assembler source listings of htide drivers and found
+ * out how they setup those cycle time interfacing values, as they at Holtek
+ * call them. IDESETUP.COM that is supplied with the drivers figures out
+ * optimal values and fetches those values to drivers. I found out that
+ * they use IDE_SELECT_REG to fetch timings to the ide board right after
+ * interface switching. After that it was quite easy to add code to
+ * ht6560b.c.
+ *
+ * IDESETUP.COM gave me values 0x24, 0x45, 0xaa, 0xff that worked fine
+ * for hda and hdc. But hdb needed higher values to work, so I guess
+ * that sometimes it is necessary to give higher value than IDESETUP
+ * gives. [see cmd640.c for an extreme example of this. -ml]
+ *
+ * Perhaps I should explain something about these timing values:
+ * The higher nibble of value is the Recovery Time (rt) and the lower nibble
+ * of the value is the Active Time (at). Minimum value 2 is the fastest and
+ * the maximum value 15 is the slowest. Default values should be 15 for both.
+ * So 0x24 means 2 for rt and 4 for at. Each of the drives should have
+ * both values, and IDESETUP gives automatically rt=15 st=15 for CDROMs or
+ * similar. If value is too small there will be all sorts of failures.
+ *
+ * Timing byte consists of
+ * High nibble: Recovery Cycle Time (rt)
+ * The valid values range from 2 to 15. The default is 15.
+ *
+ * Low nibble: Active Cycle Time (at)
+ * The valid values range from 2 to 15. The default is 15.
+ *
+ * You can obtain optimized timing values by running Holtek IDESETUP.COM
+ * for DOS. DOS drivers get their timing values from command line, where
+ * the first value is the Recovery Time and the second value is the
+ * Active Time for each drive. Smaller value gives higher speed.
+ * In case of failures you should probably fall back to a higher value.
+ */
+#define HT_TIMING(drivea) (byte)((drivea)->drive_data & 0x00ff)
+#define HT_TIMING_DEFAULT 0xff
+
+/*
+ * This routine handles interface switching for the peculiar hardware design
+ * on the F.G.I./Holtek HT-6560B VLB IDE interface.
+ * The HT-6560B can only enable one IDE port at a time, and requires a
+ * silly sequence (below) whenever we switch between primary and secondary.
+ */
+
+/*
+ * This routine is invoked from ide.c to prepare for access to a given drive.
+ */
+static void ht6560b_selectproc (ide_drive_t *drive)
+{
+ unsigned long flags;
+ static byte current_select = 0;
+ static byte current_timing = 0;
+ byte select, timing;
+
+ __save_flags (flags); /* local CPU only */
+ __cli(); /* local CPU only */
+
+ select = HT_CONFIG(drive);
+ timing = HT_TIMING(drive);
+
+ if (select != current_select || timing != current_timing) {
+ current_select = select;
+ current_timing = timing;
+ if (drive->media != ide_disk || !drive->present)
+ select |= HT_PREFETCH_MODE;
+ (void) inb(HT_CONFIG_PORT);
+ (void) inb(HT_CONFIG_PORT);
+ (void) inb(HT_CONFIG_PORT);
+ (void) inb(HT_CONFIG_PORT);
+ outb(select, HT_CONFIG_PORT);
+ /*
+ * Set timing for this drive:
+ */
+ outb(timing, IDE_SELECT_REG);
+ (void) inb(IDE_STATUS_REG);
+#ifdef DEBUG
+ printk("ht6560b: %s: select=%#x timing=%#x\n", drive->name, select, timing);
+#endif
+ }
+ __restore_flags (flags); /* local CPU only */
+}
+
+/*
+ * Autodetection and initialization of ht6560b
+ */
+static int __init try_to_init_ht6560b(void)
+{
+ byte orig_value;
+ int i;
+
+ /* Autodetect ht6560b */
+ if ((orig_value=inb(HT_CONFIG_PORT)) == 0xff)
+ return 0;
+
+ for (i=3;i>0;i--) {
+ outb(0x00, HT_CONFIG_PORT);
+ if (!( (~inb(HT_CONFIG_PORT)) & 0x3f )) {
+ outb(orig_value, HT_CONFIG_PORT);
+ return 0;
+ }
+ }
+ outb(0x00, HT_CONFIG_PORT);
+ if ((~inb(HT_CONFIG_PORT))& 0x3f) {
+ outb(orig_value, HT_CONFIG_PORT);
+ return 0;
+ }
+ /*
+ * Ht6560b autodetected
+ */
+ outb(HT_CONFIG_DEFAULT, HT_CONFIG_PORT);
+ outb(HT_TIMING_DEFAULT, 0x1f6); /* IDE_SELECT_REG */
+ (void) inb(0x1f7); /* IDE_STATUS_REG */
+
+ printk("\nht6560b " HT6560B_VERSION
+ ": chipset detected and initialized"
+#ifdef DEBUG
+ " with debug enabled"
+#endif
+ );
+ return 1;
+}
+
+static byte ht_pio2timings(ide_drive_t *drive, byte pio)
+{
+ int bus_speed, active_time, recovery_time;
+ int active_cycles, recovery_cycles;
+ ide_pio_data_t d;
+
+ if (pio) {
+ pio = ide_get_best_pio_mode(drive, pio, 5, &d);
+
+ /*
+ * Just like opti621.c we try to calculate the
+ * actual cycle time for recovery and activity
+ * according system bus speed.
+ */
+ bus_speed = ide_system_bus_speed();
+ active_time = ide_pio_timings[pio].active_time;
+ recovery_time = d.cycle_time
+ - active_time
+ - ide_pio_timings[pio].setup_time;
+ /*
+ * Cycle times should be Vesa bus cycles
+ */
+ active_cycles = (active_time * bus_speed + 999) / 1000;
+ recovery_cycles = (recovery_time * bus_speed + 999) / 1000;
+ /*
+ * Upper and lower limits
+ */
+ if (active_cycles < 2) active_cycles = 2;
+ if (recovery_cycles < 2) recovery_cycles = 2;
+ if (active_cycles > 15) active_cycles = 15;
+ if (recovery_cycles > 15) recovery_cycles = 0; /* 0==16 */
+
+#ifdef DEBUG
+ printk("ht6560b: drive %s setting pio=%d recovery=%d (%dns) active=%d (%dns)\n", drive->name, pio, recovery_cycles, recovery_time, active_cycles, active_time);
+#endif
+
+ return (byte)((recovery_cycles << 4) | active_cycles);
+ } else {
+
+#ifdef DEBUG
+ printk("ht6560b: drive %s setting pio=0\n", drive->name);
+#endif
+
+ return HT_TIMING_DEFAULT; /* default setting */
+ }
+}
+
+/*
+ * Enable/Disable so called prefetch mode
+ */
+static void ht_set_prefetch(ide_drive_t *drive, byte state)
+{
+ unsigned long flags;
+ int t = HT_PREFETCH_MODE << 8;
+
+ save_flags (flags); /* all CPUs */
+ cli(); /* all CPUs */
+
+ /*
+ * Prefetch mode and unmask irq seems to conflict
+ */
+ if (state) {
+ drive->drive_data |= t; /* enable prefetch mode */
+ drive->no_unmask = 1;
+ drive->unmask = 0;
+ } else {
+ drive->drive_data &= ~t; /* disable prefetch mode */
+ drive->no_unmask = 0;
+ }
+
+ restore_flags (flags); /* all CPUs */
+
+#ifdef DEBUG
+ printk("ht6560b: drive %s prefetch mode %sabled\n", drive->name, (state ? "en" : "dis"));
+#endif
+}
+
+static void tune_ht6560b (ide_drive_t *drive, byte pio)
+{
+ unsigned long flags;
+ byte timing;
+
+ switch (pio) {
+ case 8: /* set prefetch off */
+ case 9: /* set prefetch on */
+ ht_set_prefetch(drive, pio & 1);
+ return;
+ }
+
+ timing = ht_pio2timings(drive, pio);
+
+ save_flags (flags); /* all CPUs */
+ cli(); /* all CPUs */
+
+ drive->drive_data &= 0xff00;
+ drive->drive_data |= timing;
+
+ restore_flags (flags); /* all CPUs */
+
+#ifdef DEBUG
+ printk("ht6560b: drive %s tuned to pio mode %#x timing=%#x\n", drive->name, pio, timing);
+#endif
+}
+
+void __init init_ht6560b (void)
+{
+ int t;
+
+ if (check_region(HT_CONFIG_PORT,1)) {
+ printk(KERN_ERR "ht6560b: PORT %#x ALREADY IN USE\n", HT_CONFIG_PORT);
+ } else {
+ if (try_to_init_ht6560b()) {
+ request_region(HT_CONFIG_PORT, 1, ide_hwifs[0].name);
+ ide_hwifs[0].chipset = ide_ht6560b;
+ ide_hwifs[1].chipset = ide_ht6560b;
+ ide_hwifs[0].selectproc = &ht6560b_selectproc;
+ ide_hwifs[1].selectproc = &ht6560b_selectproc;
+ ide_hwifs[0].tuneproc = &tune_ht6560b;
+ ide_hwifs[1].tuneproc = &tune_ht6560b;
+ ide_hwifs[0].serialized = 1; /* is this needed? */
+ ide_hwifs[1].serialized = 1; /* is this needed? */
+ ide_hwifs[0].mate = &ide_hwifs[1];
+ ide_hwifs[1].mate = &ide_hwifs[0];
+ ide_hwifs[1].channel = 1;
+
+ /*
+ * Setting default configurations for drives
+ */
+ t = (HT_CONFIG_DEFAULT << 8);
+ t |= HT_TIMING_DEFAULT;
+ ide_hwifs[0].drives[0].drive_data = t;
+ ide_hwifs[0].drives[1].drive_data = t;
+ t |= (HT_SECONDARY_IF << 8);
+ ide_hwifs[1].drives[0].drive_data = t;
+ ide_hwifs[1].drives[1].drive_data = t;
+ } else
+ printk(KERN_ERR "ht6560b: not found\n");
+ }
+}
diff --git a/drivers/ide/icside.c b/drivers/ide/icside.c
new file mode 100644
index 000000000..d0e8f8328
--- /dev/null
+++ b/drivers/ide/icside.c
@@ -0,0 +1,643 @@
+/*
+ * linux/drivers/block/icside.c
+ *
+ * Copyright (c) 1996,1997 Russell King.
+ *
+ * Changelog:
+ * 08-Jun-1996 RMK Created
+ * 12-Sep-1997 RMK Added interrupt enable/disable
+ * 17-Apr-1999 RMK Added support for V6 EASI
+ * 22-May-1999 RMK Added support for V6 DMA
+ */
+
+#include <linux/config.h>
+#include <linux/string.h>
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/blkdev.h>
+#include <linux/errno.h>
+#include <linux/hdreg.h>
+#include <linux/ide.h>
+
+#include <asm/dma.h>
+#include <asm/ecard.h>
+#include <asm/io.h>
+
+extern char *ide_xfer_verbose (byte xfer_rate);
+
+/*
+ * Maximum number of interfaces per card
+ */
+#define MAX_IFS 2
+
+#define ICS_IDENT_OFFSET 0x8a0
+
+#define ICS_ARCIN_V5_INTRSTAT 0x000
+#define ICS_ARCIN_V5_INTROFFSET 0x001
+#define ICS_ARCIN_V5_IDEOFFSET 0xa00
+#define ICS_ARCIN_V5_IDEALTOFFSET 0xae0
+#define ICS_ARCIN_V5_IDESTEPPING 4
+
+#define ICS_ARCIN_V6_IDEOFFSET_1 0x800
+#define ICS_ARCIN_V6_INTROFFSET_1 0x880
+#define ICS_ARCIN_V6_INTRSTAT_1 0x8a4
+#define ICS_ARCIN_V6_IDEALTOFFSET_1 0x8e0
+#define ICS_ARCIN_V6_IDEOFFSET_2 0xc00
+#define ICS_ARCIN_V6_INTROFFSET_2 0xc80
+#define ICS_ARCIN_V6_INTRSTAT_2 0xca4
+#define ICS_ARCIN_V6_IDEALTOFFSET_2 0xce0
+#define ICS_ARCIN_V6_IDESTEPPING 4
+
+struct cardinfo {
+ unsigned int dataoffset;
+ unsigned int ctrloffset;
+ unsigned int stepping;
+};
+
+static struct cardinfo icside_cardinfo_v5 = {
+ ICS_ARCIN_V5_IDEOFFSET,
+ ICS_ARCIN_V5_IDEALTOFFSET,
+ ICS_ARCIN_V5_IDESTEPPING
+};
+
+static struct cardinfo icside_cardinfo_v6_1 = {
+ ICS_ARCIN_V6_IDEOFFSET_1,
+ ICS_ARCIN_V6_IDEALTOFFSET_1,
+ ICS_ARCIN_V6_IDESTEPPING
+};
+
+static struct cardinfo icside_cardinfo_v6_2 = {
+ ICS_ARCIN_V6_IDEOFFSET_2,
+ ICS_ARCIN_V6_IDEALTOFFSET_2,
+ ICS_ARCIN_V6_IDESTEPPING
+};
+
+static const card_ids icside_cids[] = {
+ { MANU_ICS, PROD_ICS_IDE },
+ { MANU_ICS2, PROD_ICS2_IDE },
+ { 0xffff, 0xffff }
+};
+
+typedef enum {
+ ics_if_unknown,
+ ics_if_arcin_v5,
+ ics_if_arcin_v6
+} iftype_t;
+
+/* ---------------- Version 5 PCB Support Functions --------------------- */
+/* Prototype: icside_irqenable_arcin_v5 (struct expansion_card *ec, int irqnr)
+ * Purpose : enable interrupts from card
+ */
+static void icside_irqenable_arcin_v5 (struct expansion_card *ec, int irqnr)
+{
+ unsigned int memc_port = (unsigned int)ec->irq_data;
+ outb (0, memc_port + ICS_ARCIN_V5_INTROFFSET);
+}
+
+/* Prototype: icside_irqdisable_arcin_v5 (struct expansion_card *ec, int irqnr)
+ * Purpose : disable interrupts from card
+ */
+static void icside_irqdisable_arcin_v5 (struct expansion_card *ec, int irqnr)
+{
+ unsigned int memc_port = (unsigned int)ec->irq_data;
+ inb (memc_port + ICS_ARCIN_V5_INTROFFSET);
+}
+
+static const expansioncard_ops_t icside_ops_arcin_v5 = {
+ icside_irqenable_arcin_v5,
+ icside_irqdisable_arcin_v5,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+
+/* ---------------- Version 6 PCB Support Functions --------------------- */
+/* Prototype: icside_irqenable_arcin_v6 (struct expansion_card *ec, int irqnr)
+ * Purpose : enable interrupts from card
+ */
+static void icside_irqenable_arcin_v6 (struct expansion_card *ec, int irqnr)
+{
+ unsigned int ide_base_port = (unsigned int)ec->irq_data;
+
+ outb (0, ide_base_port + ICS_ARCIN_V6_INTROFFSET_1);
+ outb (0, ide_base_port + ICS_ARCIN_V6_INTROFFSET_2);
+}
+
+/* Prototype: icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr)
+ * Purpose : disable interrupts from card
+ */
+static void icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr)
+{
+ unsigned int ide_base_port = (unsigned int)ec->irq_data;
+
+ inb (ide_base_port + ICS_ARCIN_V6_INTROFFSET_1);
+ inb (ide_base_port + ICS_ARCIN_V6_INTROFFSET_2);
+}
+
+/* Prototype: icside_irqprobe(struct expansion_card *ec)
+ * Purpose : detect an active interrupt from card
+ */
+static int icside_irqpending_arcin_v6(struct expansion_card *ec)
+{
+ unsigned int ide_base_port = (unsigned int)ec->irq_data;
+
+ return inb(ide_base_port + ICS_ARCIN_V6_INTRSTAT_1) & 1 ||
+ inb(ide_base_port + ICS_ARCIN_V6_INTRSTAT_2) & 1;
+}
+
+static const expansioncard_ops_t icside_ops_arcin_v6 = {
+ icside_irqenable_arcin_v6,
+ icside_irqdisable_arcin_v6,
+ icside_irqpending_arcin_v6,
+ NULL,
+ NULL,
+ NULL
+};
+
+/* Prototype: icside_identifyif (struct expansion_card *ec)
+ * Purpose : identify IDE interface type
+ * Notes : checks the description string
+ */
+static iftype_t icside_identifyif (struct expansion_card *ec)
+{
+ unsigned int addr;
+ iftype_t iftype;
+ int id = 0;
+
+ iftype = ics_if_unknown;
+
+ addr = ecard_address (ec, ECARD_IOC, ECARD_FAST) + ICS_IDENT_OFFSET;
+
+ id = inb (addr) & 1;
+ id |= (inb (addr + 1) & 1) << 1;
+ id |= (inb (addr + 2) & 1) << 2;
+ id |= (inb (addr + 3) & 1) << 3;
+
+ switch (id) {
+ case 0: /* A3IN */
+ printk("icside: A3IN unsupported\n");
+ break;
+
+ case 1: /* A3USER */
+ printk("icside: A3USER unsupported\n");
+ break;
+
+ case 3: /* ARCIN V6 */
+ printk(KERN_DEBUG "icside: detected ARCIN V6 in slot %d\n", ec->slot_no);
+ iftype = ics_if_arcin_v6;
+ break;
+
+ case 15:/* ARCIN V5 (no id) */
+ printk(KERN_DEBUG "icside: detected ARCIN V5 in slot %d\n", ec->slot_no);
+ iftype = ics_if_arcin_v5;
+ break;
+
+ default:/* we don't know - complain very loudly */
+ printk("icside: ***********************************\n");
+ printk("icside: *** UNKNOWN ICS INTERFACE id=%d ***\n", id);
+ printk("icside: ***********************************\n");
+ printk("icside: please report this to linux@arm.linux.org.uk\n");
+ printk("icside: defaulting to ARCIN V5\n");
+ iftype = ics_if_arcin_v5;
+ break;
+ }
+
+ return iftype;
+}
+
+#ifdef CONFIG_BLK_DEV_IDEDMA_ICS
+/*
+ * SG-DMA support.
+ *
+ * Similar to the BM-DMA, but we use the RiscPCs IOMD DMA controllers.
+ * There is only one DMA controller per card, which means that only
+ * one drive can be accessed at one time. NOTE! We do not enforce that
+ * here, but we rely on the main IDE driver spotting that both
+ * interfaces use the same IRQ, which should guarantee this.
+ */
+#define TABLE_SIZE 2048
+
+static int
+icside_build_dmatable(ide_drive_t *drive, int reading)
+{
+ struct request *rq = HWGROUP(drive)->rq;
+ struct buffer_head *bh = rq->bh;
+ unsigned long addr, size;
+ unsigned char *virt_addr;
+ unsigned int count = 0;
+ dmasg_t *sg = (dmasg_t *)HWIF(drive)->dmatable_cpu;
+
+ do {
+ if (bh == NULL) {
+ /* paging requests have (rq->bh == NULL) */
+ virt_addr = rq->buffer;
+ addr = virt_to_bus (virt_addr);
+ size = rq->nr_sectors << 9;
+ } else {
+ /* group sequential buffers into one large buffer */
+ virt_addr = bh->b_data;
+ addr = virt_to_bus (virt_addr);
+ size = bh->b_size;
+ while ((bh = bh->b_reqnext) != NULL) {
+ if ((addr + size) != virt_to_bus (bh->b_data))
+ break;
+ size += bh->b_size;
+ }
+ }
+
+ if (addr & 3) {
+ printk("%s: misaligned DMA buffer\n", drive->name);
+ return 0;
+ }
+
+ if (size) {
+ if (reading)
+ dma_cache_inv((unsigned int)virt_addr, size);
+ else
+ dma_cache_wback((unsigned int)virt_addr, size);
+ }
+
+ sg[count].address = addr;
+ sg[count].length = size;
+ if (++count >= (TABLE_SIZE / sizeof(dmasg_t))) {
+ printk("%s: DMA table too small\n", drive->name);
+ return 0;
+ }
+ } while (bh != NULL);
+
+ if (!count)
+ printk("%s: empty DMA table?\n", drive->name);
+
+ return count;
+}
+
+static int
+icside_config_if(ide_drive_t *drive, int xfer_mode)
+{
+ int func = ide_dma_off;
+
+ switch (xfer_mode) {
+ case XFER_MW_DMA_2:
+ /*
+ * The cycle time is limited to 250ns by the r/w
+ * pulse width (90ns), however we should still
+ * have a maximum burst transfer rate of 8MB/s.
+ */
+ drive->drive_data = 250;
+ break;
+
+ case XFER_MW_DMA_1:
+ drive->drive_data = 250;
+ break;
+
+ case XFER_MW_DMA_0:
+ drive->drive_data = 480;
+ break;
+
+ default:
+ drive->drive_data = 0;
+ break;
+ }
+
+ if (drive->drive_data &&
+ ide_config_drive_speed(drive, (byte) xfer_mode) == 0)
+ func = ide_dma_on;
+ else
+ drive->drive_data = 480;
+
+ printk("%s: %s selected (peak %dMB/s)\n", drive->name,
+ ide_xfer_verbose(xfer_mode), 2000 / drive->drive_data);
+
+ return func;
+}
+
+static int
+icside_dma_check(ide_drive_t *drive)
+{
+ struct hd_driveid *id = drive->id;
+ ide_hwif_t *hwif = HWIF(drive);
+ int autodma = hwif->autodma;
+ int xfer_mode = XFER_PIO_2;
+ int func = ide_dma_off_quietly;
+
+ if (!id || !(id->capability & 1) || !autodma)
+ goto out;
+
+ /*
+ * Consult the list of known "bad" drives
+ */
+ if (ide_dmaproc(ide_dma_bad_drive, drive)) {
+ func = ide_dma_off;
+ goto out;
+ }
+
+ /*
+ * Enable DMA on any drive that has multiword DMA
+ */
+ if (id->field_valid & 2) {
+ if (id->dma_mword & 4) {
+ xfer_mode = XFER_MW_DMA_2;
+ func = ide_dma_on;
+ } else if (id->dma_mword & 2) {
+ xfer_mode = XFER_MW_DMA_1;
+ func = ide_dma_on;
+ } else if (id->dma_mword & 1) {
+ xfer_mode = XFER_MW_DMA_0;
+ func = ide_dma_on;
+ }
+ goto out;
+ }
+
+ /*
+ * Consult the list of known "good" drives
+ */
+ if (ide_dmaproc(ide_dma_good_drive, drive)) {
+ if (id->eide_dma_time > 150)
+ goto out;
+ xfer_mode = XFER_MW_DMA_1;
+ func = ide_dma_on;
+ }
+
+out:
+ func = icside_config_if(drive, xfer_mode);
+
+ return hwif->dmaproc(func, drive);
+}
+
+static int
+icside_dmaproc(ide_dma_action_t func, ide_drive_t *drive)
+{
+ ide_hwif_t *hwif = HWIF(drive);
+ int count, reading = 0;
+
+ switch (func) {
+ case ide_dma_check:
+ return icside_dma_check(drive);
+
+ case ide_dma_read:
+ reading = 1;
+ case ide_dma_write:
+ count = icside_build_dmatable(drive, reading);
+ if (!count)
+ return 1;
+ disable_dma(hwif->hw.dma);
+
+ /* Route the DMA signals to
+ * to the correct interface.
+ */
+ outb(hwif->select_data, hwif->config_data);
+
+ /* Select the correct timing
+ * for this drive
+ */
+ set_dma_speed(hwif->hw.dma, drive->drive_data);
+
+ set_dma_sg(hwif->hw.dma, (dmasg_t *)hwif->dmatable_cpu, count);
+ set_dma_mode(hwif->hw.dma, reading ? DMA_MODE_READ
+ : DMA_MODE_WRITE);
+
+ drive->waiting_for_dma = 1;
+ if (drive->media != ide_disk)
+ return 0;
+
+ ide_set_handler(drive, &ide_dma_intr, WAIT_CMD, NULL);
+ OUT_BYTE(reading ? WIN_READDMA : WIN_WRITEDMA,
+ IDE_COMMAND_REG);
+
+ case ide_dma_begin:
+ enable_dma(hwif->hw.dma);
+ return 0;
+
+ case ide_dma_end:
+ drive->waiting_for_dma = 0;
+ disable_dma(hwif->hw.dma);
+ return get_dma_residue(hwif->hw.dma) != 0;
+
+ case ide_dma_test_irq:
+ return inb((unsigned long)hwif->hw.priv) & 1;
+
+ default:
+ return ide_dmaproc(func, drive);
+ }
+}
+
+static unsigned long
+icside_alloc_dmatable(void)
+{
+ static unsigned long dmatable;
+ static unsigned int leftover;
+ unsigned long table;
+
+ if (leftover < TABLE_SIZE) {
+#if PAGE_SIZE == TABLE_SIZE * 2
+ dmatable = __get_free_pages(GFP_KERNEL, 1);
+ leftover = PAGE_SIZE;
+#else
+ dmatable = kmalloc(TABLE_SIZE, GFP_KERNEL);
+ leftover = TABLE_SIZE;
+#endif
+ }
+
+ table = dmatable;
+ if (table) {
+ dmatable += TABLE_SIZE;
+ leftover -= TABLE_SIZE;
+ }
+
+ return table;
+}
+
+static int
+icside_setup_dma(ide_hwif_t *hwif, int autodma)
+{
+ unsigned long table = icside_alloc_dmatable();
+
+ printk(" %s: SG-DMA", hwif->name);
+
+ if (!table)
+ printk(" -- ERROR, unable to allocate DMA table\n");
+ else {
+ hwif->dmatable_cpu = (void *)table;
+ hwif->dmaproc = icside_dmaproc;
+ hwif->autodma = autodma;
+
+ printk(" capable%s\n", autodma ?
+ ", auto-enable" : "");
+ }
+
+ return hwif->dmatable_cpu != NULL;
+}
+#endif
+
+static ide_hwif_t *
+icside_find_hwif(unsigned long dataport)
+{
+ ide_hwif_t *hwif;
+ int index;
+
+ for (index = 0; index < MAX_HWIFS; ++index) {
+ hwif = &ide_hwifs[index];
+ if (hwif->hw.io_ports[IDE_DATA_OFFSET] == (ide_ioreg_t)dataport)
+ goto found;
+ }
+
+ for (index = 0; index < MAX_HWIFS; ++index) {
+ hwif = &ide_hwifs[index];
+ if (!hwif->hw.io_ports[IDE_DATA_OFFSET])
+ goto found;
+ }
+
+ return NULL;
+found:
+ return hwif;
+}
+
+static ide_hwif_t *
+icside_setup(unsigned long base, struct cardinfo *info, int irq)
+{
+ unsigned long port = base + info->dataoffset;
+ ide_hwif_t *hwif;
+
+ hwif = icside_find_hwif(base);
+ if (hwif) {
+ int i;
+
+ memset(&hwif->hw, 0, sizeof(hw_regs_t));
+
+ for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) {
+ hwif->hw.io_ports[i] = (ide_ioreg_t)port;
+ port += 1 << info->stepping;
+ }
+ hwif->hw.io_ports[IDE_CONTROL_OFFSET] = base + info->ctrloffset;
+ hwif->hw.irq = irq;
+ hwif->hw.dma = NO_DMA;
+ hwif->noprobe = 0;
+ hwif->chipset = ide_acorn;
+ }
+
+ return hwif;
+}
+
+static int icside_register_v5(struct expansion_card *ec, int autodma)
+{
+ unsigned long slot_port;
+ ide_hwif_t *hwif;
+
+ slot_port = ecard_address(ec, ECARD_MEMC, 0);
+
+ ec->irqaddr = (unsigned char *)ioaddr(slot_port + ICS_ARCIN_V5_INTRSTAT);
+ ec->irqmask = 1;
+ ec->irq_data = (void *)slot_port;
+ ec->ops = (expansioncard_ops_t *)&icside_ops_arcin_v5;
+
+ /*
+ * Be on the safe side - disable interrupts
+ */
+ inb(slot_port + ICS_ARCIN_V5_INTROFFSET);
+
+ hwif = icside_setup(slot_port, &icside_cardinfo_v5, ec->irq);
+
+ return hwif ? 0 : -1;
+}
+
+static int icside_register_v6(struct expansion_card *ec, int autodma)
+{
+ unsigned long slot_port, port;
+ ide_hwif_t *hwif, *mate;
+ int sel = 0;
+
+ slot_port = ecard_address(ec, ECARD_IOC, ECARD_FAST);
+ port = ecard_address(ec, ECARD_EASI, ECARD_FAST);
+
+ if (port == 0)
+ port = slot_port;
+ else
+ sel = 1 << 5;
+
+ outb(sel, slot_port);
+
+ ec->irq_data = (void *)port;
+ ec->ops = (expansioncard_ops_t *)&icside_ops_arcin_v6;
+
+ /*
+ * Be on the safe side - disable interrupts
+ */
+ inb(port + ICS_ARCIN_V6_INTROFFSET_1);
+ inb(port + ICS_ARCIN_V6_INTROFFSET_2);
+
+ hwif = icside_setup(port, &icside_cardinfo_v6_1, ec->irq);
+ mate = icside_setup(port, &icside_cardinfo_v6_2, ec->irq);
+
+#ifdef CONFIG_BLK_DEV_IDEDMA_ICS
+ if (ec->dma != NO_DMA) {
+ if (request_dma(ec->dma, hwif->name))
+ goto no_dma;
+
+ if (hwif) {
+ hwif->config_data = slot_port;
+ hwif->select_data = sel;
+ hwif->hw.dma = ec->dma;
+ hwif->hw.priv = (void *)
+ (port + ICS_ARCIN_V6_INTRSTAT_1);
+ hwif->channel = 0;
+ icside_setup_dma(hwif, autodma);
+ }
+ if (mate) {
+ mate->config_data = slot_port;
+ mate->select_data = sel | 1;
+ mate->hw.dma = ec->dma;
+ mate->hw.priv = (void *)
+ (port + ICS_ARCIN_V6_INTRSTAT_2);
+ mate->channel = 1;
+ icside_setup_dma(mate, autodma);
+ }
+ }
+#endif
+
+no_dma:
+ return hwif || mate ? 0 : -1;
+}
+
+int icside_init(void)
+{
+ int autodma = 0;
+
+#ifdef CONFIG_IDEDMA_ICS_AUTO
+ autodma = 1;
+#endif
+
+ ecard_startfind ();
+
+ do {
+ struct expansion_card *ec;
+ int result;
+
+ ec = ecard_find(0, icside_cids);
+ if (ec == NULL)
+ break;
+
+ ecard_claim(ec);
+
+ switch (icside_identifyif(ec)) {
+ case ics_if_arcin_v5:
+ result = icside_register_v5(ec, autodma);
+ break;
+
+ case ics_if_arcin_v6:
+ result = icside_register_v6(ec, autodma);
+ break;
+
+ default:
+ result = -1;
+ break;
+ }
+
+ if (result)
+ ecard_release(ec);
+ } while (1);
+
+ return 0;
+}
diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c
new file mode 100644
index 000000000..641783e9c
--- /dev/null
+++ b/drivers/ide/ide-cd.c
@@ -0,0 +1,2729 @@
+/*
+ * linux/drivers/block/ide-cd.c
+ * Copyright (C) 1994, 1995, 1996 scott snyder <snyder@fnald0.fnal.gov>
+ * Copyright (C) 1996-1998 Erik Andersen <andersee@debian.org>
+ * Copyright (C) 1998, 1999 Jens Axboe <axboe@image.dk>
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License. See linux/COPYING for more information.
+ *
+ * ATAPI CD-ROM driver. To be used with ide.c.
+ * See Documentation/cdrom/ide-cd for usage information.
+ *
+ * Suggestions are welcome. Patches that work are more welcome though. ;-)
+ * For those wishing to work on this driver, please be sure you download
+ * and comply with the latest Mt. Fuji (SFF8090 version 4) and ATAPI
+ * (SFF-8020i rev 2.6) standards. These documents can be obtained by
+ * anonymous ftp from:
+ * ftp://fission.dt.wdc.com/pub/standards/SFF/specs/INF-8020.PDF
+ * ftp://ftp.avc-pioneer.com/Mtfuji4/Spec/Fuji4r01.pdf
+ *
+ * Drives that deviate from these standards will be accomodated as much
+ * as possible via compile time or command-line options. Since I only have
+ * a few drives, you generally need to send me patches...
+ *
+ * ----------------------------------
+ * TO DO LIST:
+ * -Make it so that Pioneer CD DR-A24X and friends don't get screwed up on
+ * boot
+ *
+ * ----------------------------------
+ * 1.00 Oct 31, 1994 -- Initial version.
+ * 1.01 Nov 2, 1994 -- Fixed problem with starting request in
+ * cdrom_check_status.
+ * 1.03 Nov 25, 1994 -- leaving unmask_intr[] as a user-setting (as for disks)
+ * (from mlord) -- minor changes to cdrom_setup()
+ * -- renamed ide_dev_s to ide_drive_t, enable irq on command
+ * 2.00 Nov 27, 1994 -- Generalize packet command interface;
+ * add audio ioctls.
+ * 2.01 Dec 3, 1994 -- Rework packet command interface to handle devices
+ * which send an interrupt when ready for a command.
+ * 2.02 Dec 11, 1994 -- Cache the TOC in the driver.
+ * Don't use SCMD_PLAYAUDIO_TI; it's not included
+ * in the current version of ATAPI.
+ * Try to use LBA instead of track or MSF addressing
+ * when possible.
+ * Don't wait for READY_STAT.
+ * 2.03 Jan 10, 1995 -- Rewrite block read routines to handle block sizes
+ * other than 2k and to move multiple sectors in a
+ * single transaction.
+ * 2.04 Apr 21, 1995 -- Add work-around for Creative Labs CD220E drives.
+ * Thanks to Nick Saw <cwsaw@pts7.pts.mot.com> for
+ * help in figuring this out. Ditto for Acer and
+ * Aztech drives, which seem to have the same problem.
+ * 2.04b May 30, 1995 -- Fix to match changes in ide.c version 3.16 -ml
+ * 2.05 Jun 8, 1995 -- Don't attempt to retry after an illegal request
+ * or data protect error.
+ * Use HWIF and DEV_HWIF macros as in ide.c.
+ * Always try to do a request_sense after
+ * a failed command.
+ * Include an option to give textual descriptions
+ * of ATAPI errors.
+ * Fix a bug in handling the sector cache which
+ * showed up if the drive returned data in 512 byte
+ * blocks (like Pioneer drives). Thanks to
+ * Richard Hirst <srh@gpt.co.uk> for diagnosing this.
+ * Properly supply the page number field in the
+ * MODE_SELECT command.
+ * PLAYAUDIO12 is broken on the Aztech; work around it.
+ * 2.05x Aug 11, 1995 -- lots of data structure renaming/restructuring in ide.c
+ * (my apologies to Scott, but now ide-cd.c is independent)
+ * 3.00 Aug 22, 1995 -- Implement CDROMMULTISESSION ioctl.
+ * Implement CDROMREADAUDIO ioctl (UNTESTED).
+ * Use input_ide_data() and output_ide_data().
+ * Add door locking.
+ * Fix usage count leak in cdrom_open, which happened
+ * when a read-write mount was attempted.
+ * Try to load the disk on open.
+ * Implement CDROMEJECT_SW ioctl (off by default).
+ * Read total cdrom capacity during open.
+ * Rearrange logic in cdrom_decode_status. Issue
+ * request sense commands for failed packet commands
+ * from here instead of from cdrom_queue_packet_command.
+ * Fix a race condition in retrieving error information.
+ * Suppress printing normal unit attention errors and
+ * some drive not ready errors.
+ * Implement CDROMVOLREAD ioctl.
+ * Implement CDROMREADMODE1/2 ioctls.
+ * Fix race condition in setting up interrupt handlers
+ * when the `serialize' option is used.
+ * 3.01 Sep 2, 1995 -- Fix ordering of reenabling interrupts in
+ * cdrom_queue_request.
+ * Another try at using ide_[input,output]_data.
+ * 3.02 Sep 16, 1995 -- Stick total disk capacity in partition table as well.
+ * Make VERBOSE_IDE_CD_ERRORS dump failed command again.
+ * Dump out more information for ILLEGAL REQUEST errs.
+ * Fix handling of errors occurring before the
+ * packet command is transferred.
+ * Fix transfers with odd bytelengths.
+ * 3.03 Oct 27, 1995 -- Some Creative drives have an id of just `CD'.
+ * `DCI-2S10' drives are broken too.
+ * 3.04 Nov 20, 1995 -- So are Vertos drives.
+ * 3.05 Dec 1, 1995 -- Changes to go with overhaul of ide.c and ide-tape.c
+ * 3.06 Dec 16, 1995 -- Add support needed for partitions.
+ * More workarounds for Vertos bugs (based on patches
+ * from Holger Dietze <dietze@aix520.informatik.uni-leipzig.de>).
+ * Try to eliminate byteorder assumptions.
+ * Use atapi_cdrom_subchnl struct definition.
+ * Add STANDARD_ATAPI compilation option.
+ * 3.07 Jan 29, 1996 -- More twiddling for broken drives: Sony 55D,
+ * Vertos 300.
+ * Add NO_DOOR_LOCKING configuration option.
+ * Handle drive_cmd requests w/NULL args (for hdparm -t).
+ * Work around sporadic Sony55e audio play problem.
+ * 3.07a Feb 11, 1996 -- check drive->id for NULL before dereferencing, to fix
+ * problem with "hde=cdrom" with no drive present. -ml
+ * 3.08 Mar 6, 1996 -- More Vertos workarounds.
+ * 3.09 Apr 5, 1996 -- Add CDROMCLOSETRAY ioctl.
+ * Switch to using MSF addressing for audio commands.
+ * Reformat to match kernel tabbing style.
+ * Add CDROM_GET_UPC ioctl.
+ * 3.10 Apr 10, 1996 -- Fix compilation error with STANDARD_ATAPI.
+ * 3.11 Apr 29, 1996 -- Patch from Heiko Eissfeldt <heiko@colossus.escape.de>
+ * to remove redundant verify_area calls.
+ * 3.12 May 7, 1996 -- Rudimentary changer support. Based on patches
+ * from Gerhard Zuber <zuber@berlin.snafu.de>.
+ * Let open succeed even if there's no loaded disc.
+ * 3.13 May 19, 1996 -- Fixes for changer code.
+ * 3.14 May 29, 1996 -- Add work-around for Vertos 600.
+ * (From Hennus Bergman <hennus@sky.ow.nl>.)
+ * 3.15 July 2, 1996 -- Added support for Sanyo 3 CD changers
+ * from Ben Galliart <bgallia@luc.edu> with
+ * special help from Jeff Lightfoot
+ * <jeffml@pobox.com>
+ * 3.15a July 9, 1996 -- Improved Sanyo 3 CD changer identification
+ * 3.16 Jul 28, 1996 -- Fix from Gadi to reduce kernel stack usage for ioctl.
+ * 3.17 Sep 17, 1996 -- Tweak audio reads for some drives.
+ * Start changing CDROMLOADFROMSLOT to CDROM_SELECT_DISC.
+ * 3.18 Oct 31, 1996 -- Added module and DMA support.
+ *
+ *
+ * 4.00 Nov 5, 1996 -- New ide-cd maintainer,
+ * Erik B. Andersen <andersee@debian.org>
+ * -- Newer Creative drives don't always set the error
+ * register correctly. Make sure we see media changes
+ * regardless.
+ * -- Integrate with generic cdrom driver.
+ * -- CDROMGETSPINDOWN and CDROMSETSPINDOWN ioctls, based on
+ * a patch from Ciro Cattuto <>.
+ * -- Call set_device_ro.
+ * -- Implement CDROMMECHANISMSTATUS and CDROMSLOTTABLE
+ * ioctls, based on patch by Erik Andersen
+ * -- Add some probes of drive capability during setup.
+ *
+ * 4.01 Nov 11, 1996 -- Split into ide-cd.c and ide-cd.h
+ * -- Removed CDROMMECHANISMSTATUS and CDROMSLOTTABLE
+ * ioctls in favor of a generalized approach
+ * using the generic cdrom driver.
+ * -- Fully integrated with the 2.1.X kernel.
+ * -- Other stuff that I forgot (lots of changes)
+ *
+ * 4.02 Dec 01, 1996 -- Applied patch from Gadi Oxman <gadio@netvision.net.il>
+ * to fix the drive door locking problems.
+ *
+ * 4.03 Dec 04, 1996 -- Added DSC overlap support.
+ * 4.04 Dec 29, 1996 -- Added CDROMREADRAW ioclt based on patch
+ * by Ales Makarov (xmakarov@sun.felk.cvut.cz)
+ *
+ * 4.05 Nov 20, 1997 -- Modified to print more drive info on init
+ * Minor other changes
+ * Fix errors on CDROMSTOP (If you have a "Dolphin",
+ * you must define IHAVEADOLPHIN)
+ * Added identifier so new Sanyo CD-changer works
+ * Better detection if door locking isn't supported
+ *
+ * 4.06 Dec 17, 1997 -- fixed endless "tray open" messages -ml
+ * 4.07 Dec 17, 1997 -- fallback to set pc->stat on "tray open"
+ * 4.08 Dec 18, 1997 -- spew less noise when tray is empty
+ * -- fix speed display for ACER 24X, 18X
+ * 4.09 Jan 04, 1998 -- fix handling of the last block so we return
+ * an end of file instead of an I/O error (Gadi)
+ * 4.10 Jan 24, 1998 -- fixed a bug so now changers can change to a new
+ * slot when there is no disc in the current slot.
+ * -- Fixed a memory leak where info->changer_info was
+ * malloc'ed but never free'd when closing the device.
+ * -- Cleaned up the global namespace a bit by making more
+ * functions static that should already have been.
+ * 4.11 Mar 12, 1998 -- Added support for the CDROM_SELECT_SPEED ioctl
+ * based on a patch for 2.0.33 by Jelle Foks
+ * <jelle@scintilla.utwente.nl>, a patch for 2.0.33
+ * by Toni Giorgino <toni@pcape2.pi.infn.it>, the SCSI
+ * version, and my own efforts. -erik
+ * -- Fixed a stupid bug which egcs was kind enough to
+ * inform me of where "Illegal mode for this track"
+ * was never returned due to a comparison on data
+ * types of limited range.
+ * 4.12 Mar 29, 1998 -- Fixed bug in CDROM_SELECT_SPEED so write speed is
+ * now set ionly for CD-R and CD-RW drives. I had
+ * removed this support because it produced errors.
+ * It produced errors _only_ for non-writers. duh.
+ * 4.13 May 05, 1998 -- Suppress useless "in progress of becoming ready"
+ * messages, since this is not an error.
+ * -- Change error messages to be const
+ * -- Remove a "\t" which looks ugly in the syslogs
+ * 4.14 July 17, 1998 -- Change to pointing to .ps version of ATAPI spec
+ * since the .pdf version doesn't seem to work...
+ * -- Updated the TODO list to something more current.
+ *
+ * 4.15 Aug 25, 1998 -- Updated ide-cd.h to respect mechine endianess,
+ * patch thanks to "Eddie C. Dost" <ecd@skynet.be>
+ *
+ * 4.50 Oct 19, 1998 -- New maintainers!
+ * Jens Axboe <axboe@image.dk>
+ * Chris Zwilling <chris@cloudnet.com>
+ *
+ * 4.51 Dec 23, 1998 -- Jens Axboe <axboe@image.dk>
+ * - ide_cdrom_reset enabled since the ide subsystem
+ * handles resets fine now. <axboe@image.dk>
+ * - Transfer size fix for Samsung CD-ROMs, thanks to
+ * "Ville Hallik" <ville.hallik@mail.ee>.
+ * - other minor stuff.
+ *
+ * 4.52 Jan 19, 1999 -- Jens Axboe <axboe@image.dk>
+ * - Detect DVD-ROM/RAM drives
+ *
+ * 4.53 Feb 22, 1999 - Include other model Samsung and one Goldstar
+ * drive in transfer size limit.
+ * - Fix the I/O error when doing eject without a medium
+ * loaded on some drives.
+ * - CDROMREADMODE2 is now implemented through
+ * CDROMREADRAW, since many drives don't support
+ * MODE2 (even though ATAPI 2.6 says they must).
+ * - Added ignore parameter to ide-cd (as a module), eg
+ * insmod ide-cd ignore='hda hdb'
+ * Useful when using ide-cd in conjunction with
+ * ide-scsi. TODO: non-modular way of doing the
+ * same.
+ *
+ * 4.54 Aug 5, 1999 - Support for MMC2 class commands through the generic
+ * packet interface to cdrom.c.
+ * - Unified audio ioctl support, most of it.
+ * - cleaned up various deprecated verify_area().
+ * - Added ide_cdrom_packet() as the interface for
+ * the Uniform generic_packet().
+ * - bunch of other stuff, will fill in logs later.
+ * - report 1 slot for non-changers, like the other
+ * cd-rom drivers. don't report select disc for
+ * non-changers as well.
+ * - mask out audio playing, if the device can't do it.
+ *
+ * 4.55 Sep 1, 1999 - Eliminated the rest of the audio ioctls, except
+ * for CDROMREADTOC[ENTRY|HEADER]. Some of the drivers
+ * use this independently of the actual audio handling.
+ * They will disappear later when I get the time to
+ * do it cleanly.
+ * - Minimize the TOC reading - only do it when we
+ * know a media change has occured.
+ * - Moved all the CDROMREADx ioctls to the Uniform layer.
+ * - Heiko Eissfeldt <heiko@colossus.escape.de> supplied
+ * some fixes for CDI.
+ * - CD-ROM leaving door locked fix from Andries
+ * Brouwer <Andries.Brouwer@cwi.nl>
+ * - Erik Andersen <andersen@xmission.com> unified
+ * commands across the various drivers and how
+ * sense errors are handled.
+ *
+ * 4.56 Sep 12, 1999 - Removed changer support - it is now in the
+ * Uniform layer.
+ * - Added partition based multisession handling.
+ * - Mode sense and mode select moved to the
+ * Uniform layer.
+ * - Fixed a problem with WPI CDS-32X drive - it
+ * failed the capabilities
+ *
+ *
+ *************************************************************************/
+
+#define IDECD_VERSION "4.56"
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/cdrom.h>
+#include <linux/ide.h>
+
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/byteorder.h>
+#include <asm/uaccess.h>
+#include <asm/unaligned.h>
+
+#include "ide-cd.h"
+
+/****************************************************************************
+ * Generic packet command support and error handling routines.
+ */
+
+/* Mark that we've seen a media change, and invalidate our internal
+ buffers. */
+static void cdrom_saw_media_change (ide_drive_t *drive)
+{
+ struct cdrom_info *info = drive->driver_data;
+
+ CDROM_STATE_FLAGS (drive)->media_changed = 1;
+ CDROM_STATE_FLAGS (drive)->toc_valid = 0;
+ info->nsectors_buffered = 0;
+}
+
+
+static
+void cdrom_analyze_sense_data (ide_drive_t *drive, struct request_sense *reqbuf,
+ struct packet_command *failed_command)
+{
+ if (reqbuf->sense_key == NOT_READY ||
+ reqbuf->sense_key == UNIT_ATTENTION) {
+ /* Make good and sure we've seen this potential media change.
+ Some drives (i.e. Creative) fail to present the correct
+ sense key in the error register. */
+ cdrom_saw_media_change (drive);
+
+
+ /* Don't print not ready or unit attention errors for
+ READ_SUBCHANNEL. Workman (and probably other programs)
+ uses this command to poll the drive, and we don't want
+ to fill the syslog with useless errors. */
+ if (failed_command &&
+ failed_command->c[0] == GPCMD_READ_SUBCHANNEL)
+ return;
+ }
+
+ if (reqbuf->error_code == 0x70 && reqbuf->sense_key == 0x02
+ && ((reqbuf->asc == 0x3a && reqbuf->ascq == 0x00) ||
+ (reqbuf->asc == 0x04 && reqbuf->ascq == 0x01)))
+ {
+ /*
+ * Suppress the following errors:
+ * "Medium not present", "in progress of becoming ready",
+ * and "writing" to keep the noise level down to a dull roar.
+ */
+ return;
+ }
+
+#if VERBOSE_IDE_CD_ERRORS
+ {
+ int i;
+ const char *s;
+ char buf[80];
+
+ printk ("ATAPI device %s:\n", drive->name);
+ if (reqbuf->error_code==0x70)
+ printk(" Error: ");
+ else if (reqbuf->error_code==0x71)
+ printk(" Deferred Error: ");
+ else
+ printk(" Unknown Error Type: ");
+
+ if ( reqbuf->sense_key < ARY_LEN (sense_key_texts))
+ s = sense_key_texts[reqbuf->sense_key];
+ else
+ s = "bad sense key!";
+
+ printk ("%s -- (Sense key=0x%02x)\n", s, reqbuf->sense_key);
+
+ if (reqbuf->asc == 0x40) {
+ sprintf (buf, "Diagnostic failure on component 0x%02x",
+ reqbuf->ascq);
+ s = buf;
+ } else {
+ int lo=0, mid, hi=ARY_LEN (sense_data_texts);
+ unsigned long key = (reqbuf->sense_key << 16);
+ key |= (reqbuf->asc << 8);
+ if ( ! (reqbuf->ascq >= 0x80 && reqbuf->ascq <= 0xdd) )
+ key |= reqbuf->ascq;
+ s = NULL;
+
+ while (hi > lo) {
+ mid = (lo + hi) / 2;
+ if (sense_data_texts[mid].asc_ascq == key ||
+ sense_data_texts[mid].asc_ascq == (0xff0000|key)) {
+ s = sense_data_texts[mid].text;
+ break;
+ }
+ else if (sense_data_texts[mid].asc_ascq > key)
+ hi = mid;
+ else
+ lo = mid+1;
+ }
+ }
+
+ if (s == NULL) {
+ if (reqbuf->asc > 0x80)
+ s = "(vendor-specific error)";
+ else
+ s = "(reserved error code)";
+ }
+
+ printk (" %s -- (asc=0x%02x, ascq=0x%02x)\n",
+ s, reqbuf->asc, reqbuf->ascq);
+
+ if (failed_command != NULL) {
+
+ int lo=0, mid, hi= ARY_LEN (packet_command_texts);
+ s = NULL;
+
+ while (hi > lo) {
+ mid = (lo + hi) / 2;
+ if (packet_command_texts[mid].packet_command == failed_command->c[0]) {
+ s = packet_command_texts[mid].text;
+ break;
+ }
+ else if (packet_command_texts[mid].packet_command > failed_command->c[0])
+ hi = mid;
+ else
+ lo = mid+1;
+ }
+
+ printk (" The failed \"%s\" packet command was: \n \"", s);
+ for (i=0; i<sizeof (failed_command->c); i++)
+ printk ("%02x ", failed_command->c[i]);
+ printk ("\"\n");
+ }
+
+ /* The SKSV bit specifies validity of the sense_key_specific
+ * in the next two commands. It is bit 7 of the first byte.
+ * In the case of NOT_READY, if SKSV is set the drive can
+ * give us nice ETA readings.
+ */
+ if (reqbuf->sense_key == NOT_READY && (reqbuf->sks[0] & 0x80)) {
+ int progress = (reqbuf->sks[1] << 8 | reqbuf->sks[2]) * 100;
+ printk(" Command is %02d%% complete\n", progress / 0xffff);
+
+ }
+
+ if (reqbuf->sense_key == ILLEGAL_REQUEST &&
+ (reqbuf->sks[0] & 0x80) != 0) {
+ printk (" Error in %s byte %d",
+ (reqbuf->sks[0] & 0x40) != 0 ?
+ "command packet" : "command data",
+ (reqbuf->sks[1] << 8) + reqbuf->sks[2]);
+
+ if ((reqbuf->sks[0] & 0x40) != 0)
+ printk (" bit %d", reqbuf->sks[0] & 0x07);
+
+ printk ("\n");
+ }
+ }
+
+#else /* not VERBOSE_IDE_CD_ERRORS */
+
+ /* Suppress printing unit attention and `in progress of becoming ready'
+ errors when we're not being verbose. */
+
+ if (reqbuf->sense_key == UNIT_ATTENTION ||
+ (reqbuf->sense_key == NOT_READY && (reqbuf->asc == 4 ||
+ reqbuf->asc == 0x3a)))
+ return;
+
+ printk ("%s: error code: 0x%02x sense_key: 0x%02x asc: 0x%02x ascq: 0x%02x\n",
+ drive->name,
+ reqbuf->error_code, reqbuf->sense_key,
+ reqbuf->asc, reqbuf->ascq);
+#endif /* not VERBOSE_IDE_CD_ERRORS */
+}
+
+static void cdrom_queue_request_sense (ide_drive_t *drive,
+ struct semaphore *sem,
+ struct packet_command *failed_command)
+{
+ struct cdrom_info *info = drive->driver_data;
+ struct request *rq;
+ struct packet_command *pc;
+
+ /* Make up a new request to retrieve sense information. */
+ pc = &info->request_sense_pc;
+ memset(pc, 0, sizeof (*pc));
+
+ pc->c[0] = GPCMD_REQUEST_SENSE;
+
+ /* just get the first 18 bytes of the sense info, there might not
+ * be more available */
+ pc->c[4] = pc->buflen = 18;
+ pc->buffer = (char *)&info->sense_data;
+ pc->sense_data = (struct request_sense *)failed_command;
+
+ /* stuff the sense request in front of our current request */
+ rq = &info->request_sense_request;
+ ide_init_drive_cmd (rq);
+ rq->cmd = REQUEST_SENSE_COMMAND;
+ rq->buffer = (char *)pc;
+ rq->sem = sem;
+ (void) ide_do_drive_cmd (drive, rq, ide_preempt);
+}
+
+
+static void cdrom_end_request (int uptodate, ide_drive_t *drive)
+{
+ struct request *rq = HWGROUP(drive)->rq;
+
+ if (rq->cmd == REQUEST_SENSE_COMMAND && uptodate) {
+ struct packet_command *pc = (struct packet_command *)
+ rq->buffer;
+ cdrom_analyze_sense_data (drive,
+ (struct request_sense *) (pc->buffer - pc->c[4]),
+ (struct packet_command *) pc->sense_data);
+ }
+ if (rq->cmd == READ && !rq->current_nr_sectors)
+ uptodate = 1;
+
+ ide_end_request (uptodate, HWGROUP(drive));
+}
+
+
+/* Returns 0 if the request should be continued.
+ Returns 1 if the request was ended. */
+static int cdrom_decode_status (ide_startstop_t *startstop, ide_drive_t *drive, int good_stat,
+ int *stat_ret)
+{
+ struct request *rq = HWGROUP(drive)->rq;
+ int stat, cmd, err, sense_key;
+ struct packet_command *pc = (struct packet_command *) rq->buffer;
+
+ /* Check for errors. */
+ stat = GET_STAT();
+ *stat_ret = stat;
+
+ if (OK_STAT (stat, good_stat, BAD_R_STAT))
+ return 0;
+
+ /* Get the IDE error register. */
+ err = GET_ERR();
+ sense_key = err >> 4;
+
+ if (rq == NULL)
+ printk ("%s: missing request in cdrom_decode_status\n",
+ drive->name);
+ else {
+ cmd = rq->cmd;
+
+ if (cmd == REQUEST_SENSE_COMMAND) {
+ /* We got an error trying to get sense info
+ from the drive (probably while trying
+ to recover from a former error). Just give up. */
+
+ pc->stat = 1;
+ cdrom_end_request (1, drive);
+ *startstop = ide_error (drive, "request sense failure", stat);
+ return 1;
+
+ } else if (cmd == PACKET_COMMAND) {
+ /* All other functions, except for READ. */
+
+ struct semaphore *sem = NULL;
+
+ /* Check for tray open. */
+ if (sense_key == NOT_READY) {
+ cdrom_saw_media_change (drive);
+ } else if (sense_key == UNIT_ATTENTION) {
+ /* Check for media change. */
+ cdrom_saw_media_change (drive);
+ /*printk("%s: media changed\n",drive->name);*/
+ return 0;
+ } else {
+ /* Otherwise, print an error. */
+ ide_dump_status (drive, "packet command error",
+ stat);
+ }
+
+ /* Set the error flag and complete the request.
+ Then, if we have a CHECK CONDITION status,
+ queue a request sense command. We must be careful,
+ though: we don't want the thread in
+ cdrom_queue_packet_command to wake up until
+ the request sense has completed. We do this
+ by transferring the semaphore from the packet
+ command request to the request sense request. */
+
+ if ((stat & ERR_STAT) != 0) {
+ sem = rq->sem;
+ rq->sem = NULL;
+ }
+
+ pc->stat = 1;
+ cdrom_end_request (1, drive);
+
+ if ((stat & ERR_STAT) != 0)
+ cdrom_queue_request_sense(drive, sem, pc);
+ } else {
+ /* Handle errors from READ requests. */
+
+ if (sense_key == NOT_READY) {
+ /* Tray open. */
+ cdrom_saw_media_change (drive);
+
+ /* Fail the request. */
+ printk ("%s: tray open\n", drive->name);
+ cdrom_end_request (0, drive);
+ } else if (sense_key == UNIT_ATTENTION) {
+ /* Media change. */
+ cdrom_saw_media_change (drive);
+
+ /* Arrange to retry the request.
+ But be sure to give up if we've retried
+ too many times. */
+ if (++rq->errors > ERROR_MAX)
+ cdrom_end_request (0, drive);
+ } else if (sense_key == ILLEGAL_REQUEST ||
+ sense_key == DATA_PROTECT) {
+ /* No point in retrying after an illegal
+ request or data protect error.*/
+ ide_dump_status (drive, "command error", stat);
+ cdrom_end_request (0, drive);
+ } else if ((err & ~ABRT_ERR) != 0) {
+ /* Go to the default handler
+ for other errors. */
+ *startstop = ide_error (drive, "cdrom_decode_status", stat);
+ return 1;
+ } else if ((++rq->errors > ERROR_MAX)) {
+ /* We've racked up too many retries. Abort. */
+ cdrom_end_request (0, drive);
+ }
+
+ /* If we got a CHECK_CONDITION status,
+ queue a request sense command. */
+ if ((stat & ERR_STAT) != 0)
+ cdrom_queue_request_sense(drive, NULL, NULL);
+ }
+ }
+
+ /* Retry, or handle the next request. */
+ *startstop = ide_stopped;
+ return 1;
+}
+
+static int cdrom_timer_expiry(ide_drive_t *drive)
+{
+ struct request *rq = HWGROUP(drive)->rq;
+ struct packet_command *pc = (struct packet_command *) rq->buffer;
+ unsigned long wait = 0;
+
+ /* blank and format can take an extremly long time to
+ * complete, if the IMMED bit was not set.
+ */
+ if (pc->c[0] == GPCMD_BLANK || pc->c[0] == GPCMD_FORMAT_UNIT)
+ wait = 60*60*HZ;
+
+ return wait;
+}
+
+/* Set up the device registers for transferring a packet command on DEV,
+ expecting to later transfer XFERLEN bytes. HANDLER is the routine
+ which actually transfers the command to the drive. If this is a
+ drq_interrupt device, this routine will arrange for HANDLER to be
+ called when the interrupt from the drive arrives. Otherwise, HANDLER
+ will be called immediately after the drive is prepared for the transfer. */
+
+static ide_startstop_t cdrom_start_packet_command (ide_drive_t *drive, int xferlen,
+ ide_handler_t *handler)
+{
+ ide_startstop_t startstop;
+ struct cdrom_info *info = drive->driver_data;
+
+ /* Wait for the controller to be idle. */
+ if (ide_wait_stat(&startstop, drive, 0, BUSY_STAT, WAIT_READY))
+ return startstop;
+
+ if (info->dma)
+ info->dma = !HWIF(drive)->dmaproc(ide_dma_read, drive);
+
+ /* Set up the controller registers. */
+ OUT_BYTE (info->dma, IDE_FEATURE_REG);
+ OUT_BYTE (0, IDE_NSECTOR_REG);
+ OUT_BYTE (0, IDE_SECTOR_REG);
+
+ OUT_BYTE (xferlen & 0xff, IDE_LCYL_REG);
+ OUT_BYTE (xferlen >> 8 , IDE_HCYL_REG);
+ if (IDE_CONTROL_REG)
+ OUT_BYTE (drive->ctl, IDE_CONTROL_REG);
+
+ if (info->dma)
+ (void) (HWIF(drive)->dmaproc(ide_dma_begin, drive));
+
+ if (CDROM_CONFIG_FLAGS (drive)->drq_interrupt) {
+ ide_set_handler (drive, handler, WAIT_CMD, cdrom_timer_expiry);
+ OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* packet command */
+ return ide_started;
+ } else {
+ OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* packet command */
+ return (*handler) (drive);
+ }
+}
+
+/* Send a packet command to DRIVE described by CMD_BUF and CMD_LEN.
+ The device registers must have already been prepared
+ by cdrom_start_packet_command.
+ HANDLER is the interrupt handler to call when the command completes
+ or there's data ready. */
+static ide_startstop_t cdrom_transfer_packet_command (ide_drive_t *drive,
+ unsigned char *cmd_buf, int cmd_len,
+ ide_handler_t *handler)
+{
+ if (CDROM_CONFIG_FLAGS (drive)->drq_interrupt) {
+ /* Here we should have been called after receiving an interrupt
+ from the device. DRQ should how be set. */
+ int stat_dum;
+ ide_startstop_t startstop;
+
+ /* Check for errors. */
+ if (cdrom_decode_status (&startstop, drive, DRQ_STAT, &stat_dum))
+ return startstop;
+ } else {
+ ide_startstop_t startstop;
+ /* Otherwise, we must wait for DRQ to get set. */
+ if (ide_wait_stat (&startstop, drive, DRQ_STAT, BUSY_STAT, WAIT_READY))
+ return startstop;
+ }
+
+ /* Arm the interrupt handler. */
+ ide_set_handler (drive, handler, WAIT_CMD, cdrom_timer_expiry);
+
+ /* Send the command to the device. */
+ atapi_output_bytes (drive, cmd_buf, cmd_len);
+
+ return ide_started;
+}
+
+
+
+/****************************************************************************
+ * Block read functions.
+ */
+
+/*
+ * Buffer up to SECTORS_TO_TRANSFER sectors from the drive in our sector
+ * buffer. Once the first sector is added, any subsequent sectors are
+ * assumed to be continuous (until the buffer is cleared). For the first
+ * sector added, SECTOR is its sector number. (SECTOR is then ignored until
+ * the buffer is cleared.)
+ */
+static void cdrom_buffer_sectors (ide_drive_t *drive, unsigned long sector,
+ int sectors_to_transfer)
+{
+ struct cdrom_info *info = drive->driver_data;
+
+ /* Number of sectors to read into the buffer. */
+ int sectors_to_buffer = MIN (sectors_to_transfer,
+ (SECTOR_BUFFER_SIZE >> SECTOR_BITS) -
+ info->nsectors_buffered);
+
+ char *dest;
+
+ /* If we couldn't get a buffer, don't try to buffer anything... */
+ if (info->buffer == NULL)
+ sectors_to_buffer = 0;
+
+ /* If this is the first sector in the buffer, remember its number. */
+ if (info->nsectors_buffered == 0)
+ info->sector_buffered = sector;
+
+ /* Read the data into the buffer. */
+ dest = info->buffer + info->nsectors_buffered * SECTOR_SIZE;
+ while (sectors_to_buffer > 0) {
+ atapi_input_bytes (drive, dest, SECTOR_SIZE);
+ --sectors_to_buffer;
+ --sectors_to_transfer;
+ ++info->nsectors_buffered;
+ dest += SECTOR_SIZE;
+ }
+
+ /* Throw away any remaining data. */
+ while (sectors_to_transfer > 0) {
+ char dum[SECTOR_SIZE];
+ atapi_input_bytes (drive, dum, sizeof (dum));
+ --sectors_to_transfer;
+ }
+}
+
+/*
+ * Check the contents of the interrupt reason register from the cdrom
+ * and attempt to recover if there are problems. Returns 0 if everything's
+ * ok; nonzero if the request has been terminated.
+ */
+static inline
+int cdrom_read_check_ireason (ide_drive_t *drive, int len, int ireason)
+{
+ ireason &= 3;
+ if (ireason == 2) return 0;
+
+ if (ireason == 0) {
+ /* Whoops... The drive is expecting to receive data from us! */
+ printk ("%s: cdrom_read_intr: "
+ "Drive wants to transfer data the wrong way!\n",
+ drive->name);
+
+ /* Throw some data at the drive so it doesn't hang
+ and quit this request. */
+ while (len > 0) {
+ int dum = 0;
+ atapi_output_bytes (drive, &dum, sizeof (dum));
+ len -= sizeof (dum);
+ }
+ } else if (ireason == 1) {
+ /* Some drives (ASUS) seem to tell us that status
+ * info is available. just get it and ignore.
+ */
+ GET_STAT();
+ return 0;
+ } else {
+ /* Drive wants a command packet, or invalid ireason... */
+ printk ("%s: cdrom_read_intr: bad interrupt reason %d\n",
+ drive->name, ireason);
+ }
+
+ cdrom_end_request (0, drive);
+ return -1;
+}
+
+/*
+ * Interrupt routine. Called when a read request has completed.
+ */
+static ide_startstop_t cdrom_read_intr (ide_drive_t *drive)
+{
+ int stat;
+ int ireason, len, sectors_to_transfer, nskip;
+ struct cdrom_info *info = drive->driver_data;
+ int i, dma = info->dma, dma_error = 0;
+ ide_startstop_t startstop;
+
+ struct request *rq = HWGROUP(drive)->rq;
+
+ /* Check for errors. */
+ if (dma) {
+ info->dma = 0;
+ if ((dma_error = HWIF(drive)->dmaproc(ide_dma_end, drive)))
+ HWIF(drive)->dmaproc(ide_dma_off, drive);
+ }
+
+ if (cdrom_decode_status (&startstop, drive, 0, &stat))
+ return startstop;
+
+ if (dma) {
+ if (!dma_error) {
+ for (i = rq->nr_sectors; i > 0;) {
+ i -= rq->current_nr_sectors;
+ ide_end_request(1, HWGROUP(drive));
+ }
+ return ide_stopped;
+ } else
+ return ide_error (drive, "dma error", stat);
+ }
+
+ /* Read the interrupt reason and the transfer length. */
+ ireason = IN_BYTE (IDE_NSECTOR_REG);
+ len = IN_BYTE (IDE_LCYL_REG) + 256 * IN_BYTE (IDE_HCYL_REG);
+
+ /* If DRQ is clear, the command has completed. */
+ if ((stat & DRQ_STAT) == 0) {
+ /* If we're not done filling the current buffer, complain.
+ Otherwise, complete the command normally. */
+ if (rq->current_nr_sectors > 0) {
+ printk ("%s: cdrom_read_intr: data underrun (%ld blocks)\n",
+ drive->name, rq->current_nr_sectors);
+ cdrom_end_request (0, drive);
+ } else
+ cdrom_end_request (1, drive);
+ return ide_stopped;
+ }
+
+ /* Check that the drive is expecting to do the same thing we are. */
+ if (cdrom_read_check_ireason (drive, len, ireason))
+ return ide_stopped;
+
+ /* Assume that the drive will always provide data in multiples
+ of at least SECTOR_SIZE, as it gets hairy to keep track
+ of the transfers otherwise. */
+ if ((len % SECTOR_SIZE) != 0) {
+ printk ("%s: cdrom_read_intr: Bad transfer size %d\n",
+ drive->name, len);
+ if (CDROM_CONFIG_FLAGS (drive)->limit_nframes)
+ printk (" This drive is not supported by this version of the driver\n");
+ else {
+ printk (" Trying to limit transfer sizes\n");
+ CDROM_CONFIG_FLAGS (drive)->limit_nframes = 1;
+ }
+ cdrom_end_request (0, drive);
+ return ide_stopped;
+ }
+
+ /* The number of sectors we need to read from the drive. */
+ sectors_to_transfer = len / SECTOR_SIZE;
+
+ /* First, figure out if we need to bit-bucket
+ any of the leading sectors. */
+ nskip = MIN ((int)(rq->current_nr_sectors - (rq->bh->b_size >> SECTOR_BITS)),
+ sectors_to_transfer);
+
+ while (nskip > 0) {
+ /* We need to throw away a sector. */
+ char dum[SECTOR_SIZE];
+ atapi_input_bytes (drive, dum, sizeof (dum));
+
+ --rq->current_nr_sectors;
+ --nskip;
+ --sectors_to_transfer;
+ }
+
+ /* Now loop while we still have data to read from the drive. */
+ while (sectors_to_transfer > 0) {
+ int this_transfer;
+
+ /* If we've filled the present buffer but there's another
+ chained buffer after it, move on. */
+ if (rq->current_nr_sectors == 0 &&
+ rq->nr_sectors > 0)
+ cdrom_end_request (1, drive);
+
+ /* If the buffers are full, cache the rest of the data in our
+ internal buffer. */
+ if (rq->current_nr_sectors == 0) {
+ cdrom_buffer_sectors(drive, rq->sector, sectors_to_transfer);
+ sectors_to_transfer = 0;
+ } else {
+ /* Transfer data to the buffers.
+ Figure out how many sectors we can transfer
+ to the current buffer. */
+ this_transfer = MIN (sectors_to_transfer,
+ rq->current_nr_sectors);
+
+ /* Read this_transfer sectors
+ into the current buffer. */
+ while (this_transfer > 0) {
+ atapi_input_bytes(drive, rq->buffer, SECTOR_SIZE);
+ rq->buffer += SECTOR_SIZE;
+ --rq->nr_sectors;
+ --rq->current_nr_sectors;
+ ++rq->sector;
+ --this_transfer;
+ --sectors_to_transfer;
+ }
+ }
+ }
+
+ /* Done moving data!
+ Wait for another interrupt. */
+ ide_set_handler(drive, &cdrom_read_intr, WAIT_CMD, NULL);
+ return ide_started;
+}
+
+/*
+ * Try to satisfy some of the current read request from our cached data.
+ * Returns nonzero if the request has been completed, zero otherwise.
+ */
+static int cdrom_read_from_buffer (ide_drive_t *drive)
+{
+ struct cdrom_info *info = drive->driver_data;
+ struct request *rq = HWGROUP(drive)->rq;
+
+ /* Can't do anything if there's no buffer. */
+ if (info->buffer == NULL) return 0;
+
+ /* Loop while this request needs data and the next block is present
+ in our cache. */
+ while (rq->nr_sectors > 0 &&
+ rq->sector >= info->sector_buffered &&
+ rq->sector < info->sector_buffered + info->nsectors_buffered) {
+ if (rq->current_nr_sectors == 0)
+ cdrom_end_request (1, drive);
+
+ memcpy (rq->buffer,
+ info->buffer +
+ (rq->sector - info->sector_buffered) * SECTOR_SIZE,
+ SECTOR_SIZE);
+ rq->buffer += SECTOR_SIZE;
+ --rq->current_nr_sectors;
+ --rq->nr_sectors;
+ ++rq->sector;
+ }
+
+ /* If we've satisfied the current request,
+ terminate it successfully. */
+ if (rq->nr_sectors == 0) {
+ cdrom_end_request (1, drive);
+ return -1;
+ }
+
+ /* Move on to the next buffer if needed. */
+ if (rq->current_nr_sectors == 0)
+ cdrom_end_request (1, drive);
+
+ /* If this condition does not hold, then the kluge i use to
+ represent the number of sectors to skip at the start of a transfer
+ will fail. I think that this will never happen, but let's be
+ paranoid and check. */
+ if (rq->current_nr_sectors < (rq->bh->b_size >> SECTOR_BITS) &&
+ (rq->sector % SECTORS_PER_FRAME) != 0) {
+ printk ("%s: cdrom_read_from_buffer: buffer botch (%ld)\n",
+ drive->name, rq->sector);
+ cdrom_end_request (0, drive);
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Routine to send a read packet command to the drive.
+ * This is usually called directly from cdrom_start_read.
+ * However, for drq_interrupt devices, it is called from an interrupt
+ * when the drive is ready to accept the command.
+ */
+static ide_startstop_t cdrom_start_read_continuation (ide_drive_t *drive)
+{
+ struct packet_command pc;
+ struct request *rq = HWGROUP(drive)->rq;
+ int nsect, sector, nframes, frame, nskip;
+
+ /* Number of sectors to transfer. */
+ nsect = rq->nr_sectors;
+
+ /* Starting sector. */
+ sector = rq->sector;
+
+ /* If the requested sector doesn't start on a cdrom block boundary,
+ we must adjust the start of the transfer so that it does,
+ and remember to skip the first few sectors.
+ If the CURRENT_NR_SECTORS field is larger than the size
+ of the buffer, it will mean that we're to skip a number
+ of sectors equal to the amount by which CURRENT_NR_SECTORS
+ is larger than the buffer size. */
+ nskip = (sector % SECTORS_PER_FRAME);
+ if (nskip > 0) {
+ /* Sanity check... */
+ if (rq->current_nr_sectors != (rq->bh->b_size >> SECTOR_BITS) &&
+ (rq->sector % CD_FRAMESIZE != 0)) {
+ printk ("%s: cdrom_start_read_continuation: buffer botch (%lu)\n",
+ drive->name, rq->current_nr_sectors);
+ cdrom_end_request (0, drive);
+ return ide_stopped;
+ }
+ sector -= nskip;
+ nsect += nskip;
+ rq->current_nr_sectors += nskip;
+ }
+
+ /* Convert from sectors to cdrom blocks, rounding up the transfer
+ length if needed. */
+ nframes = (nsect + SECTORS_PER_FRAME-1) / SECTORS_PER_FRAME;
+ frame = sector / SECTORS_PER_FRAME;
+
+ /* Largest number of frames was can transfer at once is 64k-1. For
+ some drives we need to limit this even more. */
+ nframes = MIN (nframes, (CDROM_CONFIG_FLAGS (drive)->limit_nframes) ?
+ (65534 / CD_FRAMESIZE) : 65535);
+
+ /* Set up the command */
+ memset (&pc.c, 0, sizeof (pc.c));
+ pc.c[0] = GPCMD_READ_10;
+ pc.c[7] = (nframes >> 8);
+ pc.c[8] = (nframes & 0xff);
+ put_unaligned(htonl (frame), (unsigned int *) &pc.c[2]);
+
+ /* Send the command to the drive and return. */
+ return cdrom_transfer_packet_command (drive, pc.c, sizeof (pc.c),
+ &cdrom_read_intr);
+}
+
+
+#define IDECD_SEEK_THRESHOLD (1000) /* 1000 blocks */
+#define IDECD_SEEK_TIMER (5 * WAIT_MIN_SLEEP) /* 100 ms */
+#define IDECD_SEEK_TIMEOUT WAIT_CMD /* 10 sec */
+
+static ide_startstop_t cdrom_seek_intr (ide_drive_t *drive)
+{
+ struct cdrom_info *info = drive->driver_data;
+ int stat;
+ static int retry = 10;
+ ide_startstop_t startstop;
+
+ if (cdrom_decode_status (&startstop, drive, 0, &stat))
+ return startstop;
+ CDROM_CONFIG_FLAGS(drive)->seeking = 1;
+
+ if (retry && jiffies - info->start_seek > IDECD_SEEK_TIMER) {
+ if (--retry == 0) {
+ printk("%s: disabled DSC seek overlap\n", drive->name);
+ drive->dsc_overlap = 0;
+ }
+ }
+ return ide_stopped;
+}
+
+static ide_startstop_t cdrom_start_seek_continuation (ide_drive_t *drive)
+{
+ struct packet_command pc;
+ struct request *rq = HWGROUP(drive)->rq;
+ int sector, frame, nskip;
+
+ sector = rq->sector;
+ nskip = (sector % SECTORS_PER_FRAME);
+ if (nskip > 0)
+ sector -= nskip;
+ frame = sector / SECTORS_PER_FRAME;
+
+ memset (&pc.c, 0, sizeof (pc.c));
+ pc.c[0] = GPCMD_SEEK;
+ put_unaligned(cpu_to_be32(frame), (unsigned int *) &pc.c[2]);
+ return cdrom_transfer_packet_command (drive, pc.c, sizeof (pc.c), &cdrom_seek_intr);
+}
+
+static ide_startstop_t cdrom_start_seek (ide_drive_t *drive, unsigned int block)
+{
+ struct cdrom_info *info = drive->driver_data;
+
+ info->dma = 0;
+ info->start_seek = jiffies;
+ return cdrom_start_packet_command (drive, 0, cdrom_start_seek_continuation);
+}
+
+/* Fix up a possibly partially-processed request so that we can
+ start it over entirely, or even put it back on the request queue. */
+static void restore_request (struct request *rq)
+{
+ if (rq->buffer != rq->bh->b_data) {
+ int n = (rq->buffer - rq->bh->b_data) / SECTOR_SIZE;
+ rq->buffer = rq->bh->b_data;
+ rq->nr_sectors += n;
+ rq->sector -= n;
+ }
+ rq->current_nr_sectors = rq->bh->b_size >> SECTOR_BITS;
+}
+
+/*
+ * Start a read request from the CD-ROM.
+ */
+static ide_startstop_t cdrom_start_read (ide_drive_t *drive, unsigned int block)
+{
+ struct cdrom_info *info = drive->driver_data;
+ struct request *rq = HWGROUP(drive)->rq;
+ int minor = MINOR (rq->rq_dev);
+
+ /* If the request is relative to a partition, fix it up to refer to the
+ absolute address. */
+ if ((minor & PARTN_MASK) != 0) {
+ rq->sector = block;
+ minor &= ~PARTN_MASK;
+ rq->rq_dev = MKDEV (MAJOR(rq->rq_dev), minor);
+ }
+
+ /* We may be retrying this request after an error. Fix up
+ any weirdness which might be present in the request packet. */
+ restore_request (rq);
+
+ /* Satisfy whatever we can of this request from our cached sector. */
+ if (cdrom_read_from_buffer(drive))
+ return ide_stopped;
+
+ /* Clear the local sector buffer. */
+ info->nsectors_buffered = 0;
+
+ /* use dma, if possible. */
+ if (drive->using_dma && (rq->sector % SECTORS_PER_FRAME == 0) &&
+ (rq->nr_sectors % SECTORS_PER_FRAME == 0))
+ info->dma = 1;
+ else
+ info->dma = 0;
+
+ /* Start sending the read request to the drive. */
+ return cdrom_start_packet_command(drive, 32768, cdrom_start_read_continuation);
+}
+
+/****************************************************************************
+ * Execute all other packet commands.
+ */
+
+/* Forward declarations. */
+static int cdrom_lockdoor(ide_drive_t *drive, int lockflag);
+
+/* Interrupt routine for packet command completion. */
+static ide_startstop_t cdrom_pc_intr (ide_drive_t *drive)
+{
+ int ireason, len, stat, thislen;
+ struct request *rq = HWGROUP(drive)->rq;
+ struct packet_command *pc = (struct packet_command *)rq->buffer;
+ struct cdrom_info *info = drive->driver_data;
+ ide_startstop_t startstop;
+
+ pc->sense_data = &info->sense_data;
+
+ /* Check for errors. */
+ if (cdrom_decode_status (&startstop, drive, 0, &stat))
+ return startstop;
+
+ /* Read the interrupt reason and the transfer length. */
+ ireason = IN_BYTE (IDE_NSECTOR_REG);
+ len = IN_BYTE (IDE_LCYL_REG) + 256 * IN_BYTE (IDE_HCYL_REG);
+
+ /* If DRQ is clear, the command has completed.
+ Complain if we still have data left to transfer. */
+ if ((stat & DRQ_STAT) == 0) {
+ /* Some of the trailing request sense fields are optional, and
+ some drives don't send them. Sigh. */
+ if (pc->c[0] == GPCMD_REQUEST_SENSE &&
+ pc->buflen > 0 &&
+ pc->buflen <= 5) {
+ while (pc->buflen > 0) {
+ *pc->buffer++ = 0;
+ --pc->buflen;
+ }
+ }
+
+ if (pc->buflen == 0)
+ cdrom_end_request (1, drive);
+ else {
+ /* Comment this out, because this always happens
+ right after a reset occurs, and it is annoying to
+ always print expected stuff. */
+ /*
+ printk ("%s: cdrom_pc_intr: data underrun %d\n",
+ drive->name, pc->buflen);
+ */
+ pc->stat = 1;
+ cdrom_end_request (1, drive);
+ }
+ return ide_stopped;
+ }
+
+ /* Figure out how much data to transfer. */
+ thislen = pc->buflen;
+ if (thislen > len) thislen = len;
+
+ /* The drive wants to be written to. */
+ if ((ireason & 3) == 0) {
+ /* Transfer the data. */
+ atapi_output_bytes (drive, pc->buffer, thislen);
+
+ /* If we haven't moved enough data to satisfy the drive,
+ add some padding. */
+ while (len > thislen) {
+ int dum = 0;
+ atapi_output_bytes (drive, &dum, sizeof (dum));
+ len -= sizeof (dum);
+ }
+
+ /* Keep count of how much data we've moved. */
+ pc->buffer += thislen;
+ pc->buflen -= thislen;
+ }
+
+ /* Same drill for reading. */
+ else if ((ireason & 3) == 2) {
+
+ /* Transfer the data. */
+ atapi_input_bytes (drive, pc->buffer, thislen);
+
+ /* If we haven't moved enough data to satisfy the drive,
+ add some padding. */
+ while (len > thislen) {
+ int dum = 0;
+ atapi_input_bytes (drive, &dum, sizeof (dum));
+ len -= sizeof (dum);
+ }
+
+ /* Keep count of how much data we've moved. */
+ pc->buffer += thislen;
+ pc->buflen -= thislen;
+ } else {
+ printk ("%s: cdrom_pc_intr: The drive "
+ "appears confused (ireason = 0x%2x)\n",
+ drive->name, ireason);
+ pc->stat = 1;
+ }
+
+ /* Now we wait for another interrupt. */
+ ide_set_handler (drive, &cdrom_pc_intr, WAIT_CMD, cdrom_timer_expiry);
+ return ide_started;
+}
+
+
+static ide_startstop_t cdrom_do_pc_continuation (ide_drive_t *drive)
+{
+ struct request *rq = HWGROUP(drive)->rq;
+ struct packet_command *pc = (struct packet_command *)rq->buffer;
+
+ /* Send the command to the drive and return. */
+ return cdrom_transfer_packet_command (drive, pc->c,
+ sizeof (pc->c), &cdrom_pc_intr);
+}
+
+
+static ide_startstop_t cdrom_do_packet_command (ide_drive_t *drive)
+{
+ int len;
+ struct request *rq = HWGROUP(drive)->rq;
+ struct packet_command *pc = (struct packet_command *)rq->buffer;
+ struct cdrom_info *info = drive->driver_data;
+
+ info->dma = 0;
+ pc->stat = 0;
+ len = pc->buflen;
+
+ /* Start sending the command to the drive. */
+ return cdrom_start_packet_command (drive, len, cdrom_do_pc_continuation);
+}
+
+
+/* Sleep for TIME jiffies.
+ Not to be called from an interrupt handler. */
+static
+void cdrom_sleep (int time)
+{
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(time);
+}
+
+static
+int cdrom_queue_packet_command(ide_drive_t *drive, struct packet_command *pc)
+{
+ int retries = 10;
+ struct request req;
+
+ /* Start of retry loop. */
+ do {
+ ide_init_drive_cmd (&req);
+ req.cmd = PACKET_COMMAND;
+ req.buffer = (char *)pc;
+ if (ide_do_drive_cmd (drive, &req, ide_wait)) {
+ printk("%s: do_drive_cmd returned stat=%02x,err=%02x\n",
+ drive->name, req.buffer[0], req.buffer[1]);
+ /* FIXME: we should probably abort/retry or something */
+ }
+ if (pc->stat != 0) {
+ /* The request failed. Retry if it was due to a unit
+ attention status
+ (usually means media was changed). */
+ struct request_sense *reqbuf = pc->sense_data;
+
+ if (reqbuf->sense_key == UNIT_ATTENTION)
+ cdrom_saw_media_change (drive);
+ else if (reqbuf->sense_key == NOT_READY &&
+ reqbuf->asc == 4 && reqbuf->ascq != 4) {
+ /* The drive is in the process of loading
+ a disk. Retry, but wait a little to give
+ the drive time to complete the load. */
+ cdrom_sleep (HZ);
+ } else {
+ /* Otherwise, don't retry. */
+ retries = 0;
+ }
+ --retries;
+ }
+
+ /* End of retry loop. */
+ } while (pc->stat != 0 && retries >= 0);
+
+ /* Return an error if the command failed. */
+ if (pc->stat)
+ return -EIO;
+
+ /* The command succeeded. If it was anything other than
+ a request sense, eject, or door lock command,
+ and we think that the door is presently unlocked, lock it
+ again. (The door was probably unlocked via an explicit
+ CDROMEJECT ioctl.) */
+ if (CDROM_STATE_FLAGS (drive)->door_locked == 0 &&
+ (pc->c[0] != GPCMD_TEST_UNIT_READY &&
+ pc->c[0] != GPCMD_REQUEST_SENSE &&
+ pc->c[0] != GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL &&
+ pc->c[0] != GPCMD_START_STOP_UNIT &&
+ pc->c[0] != GPCMD_MODE_SENSE_10 &&
+ pc->c[0] != GPCMD_MODE_SELECT_10)) {
+ (void) cdrom_lockdoor (drive, 1);
+ }
+ return 0;
+}
+
+/****************************************************************************
+ * cdrom driver request routine.
+ */
+static ide_startstop_t
+ide_do_rw_cdrom (ide_drive_t *drive, struct request *rq, unsigned long block)
+{
+ ide_startstop_t action;
+ struct cdrom_info *info = drive->driver_data;
+
+ switch (rq->cmd) {
+ case READ: {
+ if (CDROM_CONFIG_FLAGS(drive)->seeking) {
+ unsigned long elpased = jiffies - info->start_seek;
+ int stat = GET_STAT();
+
+ if ((stat & SEEK_STAT) != SEEK_STAT) {
+ if (elpased < IDECD_SEEK_TIMEOUT) {
+ ide_stall_queue(drive, IDECD_SEEK_TIMER);
+ return ide_stopped;
+ }
+ printk ("%s: DSC timeout\n", drive->name);
+ }
+ CDROM_CONFIG_FLAGS(drive)->seeking = 0;
+ }
+ if (IDE_LARGE_SEEK(info->last_block, block, IDECD_SEEK_THRESHOLD) && drive->dsc_overlap)
+ action = cdrom_start_seek (drive, block);
+ else
+ action = cdrom_start_read (drive, block);
+ info->last_block = block;
+ return action;
+ }
+
+ case PACKET_COMMAND:
+ case REQUEST_SENSE_COMMAND: {
+ return cdrom_do_packet_command(drive);
+ }
+
+ case RESET_DRIVE_COMMAND: {
+ cdrom_end_request(1, drive);
+ return ide_do_reset(drive);
+ }
+
+ default: {
+ printk("ide-cd: bad cmd %d\n", rq -> cmd);
+ cdrom_end_request(0, drive);
+ return ide_stopped;
+ }
+ }
+}
+
+
+
+/****************************************************************************
+ * Ioctl handling.
+ *
+ * Routines which queue packet commands take as a final argument a pointer
+ * to a request_sense struct. If execution of the command results
+ * in an error with a CHECK CONDITION status, this structure will be filled
+ * with the results of the subsequent request sense command. The pointer
+ * can also be NULL, in which case no sense information is returned.
+ */
+
+#if ! STANDARD_ATAPI
+static inline
+int bin2bcd (int x)
+{
+ return (x%10) | ((x/10) << 4);
+}
+
+
+static inline
+int bcd2bin (int x)
+{
+ return (x >> 4) * 10 + (x & 0x0f);
+}
+
+static
+void msf_from_bcd (struct atapi_msf *msf)
+{
+ msf->minute = bcd2bin (msf->minute);
+ msf->second = bcd2bin (msf->second);
+ msf->frame = bcd2bin (msf->frame);
+}
+
+#endif /* not STANDARD_ATAPI */
+
+
+static inline
+void lba_to_msf (int lba, byte *m, byte *s, byte *f)
+{
+ lba += CD_MSF_OFFSET;
+ lba &= 0xffffff; /* negative lbas use only 24 bits */
+ *m = lba / (CD_SECS * CD_FRAMES);
+ lba %= (CD_SECS * CD_FRAMES);
+ *s = lba / CD_FRAMES;
+ *f = lba % CD_FRAMES;
+}
+
+
+static inline
+int msf_to_lba (byte m, byte s, byte f)
+{
+ return (((m * CD_SECS) + s) * CD_FRAMES + f) - CD_MSF_OFFSET;
+}
+
+static int cdrom_check_status (ide_drive_t *drive)
+{
+ struct packet_command pc;
+ struct cdrom_info *info = drive->driver_data;
+ struct cdrom_device_info *cdi = &info->devinfo;
+
+ memset(&pc, 0, sizeof(pc));
+
+ pc.c[0] = GPCMD_TEST_UNIT_READY;
+
+#if ! STANDARD_ATAPI
+ /* the Sanyo 3 CD changer uses byte 7 of TEST_UNIT_READY to
+ switch CDs instead of supporting the LOAD_UNLOAD opcode */
+
+ pc.c[7] = cdi->sanyo_slot % 3;
+#endif /* not STANDARD_ATAPI */
+
+ return cdrom_queue_packet_command(drive, &pc);
+}
+
+
+/* Lock the door if LOCKFLAG is nonzero; unlock it otherwise. */
+static int
+cdrom_lockdoor(ide_drive_t *drive, int lockflag)
+{
+ struct request_sense *sense;
+ struct packet_command pc;
+ int stat;
+
+ /* If the drive cannot lock the door, just pretend. */
+ if (CDROM_CONFIG_FLAGS (drive)->no_doorlock)
+ stat = 0;
+ else {
+ memset(&pc, 0, sizeof(pc));
+ pc.c[0] = GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL;
+ pc.c[4] = (lockflag != 0);
+ stat = cdrom_queue_packet_command (drive, &pc);
+ }
+
+ sense = pc.sense_data;
+
+ /* If we got an illegal field error, the drive
+ probably cannot lock the door. */
+ if (stat != 0 &&
+ sense->sense_key == ILLEGAL_REQUEST &&
+ (sense->asc == 0x24 || sense->asc == 0x20)) {
+ printk ("%s: door locking not supported\n",
+ drive->name);
+ CDROM_CONFIG_FLAGS (drive)->no_doorlock = 1;
+ stat = 0;
+ }
+
+ /* no medium, that's alright. */
+ if (stat != 0 && sense->sense_key == NOT_READY && sense->asc == 0x3a)
+ stat = 0;
+
+ if (stat == 0)
+ CDROM_STATE_FLAGS (drive)->door_locked = lockflag;
+
+ return stat;
+}
+
+
+/* Eject the disk if EJECTFLAG is 0.
+ If EJECTFLAG is 1, try to reload the disk. */
+static int cdrom_eject(ide_drive_t *drive, int ejectflag)
+{
+ struct packet_command pc;
+
+ if (CDROM_CONFIG_FLAGS(drive)->no_eject && !ejectflag)
+ return -EDRIVE_CANT_DO_THIS;
+
+ /* reload fails on some drives, if the tray is locked */
+ if (CDROM_STATE_FLAGS(drive)->door_locked && ejectflag)
+ return 0;
+
+ memset(&pc, 0, sizeof (pc));
+
+ pc.c[0] = GPCMD_START_STOP_UNIT;
+ pc.c[4] = 0x02 + (ejectflag != 0);
+ return cdrom_queue_packet_command (drive, &pc);
+}
+
+static int cdrom_read_capacity(ide_drive_t *drive, unsigned *capacity)
+{
+ struct {
+ __u32 lba;
+ __u32 blocklen;
+ } capbuf;
+
+ int stat;
+ struct packet_command pc;
+
+ memset(&pc, 0, sizeof (pc));
+
+ pc.c[0] = GPCMD_READ_CDVD_CAPACITY;
+ pc.buffer = (char *)&capbuf;
+ pc.buflen = sizeof(capbuf);
+
+ stat = cdrom_queue_packet_command(drive, &pc);
+ if (stat == 0)
+ *capacity = be32_to_cpu(capbuf.lba);
+
+ return stat;
+}
+
+static int cdrom_read_tocentry(ide_drive_t *drive, int trackno, int msf_flag,
+ int format, char *buf, int buflen)
+{
+ struct packet_command pc;
+
+ memset(&pc, 0, sizeof(pc));
+
+ pc.buffer = buf;
+ pc.buflen = buflen;
+ pc.c[0] = GPCMD_READ_TOC_PMA_ATIP;
+ pc.c[6] = trackno;
+ pc.c[7] = (buflen >> 8);
+ pc.c[8] = (buflen & 0xff);
+ pc.c[9] = (format << 6);
+
+ if (msf_flag)
+ pc.c[1] = 2;
+
+ return cdrom_queue_packet_command (drive, &pc);
+}
+
+
+/* Try to read the entire TOC for the disk into our internal buffer. */
+static int cdrom_read_toc (ide_drive_t *drive)
+{
+ int stat, ntracks, i;
+ struct cdrom_info *info = drive->driver_data;
+ struct atapi_toc *toc = info->toc;
+ int minor = drive->select.b.unit << PARTN_BITS;
+ struct {
+ struct atapi_toc_header hdr;
+ struct atapi_toc_entry ent;
+ } ms_tmp;
+
+ if (toc == NULL) {
+ /* Try to allocate space. */
+ toc = (struct atapi_toc *) kmalloc (sizeof (struct atapi_toc),
+ GFP_KERNEL);
+ info->toc = toc;
+ if (toc == NULL) {
+ printk ("%s: No cdrom TOC buffer!\n", drive->name);
+ return -ENOMEM;
+ }
+ }
+
+ /* Check to see if the existing data is still valid.
+ If it is, just return. */
+ if (CDROM_STATE_FLAGS (drive)->toc_valid)
+ (void) cdrom_check_status(drive);
+
+ if (CDROM_STATE_FLAGS (drive)->toc_valid) return 0;
+
+ /* First read just the header, so we know how long the TOC is. */
+ stat = cdrom_read_tocentry (drive, 0, 1, 0, (char *)&toc->hdr,
+ sizeof (struct atapi_toc_header));
+ if (stat) return stat;
+
+#if ! STANDARD_ATAPI
+ if (CDROM_CONFIG_FLAGS (drive)->toctracks_as_bcd) {
+ toc->hdr.first_track = bcd2bin (toc->hdr.first_track);
+ toc->hdr.last_track = bcd2bin (toc->hdr.last_track);
+ }
+#endif /* not STANDARD_ATAPI */
+
+ ntracks = toc->hdr.last_track - toc->hdr.first_track + 1;
+ if (ntracks <= 0) return -EIO;
+ if (ntracks > MAX_TRACKS) ntracks = MAX_TRACKS;
+
+ /* Now read the whole schmeer. */
+ stat = cdrom_read_tocentry (drive, toc->hdr.first_track, 1, 0, (char *)&toc->hdr,
+ sizeof (struct atapi_toc_header) +
+ (ntracks + 1) *
+ sizeof (struct atapi_toc_entry));
+
+ if (stat && toc->hdr.first_track > 1) {
+ /* Cds with CDI tracks only don't have any TOC entries,
+ despite of this the returned values are
+ first_track == last_track = number of CDI tracks + 1,
+ so that this case is indistinguishable from the same
+ layout plus an additional audio track.
+ If we get an error for the regular case, we assume
+ a CDI without additional audio tracks. In this case
+ the readable TOC is empty (CDI tracks are not included)
+ and only holds the Leadout entry. Heiko Eißfeldt */
+ ntracks = 0;
+ stat = cdrom_read_tocentry (drive, CDROM_LEADOUT, 1,
+ 0, (char *)&toc->hdr,
+ sizeof (struct atapi_toc_header) +
+ (ntracks+1) *
+ sizeof (struct atapi_toc_entry));
+ if (stat) {
+ return stat;
+ }
+#if ! STANDARD_ATAPI
+ if (CDROM_CONFIG_FLAGS (drive)->toctracks_as_bcd) {
+ toc->hdr.first_track = bin2bcd(CDROM_LEADOUT);
+ toc->hdr.last_track = bin2bcd(CDROM_LEADOUT);
+ } else
+#endif /* not STANDARD_ATAPI */
+ {
+ toc->hdr.first_track = CDROM_LEADOUT;
+ toc->hdr.last_track = CDROM_LEADOUT;
+ }
+ } else if (stat) {
+ return stat;
+ }
+ if (stat) return stat;
+
+ toc->hdr.toc_length = ntohs (toc->hdr.toc_length);
+
+#if ! STANDARD_ATAPI
+ if (CDROM_CONFIG_FLAGS (drive)->toctracks_as_bcd) {
+ toc->hdr.first_track = bcd2bin (toc->hdr.first_track);
+ toc->hdr.last_track = bcd2bin (toc->hdr.last_track);
+ }
+#endif /* not STANDARD_ATAPI */
+
+ for (i=0; i<=ntracks; i++) {
+#if ! STANDARD_ATAPI
+ if (CDROM_CONFIG_FLAGS (drive)->tocaddr_as_bcd) {
+ if (CDROM_CONFIG_FLAGS (drive)->toctracks_as_bcd)
+ toc->ent[i].track = bcd2bin (toc->ent[i].track);
+ msf_from_bcd (&toc->ent[i].addr.msf);
+ }
+#endif /* not STANDARD_ATAPI */
+ toc->ent[i].addr.lba = msf_to_lba (toc->ent[i].addr.msf.minute,
+ toc->ent[i].addr.msf.second,
+ toc->ent[i].addr.msf.frame);
+ }
+
+ /* Read the multisession information. */
+ if (toc->hdr.first_track != CDROM_LEADOUT) {
+ /* Read the multisession information. */
+ stat = cdrom_read_tocentry (drive, 0, 1, 1,
+ (char *)&ms_tmp, sizeof (ms_tmp));
+ if (stat) return stat;
+ } else {
+ ms_tmp.ent.addr.msf.minute = 0;
+ ms_tmp.ent.addr.msf.second = 2;
+ ms_tmp.ent.addr.msf.frame = 0;
+ ms_tmp.hdr.first_track = ms_tmp.hdr.last_track = CDROM_LEADOUT;
+ }
+
+#if ! STANDARD_ATAPI
+ if (CDROM_CONFIG_FLAGS (drive)->tocaddr_as_bcd)
+ msf_from_bcd (&ms_tmp.ent.addr.msf);
+#endif /* not STANDARD_ATAPI */
+
+ toc->last_session_lba = msf_to_lba (ms_tmp.ent.addr.msf.minute,
+ ms_tmp.ent.addr.msf.second,
+ ms_tmp.ent.addr.msf.frame);
+
+ toc->xa_flag = (ms_tmp.hdr.first_track != ms_tmp.hdr.last_track);
+
+ /* Now try to get the total cdrom capacity. */
+#if 0
+ stat = cdrom_get_last_written(MKDEV(HWIF(drive)->major, minor),
+ (long *)&toc->capacity);
+ if (stat)
+#endif
+ stat = cdrom_read_capacity (drive, &toc->capacity);
+ if (stat) toc->capacity = 0x1fffff;
+
+ /* for general /dev/cdrom like mounting, one big disc */
+ drive->part[0].nr_sects = toc->capacity * SECTORS_PER_FRAME;
+ HWIF(drive)->gd->sizes[minor] = (toc->capacity * SECTORS_PER_FRAME) >>
+ (BLOCK_SIZE_BITS - 9);
+
+ /* Remember that we've read this stuff. */
+ CDROM_STATE_FLAGS (drive)->toc_valid = 1;
+
+ /* should be "if multisession", but it does no harm. */
+ if (ntracks == 1)
+ return 0;
+
+ /* setup each minor to respond to a session */
+ minor++;
+ i = toc->hdr.first_track;
+ while ((i <= ntracks) && ((minor & CD_PART_MASK) < CD_PART_MAX)) {
+ drive->part[minor & PARTN_MASK].start_sect = 0;
+ drive->part[minor & PARTN_MASK].nr_sects =
+ (toc->ent[i].addr.lba *
+ SECTORS_PER_FRAME) << (BLOCK_SIZE_BITS - 9);
+ HWIF(drive)->gd->sizes[minor] = (toc->ent[i].addr.lba *
+ SECTORS_PER_FRAME) >> (BLOCK_SIZE_BITS - 9);
+ i++;
+ minor++;
+ }
+
+ return 0;
+}
+
+
+static int cdrom_read_subchannel(ide_drive_t *drive, int format, char *buf,
+ int buflen)
+{
+ struct packet_command pc;
+
+ memset(&pc, 0, sizeof(pc));
+
+ pc.buffer = buf;
+ pc.buflen = buflen;
+ pc.c[0] = GPCMD_READ_SUBCHANNEL;
+ pc.c[1] = 2; /* MSF addressing */
+ pc.c[2] = 0x40; /* request subQ data */
+ pc.c[3] = format;
+ pc.c[7] = (buflen >> 8);
+ pc.c[8] = (buflen & 0xff);
+ return cdrom_queue_packet_command(drive, &pc);
+}
+
+/* ATAPI cdrom drives are free to select the speed you request or any slower
+ rate :-( Requesting too fast a speed will _not_ produce an error. */
+static int cdrom_select_speed (ide_drive_t *drive, int speed)
+{
+ struct packet_command pc;
+ memset(&pc, 0, sizeof(pc));
+
+ if (speed == 0)
+ speed = 0xffff; /* set to max */
+ else
+ speed *= 177; /* Nx to kbytes/s */
+
+ pc.c[0] = GPCMD_SET_SPEED;
+ /* Read Drive speed in kbytes/second MSB */
+ pc.c[2] = (speed >> 8) & 0xff;
+ /* Read Drive speed in kbytes/second LSB */
+ pc.c[3] = speed & 0xff;
+ if ( CDROM_CONFIG_FLAGS(drive)->cd_r ||
+ CDROM_CONFIG_FLAGS(drive)->cd_rw ) {
+ /* Write Drive speed in kbytes/second MSB */
+ pc.c[4] = (speed >> 8) & 0xff;
+ /* Write Drive speed in kbytes/second LSB */
+ pc.c[5] = speed & 0xff;
+ }
+
+ return cdrom_queue_packet_command (drive, &pc);
+}
+
+
+static int cdrom_get_toc_entry(ide_drive_t *drive, int track,
+ struct atapi_toc_entry **ent)
+{
+ struct cdrom_info *info = drive->driver_data;
+ struct atapi_toc *toc = info->toc;
+ int ntracks;
+
+ /* Check validity of requested track number. */
+ ntracks = toc->hdr.last_track - toc->hdr.first_track + 1;
+ if (toc->hdr.first_track == CDROM_LEADOUT) ntracks = 0;
+ if (track == CDROM_LEADOUT)
+ *ent = &toc->ent[ntracks];
+ else if (track < toc->hdr.first_track ||
+ track > toc->hdr.last_track)
+ return -EINVAL;
+ else
+ *ent = &toc->ent[track - toc->hdr.first_track];
+
+ return 0;
+}
+
+
+
+
+
+/* the generic packet interface to cdrom.c */
+static int ide_cdrom_packet(struct cdrom_device_info *cdi,
+ struct cdrom_generic_command *cgc)
+{
+ struct packet_command pc;
+ ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+
+ /* here we queue the commands from the uniform CD-ROM
+ layer. the packet must be complete, as we do not
+ touch it at all. */
+ memset(&pc, 0, sizeof(pc));
+ memcpy(pc.c, cgc->cmd, CDROM_PACKET_SIZE);
+ pc.buffer = cgc->buffer;
+ pc.buflen = cgc->buflen;
+ cgc->stat = cdrom_queue_packet_command(drive, &pc);
+
+ /* There was an error, assign sense. */
+ if (cgc->stat)
+ cgc->sense = pc.sense_data;
+
+ return cgc->stat;
+}
+
+static
+int ide_cdrom_dev_ioctl (struct cdrom_device_info *cdi,
+ unsigned int cmd, unsigned long arg)
+{
+ struct cdrom_generic_command cgc;
+ char buffer[16];
+ int stat;
+
+ init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_UNKNOWN);
+
+ /* These will be moved into the Uniform layer shortly... */
+ switch (cmd) {
+ case CDROMSETSPINDOWN: {
+ char spindown;
+
+ if (copy_from_user(&spindown, (void *) arg, sizeof(char)))
+ return -EFAULT;
+
+ if ((stat = cdrom_mode_sense(cdi, &cgc, GPMODE_CDROM_PAGE, 0)))
+ return stat;
+
+ buffer[11] = (buffer[11] & 0xf0) | (spindown & 0x0f);
+
+ return cdrom_mode_select(cdi, &cgc);
+ }
+
+ case CDROMGETSPINDOWN: {
+ char spindown;
+
+ if ((stat = cdrom_mode_sense(cdi, &cgc, GPMODE_CDROM_PAGE, 0)))
+ return stat;
+
+ spindown = buffer[11] & 0x0f;
+
+ if (copy_to_user((void *) arg, &spindown, sizeof (char)))
+ return -EFAULT;
+
+ return 0;
+ }
+
+ default:
+ return -EINVAL;
+ }
+
+}
+
+static
+int ide_cdrom_audio_ioctl (struct cdrom_device_info *cdi,
+ unsigned int cmd, void *arg)
+
+{
+ ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+ struct cdrom_info *info = drive->driver_data;
+
+ switch (cmd) {
+ case CDROMREADTOCHDR: {
+ int stat;
+ struct cdrom_tochdr *tochdr = (struct cdrom_tochdr *) arg;
+ struct atapi_toc *toc;
+
+ /* Make sure our saved TOC is valid. */
+ stat = cdrom_read_toc(drive);
+ if (stat) return stat;
+
+ toc = info->toc;
+ tochdr->cdth_trk0 = toc->hdr.first_track;
+ tochdr->cdth_trk1 = toc->hdr.last_track;
+
+ return 0;
+ }
+
+ case CDROMREADTOCENTRY: {
+ int stat;
+ struct cdrom_tocentry *tocentry = (struct cdrom_tocentry*) arg;
+ struct atapi_toc_entry *toce;
+
+ stat = cdrom_get_toc_entry (drive, tocentry->cdte_track, &toce);
+ if (stat) return stat;
+
+ tocentry->cdte_ctrl = toce->control;
+ tocentry->cdte_adr = toce->adr;
+ if (tocentry->cdte_format == CDROM_MSF) {
+ lba_to_msf (toce->addr.lba,
+ &tocentry->cdte_addr.msf.minute,
+ &tocentry->cdte_addr.msf.second,
+ &tocentry->cdte_addr.msf.frame);
+ } else
+ tocentry->cdte_addr.lba = toce->addr.lba;
+
+ return 0;
+ }
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static
+int ide_cdrom_reset (struct cdrom_device_info *cdi)
+{
+ ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+ struct request req;
+
+ ide_init_drive_cmd (&req);
+ req.cmd = RESET_DRIVE_COMMAND;
+ return ide_do_drive_cmd (drive, &req, ide_wait);
+}
+
+
+static
+int ide_cdrom_tray_move (struct cdrom_device_info *cdi, int position)
+{
+ ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+
+ if (position) {
+ int stat = cdrom_lockdoor (drive, 0);
+ if (stat) return stat;
+ }
+
+ return cdrom_eject(drive, !position);
+}
+
+static
+int ide_cdrom_lock_door (struct cdrom_device_info *cdi, int lock)
+{
+ ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+ return cdrom_lockdoor (drive, lock);
+}
+
+#undef __ACER50__
+
+#ifdef __ACER50__
+/*
+ * the buffer struct used by ide_cdrom_get_capabilities()
+ */
+struct get_capabilities_buf {
+ char pad[8];
+ struct atapi_capabilities_page cap; /* this is 4 bytes short of ATAPI standard */
+ char extra_cap[4]; /* Acer 50X needs the regulation size buffer */
+};
+
+static
+int ide_cdrom_get_capabilities (struct cdrom_device_info *cdi, struct get_capabilities_buf *buf)
+{
+ int stat, attempts = 3, buflen = sizeof(*buf);
+ ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+ struct cdrom_generic_command cgc;
+
+ /*
+ * Most drives don't care about the buffer size;
+ * they return as much info as there's room for.
+ * But some older drives (?) had trouble with the
+ * standard size, preferring 4 bytes less.
+ * And the modern Acer 50X rejects anything smaller
+ * than the standard size.
+ */
+ if (!(drive->id && !strcmp(drive->id->model,"ATAPI CD ROM DRIVE 50X MAX")))
+ buflen -= sizeof(buf->extra_cap); /* for all drives except Acer 50X */
+
+ do { /* we seem to get stat=0x01,err=0x00 the first time (??) */
+ stat = cdrom_mode_sense(cdi, &cgc, GPMODE_CAPABILITIES_PAGE, 0);
+ if (stat == 0) {
+ /*
+ * The ACER/AOpen 24X cdrom has the speed
+ * fields byte-swapped from the standard.
+ */
+ if (!(drive->id && !drive->id->model[0] && !strncmp(drive->id->fw_rev, "241N", 4))) {
+ buf->cap.curspeed = ntohs(buf->cap.curspeed);
+ buf->cap.maxspeed = ntohs(buf->cap.maxspeed);
+ }
+ CDROM_STATE_FLAGS (drive)->current_speed = (((unsigned int)buf->cap.curspeed) + (176/2)) / 176;
+ CDROM_CONFIG_FLAGS(drive)->max_speed = (((unsigned int)buf->cap.maxspeed) + (176/2)) / 176;
+ return 0;
+ }
+ } while (--attempts);
+ return stat;
+}
+#endif /* __ACER50__ */
+
+static
+int ide_cdrom_select_speed (struct cdrom_device_info *cdi, int speed)
+{
+#ifndef __ACER50__
+ int stat, attempts = 3;
+ ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+ struct cdrom_generic_command cgc;
+ struct {
+ char pad[8];
+ struct atapi_capabilities_page cap;
+ } buf;
+#else
+ int stat;
+ ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+ struct cdrom_generic_command cgc;
+ struct get_capabilities_buf buf;
+#endif /* __ACER50__ */
+
+ if ((stat = cdrom_select_speed (drive, speed)) < 0)
+ return stat;
+
+ init_cdrom_command(&cgc, &buf, sizeof(buf), CGC_DATA_UNKNOWN);
+
+#ifndef __ACER50__
+ /* Now with that done, update the speed fields */
+ do { /* we seem to get stat=0x01,err=0x00 the first time (??) */
+ if (attempts-- <= 0)
+ return 0;
+ stat = cdrom_mode_sense(cdi, &cgc, GPMODE_CAPABILITIES_PAGE, 0);
+ } while (stat);
+
+ /* The ACER/AOpen 24X cdrom has the speed fields byte-swapped */
+ if (drive->id && !drive->id->model[0] && !strncmp(drive->id->fw_rev, "241N", 4)) {
+ CDROM_STATE_FLAGS (drive)->current_speed =
+ (((unsigned int)buf.cap.curspeed) + (176/2)) / 176;
+ CDROM_CONFIG_FLAGS (drive)->max_speed =
+ (((unsigned int)buf.cap.maxspeed) + (176/2)) / 176;
+ } else {
+ CDROM_STATE_FLAGS (drive)->current_speed =
+ (ntohs(buf.cap.curspeed) + (176/2)) / 176;
+ CDROM_CONFIG_FLAGS (drive)->max_speed =
+ (ntohs(buf.cap.maxspeed) + (176/2)) / 176;
+ }
+#else
+ if (ide_cdrom_get_capabilities(cdi,&buf))
+ return 0;
+#endif /* __ACER50__ */
+
+ cdi->speed = CDROM_STATE_FLAGS (drive)->current_speed;
+ return 0;
+}
+
+static
+int ide_cdrom_drive_status (struct cdrom_device_info *cdi, int slot_nr)
+{
+ ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+ struct cdrom_info *info = drive->driver_data;
+
+ if (slot_nr == CDSL_CURRENT) {
+ struct request_sense *sense = &info->sense_data;
+ int stat = cdrom_check_status(drive);
+ if (stat == 0 || sense->sense_key == UNIT_ATTENTION)
+ return CDS_DISC_OK;
+
+ if (sense->sense_key == NOT_READY && sense->asc == 0x04 &&
+ sense->ascq == 0x04)
+ return CDS_DISC_OK;
+
+ if (sense->sense_key == NOT_READY) {
+ /* ATAPI doesn't have anything that can help
+ us decide whether the drive is really
+ emtpy or the tray is just open. irk. */
+ return CDS_TRAY_OPEN;
+ }
+
+ return CDS_DRIVE_NOT_READY;
+ } else {
+ return -EINVAL;
+ }
+}
+
+static
+int ide_cdrom_get_last_session (struct cdrom_device_info *cdi,
+ struct cdrom_multisession *ms_info)
+{
+ struct atapi_toc *toc;
+ ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+ struct cdrom_info *info = drive->driver_data;
+
+ toc = info->toc;
+ ms_info->addr.lba = toc->last_session_lba;
+ ms_info->xa_flag = toc->xa_flag;
+
+ return 0;
+}
+
+static
+int ide_cdrom_get_mcn (struct cdrom_device_info *cdi,
+ struct cdrom_mcn *mcn_info)
+{
+ int stat;
+ char mcnbuf[24];
+ ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+
+/* get MCN */
+ if ((stat = cdrom_read_subchannel(drive, 2, mcnbuf, sizeof (mcnbuf))))
+ return stat;
+
+ memcpy (mcn_info->medium_catalog_number, mcnbuf+9,
+ sizeof (mcn_info->medium_catalog_number)-1);
+ mcn_info->medium_catalog_number[sizeof (mcn_info->medium_catalog_number)-1]
+ = '\0';
+
+ return 0;
+}
+
+
+
+/****************************************************************************
+ * Other driver requests (open, close, check media change).
+ */
+
+static
+int ide_cdrom_check_media_change_real (struct cdrom_device_info *cdi,
+ int slot_nr)
+{
+ ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+
+ if (slot_nr == CDSL_CURRENT) {
+ (void) cdrom_check_status(drive);
+ CDROM_STATE_FLAGS (drive)->media_changed = 0;
+ return CDROM_STATE_FLAGS (drive)->media_changed;
+ } else {
+ return -EINVAL;
+ }
+}
+
+
+static
+int ide_cdrom_open_real (struct cdrom_device_info *cdi, int purpose)
+{
+ return 0;
+}
+
+
+/*
+ * Close down the device. Invalidate all cached blocks.
+ */
+
+static
+void ide_cdrom_release_real (struct cdrom_device_info *cdi)
+{
+}
+
+
+
+/****************************************************************************
+ * Device initialization.
+ */
+
+static
+struct cdrom_device_ops ide_cdrom_dops = {
+ ide_cdrom_open_real, /* open */
+ ide_cdrom_release_real, /* release */
+ ide_cdrom_drive_status, /* drive_status */
+ ide_cdrom_check_media_change_real, /* media_changed */
+ ide_cdrom_tray_move, /* tray_move */
+ ide_cdrom_lock_door, /* lock_door */
+ ide_cdrom_select_speed, /* select_speed */
+ NULL, /* select_disc */
+ ide_cdrom_get_last_session, /* get_last_session */
+ ide_cdrom_get_mcn, /* get_mcn */
+ ide_cdrom_reset, /* reset */
+ ide_cdrom_audio_ioctl, /* audio_ioctl */
+ ide_cdrom_dev_ioctl, /* dev_ioctl */
+ CDC_CLOSE_TRAY | CDC_OPEN_TRAY | CDC_LOCK | CDC_SELECT_SPEED
+ | CDC_SELECT_DISC | CDC_MULTI_SESSION | CDC_MCN
+ | CDC_MEDIA_CHANGED | CDC_PLAY_AUDIO | CDC_RESET | CDC_IOCTLS
+ | CDC_DRIVE_STATUS | CDC_CD_R | CDC_CD_RW | CDC_DVD
+ | CDC_DVD_R| CDC_DVD_RAM | CDC_GENERIC_PACKET, /* capability */
+ 0, /* n_minors */
+ ide_cdrom_packet
+};
+
+static int ide_cdrom_register (ide_drive_t *drive, int nslots)
+{
+ struct cdrom_info *info = drive->driver_data;
+ struct cdrom_device_info *devinfo = &info->devinfo;
+ int minor = (drive->select.b.unit)<<PARTN_BITS;
+
+ devinfo->dev = MKDEV (HWIF(drive)->major, minor | CD_PART_MASK);
+ devinfo->ops = &ide_cdrom_dops;
+ devinfo->mask = 0;
+ *(int *)&devinfo->speed = CDROM_STATE_FLAGS (drive)->current_speed;
+ *(int *)&devinfo->capacity = nslots;
+ devinfo->handle = (void *) drive;
+ strcpy(devinfo->name, drive->name);
+
+ /* set capability mask to match the probe. */
+ if (!CDROM_CONFIG_FLAGS (drive)->cd_r)
+ devinfo->mask |= CDC_CD_R;
+ if (!CDROM_CONFIG_FLAGS (drive)->cd_rw)
+ devinfo->mask |= CDC_CD_RW;
+ if (!CDROM_CONFIG_FLAGS (drive)->dvd)
+ devinfo->mask |= CDC_DVD;
+ if (!CDROM_CONFIG_FLAGS (drive)->dvd_r)
+ devinfo->mask |= CDC_DVD_R;
+ if (!CDROM_CONFIG_FLAGS (drive)->dvd_ram)
+ devinfo->mask |= CDC_DVD_RAM;
+ if (!CDROM_CONFIG_FLAGS (drive)->is_changer)
+ devinfo->mask |= CDC_SELECT_DISC;
+ if (!CDROM_CONFIG_FLAGS (drive)->audio_play)
+ devinfo->mask |= CDC_PLAY_AUDIO;
+ if (!CDROM_CONFIG_FLAGS (drive)->close_tray)
+ devinfo->mask |= CDC_CLOSE_TRAY;
+
+ devinfo->de = devfs_register (drive->de, "cd", 2, DEVFS_FL_DEFAULT,
+ HWIF(drive)->major, minor,
+ S_IFBLK | S_IRUGO | S_IWUGO, 0, 0,
+ ide_fops, NULL);
+
+ return register_cdrom (devinfo);
+}
+
+
+static
+int ide_cdrom_probe_capabilities (ide_drive_t *drive)
+{
+ struct cdrom_info *info = drive->driver_data;
+ struct cdrom_device_info *cdi = &info->devinfo;
+#ifndef __ACER50__
+ int stat, nslots = 1, attempts = 3;
+ struct cdrom_generic_command cgc;
+ struct {
+ char pad[8];
+ struct atapi_capabilities_page cap;
+ } buf;
+#else
+ int nslots = 1;
+ struct cdrom_generic_command cgc;
+ struct get_capabilities_buf buf;
+#endif /* __ACER50__ */
+
+ if (CDROM_CONFIG_FLAGS (drive)->nec260) {
+ CDROM_CONFIG_FLAGS (drive)->no_eject = 0;
+ CDROM_CONFIG_FLAGS (drive)->audio_play = 1;
+ return nslots;
+ }
+
+ init_cdrom_command(&cgc, &buf, sizeof(buf), CGC_DATA_UNKNOWN);
+ /* we have to cheat a little here. the packet will eventually
+ * be queued with ide_cdrom_packet(), which extracts the
+ * drive from cdi->handle. Since this device hasn't been
+ * registered with the Uniform layer yet, it can't do this.
+ * Same goes for cdi->ops.
+ */
+ cdi->handle = (ide_drive_t *) drive;
+ cdi->ops = &ide_cdrom_dops;
+#ifndef __ACER50__
+ /* we seem to get stat=0x01,err=0x00 the first time (??) */
+ do {
+ if (attempts-- <= 0)
+ return 0;
+ stat = cdrom_mode_sense(cdi, &cgc, GPMODE_CAPABILITIES_PAGE, 0);
+ } while (stat);
+#else
+ if (ide_cdrom_get_capabilities(cdi,&buf))
+ return 0;
+#endif /* __ACER50__ */
+
+ if (buf.cap.lock == 0)
+ CDROM_CONFIG_FLAGS (drive)->no_doorlock = 1;
+ if (buf.cap.eject)
+ CDROM_CONFIG_FLAGS (drive)->no_eject = 0;
+ if (buf.cap.cd_r_write)
+ CDROM_CONFIG_FLAGS (drive)->cd_r = 1;
+ if (buf.cap.cd_rw_write)
+ CDROM_CONFIG_FLAGS (drive)->cd_rw = 1;
+ if (buf.cap.test_write)
+ CDROM_CONFIG_FLAGS (drive)->test_write = 1;
+ if (buf.cap.dvd_ram_read || buf.cap.dvd_r_read || buf.cap.dvd_rom)
+ CDROM_CONFIG_FLAGS (drive)->dvd = 1;
+ if (buf.cap.dvd_ram_write)
+ CDROM_CONFIG_FLAGS (drive)->dvd_r = 1;
+ if (buf.cap.dvd_r_write)
+ CDROM_CONFIG_FLAGS (drive)->dvd_ram = 1;
+ if (buf.cap.audio_play)
+ CDROM_CONFIG_FLAGS (drive)->audio_play = 1;
+ if (buf.cap.mechtype == 0)
+ CDROM_CONFIG_FLAGS (drive)->close_tray = 0;
+
+#if ! STANDARD_ATAPI
+ if (cdi->sanyo_slot > 0) {
+ CDROM_CONFIG_FLAGS (drive)->is_changer = 1;
+ nslots = 3;
+ }
+
+ else
+#endif /* not STANDARD_ATAPI */
+ if (buf.cap.mechtype == mechtype_individual_changer ||
+ buf.cap.mechtype == mechtype_cartridge_changer) {
+ if ((nslots = cdrom_number_of_slots(cdi)) > 1) {
+ CDROM_CONFIG_FLAGS (drive)->is_changer = 1;
+ CDROM_CONFIG_FLAGS (drive)->supp_disc_present = 1;
+ }
+ }
+
+#ifndef __ACER50__
+ /* The ACER/AOpen 24X cdrom has the speed fields byte-swapped */
+ if (drive->id && !drive->id->model[0] && !strncmp(drive->id->fw_rev, "241N", 4)) {
+ CDROM_STATE_FLAGS (drive)->current_speed =
+ (((unsigned int)buf.cap.curspeed) + (176/2)) / 176;
+ CDROM_CONFIG_FLAGS (drive)->max_speed =
+ (((unsigned int)buf.cap.maxspeed) + (176/2)) / 176;
+ } else {
+ CDROM_STATE_FLAGS (drive)->current_speed =
+ (ntohs(buf.cap.curspeed) + (176/2)) / 176;
+ CDROM_CONFIG_FLAGS (drive)->max_speed =
+ (ntohs(buf.cap.maxspeed) + (176/2)) / 176;
+ }
+#endif /* __ACER50__ */
+
+ /* don't print speed if the drive reported 0.
+ */
+ printk("%s: ATAPI", drive->name);
+ if (CDROM_CONFIG_FLAGS(drive)->max_speed)
+ printk(" %dX", CDROM_CONFIG_FLAGS(drive)->max_speed);
+ printk(" %s", CDROM_CONFIG_FLAGS(drive)->dvd ? "DVD-ROM" : "CD-ROM");
+
+ if (CDROM_CONFIG_FLAGS (drive)->dvd_r|CDROM_CONFIG_FLAGS (drive)->dvd_ram)
+ printk (" DVD%s%s",
+ (CDROM_CONFIG_FLAGS (drive)->dvd_r)? "-R" : "",
+ (CDROM_CONFIG_FLAGS (drive)->dvd_ram)? "AM" : "");
+
+ if (CDROM_CONFIG_FLAGS (drive)->cd_r|CDROM_CONFIG_FLAGS (drive)->cd_rw)
+ printk (" CD%s%s",
+ (CDROM_CONFIG_FLAGS (drive)->cd_r)? "-R" : "",
+ (CDROM_CONFIG_FLAGS (drive)->cd_rw)? "/RW" : "");
+
+ if (CDROM_CONFIG_FLAGS (drive)->is_changer)
+ printk (" changer w/%d slots", nslots);
+ else
+ printk (" drive");
+
+ printk (", %dkB Cache", be16_to_cpu(buf.cap.buffer_size));
+
+ if (drive->using_dma) {
+ if ((drive->id->field_valid & 4) &&
+ (drive->id->hw_config & 0x2000) &&
+ (HWIF(drive)->udma_four) &&
+ (drive->id->dma_ultra & (drive->id->dma_ultra >> 11) & 3)) {
+ printk(", UDMA(66)"); /* UDMA BIOS-enabled! */
+ } else if ((drive->id->field_valid & 4) &&
+ (drive->id->dma_ultra & (drive->id->dma_ultra >> 8) & 7)) {
+ printk(", UDMA(33)"); /* UDMA BIOS-enabled! */
+ } else if (drive->id->field_valid & 4) {
+ printk(", (U)DMA"); /* Can be BIOS-enabled! */
+ } else {
+ printk(", DMA");
+ }
+ }
+ printk("\n");
+
+ return nslots;
+}
+
+static void ide_cdrom_add_settings(ide_drive_t *drive)
+{
+ int major = HWIF(drive)->major;
+ int minor = drive->select.b.unit << PARTN_BITS;
+
+ ide_add_setting(drive, "breada_readahead", SETTING_RW, BLKRAGET, BLKRASET, TYPE_INT, 0, 255, 1, 2, &read_ahead[major], NULL);
+ ide_add_setting(drive, "file_readahead", SETTING_RW, BLKFRAGET, BLKFRASET, TYPE_INTA, 0, INT_MAX, 1, 1024, &max_readahead[major][minor], NULL);
+ ide_add_setting(drive, "max_kb_per_request", SETTING_RW, BLKSECTGET, BLKSECTSET, TYPE_INTA, 1, 255, 1, 2, &max_sectors[major][minor], NULL);
+ ide_add_setting(drive, "dsc_overlap", SETTING_RW, -1, -1, TYPE_BYTE, 0, 1, 1, 1, &drive->dsc_overlap, NULL);
+}
+
+static
+int ide_cdrom_setup (ide_drive_t *drive)
+{
+ struct cdrom_info *info = drive->driver_data;
+ struct cdrom_device_info *cdi = &info->devinfo;
+ int minor = drive->select.b.unit << PARTN_BITS;
+ int nslots;
+
+ set_device_ro(MKDEV(HWIF(drive)->major, minor), 1);
+ set_blocksize(MKDEV(HWIF(drive)->major, minor), CD_FRAMESIZE);
+
+ drive->special.all = 0;
+ drive->ready_stat = 0;
+
+ CDROM_STATE_FLAGS (drive)->media_changed = 1;
+ CDROM_STATE_FLAGS (drive)->toc_valid = 0;
+ CDROM_STATE_FLAGS (drive)->door_locked = 0;
+
+#if NO_DOOR_LOCKING
+ CDROM_CONFIG_FLAGS (drive)->no_doorlock = 1;
+#else
+ CDROM_CONFIG_FLAGS (drive)->no_doorlock = 0;
+#endif
+
+ if (drive->id != NULL)
+ CDROM_CONFIG_FLAGS (drive)->drq_interrupt =
+ ((drive->id->config & 0x0060) == 0x20);
+ else
+ CDROM_CONFIG_FLAGS (drive)->drq_interrupt = 0;
+
+ CDROM_CONFIG_FLAGS (drive)->is_changer = 0;
+ CDROM_CONFIG_FLAGS (drive)->cd_r = 0;
+ CDROM_CONFIG_FLAGS (drive)->cd_rw = 0;
+ CDROM_CONFIG_FLAGS (drive)->test_write = 0;
+ CDROM_CONFIG_FLAGS (drive)->dvd = 0;
+ CDROM_CONFIG_FLAGS (drive)->dvd_r = 0;
+ CDROM_CONFIG_FLAGS (drive)->dvd_ram = 0;
+ CDROM_CONFIG_FLAGS (drive)->no_eject = 1;
+ CDROM_CONFIG_FLAGS (drive)->supp_disc_present = 0;
+ CDROM_CONFIG_FLAGS (drive)->audio_play = 0;
+ CDROM_CONFIG_FLAGS (drive)->close_tray = 1;
+
+ /* limit transfer size per interrupt. */
+ CDROM_CONFIG_FLAGS (drive)->limit_nframes = 0;
+ if (drive->id != NULL) {
+ /* a testament to the nice quality of Samsung drives... */
+ if (!strcmp(drive->id->model, "SAMSUNG CD-ROM SCR-2430"))
+ CDROM_CONFIG_FLAGS (drive)->limit_nframes = 1;
+ else if (!strcmp(drive->id->model, "SAMSUNG CD-ROM SCR-2432"))
+ CDROM_CONFIG_FLAGS (drive)->limit_nframes = 1;
+ /* the 3231 model does not support the SET_CD_SPEED command */
+ else if (!strcmp(drive->id->model, "SAMSUNG CD-ROM SCR-3231"))
+ cdi->mask |= CDC_SELECT_SPEED;
+ }
+
+#if ! STANDARD_ATAPI
+ /* by default Sanyo 3 CD changer support is turned off and
+ ATAPI Rev 2.2+ standard support for CD changers is used */
+ cdi->sanyo_slot = 0;
+
+ CDROM_CONFIG_FLAGS (drive)->nec260 = 0;
+ CDROM_CONFIG_FLAGS (drive)->toctracks_as_bcd = 0;
+ CDROM_CONFIG_FLAGS (drive)->tocaddr_as_bcd = 0;
+ CDROM_CONFIG_FLAGS (drive)->playmsf_as_bcd = 0;
+ CDROM_CONFIG_FLAGS (drive)->subchan_as_bcd = 0;
+
+ if (drive->id != NULL) {
+ if (strcmp (drive->id->model, "V003S0DS") == 0 &&
+ drive->id->fw_rev[4] == '1' &&
+ drive->id->fw_rev[6] <= '2') {
+ /* Vertos 300.
+ Some versions of this drive like to talk BCD. */
+ CDROM_CONFIG_FLAGS (drive)->toctracks_as_bcd = 1;
+ CDROM_CONFIG_FLAGS (drive)->tocaddr_as_bcd = 1;
+ CDROM_CONFIG_FLAGS (drive)->playmsf_as_bcd = 1;
+ CDROM_CONFIG_FLAGS (drive)->subchan_as_bcd = 1;
+ }
+
+ else if (strcmp (drive->id->model, "V006E0DS") == 0 &&
+ drive->id->fw_rev[4] == '1' &&
+ drive->id->fw_rev[6] <= '2') {
+ /* Vertos 600 ESD. */
+ CDROM_CONFIG_FLAGS (drive)->toctracks_as_bcd = 1;
+ }
+
+ else if (strcmp (drive->id->model,
+ "NEC CD-ROM DRIVE:260") == 0 &&
+ strncmp (drive->id->fw_rev, "1.01", 4) == 0) { /* FIXME */
+ /* Old NEC260 (not R).
+ This drive was released before the 1.2 version
+ of the spec. */
+ CDROM_CONFIG_FLAGS (drive)->tocaddr_as_bcd = 1;
+ CDROM_CONFIG_FLAGS (drive)->playmsf_as_bcd = 1;
+ CDROM_CONFIG_FLAGS (drive)->subchan_as_bcd = 1;
+ CDROM_CONFIG_FLAGS (drive)->nec260 = 1;
+ }
+
+ else if (strcmp (drive->id->model, "WEARNES CDD-120") == 0 &&
+ strncmp (drive->id->fw_rev, "A1.1", 4) == 0) { /* FIXME */
+ /* Wearnes */
+ CDROM_CONFIG_FLAGS (drive)->playmsf_as_bcd = 1;
+ CDROM_CONFIG_FLAGS (drive)->subchan_as_bcd = 1;
+ }
+
+ /* Sanyo 3 CD changer uses a non-standard command
+ for CD changing */
+ else if ((strcmp(drive->id->model, "CD-ROM CDR-C3 G") == 0) ||
+ (strcmp(drive->id->model, "CD-ROM CDR-C3G") == 0) ||
+ (strcmp(drive->id->model, "CD-ROM CDR_C36") == 0)) {
+ /* uses CD in slot 0 when value is set to 3 */
+ cdi->sanyo_slot = 3;
+ }
+
+
+ }
+#endif /* not STANDARD_ATAPI */
+
+ info->toc = NULL;
+ info->buffer = NULL;
+ info->sector_buffered = 0;
+ info->nsectors_buffered = 0;
+ info->changer_info = NULL;
+ info->last_block = 0;
+ info->start_seek = 0;
+
+ nslots = ide_cdrom_probe_capabilities (drive);
+
+ if (ide_cdrom_register (drive, nslots)) {
+ printk ("%s: ide_cdrom_setup failed to register device with the cdrom driver.\n", drive->name);
+ info->devinfo.handle = NULL;
+ return 1;
+ }
+ ide_cdrom_add_settings(drive);
+ return 0;
+}
+
+/* Forwarding functions to generic routines. */
+static
+int ide_cdrom_ioctl (ide_drive_t *drive,
+ struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return cdrom_fops.ioctl (inode, file, cmd, arg);
+}
+
+static
+int ide_cdrom_open (struct inode *ip, struct file *fp, ide_drive_t *drive)
+{
+ struct cdrom_info *info = drive->driver_data;
+ int rc;
+
+ MOD_INC_USE_COUNT;
+ if (info->buffer == NULL)
+ info->buffer = (char *) kmalloc(SECTOR_BUFFER_SIZE, GFP_KERNEL);
+ rc = cdrom_fops.open (ip, fp);
+ if (rc) {
+ drive->usage--;
+ MOD_DEC_USE_COUNT;
+ }
+ return rc;
+}
+
+static
+void ide_cdrom_release (struct inode *inode, struct file *file,
+ ide_drive_t *drive)
+{
+ cdrom_fops.release (inode, file);
+ MOD_DEC_USE_COUNT;
+}
+
+static
+int ide_cdrom_check_media_change (ide_drive_t *drive)
+{
+ return cdrom_fops.check_media_change
+ (MKDEV (HWIF (drive)->major,
+ (drive->select.b.unit)<<PARTN_BITS));
+}
+
+static
+int ide_cdrom_cleanup(ide_drive_t *drive)
+{
+ struct cdrom_info *info = drive->driver_data;
+ struct cdrom_device_info *devinfo = &info->devinfo;
+
+ if (ide_unregister_subdriver (drive))
+ return 1;
+ if (info->buffer != NULL)
+ kfree(info->buffer);
+ if (info->toc != NULL)
+ kfree(info->toc);
+ if (info->changer_info != NULL)
+ kfree(info->changer_info);
+ if (devinfo->handle == drive && unregister_cdrom (devinfo))
+ printk ("%s: ide_cdrom_cleanup failed to unregister device from the cdrom driver.\n", drive->name);
+ kfree(info);
+ drive->driver_data = NULL;
+ return 0;
+}
+
+static ide_driver_t ide_cdrom_driver = {
+ "ide-cdrom", /* name */
+ IDECD_VERSION, /* version */
+ ide_cdrom, /* media */
+ 0, /* busy */
+ 1, /* supports_dma */
+ 1, /* supports_dsc_overlap */
+ ide_cdrom_cleanup, /* cleanup */
+ ide_do_rw_cdrom, /* do_request */
+ NULL, /* ??? or perhaps cdrom_end_request? */
+ ide_cdrom_ioctl, /* ioctl */
+ ide_cdrom_open, /* open */
+ ide_cdrom_release, /* release */
+ ide_cdrom_check_media_change, /* media_change */
+ NULL, /* pre_reset */
+ NULL, /* capacity */
+ NULL, /* special */
+ NULL /* proc */
+};
+
+int ide_cdrom_init (void);
+static ide_module_t ide_cdrom_module = {
+ IDE_DRIVER_MODULE,
+ ide_cdrom_init,
+ &ide_cdrom_driver,
+ NULL
+};
+
+/* options */
+char *ignore = NULL;
+
+#ifdef MODULE
+MODULE_PARM(ignore, "s");
+MODULE_DESCRIPTION("ATAPI CD-ROM Driver");
+
+void __exit ide_cdrom_exit(void)
+{
+ ide_drive_t *drive;
+ int failed = 0;
+
+ while ((drive = ide_scan_devices (ide_cdrom, ide_cdrom_driver.name, &ide_cdrom_driver, failed)) != NULL)
+ if (ide_cdrom_cleanup (drive)) {
+ printk ("%s: cleanup_module() called while still busy\n", drive->name);
+ failed++;
+ }
+ ide_unregister_module (&ide_cdrom_module);
+}
+#endif /* MODULE */
+
+int ide_cdrom_init (void)
+{
+ ide_drive_t *drive;
+ struct cdrom_info *info;
+ int failed = 0;
+
+ MOD_INC_USE_COUNT;
+ while ((drive = ide_scan_devices (ide_cdrom, ide_cdrom_driver.name, NULL, failed++)) != NULL) {
+ /* skip drives that we were told to ignore */
+ if (ignore != NULL) {
+ if (strstr(ignore, drive->name)) {
+ printk("ide-cd: ignoring drive %s\n", drive->name);
+ continue;
+ }
+ }
+ if (drive->scsi) {
+ printk("ide-cd: passing drive %s to ide-scsi emulation.\n", drive->name);
+ continue;
+ }
+ info = (struct cdrom_info *) kmalloc (sizeof (struct cdrom_info), GFP_KERNEL);
+ if (info == NULL) {
+ printk ("%s: Can't allocate a cdrom structure\n", drive->name);
+ continue;
+ }
+ if (ide_register_subdriver (drive, &ide_cdrom_driver, IDE_SUBDRIVER_VERSION)) {
+ printk ("%s: Failed to register the driver with ide.c\n", drive->name);
+ kfree (info);
+ continue;
+ }
+ memset (info, 0, sizeof (struct cdrom_info));
+ drive->driver_data = info;
+ DRIVER(drive)->busy++;
+ if (ide_cdrom_setup (drive)) {
+ DRIVER(drive)->busy--;
+ if (ide_cdrom_cleanup (drive))
+ printk ("%s: ide_cdrom_cleanup failed in ide_cdrom_init\n", drive->name);
+ continue;
+ }
+ DRIVER(drive)->busy--;
+ failed--;
+ }
+ ide_register_module(&ide_cdrom_module);
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+module_init(ide_cdrom_init);
+module_exit(ide_cdrom_exit);
diff --git a/drivers/ide/ide-cd.h b/drivers/ide/ide-cd.h
new file mode 100644
index 000000000..1eb48ef6c
--- /dev/null
+++ b/drivers/ide/ide-cd.h
@@ -0,0 +1,736 @@
+/*
+ * linux/drivers/block/ide_cd.h
+ *
+ * Copyright (C) 1996-98 Erik Andersen
+ * Copyright (C) 1998-2000 Jens Axboe
+ */
+#ifndef _IDE_CD_H
+#define _IDE_CD_H
+
+#include <linux/cdrom.h>
+#include <asm/byteorder.h>
+
+/* Turn this on to have the driver print out the meanings of the
+ ATAPI error codes. This will use up additional kernel-space
+ memory, though. */
+
+#ifndef VERBOSE_IDE_CD_ERRORS
+#define VERBOSE_IDE_CD_ERRORS 1
+#endif
+
+
+/* Turning this on will remove code to work around various nonstandard
+ ATAPI implementations. If you know your drive follows the standard,
+ this will give you a slightly smaller kernel. */
+
+#ifndef STANDARD_ATAPI
+#define STANDARD_ATAPI 0
+#endif
+
+
+/* Turning this on will disable the door-locking functionality.
+ This is apparently needed for supermount. */
+
+#ifndef NO_DOOR_LOCKING
+#define NO_DOOR_LOCKING 0
+#endif
+
+/************************************************************************/
+
+#define SECTOR_SIZE 512
+#define SECTOR_BITS 9
+#define SECTORS_PER_FRAME (CD_FRAMESIZE / SECTOR_SIZE)
+#define SECTOR_BUFFER_SIZE (CD_FRAMESIZE * 32)
+#define SECTORS_BUFFER (SECTOR_BUFFER_SIZE / SECTOR_SIZE)
+
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+
+/* special command codes for strategy routine. */
+#define PACKET_COMMAND 4315
+#define REQUEST_SENSE_COMMAND 4316
+#define RESET_DRIVE_COMMAND 4317
+
+
+/* Configuration flags. These describe the capabilities of the drive.
+ They generally do not change after initialization, unless we learn
+ more about the drive from stuff failing. */
+struct ide_cd_config_flags {
+ __u8 drq_interrupt : 1; /* Device sends an interrupt when ready
+ for a packet command. */
+ __u8 no_doorlock : 1; /* Drive cannot lock the door. */
+ __u8 no_eject : 1; /* Drive cannot eject the disc. */
+ __u8 nec260 : 1; /* Drive is a pre-1.2 NEC 260 drive. */
+ __u8 playmsf_as_bcd : 1; /* PLAYMSF command takes BCD args. */
+ __u8 tocaddr_as_bcd : 1; /* TOC addresses are in BCD. */
+ __u8 toctracks_as_bcd : 1; /* TOC track numbers are in BCD. */
+ __u8 subchan_as_bcd : 1; /* Subchannel info is in BCD. */
+ __u8 is_changer : 1; /* Drive is a changer. */
+ __u8 cd_r : 1; /* Drive can write to CD-R media . */
+ __u8 cd_rw : 1; /* Drive can write to CD-R/W media . */
+ __u8 dvd : 1; /* Drive is a DVD-ROM */
+ __u8 dvd_r : 1; /* Drive can write DVD-R */
+ __u8 dvd_ram : 1; /* Drive can write DVD-RAM */
+ __u8 test_write : 1; /* Drive can fake writes */
+ __u8 supp_disc_present : 1; /* Changer can report exact contents
+ of slots. */
+ __u8 limit_nframes : 1; /* Drive does not provide data in
+ multiples of SECTOR_SIZE when more
+ than one interrupt is needed. */
+ __u8 seeking : 1; /* Seeking in progress */
+ __u8 audio_play : 1; /* can do audio related commands */
+ __u8 close_tray : 1; /* can close the tray */
+ __u8 writing : 1; /* pseudo write in progress */
+ __u8 reserved : 3;
+ byte max_speed; /* Max speed of the drive */
+};
+#define CDROM_CONFIG_FLAGS(drive) (&(((struct cdrom_info *)(drive->driver_data))->config_flags))
+
+
+/* State flags. These give information about the current state of the
+ drive, and will change during normal operation. */
+struct ide_cd_state_flags {
+ __u8 media_changed : 1; /* Driver has noticed a media change. */
+ __u8 toc_valid : 1; /* Saved TOC information is current. */
+ __u8 door_locked : 1; /* We think that the drive door is locked. */
+ __u8 writing : 1; /* the drive is currently writing */
+ __u8 reserved : 4;
+ byte current_speed; /* Current speed of the drive */
+};
+
+#define CDROM_STATE_FLAGS(drive) (&(((struct cdrom_info *)(drive->driver_data))->state_flags))
+
+struct packet_command {
+ char *buffer;
+ int buflen;
+ int stat;
+ struct request_sense *sense_data;
+ unsigned char c[12];
+};
+
+/* Structure of a MSF cdrom address. */
+struct atapi_msf {
+ byte reserved;
+ byte minute;
+ byte second;
+ byte frame;
+};
+
+/* Space to hold the disk TOC. */
+#define MAX_TRACKS 99
+struct atapi_toc_header {
+ unsigned short toc_length;
+ byte first_track;
+ byte last_track;
+};
+
+struct atapi_toc_entry {
+ byte reserved1;
+#if defined(__BIG_ENDIAN_BITFIELD)
+ __u8 adr : 4;
+ __u8 control : 4;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+ __u8 control : 4;
+ __u8 adr : 4;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+ byte track;
+ byte reserved2;
+ union {
+ unsigned lba;
+ struct atapi_msf msf;
+ } addr;
+};
+
+struct atapi_toc {
+ int last_session_lba;
+ int xa_flag;
+ unsigned capacity;
+ struct atapi_toc_header hdr;
+ struct atapi_toc_entry ent[MAX_TRACKS+1];
+ /* One extra for the leadout. */
+};
+
+
+/* This structure is annoyingly close to, but not identical with,
+ the cdrom_subchnl structure from cdrom.h. */
+struct atapi_cdrom_subchnl {
+ u_char acdsc_reserved;
+ u_char acdsc_audiostatus;
+ u_short acdsc_length;
+ u_char acdsc_format;
+
+#if defined(__BIG_ENDIAN_BITFIELD)
+ u_char acdsc_ctrl: 4;
+ u_char acdsc_adr: 4;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+ u_char acdsc_adr: 4;
+ u_char acdsc_ctrl: 4;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+ u_char acdsc_trk;
+ u_char acdsc_ind;
+ union {
+ struct atapi_msf msf;
+ int lba;
+ } acdsc_absaddr;
+ union {
+ struct atapi_msf msf;
+ int lba;
+ } acdsc_reladdr;
+};
+
+
+
+/* This should probably go into cdrom.h along with the other
+ * generic stuff now in the Mt. Fuji spec.
+ */
+struct atapi_capabilities_page {
+#if defined(__BIG_ENDIAN_BITFIELD)
+ __u8 parameters_saveable : 1;
+ __u8 reserved1 : 1;
+ __u8 page_code : 6;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+ __u8 page_code : 6;
+ __u8 reserved1 : 1;
+ __u8 parameters_saveable : 1;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+
+ byte page_length;
+
+#if defined(__BIG_ENDIAN_BITFIELD)
+ __u8 reserved2 : 2;
+ /* Drive supports reading of DVD-RAM discs */
+ __u8 dvd_ram_read : 1;
+ /* Drive supports reading of DVD-R discs */
+ __u8 dvd_r_read : 1;
+ /* Drive supports reading of DVD-ROM discs */
+ __u8 dvd_rom : 1;
+ /* Drive supports reading CD-R discs with addressing method 2 */
+ __u8 method2 : 1; /* reserved in 1.2 */
+ /* Drive can read from CD-R/W (CD-E) discs (orange book, part III) */
+ __u8 cd_rw_read : 1; /* reserved in 1.2 */
+ /* Drive supports read from CD-R discs (orange book, part II) */
+ __u8 cd_r_read : 1; /* reserved in 1.2 */
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+ /* Drive supports read from CD-R discs (orange book, part II) */
+ __u8 cd_r_read : 1; /* reserved in 1.2 */
+ /* Drive can read from CD-R/W (CD-E) discs (orange book, part III) */
+ __u8 cd_rw_read : 1; /* reserved in 1.2 */
+ /* Drive supports reading CD-R discs with addressing method 2 */
+ __u8 method2 : 1;
+ /* Drive supports reading of DVD-ROM discs */
+ __u8 dvd_rom : 1;
+ /* Drive supports reading of DVD-R discs */
+ __u8 dvd_r_read : 1;
+ /* Drive supports reading of DVD-RAM discs */
+ __u8 dvd_ram_read : 1;
+ __u8 reserved2 : 2;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+
+#if defined(__BIG_ENDIAN_BITFIELD)
+ __u8 reserved3 : 2;
+ /* Drive can write DVD-RAM discs */
+ __u8 dvd_ram_write : 1;
+ /* Drive can write DVD-R discs */
+ __u8 dvd_r_write : 1;
+ __u8 reserved3a : 1;
+ /* Drive can fake writes */
+ __u8 test_write : 1;
+ /* Drive can write to CD-R/W (CD-E) discs (orange book, part III) */
+ __u8 cd_rw_write : 1; /* reserved in 1.2 */
+ /* Drive supports write to CD-R discs (orange book, part II) */
+ __u8 cd_r_write : 1; /* reserved in 1.2 */
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+ /* Drive can write to CD-R discs (orange book, part II) */
+ __u8 cd_r_write : 1; /* reserved in 1.2 */
+ /* Drive can write to CD-R/W (CD-E) discs (orange book, part III) */
+ __u8 cd_rw_write : 1; /* reserved in 1.2 */
+ /* Drive can fake writes */
+ __u8 test_write : 1;
+ __u8 reserved3a : 1;
+ /* Drive can write DVD-R discs */
+ __u8 dvd_r_write : 1;
+ /* Drive can write DVD-RAM discs */
+ __u8 dvd_ram_write : 1;
+ __u8 reserved3 : 2;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+
+#if defined(__BIG_ENDIAN_BITFIELD)
+ __u8 reserved4 : 1;
+ /* Drive can read multisession discs. */
+ __u8 multisession : 1;
+ /* Drive can read mode 2, form 2 data. */
+ __u8 mode2_form2 : 1;
+ /* Drive can read mode 2, form 1 (XA) data. */
+ __u8 mode2_form1 : 1;
+ /* Drive supports digital output on port 2. */
+ __u8 digport2 : 1;
+ /* Drive supports digital output on port 1. */
+ __u8 digport1 : 1;
+ /* Drive can deliver a composite audio/video data stream. */
+ __u8 composite : 1;
+ /* Drive supports audio play operations. */
+ __u8 audio_play : 1;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+ /* Drive supports audio play operations. */
+ __u8 audio_play : 1;
+ /* Drive can deliver a composite audio/video data stream. */
+ __u8 composite : 1;
+ /* Drive supports digital output on port 1. */
+ __u8 digport1 : 1;
+ /* Drive supports digital output on port 2. */
+ __u8 digport2 : 1;
+ /* Drive can read mode 2, form 1 (XA) data. */
+ __u8 mode2_form1 : 1;
+ /* Drive can read mode 2, form 2 data. */
+ __u8 mode2_form2 : 1;
+ /* Drive can read multisession discs. */
+ __u8 multisession : 1;
+ __u8 reserved4 : 1;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+
+#if defined(__BIG_ENDIAN_BITFIELD)
+ __u8 reserved5 : 1;
+ /* Drive can return Media Catalog Number (UPC) info. */
+ __u8 upc : 1;
+ /* Drive can return International Standard Recording Code info. */
+ __u8 isrc : 1;
+ /* Drive supports C2 error pointers. */
+ __u8 c2_pointers : 1;
+ /* R-W data will be returned deinterleaved and error corrected. */
+ __u8 rw_corr : 1;
+ /* Subchannel reads can return combined R-W information. */
+ __u8 rw_supported : 1;
+ /* Drive can continue a read cdda operation from a loss of streaming.*/
+ __u8 cdda_accurate : 1;
+ /* Drive can read Red Book audio data. */
+ __u8 cdda : 1;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+ /* Drive can read Red Book audio data. */
+ __u8 cdda : 1;
+ /* Drive can continue a read cdda operation from a loss of streaming.*/
+ __u8 cdda_accurate : 1;
+ /* Subchannel reads can return combined R-W information. */
+ __u8 rw_supported : 1;
+ /* R-W data will be returned deinterleaved and error corrected. */
+ __u8 rw_corr : 1;
+ /* Drive supports C2 error pointers. */
+ __u8 c2_pointers : 1;
+ /* Drive can return International Standard Recording Code info. */
+ __u8 isrc : 1;
+ /* Drive can return Media Catalog Number (UPC) info. */
+ __u8 upc : 1;
+ __u8 reserved5 : 1;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+
+#if defined(__BIG_ENDIAN_BITFIELD)
+ /* Drive mechanism types. */
+ mechtype_t mechtype : 3;
+ __u8 reserved6 : 1;
+ /* Drive can eject a disc or changer cartridge. */
+ __u8 eject : 1;
+ /* State of prevent/allow jumper. */
+ __u8 prevent_jumper : 1;
+ /* Present state of door lock. */
+ __u8 lock_state : 1;
+ /* Drive can lock the door. */
+ __u8 lock : 1;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+
+ /* Drive can lock the door. */
+ __u8 lock : 1;
+ /* Present state of door lock. */
+ __u8 lock_state : 1;
+ /* State of prevent/allow jumper. */
+ __u8 prevent_jumper : 1;
+ /* Drive can eject a disc or changer cartridge. */
+ __u8 eject : 1;
+ __u8 reserved6 : 1;
+ /* Drive mechanism types. */
+ mechtype_t mechtype : 3;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+
+#if defined(__BIG_ENDIAN_BITFIELD)
+ __u8 reserved7 : 4;
+ /* Drive supports software slot selection. */
+ __u8 sss : 1; /* reserved in 1.2 */
+ /* Changer can report exact contents of slots. */
+ __u8 disc_present : 1; /* reserved in 1.2 */
+ /* Audio for each channel can be muted independently. */
+ __u8 separate_mute : 1;
+ /* Audio level for each channel can be controlled independently. */
+ __u8 separate_volume : 1;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+
+ /* Audio level for each channel can be controlled independently. */
+ __u8 separate_volume : 1;
+ /* Audio for each channel can be muted independently. */
+ __u8 separate_mute : 1;
+ /* Changer can report exact contents of slots. */
+ __u8 disc_present : 1; /* reserved in 1.2 */
+ /* Drive supports software slot selection. */
+ __u8 sss : 1; /* reserved in 1.2 */
+ __u8 reserved7 : 4;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+
+ /* Note: the following four fields are returned in big-endian form. */
+ /* Maximum speed (in kB/s). */
+ unsigned short maxspeed;
+ /* Number of discrete volume levels. */
+ unsigned short n_vol_levels;
+ /* Size of cache in drive, in kB. */
+ unsigned short buffer_size;
+ /* Current speed (in kB/s). */
+ unsigned short curspeed;
+
+ /* Truncate the structure here, so we don't have headaches reading
+ from older drives. */
+};
+
+
+struct atapi_mechstat_header {
+#if defined(__BIG_ENDIAN_BITFIELD)
+ __u8 fault : 1;
+ __u8 changer_state : 2;
+ __u8 curslot : 5;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+ __u8 curslot : 5;
+ __u8 changer_state : 2;
+ __u8 fault : 1;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+
+#if defined(__BIG_ENDIAN_BITFIELD)
+ __u8 mech_state : 3;
+ __u8 door_open : 1;
+ __u8 reserved1 : 4;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+ __u8 reserved1 : 4;
+ __u8 door_open : 1;
+ __u8 mech_state : 3;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+
+ byte curlba[3];
+ byte nslots;
+ __u8 short slot_tablelen;
+};
+
+
+struct atapi_slot {
+#if defined(__BIG_ENDIAN_BITFIELD)
+ __u8 disc_present : 1;
+ __u8 reserved1 : 6;
+ __u8 change : 1;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+ __u8 change : 1;
+ __u8 reserved1 : 6;
+ __u8 disc_present : 1;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+
+ byte reserved2[3];
+};
+
+struct atapi_changer_info {
+ struct atapi_mechstat_header hdr;
+ struct atapi_slot slots[0];
+};
+
+/* Extra per-device info for cdrom drives. */
+struct cdrom_info {
+
+ /* Buffer for table of contents. NULL if we haven't allocated
+ a TOC buffer for this device yet. */
+
+ struct atapi_toc *toc;
+
+ unsigned long sector_buffered;
+ unsigned long nsectors_buffered;
+ unsigned char *buffer;
+
+ /* The result of the last successful request sense command
+ on this device. */
+ struct request_sense sense_data;
+
+ struct request request_sense_request;
+ struct packet_command request_sense_pc;
+ int dma;
+ unsigned long last_block;
+ unsigned long start_seek;
+ /* Buffer to hold mechanism status and changer slot table. */
+ struct atapi_changer_info *changer_info;
+
+ struct ide_cd_config_flags config_flags;
+ struct ide_cd_state_flags state_flags;
+
+ /* Per-device info needed by cdrom.c generic driver. */
+ struct cdrom_device_info devinfo;
+};
+
+/****************************************************************************
+ * Descriptions of ATAPI error codes.
+ */
+
+#define ARY_LEN(a) ((sizeof(a) / sizeof(a[0])))
+
+/* This stuff should be in cdrom.h, since it is now generic... */
+
+/* ATAPI sense keys (from table 140 of ATAPI 2.6) */
+#define NO_SENSE 0x00
+#define RECOVERED_ERROR 0x01
+#define NOT_READY 0x02
+#define MEDIUM_ERROR 0x03
+#define HARDWARE_ERROR 0x04
+#define ILLEGAL_REQUEST 0x05
+#define UNIT_ATTENTION 0x06
+#define DATA_PROTECT 0x07
+#define ABORTED_COMMAND 0x0b
+#define MISCOMPARE 0x0e
+
+
+
+/* This stuff should be in cdrom.h, since it is now generic... */
+#if VERBOSE_IDE_CD_ERRORS
+
+ /* The generic packet command opcodes for CD/DVD Logical Units,
+ * From Table 57 of the SFF8090 Ver. 3 (Mt. Fuji) draft standard. */
+const struct {
+ unsigned short packet_command;
+ const char * const text;
+} packet_command_texts[] = {
+ { GPCMD_TEST_UNIT_READY, "Test Unit Ready" },
+ { GPCMD_REQUEST_SENSE, "Request Sense" },
+ { GPCMD_FORMAT_UNIT, "Format Unit" },
+ { GPCMD_INQUIRY, "Inquiry" },
+ { GPCMD_START_STOP_UNIT, "Start/Stop Unit" },
+ { GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL, "Prevent/Allow Medium Removal" },
+ { GPCMD_READ_FORMAT_CAPACITIES, "Read Format Capacities" },
+ { GPCMD_READ_CDVD_CAPACITY, "Read Cd/Dvd Capacity" },
+ { GPCMD_READ_10, "Read 10" },
+ { GPCMD_WRITE_10, "Write 10" },
+ { GPCMD_SEEK, "Seek" },
+ { GPCMD_WRITE_AND_VERIFY_10, "Write and Verify 10" },
+ { GPCMD_VERIFY_10, "Verify 10" },
+ { GPCMD_FLUSH_CACHE, "Flush Cache" },
+ { GPCMD_READ_SUBCHANNEL, "Read Subchannel" },
+ { GPCMD_READ_TOC_PMA_ATIP, "Read Table of Contents" },
+ { GPCMD_READ_HEADER, "Read Header" },
+ { GPCMD_PLAY_AUDIO_10, "Play Audio 10" },
+ { GPCMD_GET_CONFIGURATION, "Get Configuration" },
+ { GPCMD_PLAY_AUDIO_MSF, "Play Audio MSF" },
+ { GPCMD_PLAYAUDIO_TI, "Play Audio TrackIndex" },
+ { GPCMD_GET_EVENT_STATUS_NOTIFICATION, "Get Event Status Notification" },
+ { GPCMD_PAUSE_RESUME, "Pause/Resume" },
+ { GPCMD_STOP_PLAY_SCAN, "Stop Play/Scan" },
+ { GPCMD_READ_DISC_INFO, "Read Disc Info" },
+ { GPCMD_READ_TRACK_RZONE_INFO, "Read Track Rzone Info" },
+ { GPCMD_RESERVE_RZONE_TRACK, "Reserve Rzone Track" },
+ { GPCMD_SEND_OPC, "Send OPC" },
+ { GPCMD_MODE_SELECT_10, "Mode Select 10" },
+ { GPCMD_REPAIR_RZONE_TRACK, "Repair Rzone Track" },
+ { GPCMD_MODE_SENSE_10, "Mode Sense 10" },
+ { GPCMD_CLOSE_TRACK, "Close Track" },
+ { GPCMD_BLANK, "Blank" },
+ { GPCMD_SEND_EVENT, "Send Event" },
+ { GPCMD_SEND_KEY, "Send Key" },
+ { GPCMD_REPORT_KEY, "Report Key" },
+ { GPCMD_LOAD_UNLOAD, "Load/Unload" },
+ { GPCMD_SET_READ_AHEAD, "Set Read-ahead" },
+ { GPCMD_READ_12, "Read 12" },
+ { GPCMD_GET_PERFORMANCE, "Get Performance" },
+ { GPCMD_SEND_DVD_STRUCTURE, "Send DVD Structure" },
+ { GPCMD_READ_DVD_STRUCTURE, "Read DVD Structure" },
+ { GPCMD_SET_STREAMING, "Set Streaming" },
+ { GPCMD_READ_CD_MSF, "Read CD MSF" },
+ { GPCMD_SCAN, "Scan" },
+ { GPCMD_SET_SPEED, "Set Speed" },
+ { GPCMD_PLAY_CD, "Play CD" },
+ { GPCMD_MECHANISM_STATUS, "Mechanism Status" },
+ { GPCMD_READ_CD, "Read CD" },
+};
+
+
+
+/* From Table 303 of the SFF8090 Ver. 3 (Mt. Fuji) draft standard. */
+const char * const sense_key_texts[16] = {
+ "No sense data",
+ "Recovered error",
+ "Not ready",
+ "Medium error",
+ "Hardware error",
+ "Illegal request",
+ "Unit attention",
+ "Data protect",
+ "(reserved)",
+ "(reserved)",
+ "(reserved)",
+ "Aborted command",
+ "(reserved)",
+ "(reserved)",
+ "Miscompare",
+ "(reserved)",
+};
+
+/* From Table 304 of the SFF8090 Ver. 3 (Mt. Fuji) draft standard. */
+const struct {
+ unsigned long asc_ascq;
+ const char * const text;
+} sense_data_texts[] = {
+ { 0x000000, "No additional sense information" },
+ { 0x000011, "Play operation in progress" },
+ { 0x000012, "Play operation paused" },
+ { 0x000013, "Play operation successfully completed" },
+ { 0x000014, "Play operation stopped due to error" },
+ { 0x000015, "No current audio status to return" },
+ { 0x010c0a, "Write error - padding blocks added" },
+ { 0x011700, "Recovered data with no error correction applied" },
+ { 0x011701, "Recovered data with retries" },
+ { 0x011702, "Recovered data with positive head offset" },
+ { 0x011703, "Recovered data with negative head offset" },
+ { 0x011704, "Recovered data with retries and/or CIRC applied" },
+ { 0x011705, "Recovered data using previous sector ID" },
+ { 0x011800, "Recovered data with error correction applied" },
+ { 0x011801, "Recovered data with error correction and retries applied"},
+ { 0x011802, "Recovered data - the data was auto-reallocated" },
+ { 0x011803, "Recovered data with CIRC" },
+ { 0x011804, "Recovered data with L-EC" },
+ { 0x015d00,
+ "Failure prediction threshold exceeded - Predicted logical unit failure" },
+ { 0x015d01,
+ "Failure prediction threshold exceeded - Predicted media failure" },
+ { 0x015dff, "Failure prediction threshold exceeded - False" },
+ { 0x017301, "Power calibration area almost full" },
+ { 0x020400, "Logical unit not ready - cause not reportable" },
+ /* Following is misspelled in ATAPI 2.6, _and_ in Mt. Fuji */
+ { 0x020401,
+ "Logical unit not ready - in progress [sic] of becoming ready" },
+ { 0x020402, "Logical unit not ready - initializing command required" },
+ { 0x020403, "Logical unit not ready - manual intervention required" },
+ { 0x020404, "In process of becoming ready - writing" },
+ { 0x020600, "No reference position found (media may be upside down)" },
+ { 0x023000, "Incompatible medium installed" },
+ { 0x023a00, "Medium not present" },
+ { 0x025300, "Media load or eject failed" },
+ { 0x025700, "Unable to recover table of contents" },
+ { 0x030300, "Peripheral device write fault" },
+ { 0x030301, "No write current" },
+ { 0x030302, "Excessive write errors" },
+ { 0x030c00, "Write error" },
+ { 0x030c01, "Write error - Recovered with auto reallocation" },
+ { 0x030c02, "Write error - auto reallocation failed" },
+ { 0x030c03, "Write error - recommend reassignment" },
+ { 0x030c04, "Compression check miscompare error" },
+ { 0x030c05, "Data expansion occurred during compress" },
+ { 0x030c06, "Block not compressible" },
+ { 0x030c07, "Write error - recovery needed" },
+ { 0x030c08, "Write error - recovery failed" },
+ { 0x030c09, "Write error - loss of streaming" },
+ { 0x031100, "Unrecovered read error" },
+ { 0x031106, "CIRC unrecovered error" },
+ { 0x033101, "Format command failed" },
+ { 0x033200, "No defect spare location available" },
+ { 0x033201, "Defect list update failure" },
+ { 0x035100, "Erase failure" },
+ { 0x037200, "Session fixation error" },
+ { 0x037201, "Session fixation error writin lead-in" },
+ { 0x037202, "Session fixation error writin lead-out" },
+ { 0x037300, "CD control error" },
+ { 0x037302, "Power calibration area is full" },
+ { 0x037303, "Power calibration area error" },
+ { 0x037304, "Program memory area / RMA update failure" },
+ { 0x037305, "Program memory area / RMA is full" },
+ { 0x037306, "Program memory area / RMA is (almost) full" },
+
+ { 0x040200, "No seek complete" },
+ { 0x040300, "Write fault" },
+ { 0x040900, "Track following error" },
+ { 0x040901, "Tracking servo failure" },
+ { 0x040902, "Focus servo failure" },
+ { 0x040903, "Spindle servo failure" },
+ { 0x041500, "Random positioning error" },
+ { 0x041501, "Mechanical positioning or changer error" },
+ { 0x041502, "Positioning error detected by read of medium" },
+ { 0x043c00, "Mechanical positioning or changer error" },
+ { 0x044000, "Diagnostic failure on component (ASCQ)" },
+ { 0x044400, "Internal CD/DVD logical unit failure" },
+ { 0x04b600, "Media load mechanism failed" },
+ { 0x051a00, "Parameter list length error" },
+ { 0x052000, "Invalid command operation code" },
+ { 0x052c00, "Command sequence error" },
+ { 0x052100, "Logical block address out of range" },
+ { 0x052102, "Invalid address for write" },
+ { 0x052400, "Invalid field in command packet" },
+ { 0x052600, "Invalid field in parameter list" },
+ { 0x052601, "Parameter not supported" },
+ { 0x052602, "Parameter value invalid" },
+ { 0x052700, "Write protected media" },
+ { 0x052c00, "Command sequence error" },
+ { 0x052c03, "Current program area is not empty" },
+ { 0x052c04, "Current program area is empty" },
+ { 0x053001, "Cannot read medium - unknown format" },
+ { 0x053002, "Cannot read medium - incompatible format" },
+ { 0x053900, "Saving parameters not supported" },
+ { 0x054e00, "Overlapped commands attempted" },
+ { 0x055302, "Medium removal prevented" },
+ { 0x055500, "System resource failure" },
+ { 0x056300, "End of user area encountered on this track" },
+ { 0x056400, "Illegal mode for this track or incompatible medium" },
+ { 0x056f00, "Copy protection key exchange failure - Authentication failure" },
+ { 0x056f01, "Copy protection key exchange failure - Key not present" },
+ { 0x056f02, "Copy protection key exchange failure - Key not established" },
+ { 0x056f03, "Read of scrambled sector without authentication" },
+ { 0x056f04, "Media region code is mismatched to logical unit" },
+ { 0x056f05, "Drive region must be permanent / region reset count error" },
+ { 0x057203, "Session fixation error - incomplete track in session" },
+ { 0x057204, "Empty or partially written reserved track" },
+ { 0x057205, "No more RZONE reservations are allowed" },
+ { 0x05bf00, "Loss of streaming" },
+ { 0x062800, "Not ready to ready transition, medium may have changed" },
+ { 0x062900, "Power on, reset or hardware reset occurred" },
+ { 0x062a00, "Parameters changed" },
+ { 0x062a01, "Mode parameters changed" },
+ { 0x062e00, "Insufficient time for operation" },
+ { 0x063f00, "Logical unit operating conditions have changed" },
+ { 0x063f01, "Microcode has been changed" },
+ { 0x065a00, "Operator request or state change input (unspecified)" },
+ { 0x065a01, "Operator medium removal request" },
+ { 0x0bb900, "Play operation aborted" },
+
+ /* Here we use 0xff for the key (not a valid key) to signify
+ * that these can have _any_ key value associated with them... */
+ { 0xff0401, "Logical unit is in process of becoming ready" },
+ { 0xff0400, "Logical unit not ready, cause not reportable" },
+ { 0xff0402, "Logical unit not ready, initializing command required" },
+ { 0xff0403, "Logical unit not ready, manual intervention required" },
+ { 0xff0500, "Logical unit does not respond to selection" },
+ { 0xff0800, "Logical unit communication failure" },
+ { 0xff0802, "Logical unit communication parity error" },
+ { 0xff0801, "Logical unit communication time-out" },
+ { 0xff2500, "Logical unit not supported" },
+ { 0xff4c00, "Logical unit failed self-configuration" },
+ { 0xff3e00, "Logical unit has not self-configured yet" },
+};
+#endif
+
+
+#endif /* _IDE_CD_H */
diff --git a/drivers/ide/ide-cs.c b/drivers/ide/ide-cs.c
new file mode 100644
index 000000000..73d285cb1
--- /dev/null
+++ b/drivers/ide/ide-cs.c
@@ -0,0 +1,481 @@
+/*======================================================================
+
+ A driver for PCMCIA IDE/ATA disk cards
+
+ ide_cs.c 1.26 1999/11/16 02:10:49
+
+ The contents of this file are subject to the Mozilla Public
+ License Version 1.1 (the "License"); you may not use this file
+ except in compliance with the License. You may obtain a copy of
+ the License at http://www.mozilla.org/MPL/
+
+ Software distributed under the License is distributed on an "AS
+ IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ implied. See the License for the specific language governing
+ rights and limitations under the License.
+
+ The initial developer of the original code is David A. Hinds
+ <dhinds@pcmcia.sourceforge.org>. Portions created by David A. Hinds
+ are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
+
+ Alternatively, the contents of this file may be used under the
+ terms of the GNU Public License version 2 (the "GPL"), in which
+ case the provisions of the GPL are applicable instead of the
+ above. If you wish to allow the use of your version of this file
+ only under the terms of the GPL and not to allow others to use
+ your version of this file under the MPL, indicate your decision
+ by deleting the provisions above and replace them with the notice
+ and other provisions required by the GPL. If you do not delete
+ the provisions above, a recipient may use your version of this
+ file under either the MPL or the GPL.
+
+======================================================================*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/ioport.h>
+#include <linux/hdreg.h>
+#include <linux/major.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/cisreg.h>
+
+#ifdef PCMCIA_DEBUG
+static int pc_debug = PCMCIA_DEBUG;
+MODULE_PARM(pc_debug, "i");
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
+static char *version =
+"ide_cs.c 1.26 1999/11/16 02:10:49 (David Hinds)";
+#else
+#define DEBUG(n, args...)
+#endif
+
+/*====================================================================*/
+
+/* Parameters that can be set with 'insmod' */
+
+/* Bit map of interrupts to choose from */
+static u_int irq_mask = 0xdeb8;
+static int irq_list[4] = { -1 };
+
+MODULE_PARM(irq_mask, "i");
+MODULE_PARM(irq_list, "1-4i");
+
+/*====================================================================*/
+
+static const char ide_major[] = {
+ IDE0_MAJOR, IDE1_MAJOR, IDE2_MAJOR, IDE3_MAJOR,
+#ifdef IDE4_MAJOR
+ IDE4_MAJOR, IDE5_MAJOR
+#endif
+};
+
+typedef struct ide_info_t {
+ dev_link_t link;
+ int ndev;
+ dev_node_t node;
+ int hd;
+} ide_info_t;
+
+static void ide_config(dev_link_t *link);
+static void ide_release(u_long arg);
+static int ide_event(event_t event, int priority,
+ event_callback_args_t *args);
+
+static dev_info_t dev_info = "ide_cs";
+
+static dev_link_t *ide_attach(void);
+static void ide_detach(dev_link_t *);
+
+static dev_link_t *dev_list = NULL;
+
+/*====================================================================*/
+
+static void cs_error(client_handle_t handle, int func, int ret)
+{
+ error_info_t err = { func, ret };
+ CardServices(ReportError, handle, &err);
+}
+
+/*======================================================================
+
+ ide_attach() creates an "instance" of the driver, allocating
+ local data structures for one device. The device is registered
+ with Card Services.
+
+======================================================================*/
+
+static dev_link_t *ide_attach(void)
+{
+ ide_info_t *info;
+ dev_link_t *link;
+ client_reg_t client_reg;
+ int i, ret;
+
+ DEBUG(0, "ide_attach()\n");
+
+ /* Create new ide device */
+ info = kmalloc(sizeof(*info), GFP_KERNEL);
+ if (!info) return NULL;
+ memset(info, 0, sizeof(*info));
+ link = &info->link; link->priv = info;
+
+ link->release.function = &ide_release;
+ link->release.data = (u_long)link;
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+ link->io.Attributes2 = IO_DATA_PATH_WIDTH_8;
+ link->io.IOAddrLines = 3;
+ link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+ link->irq.IRQInfo1 = IRQ_INFO2_VALID|IRQ_LEVEL_ID;
+ if (irq_list[0] == -1)
+ link->irq.IRQInfo2 = irq_mask;
+ else
+ for (i = 0; i < 4; i++)
+ link->irq.IRQInfo2 |= 1 << irq_list[i];
+ link->conf.Attributes = CONF_ENABLE_IRQ;
+ link->conf.Vcc = 50;
+ link->conf.IntType = INT_MEMORY_AND_IO;
+
+ /* Register with Card Services */
+ link->next = dev_list;
+ dev_list = link;
+ client_reg.dev_info = &dev_info;
+ client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
+ client_reg.EventMask =
+ CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+ CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
+ CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+ client_reg.event_handler = &ide_event;
+ client_reg.Version = 0x0210;
+ client_reg.event_callback_args.client_data = link;
+ ret = CardServices(RegisterClient, &link->handle, &client_reg);
+ if (ret != CS_SUCCESS) {
+ cs_error(link->handle, RegisterClient, ret);
+ ide_detach(link);
+ return NULL;
+ }
+
+ return link;
+} /* ide_attach */
+
+/*======================================================================
+
+ This deletes a driver "instance". The device is de-registered
+ with Card Services. If it has been released, all local data
+ structures are freed. Otherwise, the structures will be freed
+ when the device is released.
+
+======================================================================*/
+
+static void ide_detach(dev_link_t *link)
+{
+ dev_link_t **linkp;
+ long flags;
+ int ret;
+
+ DEBUG(0, "ide_detach(0x%p)\n", link);
+
+ /* Locate device structure */
+ for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+ if (*linkp == link) break;
+ if (*linkp == NULL)
+ return;
+
+ save_flags(flags);
+ cli();
+ if (link->state & DEV_RELEASE_PENDING) {
+ del_timer(&link->release);
+ link->state &= ~DEV_RELEASE_PENDING;
+ }
+ restore_flags(flags);
+
+ if (link->state & DEV_CONFIG)
+ ide_release((u_long)link);
+
+ if (link->handle) {
+ ret = CardServices(DeregisterClient, link->handle);
+ if (ret != CS_SUCCESS)
+ cs_error(link->handle, DeregisterClient, ret);
+ }
+
+ /* Unlink, free device structure */
+ *linkp = link->next;
+ kfree(link->priv);
+
+} /* ide_detach */
+
+/*======================================================================
+
+ ide_config() is scheduled to run after a CARD_INSERTION event
+ is received, to configure the PCMCIA socket, and to make the
+ ide device available to the system.
+
+======================================================================*/
+
+#define CS_CHECK(fn, args...) \
+while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed
+
+#define CFG_CHECK(fn, args...) \
+if (CardServices(fn, args) != 0) goto next_entry
+
+void ide_config(dev_link_t *link)
+{
+ client_handle_t handle = link->handle;
+ ide_info_t *info = link->priv;
+ tuple_t tuple;
+ u_short buf[128];
+ cisparse_t parse;
+ config_info_t conf;
+ cistpl_cftable_entry_t *cfg = &parse.cftable_entry;
+ cistpl_cftable_entry_t dflt = { 0 };
+ int i, pass, last_ret, last_fn, hd, io_base, ctl_base;
+
+ DEBUG(0, "ide_config(0x%p)\n", link);
+
+ tuple.TupleData = (cisdata_t *)buf;
+ tuple.TupleOffset = 0; tuple.TupleDataMax = 255;
+ tuple.Attributes = 0;
+ tuple.DesiredTuple = CISTPL_CONFIG;
+ CS_CHECK(GetFirstTuple, handle, &tuple);
+ CS_CHECK(GetTupleData, handle, &tuple);
+ CS_CHECK(ParseTuple, handle, &tuple, &parse);
+ link->conf.ConfigBase = parse.config.base;
+ link->conf.Present = parse.config.rmask[0];
+
+ /* Configure card */
+ link->state |= DEV_CONFIG;
+
+ /* Not sure if this is right... look up the current Vcc */
+ CS_CHECK(GetConfigurationInfo, handle, &conf);
+ link->conf.Vcc = conf.Vcc;
+
+ pass = io_base = ctl_base = 0;
+ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+ tuple.Attributes = 0;
+ CS_CHECK(GetFirstTuple, handle, &tuple);
+ while (1) {
+ CFG_CHECK(GetTupleData, handle, &tuple);
+ CFG_CHECK(ParseTuple, handle, &tuple, &parse);
+
+ /* Check for matching Vcc, unless we're desperate */
+ if (!pass) {
+ if (cfg->vcc.present & (1<<CISTPL_POWER_VNOM)) {
+ if (conf.Vcc != cfg->vcc.param[CISTPL_POWER_VNOM]/10000)
+ goto next_entry;
+ } else if (dflt.vcc.present & (1<<CISTPL_POWER_VNOM)) {
+ if (conf.Vcc != dflt.vcc.param[CISTPL_POWER_VNOM]/10000)
+ goto next_entry;
+ }
+ }
+
+ if (cfg->vpp1.present & (1<<CISTPL_POWER_VNOM))
+ link->conf.Vpp1 = link->conf.Vpp2 =
+ cfg->vpp1.param[CISTPL_POWER_VNOM]/10000;
+ else if (dflt.vpp1.present & (1<<CISTPL_POWER_VNOM))
+ link->conf.Vpp1 = link->conf.Vpp2 =
+ dflt.vpp1.param[CISTPL_POWER_VNOM]/10000;
+
+ if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) {
+ cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io;
+ link->conf.ConfigIndex = cfg->index;
+ link->io.BasePort1 = io->win[0].base;
+ link->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
+ if (!(io->flags & CISTPL_IO_16BIT))
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ if (io->nwin == 2) {
+ link->io.NumPorts1 = 8;
+ link->io.BasePort2 = io->win[1].base;
+ link->io.NumPorts2 = 1;
+ CFG_CHECK(RequestIO, link->handle, &link->io);
+ io_base = link->io.BasePort1;
+ ctl_base = link->io.BasePort2;
+ } else if ((io->nwin == 1) && (io->win[0].len >= 16)) {
+ link->io.NumPorts1 = io->win[0].len;
+ link->io.NumPorts2 = 0;
+ CFG_CHECK(RequestIO, link->handle, &link->io);
+ io_base = link->io.BasePort1;
+ ctl_base = link->io.BasePort1+0x0e;
+ } else goto next_entry;
+ /* If we've got this far, we're done */
+ break;
+ }
+
+ next_entry:
+ if (cfg->flags & CISTPL_CFTABLE_DEFAULT) dflt = *cfg;
+ if (pass) {
+ CS_CHECK(GetNextTuple, handle, &tuple);
+ } else if (CardServices(GetNextTuple, handle, &tuple) != 0) {
+ CS_CHECK(GetFirstTuple, handle, &tuple);
+ memset(&dflt, 0, sizeof(dflt));
+ pass++;
+ }
+ }
+
+ CS_CHECK(RequestIRQ, handle, &link->irq);
+ CS_CHECK(RequestConfiguration, handle, &link->conf);
+
+ /* deal with brain dead IDE resource management */
+ release_region(link->io.BasePort1, link->io.NumPorts1);
+ if (link->io.NumPorts2)
+ release_region(link->io.BasePort2, link->io.NumPorts2);
+
+ /* retry registration in case device is still spinning up */
+ for (i = 0; i < 10; i++) {
+ hd = ide_register(io_base, ctl_base, link->irq.AssignedIRQ);
+ if (hd >= 0) break;
+ if (link->io.NumPorts1 == 0x20) {
+ hd = ide_register(io_base+0x10, ctl_base+0x10,
+ link->irq.AssignedIRQ);
+ if (hd >= 0) {
+ io_base += 0x10; ctl_base += 0x10;
+ break;
+ }
+ }
+ __set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(HZ/10);
+ }
+
+ if (hd < 0) {
+ printk(KERN_NOTICE "ide_cs: ide_register() at 0x%3x & 0x%3x"
+ ", irq %u failed\n", io_base, ctl_base,
+ link->irq.AssignedIRQ);
+ goto failed;
+ }
+
+ MOD_INC_USE_COUNT;
+ info->ndev = 1;
+ sprintf(info->node.dev_name, "hd%c", 'a'+(hd*2));
+ info->node.major = ide_major[hd];
+ info->node.minor = 0;
+ info->hd = hd;
+ link->dev = &info->node;
+ printk(KERN_INFO "ide_cs: %s: Vcc = %d.%d, Vpp = %d.%d\n",
+ info->node.dev_name, link->conf.Vcc/10, link->conf.Vcc%10,
+ link->conf.Vpp1/10, link->conf.Vpp1%10);
+
+ link->state &= ~DEV_CONFIG_PENDING;
+ return;
+
+cs_failed:
+ cs_error(link->handle, last_fn, last_ret);
+failed:
+ ide_release((u_long)link);
+
+} /* ide_config */
+
+/*======================================================================
+
+ After a card is removed, ide_release() will unregister the net
+ device, and release the PCMCIA configuration. If the device is
+ still open, this will be postponed until it is closed.
+
+======================================================================*/
+
+void ide_release(u_long arg)
+{
+ dev_link_t *link = (dev_link_t *)arg;
+ ide_info_t *info = link->priv;
+
+ DEBUG(0, "ide_release(0x%p)\n", link);
+
+ if (info->ndev) {
+ ide_unregister(info->hd);
+ MOD_DEC_USE_COUNT;
+ }
+ info->ndev = 0;
+ link->dev = NULL;
+
+ CardServices(ReleaseConfiguration, link->handle);
+ CardServices(ReleaseIO, link->handle, &link->io);
+ CardServices(ReleaseIRQ, link->handle, &link->irq);
+
+ link->state &= ~DEV_CONFIG;
+
+} /* ide_release */
+
+/*======================================================================
+
+ The card status event handler. Mostly, this schedules other
+ stuff to run after an event is received. A CARD_REMOVAL event
+ also sets some flags to discourage the ide drivers from
+ talking to the ports.
+
+======================================================================*/
+
+int ide_event(event_t event, int priority,
+ event_callback_args_t *args)
+{
+ dev_link_t *link = args->client_data;
+
+ DEBUG(1, "ide_event(0x%06x)\n", event);
+
+ switch (event) {
+ case CS_EVENT_CARD_REMOVAL:
+ link->state &= ~DEV_PRESENT;
+ if (link->state & DEV_CONFIG) {
+ link->release.expires = jiffies + HZ/20;
+ link->state |= DEV_RELEASE_PENDING;
+ add_timer(&link->release);
+ }
+ break;
+ case CS_EVENT_CARD_INSERTION:
+ link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+ ide_config(link);
+ break;
+ case CS_EVENT_PM_SUSPEND:
+ link->state |= DEV_SUSPEND;
+ /* Fall through... */
+ case CS_EVENT_RESET_PHYSICAL:
+ if (link->state & DEV_CONFIG)
+ CardServices(ReleaseConfiguration, link->handle);
+ break;
+ case CS_EVENT_PM_RESUME:
+ link->state &= ~DEV_SUSPEND;
+ /* Fall through... */
+ case CS_EVENT_CARD_RESET:
+ if (DEV_OK(link))
+ CardServices(RequestConfiguration, link->handle, &link->conf);
+ break;
+ }
+ return 0;
+} /* ide_event */
+
+/*====================================================================*/
+
+static int __init init_ide_cs(void)
+{
+ servinfo_t serv;
+ DEBUG(0, "%s\n", version);
+ CardServices(GetCardServicesInfo, &serv);
+ if (serv.Revision != CS_RELEASE_CODE) {
+ printk(KERN_NOTICE "ide_cs: Card Services release "
+ "does not match!\n");
+ return -1;
+ }
+ register_pccard_driver(&dev_info, &ide_attach, &ide_detach);
+ return 0;
+}
+
+static void __exit exit_ide_cs(void)
+{
+ DEBUG(0, "ide_cs: unloading\n");
+ unregister_pccard_driver(&dev_info);
+ while (dev_list != NULL)
+ ide_detach(dev_list);
+}
+
+module_init(init_ide_cs);
+module_exit(exit_ide_cs);
diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c
new file mode 100644
index 000000000..2ef50f285
--- /dev/null
+++ b/drivers/ide/ide-disk.c
@@ -0,0 +1,906 @@
+/*
+ * linux/drivers/block/ide-disk.c Version 1.09 April 23, 1999
+ *
+ * Copyright (C) 1994-1998 Linus Torvalds & authors (see below)
+ */
+
+/*
+ * Mostly written by Mark Lord <mlord@pobox.com>
+ * and Gadi Oxman <gadio@netvision.net.il>
+ *
+ * See linux/MAINTAINERS for address of current maintainer.
+ *
+ * This is the IDE/ATA disk driver, as evolved from hd.c and ide.c.
+ *
+ * Version 1.00 move disk only code from ide.c to ide-disk.c
+ * support optional byte-swapping of all data
+ * Version 1.01 fix previous byte-swapping code
+ * Version 1.02 remove ", LBA" from drive identification msgs
+ * Version 1.03 fix display of id->buf_size for big-endian
+ * Version 1.04 add /proc configurable settings and S.M.A.R.T support
+ * Version 1.05 add capacity support for ATA3 >= 8GB
+ * Version 1.06 get boot-up messages to show full cyl count
+ * Version 1.07 disable door-locking if it fails
+ * Version 1.08 fixed CHS/LBA translations for ATA4 > 8GB,
+ * process of adding new ATA4 compliance.
+ * fixed problems in allowing fdisk to see
+ * the entire disk.
+ * Version 1.09 added increment of rq->sector in ide_multwrite
+ * added UDMA 3/4 reporting
+ */
+
+#define IDEDISK_VERSION "1.09"
+
+#undef REALLY_SLOW_IO /* most systems can safely undef this */
+
+#define _IDE_DISK_C /* Tell linux/hdsmart.h it's really us */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/major.h>
+#include <linux/errno.h>
+#include <linux/genhd.h>
+#include <linux/malloc.h>
+#include <linux/delay.h>
+#include <linux/ide.h>
+
+#include <asm/byteorder.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+
+#ifdef CONFIG_BLK_DEV_PDC4030
+#define IS_PDC4030_DRIVE (HWIF(drive)->chipset == ide_pdc4030)
+#else
+#define IS_PDC4030_DRIVE (0) /* auto-NULLs out pdc4030 code */
+#endif
+
+static void idedisk_bswap_data (void *buffer, int wcount)
+{
+ u16 *p = buffer;
+
+ while (wcount--) {
+ *p++ = *p << 8 | *p >> 8;
+ *p++ = *p << 8 | *p >> 8;
+ }
+}
+
+static inline void idedisk_input_data (ide_drive_t *drive, void *buffer, unsigned int wcount)
+{
+ ide_input_data(drive, buffer, wcount);
+ if (drive->bswap)
+ idedisk_bswap_data(buffer, wcount);
+}
+
+static inline void idedisk_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount)
+{
+ if (drive->bswap) {
+ idedisk_bswap_data(buffer, wcount);
+ ide_output_data(drive, buffer, wcount);
+ idedisk_bswap_data(buffer, wcount);
+ } else
+ ide_output_data(drive, buffer, wcount);
+}
+
+/*
+ * lba_capacity_is_ok() performs a sanity check on the claimed "lba_capacity"
+ * value for this drive (from its reported identification information).
+ *
+ * Returns: 1 if lba_capacity looks sensible
+ * 0 otherwise
+ *
+ * It is called only once for each drive.
+ */
+static int lba_capacity_is_ok (struct hd_driveid *id)
+{
+ unsigned long lba_sects, chs_sects, head, tail;
+
+ /*
+ * The ATA spec tells large drives to return
+ * C/H/S = 16383/16/63 independent of their size.
+ * Some drives can be jumpered to use 15 heads instead of 16.
+ * Some drives can be jumpered to use 4092 cyls instead of 16383.
+ */
+ if ((id->cyls == 16383
+ || (id->cyls == 4092 && id->cur_cyls == 16383)) &&
+ id->sectors == 63 &&
+ (id->heads == 15 || id->heads == 16) &&
+ id->lba_capacity >= 16383*63*id->heads)
+ return 1;
+
+ lba_sects = id->lba_capacity;
+ chs_sects = id->cyls * id->heads * id->sectors;
+
+ /* perform a rough sanity check on lba_sects: within 10% is OK */
+ if ((lba_sects - chs_sects) < chs_sects/10)
+ return 1;
+
+ /* some drives have the word order reversed */
+ head = ((lba_sects >> 16) & 0xffff);
+ tail = (lba_sects & 0xffff);
+ lba_sects = (head | (tail << 16));
+ if ((lba_sects - chs_sects) < chs_sects/10) {
+ id->lba_capacity = lba_sects;
+ return 1; /* lba_capacity is (now) good */
+ }
+
+ return 0; /* lba_capacity value may be bad */
+}
+
+/*
+ * read_intr() is the handler for disk read/multread interrupts
+ */
+static ide_startstop_t read_intr (ide_drive_t *drive)
+{
+ byte stat;
+ int i;
+ unsigned int msect, nsect;
+ struct request *rq;
+#if 0
+ if (!OK_STAT(stat=GET_STAT(),DATA_READY,BAD_R_STAT)) {
+ return ide_error(drive, "read_intr", stat);
+ }
+#else /* new way for dealing with premature shared PCI interrupts */
+ if (!OK_STAT(stat=GET_STAT(),DATA_READY,BAD_R_STAT)) {
+ if (stat & (ERR_STAT|DRQ_STAT)) {
+ return ide_error(drive, "read_intr", stat);
+ }
+ /* no data yet, so wait for another interrupt */
+ ide_set_handler(drive, &read_intr, WAIT_CMD, NULL);
+ return ide_started;
+ }
+#endif
+ msect = drive->mult_count;
+
+read_next:
+ rq = HWGROUP(drive)->rq;
+ if (msect) {
+ if ((nsect = rq->current_nr_sectors) > msect)
+ nsect = msect;
+ msect -= nsect;
+ } else
+ nsect = 1;
+ idedisk_input_data(drive, rq->buffer, nsect * SECTOR_WORDS);
+#ifdef DEBUG
+ printk("%s: read: sectors(%ld-%ld), buffer=0x%08lx, remaining=%ld\n",
+ drive->name, rq->sector, rq->sector+nsect-1,
+ (unsigned long) rq->buffer+(nsect<<9), rq->nr_sectors-nsect);
+#endif
+ rq->sector += nsect;
+ rq->buffer += nsect<<9;
+ rq->errors = 0;
+ i = (rq->nr_sectors -= nsect);
+ if (((long)(rq->current_nr_sectors -= nsect)) <= 0)
+ ide_end_request(1, HWGROUP(drive));
+ if (i > 0) {
+ if (msect)
+ goto read_next;
+ ide_set_handler (drive, &read_intr, WAIT_CMD, NULL);
+ return ide_started;
+ }
+ return ide_stopped;
+}
+
+/*
+ * write_intr() is the handler for disk write interrupts
+ */
+static ide_startstop_t write_intr (ide_drive_t *drive)
+{
+ byte stat;
+ int i;
+ ide_hwgroup_t *hwgroup = HWGROUP(drive);
+ struct request *rq = hwgroup->rq;
+
+ if (!OK_STAT(stat=GET_STAT(),DRIVE_READY,drive->bad_wstat)) {
+ printk("%s: write_intr error1: nr_sectors=%ld, stat=0x%02x\n", drive->name, rq->nr_sectors, stat);
+ } else {
+#ifdef DEBUG
+ printk("%s: write: sector %ld, buffer=0x%08lx, remaining=%ld\n",
+ drive->name, rq->sector, (unsigned long) rq->buffer,
+ rq->nr_sectors-1);
+#endif
+ if ((rq->nr_sectors == 1) ^ ((stat & DRQ_STAT) != 0)) {
+ rq->sector++;
+ rq->buffer += 512;
+ rq->errors = 0;
+ i = --rq->nr_sectors;
+ --rq->current_nr_sectors;
+ if (((long)rq->current_nr_sectors) <= 0)
+ ide_end_request(1, hwgroup);
+ if (i > 0) {
+ idedisk_output_data (drive, rq->buffer, SECTOR_WORDS);
+ ide_set_handler (drive, &write_intr, WAIT_CMD, NULL);
+ return ide_started;
+ }
+ return ide_stopped;
+ }
+ return ide_stopped; /* the original code did this here (?) */
+ }
+ return ide_error(drive, "write_intr", stat);
+}
+
+/*
+ * ide_multwrite() transfers a block of up to mcount sectors of data
+ * to a drive as part of a disk multiple-sector write operation.
+ *
+ * Returns 0 if successful; returns 1 if request had to be aborted due to corrupted buffer list.
+ */
+int ide_multwrite (ide_drive_t *drive, unsigned int mcount)
+{
+ ide_hwgroup_t *hwgroup= HWGROUP(drive);
+
+ /*
+ * This may look a bit odd, but remember wrq is a copy of the
+ * request not the original. The pointers are real however so the
+ * bh's are not copies. Remember that or bad stuff will happen
+ *
+ * At the point we are called the drive has asked us for the
+ * data, and its our job to feed it, walking across bh boundaries
+ * if need be.
+ */
+
+ struct request *rq = &hwgroup->wrq;
+
+ do {
+ unsigned long flags;
+ unsigned int nsect = rq->current_nr_sectors;
+ if (nsect > mcount)
+ nsect = mcount;
+ mcount -= nsect;
+
+ idedisk_output_data(drive, rq->buffer, nsect<<7);
+#ifdef DEBUG
+ printk("%s: multwrite: sector %ld, buffer=0x%08lx, count=%d, remaining=%ld\n",
+ drive->name, rq->sector, (unsigned long) rq->buffer,
+ nsect, rq->nr_sectors - nsect);
+#endif
+ spin_lock_irqsave(&io_request_lock, flags); /* Is this really necessary? */
+#ifdef CONFIG_BLK_DEV_PDC4030
+ rq->sector += nsect;
+#endif
+ if (((long)(rq->nr_sectors -= nsect)) <= 0) {
+#ifdef DEBUG
+ printk("%s: multwrite: count=%d, current=%ld\n",
+ drive->name, nsect, rq->nr_sectors);
+#endif
+ spin_unlock_irqrestore(&io_request_lock, flags);
+ break;
+ }
+ if ((rq->current_nr_sectors -= nsect) == 0) {
+ if ((rq->bh = rq->bh->b_reqnext) != NULL) {
+ rq->current_nr_sectors = rq->bh->b_size>>9;
+ rq->buffer = rq->bh->b_data;
+ } else {
+ spin_unlock_irqrestore(&io_request_lock, flags);
+ printk("%s: buffer list corrupted (%ld, %ld, %d)\n",
+ drive->name, rq->current_nr_sectors,
+ rq->nr_sectors, nsect);
+ ide_end_request(0, hwgroup);
+ return 1;
+ }
+ } else {
+ /* Fix the pointer.. we ate data */
+ rq->buffer += nsect << 9;
+ }
+ spin_unlock_irqrestore(&io_request_lock, flags);
+ } while (mcount);
+ return 0;
+}
+
+/*
+ * multwrite_intr() is the handler for disk multwrite interrupts
+ */
+static ide_startstop_t multwrite_intr (ide_drive_t *drive)
+{
+ byte stat;
+ int i;
+ ide_hwgroup_t *hwgroup = HWGROUP(drive);
+ struct request *rq = &hwgroup->wrq;
+
+ if (OK_STAT(stat=GET_STAT(),DRIVE_READY,drive->bad_wstat)) {
+ if (stat & DRQ_STAT) {
+ /*
+ * The drive wants data. Remember rq is the copy
+ * of the request
+ */
+ if (rq->nr_sectors) {
+ if (ide_multwrite(drive, drive->mult_count))
+ return ide_stopped;
+ ide_set_handler (drive, &multwrite_intr, WAIT_CMD, NULL);
+ return ide_started;
+ }
+ } else {
+ /*
+ * If the copy has all the blocks completed then
+ * we can end the original request.
+ */
+ if (!rq->nr_sectors) { /* all done? */
+ rq = hwgroup->rq;
+ for (i = rq->nr_sectors; i > 0;){
+ i -= rq->current_nr_sectors;
+ ide_end_request(1, hwgroup);
+ }
+ return ide_stopped;
+ }
+ }
+ return ide_stopped; /* the original code did this here (?) */
+ }
+ return ide_error(drive, "multwrite_intr", stat);
+}
+
+/*
+ * set_multmode_intr() is invoked on completion of a WIN_SETMULT cmd.
+ */
+static ide_startstop_t set_multmode_intr (ide_drive_t *drive)
+{
+ byte stat;
+
+ if (OK_STAT(stat=GET_STAT(),READY_STAT,BAD_STAT)) {
+ drive->mult_count = drive->mult_req;
+ } else {
+ drive->mult_req = drive->mult_count = 0;
+ drive->special.b.recalibrate = 1;
+ (void) ide_dump_status(drive, "set_multmode", stat);
+ }
+ return ide_stopped;
+}
+
+/*
+ * set_geometry_intr() is invoked on completion of a WIN_SPECIFY cmd.
+ */
+static ide_startstop_t set_geometry_intr (ide_drive_t *drive)
+{
+ byte stat;
+
+ if (OK_STAT(stat=GET_STAT(),READY_STAT,BAD_STAT))
+ return ide_stopped;
+
+ if (stat & (ERR_STAT|DRQ_STAT))
+ return ide_error(drive, "set_geometry_intr", stat);
+
+ ide_set_handler(drive, &set_geometry_intr, WAIT_CMD, NULL);
+ return ide_started;
+}
+
+/*
+ * recal_intr() is invoked on completion of a WIN_RESTORE (recalibrate) cmd.
+ */
+static ide_startstop_t recal_intr (ide_drive_t *drive)
+{
+ byte stat = GET_STAT();
+
+ if (!OK_STAT(stat,READY_STAT,BAD_STAT))
+ return ide_error(drive, "recal_intr", stat);
+ return ide_stopped;
+}
+
+/*
+ * do_rw_disk() issues READ and WRITE commands to a disk,
+ * using LBA if supported, or CHS otherwise, to address sectors.
+ * It also takes care of issuing special DRIVE_CMDs.
+ */
+static ide_startstop_t do_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long block)
+{
+ if (IDE_CONTROL_REG)
+ OUT_BYTE(drive->ctl,IDE_CONTROL_REG);
+ OUT_BYTE(rq->nr_sectors,IDE_NSECTOR_REG);
+#ifdef CONFIG_BLK_DEV_PDC4030
+ if (drive->select.b.lba || IS_PDC4030_DRIVE) {
+#else /* !CONFIG_BLK_DEV_PDC4030 */
+ if (drive->select.b.lba) {
+#endif /* CONFIG_BLK_DEV_PDC4030 */
+#ifdef DEBUG
+ printk("%s: %sing: LBAsect=%ld, sectors=%ld, buffer=0x%08lx\n",
+ drive->name, (rq->cmd==READ)?"read":"writ",
+ block, rq->nr_sectors, (unsigned long) rq->buffer);
+#endif
+ OUT_BYTE(block,IDE_SECTOR_REG);
+ OUT_BYTE(block>>=8,IDE_LCYL_REG);
+ OUT_BYTE(block>>=8,IDE_HCYL_REG);
+ OUT_BYTE(((block>>8)&0x0f)|drive->select.all,IDE_SELECT_REG);
+ } else {
+ unsigned int sect,head,cyl,track;
+ track = block / drive->sect;
+ sect = block % drive->sect + 1;
+ OUT_BYTE(sect,IDE_SECTOR_REG);
+ head = track % drive->head;
+ cyl = track / drive->head;
+ OUT_BYTE(cyl,IDE_LCYL_REG);
+ OUT_BYTE(cyl>>8,IDE_HCYL_REG);
+ OUT_BYTE(head|drive->select.all,IDE_SELECT_REG);
+#ifdef DEBUG
+ printk("%s: %sing: CHS=%d/%d/%d, sectors=%ld, buffer=0x%08lx\n",
+ drive->name, (rq->cmd==READ)?"read":"writ", cyl,
+ head, sect, rq->nr_sectors, (unsigned long) rq->buffer);
+#endif
+ }
+#ifdef CONFIG_BLK_DEV_PDC4030
+ if (IS_PDC4030_DRIVE) {
+ extern ide_startstop_t do_pdc4030_io(ide_drive_t *, struct request *);
+ return do_pdc4030_io (drive, rq);
+ }
+#endif /* CONFIG_BLK_DEV_PDC4030 */
+ if (rq->cmd == READ) {
+#ifdef CONFIG_BLK_DEV_IDEDMA
+ if (drive->using_dma && !(HWIF(drive)->dmaproc(ide_dma_read, drive)))
+ return ide_started;
+#endif /* CONFIG_BLK_DEV_IDEDMA */
+ ide_set_handler(drive, &read_intr, WAIT_CMD, NULL);
+ OUT_BYTE(drive->mult_count ? WIN_MULTREAD : WIN_READ, IDE_COMMAND_REG);
+ return ide_started;
+ }
+ if (rq->cmd == WRITE) {
+ ide_startstop_t startstop;
+#ifdef CONFIG_BLK_DEV_IDEDMA
+ if (drive->using_dma && !(HWIF(drive)->dmaproc(ide_dma_write, drive)))
+ return ide_started;
+#endif /* CONFIG_BLK_DEV_IDEDMA */
+ OUT_BYTE(drive->mult_count ? WIN_MULTWRITE : WIN_WRITE, IDE_COMMAND_REG);
+ if (ide_wait_stat(&startstop, drive, DATA_READY, drive->bad_wstat, WAIT_DRQ)) {
+ printk(KERN_ERR "%s: no DRQ after issuing %s\n", drive->name,
+ drive->mult_count ? "MULTWRITE" : "WRITE");
+ return startstop;
+ }
+ if (!drive->unmask)
+ __cli(); /* local CPU only */
+ if (drive->mult_count) {
+ ide_hwgroup_t *hwgroup = HWGROUP(drive);
+ /*
+ * Ugh.. this part looks ugly because we MUST set up
+ * the interrupt handler before outputting the first block
+ * of data to be written. If we hit an error (corrupted buffer list)
+ * in ide_multwrite(), then we need to remove the handler/timer
+ * before returning. Fortunately, this NEVER happens (right?).
+ *
+ * Except when you get an error it seems...
+ */
+ hwgroup->wrq = *rq; /* scratchpad */
+ ide_set_handler (drive, &multwrite_intr, WAIT_CMD, NULL);
+ if (ide_multwrite(drive, drive->mult_count)) {
+ unsigned long flags;
+ spin_lock_irqsave(&io_request_lock, flags);
+ hwgroup->handler = NULL;
+ del_timer(&hwgroup->timer);
+ spin_unlock_irqrestore(&io_request_lock, flags);
+ return ide_stopped;
+ }
+ } else {
+ ide_set_handler (drive, &write_intr, WAIT_CMD, NULL);
+ idedisk_output_data(drive, rq->buffer, SECTOR_WORDS);
+ }
+ return ide_started;
+ }
+ printk(KERN_ERR "%s: bad command: %d\n", drive->name, rq->cmd);
+ ide_end_request(0, HWGROUP(drive));
+ return ide_stopped;
+}
+
+static int idedisk_open (struct inode *inode, struct file *filp, ide_drive_t *drive)
+{
+ MOD_INC_USE_COUNT;
+ if (drive->removable && drive->usage == 1) {
+ check_disk_change(inode->i_rdev);
+ /*
+ * Ignore the return code from door_lock,
+ * since the open() has already succeeded,
+ * and the door_lock is irrelevant at this point.
+ */
+ if (drive->doorlocking && ide_wait_cmd(drive, WIN_DOORLOCK, 0, 0, 0, NULL))
+ drive->doorlocking = 0;
+ }
+ return 0;
+}
+
+static void idedisk_release (struct inode *inode, struct file *filp, ide_drive_t *drive)
+{
+ if (drive->removable && !drive->usage) {
+ invalidate_buffers(inode->i_rdev);
+ if (drive->doorlocking && ide_wait_cmd(drive, WIN_DOORUNLOCK, 0, 0, 0, NULL))
+ drive->doorlocking = 0;
+ }
+ MOD_DEC_USE_COUNT;
+}
+
+static int idedisk_media_change (ide_drive_t *drive)
+{
+ return drive->removable; /* if removable, always assume it was changed */
+}
+
+/*
+ * Compute drive->capacity, the full capacity of the drive
+ * Called with drive->id != NULL.
+ */
+static void init_idedisk_capacity (ide_drive_t *drive)
+{
+ struct hd_driveid *id = drive->id;
+ unsigned long capacity = drive->cyl * drive->head * drive->sect;
+
+ drive->select.b.lba = 0;
+
+ /* Determine capacity, and use LBA if the drive properly supports it */
+ if ((id->capability & 2) && lba_capacity_is_ok(id)) {
+ capacity = id->lba_capacity;
+ drive->cyl = capacity / (drive->head * drive->sect);
+ drive->select.b.lba = 1;
+ }
+ drive->capacity = capacity;
+}
+
+static unsigned long idedisk_capacity (ide_drive_t *drive)
+{
+ return (drive->capacity - drive->sect0);
+}
+
+static ide_startstop_t idedisk_special (ide_drive_t *drive)
+{
+ special_t *s = &drive->special;
+
+ if (s->b.set_geometry) {
+ s->b.set_geometry = 0;
+ OUT_BYTE(drive->sect,IDE_SECTOR_REG);
+ OUT_BYTE(drive->cyl,IDE_LCYL_REG);
+ OUT_BYTE(drive->cyl>>8,IDE_HCYL_REG);
+ OUT_BYTE(((drive->head-1)|drive->select.all)&0xBF,IDE_SELECT_REG);
+ if (!IS_PDC4030_DRIVE)
+ ide_cmd(drive, WIN_SPECIFY, drive->sect, &set_geometry_intr);
+ } else if (s->b.recalibrate) {
+ s->b.recalibrate = 0;
+ if (!IS_PDC4030_DRIVE)
+ ide_cmd(drive, WIN_RESTORE, drive->sect, &recal_intr);
+ } else if (s->b.set_multmode) {
+ s->b.set_multmode = 0;
+ if (drive->id && drive->mult_req > drive->id->max_multsect)
+ drive->mult_req = drive->id->max_multsect;
+ if (!IS_PDC4030_DRIVE)
+ ide_cmd(drive, WIN_SETMULT, drive->mult_req, &set_multmode_intr);
+ } else if (s->all) {
+ int special = s->all;
+ s->all = 0;
+ printk(KERN_ERR "%s: bad special flag: 0x%02x\n", drive->name, special);
+ return ide_stopped;
+ }
+ return IS_PDC4030_DRIVE ? ide_stopped : ide_started;
+}
+
+static void idedisk_pre_reset (ide_drive_t *drive)
+{
+ drive->special.all = 0;
+ drive->special.b.set_geometry = 1;
+ drive->special.b.recalibrate = 1;
+ if (OK_TO_RESET_CONTROLLER)
+ drive->mult_count = 0;
+ if (!drive->keep_settings && !drive->using_dma)
+ drive->mult_req = 0;
+ if (drive->mult_req != drive->mult_count)
+ drive->special.b.set_multmode = 1;
+}
+
+#ifdef CONFIG_PROC_FS
+
+static int smart_enable(ide_drive_t *drive)
+{
+ return ide_wait_cmd(drive, WIN_SMART, 0, SMART_ENABLE, 0, NULL);
+}
+
+static int get_smart_values(ide_drive_t *drive, byte *buf)
+{
+ (void) smart_enable(drive);
+ return ide_wait_cmd(drive, WIN_SMART, 0, SMART_READ_VALUES, 1, buf);
+}
+
+static int get_smart_thresholds(ide_drive_t *drive, byte *buf)
+{
+ (void) smart_enable(drive);
+ return ide_wait_cmd(drive, WIN_SMART, 0, SMART_READ_THRESHOLDS, 1, buf);
+}
+
+static int proc_idedisk_read_cache
+ (char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+ ide_drive_t *drive = (ide_drive_t *) data;
+ char *out = page;
+ int len;
+
+ if (drive->id)
+ len = sprintf(out,"%i\n", drive->id->buf_size / 2);
+ else
+ len = sprintf(out,"(none)\n");
+ PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
+static int proc_idedisk_read_smart_thresholds
+ (char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+ ide_drive_t *drive = (ide_drive_t *)data;
+ int len = 0, i = 0;
+
+ if (!get_smart_thresholds(drive, page)) {
+ unsigned short *val = ((unsigned short *)page) + 2;
+ char *out = ((char *)val) + (SECTOR_WORDS * 4);
+ page = out;
+ do {
+ out += sprintf(out, "%04x%c", le16_to_cpu(*val), (++i & 7) ? ' ' : '\n');
+ val += 1;
+ } while (i < (SECTOR_WORDS * 2));
+ len = out - page;
+ }
+ PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
+static int proc_idedisk_read_smart_values
+ (char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+ ide_drive_t *drive = (ide_drive_t *)data;
+ int len = 0, i = 0;
+
+ if (!get_smart_values(drive, page)) {
+ unsigned short *val = ((unsigned short *)page) + 2;
+ char *out = ((char *)val) + (SECTOR_WORDS * 4);
+ page = out;
+ do {
+ out += sprintf(out, "%04x%c", le16_to_cpu(*val), (++i & 7) ? ' ' : '\n');
+ val += 1;
+ } while (i < (SECTOR_WORDS * 2));
+ len = out - page;
+ }
+ PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
+static ide_proc_entry_t idedisk_proc[] = {
+ { "cache", S_IFREG|S_IRUGO, proc_idedisk_read_cache, NULL },
+ { "geometry", S_IFREG|S_IRUGO, proc_ide_read_geometry, NULL },
+ { "smart_values", S_IFREG|S_IRUSR, proc_idedisk_read_smart_values, NULL },
+ { "smart_thresholds", S_IFREG|S_IRUSR, proc_idedisk_read_smart_thresholds, NULL },
+ { NULL, 0, NULL, NULL }
+};
+
+#else
+
+#define idedisk_proc NULL
+
+#endif /* CONFIG_PROC_FS */
+
+static int set_multcount(ide_drive_t *drive, int arg)
+{
+ struct request rq;
+
+ if (drive->special.b.set_multmode)
+ return -EBUSY;
+ ide_init_drive_cmd (&rq);
+ drive->mult_req = arg;
+ drive->special.b.set_multmode = 1;
+ (void) ide_do_drive_cmd (drive, &rq, ide_wait);
+ return (drive->mult_count == arg) ? 0 : -EIO;
+}
+
+static int set_nowerr(ide_drive_t *drive, int arg)
+{
+ unsigned long flags;
+
+ if (ide_spin_wait_hwgroup(drive, &flags))
+ return -EBUSY;
+ drive->nowerr = arg;
+ drive->bad_wstat = arg ? BAD_R_STAT : BAD_W_STAT;
+ spin_unlock_irqrestore(&io_request_lock, flags);
+ return 0;
+}
+
+static void idedisk_add_settings(ide_drive_t *drive)
+{
+ struct hd_driveid *id = drive->id;
+ int major = HWIF(drive)->major;
+ int minor = drive->select.b.unit << PARTN_BITS;
+
+ ide_add_setting(drive, "bios_cyl", SETTING_RW, -1, -1, TYPE_INT, 0, 65535, 1, 1, &drive->bios_cyl, NULL);
+ ide_add_setting(drive, "bios_head", SETTING_RW, -1, -1, TYPE_BYTE, 0, 255, 1, 1, &drive->bios_head, NULL);
+ ide_add_setting(drive, "bios_sect", SETTING_RW, -1, -1, TYPE_BYTE, 0, 63, 1, 1, &drive->bios_sect, NULL);
+ ide_add_setting(drive, "bswap", SETTING_READ, -1, -1, TYPE_BYTE, 0, 1, 1, 1, &drive->bswap, NULL);
+ ide_add_setting(drive, "multcount", id ? SETTING_RW : SETTING_READ, HDIO_GET_MULTCOUNT, HDIO_SET_MULTCOUNT, TYPE_BYTE, 0, id ? id->max_multsect : 0, 1, 2, &drive->mult_count, set_multcount);
+ ide_add_setting(drive, "nowerr", SETTING_RW, HDIO_GET_NOWERR, HDIO_SET_NOWERR, TYPE_BYTE, 0, 1, 1, 1, &drive->nowerr, set_nowerr);
+ ide_add_setting(drive, "breada_readahead", SETTING_RW, BLKRAGET, BLKRASET, TYPE_INT, 0, 255, 1, 2, &read_ahead[major], NULL);
+ ide_add_setting(drive, "file_readahead", SETTING_RW, BLKFRAGET, BLKFRASET, TYPE_INTA, 0, INT_MAX, 1, 1024, &max_readahead[major][minor], NULL);
+ ide_add_setting(drive, "max_kb_per_request", SETTING_RW, BLKSECTGET, BLKSECTSET, TYPE_INTA, 1, 255, 1, 2, &max_sectors[major][minor], NULL);
+
+}
+
+/*
+ * IDE subdriver functions, registered with ide.c
+ */
+static ide_driver_t idedisk_driver = {
+ "ide-disk", /* name */
+ IDEDISK_VERSION, /* version */
+ ide_disk, /* media */
+ 0, /* busy */
+ 1, /* supports_dma */
+ 0, /* supports_dsc_overlap */
+ NULL, /* cleanup */
+ do_rw_disk, /* do_request */
+ NULL, /* end_request */
+ NULL, /* ioctl */
+ idedisk_open, /* open */
+ idedisk_release, /* release */
+ idedisk_media_change, /* media_change */
+ idedisk_pre_reset, /* pre_reset */
+ idedisk_capacity, /* capacity */
+ idedisk_special, /* special */
+ idedisk_proc /* proc */
+};
+
+int idedisk_init (void);
+static ide_module_t idedisk_module = {
+ IDE_DRIVER_MODULE,
+ idedisk_init,
+ &idedisk_driver,
+ NULL
+};
+
+static int idedisk_cleanup (ide_drive_t *drive)
+{
+ return ide_unregister_subdriver(drive);
+}
+
+static void idedisk_setup (ide_drive_t *drive)
+{
+ int i;
+
+ struct hd_driveid *id = drive->id;
+ unsigned long capacity;
+
+ idedisk_add_settings(drive);
+
+ if (id == NULL)
+ return;
+
+ /*
+ * CompactFlash cards and their brethern look just like hard drives
+ * to us, but they are removable and don't have a doorlock mechanism.
+ */
+ if (drive->removable && !drive_is_flashcard(drive)) {
+ /*
+ * Removable disks (eg. SYQUEST); ignore 'WD' drives
+ */
+ if (id->model[0] != 'W' || id->model[1] != 'D') {
+ drive->doorlocking = 1;
+ }
+ }
+ for (i = 0; i < MAX_DRIVES; ++i) {
+ ide_hwif_t *hwif = HWIF(drive);
+
+ if (drive != &hwif->drives[i]) continue;
+ hwif->gd->de_arr[i] = drive->de;
+ if (drive->removable)
+ hwif->gd->flags[i] |= GENHD_FL_REMOVABLE;
+ break;
+ }
+
+ /* Extract geometry if we did not already have one for the drive */
+ if (!drive->cyl || !drive->head || !drive->sect) {
+ drive->cyl = drive->bios_cyl = id->cyls;
+ drive->head = drive->bios_head = id->heads;
+ drive->sect = drive->bios_sect = id->sectors;
+ }
+
+ /* Handle logical geometry translation by the drive */
+ if ((id->field_valid & 1) && id->cur_cyls &&
+ id->cur_heads && (id->cur_heads <= 16) && id->cur_sectors) {
+ drive->cyl = id->cur_cyls;
+ drive->head = id->cur_heads;
+ drive->sect = id->cur_sectors;
+ }
+
+ /* Use physical geometry if what we have still makes no sense */
+ if (drive->head > 16 && id->heads && id->heads <= 16) {
+ drive->cyl = id->cyls;
+ drive->head = id->heads;
+ drive->sect = id->sectors;
+ }
+
+ /* calculate drive capacity, and select LBA if possible */
+ init_idedisk_capacity (drive);
+
+ /*
+ * if possible, give fdisk access to more of the drive,
+ * by correcting bios_cyls:
+ */
+ capacity = idedisk_capacity (drive);
+ if ((capacity >= (drive->bios_cyl * drive->bios_sect * drive->bios_head)) &&
+ (!drive->forced_geom) && drive->bios_sect && drive->bios_head)
+ drive->bios_cyl = (capacity / drive->bios_sect) / drive->bios_head;
+
+#if 0 /* done instead for entire identify block in arch/ide.h stuff */
+ /* fix byte-ordering of buffer size field */
+ id->buf_size = le16_to_cpu(id->buf_size);
+#endif
+ printk (KERN_INFO "%s: %.40s, %ldMB w/%dkB Cache, CHS=%d/%d/%d",
+ drive->name, id->model,
+ capacity/2048L, id->buf_size/2,
+ drive->bios_cyl, drive->bios_head, drive->bios_sect);
+
+ if (drive->using_dma) {
+ if ((id->field_valid & 4) && (id->hw_config & 0x2000) &&
+ (HWIF(drive)->udma_four) &&
+ (id->dma_ultra & (id->dma_ultra >> 11) & 3)) {
+ printk(", UDMA(66)"); /* UDMA BIOS-enabled! */
+ } else if ((id->field_valid & 4) &&
+ (id->dma_ultra & (id->dma_ultra >> 8) & 7)) {
+ printk(", UDMA(33)"); /* UDMA BIOS-enabled! */
+ } else if (id->field_valid & 4) {
+ printk(", (U)DMA"); /* Can be BIOS-enabled! */
+ } else {
+ printk(", DMA");
+ }
+ }
+ printk("\n");
+
+ drive->mult_count = 0;
+ if (id->max_multsect) {
+#ifdef CONFIG_IDEDISK_MULTI_MODE
+ id->multsect = ((id->max_multsect/2) > 1) ? id->max_multsect : 0;
+ id->multsect_valid = id->multsect ? 1 : 0;
+ drive->mult_req = id->multsect_valid ? id->max_multsect : INITIAL_MULT_COUNT;
+ drive->special.b.set_multmode = drive->mult_req ? 1 : 0;
+#else /* original, pre IDE-NFG, per request of AC */
+ drive->mult_req = INITIAL_MULT_COUNT;
+ if (drive->mult_req > id->max_multsect)
+ drive->mult_req = id->max_multsect;
+ if (drive->mult_req || ((id->multsect_valid & 1) && id->multsect))
+ drive->special.b.set_multmode = 1;
+#endif
+ }
+ drive->no_io_32bit = id->dword_io ? 1 : 0;
+}
+
+int idedisk_init (void)
+{
+ ide_drive_t *drive;
+ int failed = 0;
+
+ MOD_INC_USE_COUNT;
+ while ((drive = ide_scan_devices (ide_disk, idedisk_driver.name, NULL, failed++)) != NULL) {
+ if (ide_register_subdriver (drive, &idedisk_driver, IDE_SUBDRIVER_VERSION)) {
+ printk (KERN_ERR "ide-disk: %s: Failed to register the driver with ide.c\n", drive->name);
+ continue;
+ }
+ idedisk_setup(drive);
+ if ((!drive->head || drive->head > 16) && !drive->select.b.lba) {
+ printk(KERN_ERR "%s: INVALID GEOMETRY: %d PHYSICAL HEADS?\n", drive->name, drive->head);
+ (void) idedisk_cleanup(drive);
+ continue;
+ }
+ failed--;
+ }
+ ide_register_module(&idedisk_module);
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+#ifdef MODULE
+int init_module (void)
+{
+ return idedisk_init();
+}
+
+void cleanup_module (void)
+{
+ ide_drive_t *drive;
+ int failed = 0;
+
+ while ((drive = ide_scan_devices (ide_disk, idedisk_driver.name, &idedisk_driver, failed)) != NULL) {
+ if (idedisk_cleanup (drive)) {
+ printk (KERN_ERR "%s: cleanup_module() called while still busy\n", drive->name);
+ failed++;
+ }
+ /* We must remove proc entries defined in this module.
+ Otherwise we oops while accessing these entries */
+ if (drive->proc)
+ ide_remove_proc_entries(drive->proc, idedisk_proc);
+ }
+ ide_unregister_module(&idedisk_module);
+}
+#endif /* MODULE */
diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c
new file mode 100644
index 000000000..751424831
--- /dev/null
+++ b/drivers/ide/ide-dma.c
@@ -0,0 +1,580 @@
+/*
+ * linux/drivers/block/ide-dma.c Version 4.09 April 23, 1999
+ *
+ * Copyright (c) 1999 Andre Hedrick
+ * May be copied or modified under the terms of the GNU General Public License
+ */
+
+/*
+ * Special Thanks to Mark for his Six years of work.
+ *
+ * Copyright (c) 1995-1998 Mark Lord
+ * May be copied or modified under the terms of the GNU General Public License
+ */
+
+/*
+ * This module provides support for the bus-master IDE DMA functions
+ * of various PCI chipsets, including the Intel PIIX (i82371FB for
+ * the 430 FX chipset), the PIIX3 (i82371SB for the 430 HX/VX and
+ * 440 chipsets), and the PIIX4 (i82371AB for the 430 TX chipset)
+ * ("PIIX" stands for "PCI ISA IDE Xcellerator").
+ *
+ * Pretty much the same code works for other IDE PCI bus-mastering chipsets.
+ *
+ * DMA is supported for all IDE devices (disk drives, cdroms, tapes, floppies).
+ *
+ * By default, DMA support is prepared for use, but is currently enabled only
+ * for drives which already have DMA enabled (UltraDMA or mode 2 multi/single),
+ * or which are recognized as "good" (see table below). Drives with only mode0
+ * or mode1 (multi/single) DMA should also work with this chipset/driver
+ * (eg. MC2112A) but are not enabled by default.
+ *
+ * Use "hdparm -i" to view modes supported by a given drive.
+ *
+ * The hdparm-3.5 (or later) utility can be used for manually enabling/disabling
+ * DMA support, but must be (re-)compiled against this kernel version or later.
+ *
+ * To enable DMA, use "hdparm -d1 /dev/hd?" on a per-drive basis after booting.
+ * If problems arise, ide.c will disable DMA operation after a few retries.
+ * This error recovery mechanism works and has been extremely well exercised.
+ *
+ * IDE drives, depending on their vintage, may support several different modes
+ * of DMA operation. The boot-time modes are indicated with a "*" in
+ * the "hdparm -i" listing, and can be changed with *knowledgeable* use of
+ * the "hdparm -X" feature. There is seldom a need to do this, as drives
+ * normally power-up with their "best" PIO/DMA modes enabled.
+ *
+ * Testing has been done with a rather extensive number of drives,
+ * with Quantum & Western Digital models generally outperforming the pack,
+ * and Fujitsu & Conner (and some Seagate which are really Conner) drives
+ * showing more lackluster throughput.
+ *
+ * Keep an eye on /var/adm/messages for "DMA disabled" messages.
+ *
+ * Some people have reported trouble with Intel Zappa motherboards.
+ * This can be fixed by upgrading the AMI BIOS to version 1.00.04.BS0,
+ * available from ftp://ftp.intel.com/pub/bios/10004bs0.exe
+ * (thanks to Glen Morrell <glen@spin.Stanford.edu> for researching this).
+ *
+ * Thanks to "Christopher J. Reimer" <reimer@doe.carleton.ca> for
+ * fixing the problem with the BIOS on some Acer motherboards.
+ *
+ * Thanks to "Benoit Poulot-Cazajous" <poulot@chorus.fr> for testing
+ * "TX" chipset compatibility and for providing patches for the "TX" chipset.
+ *
+ * Thanks to Christian Brunner <chb@muc.de> for taking a good first crack
+ * at generic DMA -- his patches were referred to when preparing this code.
+ *
+ * Most importantly, thanks to Robert Bringman <rob@mars.trion.com>
+ * for supplying a Promise UDMA board & WD UDMA drive for this work!
+ *
+ * And, yes, Intel Zappa boards really *do* use both PIIX IDE ports.
+ *
+ * ACARD ATP850UF Chipset "Modified SCSI Class" with other names
+ * AEC6210 U/UF
+ * SIIG's UltraIDE Pro CN-2449
+ * TTI HPT343 Chipset "Modified SCSI Class" but reports as an
+ * unknown storage device.
+ * NEW check_drive_lists(ide_drive_t *drive, int good_bad)
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+extern char *ide_dmafunc_verbose(ide_dma_action_t dmafunc);
+
+#ifdef CONFIG_IDEDMA_NEW_DRIVE_LISTINGS
+
+struct drive_list_entry {
+ char * id_model;
+ char * id_firmware;
+};
+
+struct drive_list_entry drive_whitelist [] = {
+
+ { "Micropolis 2112A" , "ALL" },
+ { "CONNER CTMA 4000" , "ALL" },
+ { "CONNER CTT8000-A" , "ALL" },
+ { "ST34342A" , "ALL" },
+ { 0 , 0 }
+};
+
+struct drive_list_entry drive_blacklist [] = {
+
+ { "WDC AC11000H" , "ALL" },
+ { "WDC AC22100H" , "ALL" },
+ { "WDC AC32500H" , "ALL" },
+ { "WDC AC33100H" , "ALL" },
+ { "WDC AC31600H" , "ALL" },
+ { "WDC AC32100H" , "24.09P07" },
+ { "WDC AC23200L" , "21.10N21" },
+ { 0 , 0 }
+
+};
+
+int in_drive_list(struct hd_driveid *id, struct drive_list_entry * drive_table)
+{
+ for ( ; drive_table->id_model ; drive_table++)
+ if ((!strcmp(drive_table->id_model, id->model)) &&
+ ((!strstr(drive_table->id_firmware, id->fw_rev)) ||
+ (!strcmp(drive_table->id_firmware, "ALL"))))
+ return 1;
+ return 0;
+}
+
+#else /* !CONFIG_IDEDMA_NEW_DRIVE_LISTINGS */
+
+/*
+ * good_dma_drives() lists the model names (from "hdparm -i")
+ * of drives which do not support mode2 DMA but which are
+ * known to work fine with this interface under Linux.
+ */
+const char *good_dma_drives[] = {"Micropolis 2112A",
+ "CONNER CTMA 4000",
+ "CONNER CTT8000-A",
+ "ST34342A", /* for Sun Ultra */
+ NULL};
+
+/*
+ * bad_dma_drives() lists the model names (from "hdparm -i")
+ * of drives which supposedly support (U)DMA but which are
+ * known to corrupt data with this interface under Linux.
+ *
+ * This is an empirical list. Its generated from bug reports. That means
+ * while it reflects actual problem distributions it doesn't answer whether
+ * the drive or the controller, or cabling, or software, or some combination
+ * thereof is the fault. If you don't happen to agree with the kernel's
+ * opinion of your drive - use hdparm to turn DMA on.
+ */
+const char *bad_dma_drives[] = {"WDC AC11000H",
+ "WDC AC22100H",
+ "WDC AC32100H",
+ "WDC AC32500H",
+ "WDC AC33100H",
+ "WDC AC31600H",
+ NULL};
+
+#endif /* CONFIG_IDEDMA_NEW_DRIVE_LISTINGS */
+
+/*
+ * Our Physical Region Descriptor (PRD) table should be large enough
+ * to handle the biggest I/O request we are likely to see. Since requests
+ * can have no more than 256 sectors, and since the typical blocksize is
+ * two or more sectors, we could get by with a limit of 128 entries here for
+ * the usual worst case. Most requests seem to include some contiguous blocks,
+ * further reducing the number of table entries required.
+ *
+ * The driver reverts to PIO mode for individual requests that exceed
+ * this limit (possible with 512 byte blocksizes, eg. MSDOS f/s), so handling
+ * 100% of all crazy scenarios here is not necessary.
+ *
+ * As it turns out though, we must allocate a full 4KB page for this,
+ * so the two PRD tables (ide0 & ide1) will each get half of that,
+ * allowing each to have about 256 entries (8 bytes each) from this.
+ */
+#define PRD_BYTES 8
+#define PRD_ENTRIES (PAGE_SIZE / (2 * PRD_BYTES))
+
+/*
+ * dma_intr() is the handler for disk read/write DMA interrupts
+ */
+ide_startstop_t ide_dma_intr (ide_drive_t *drive)
+{
+ int i;
+ byte stat, dma_stat;
+
+ dma_stat = HWIF(drive)->dmaproc(ide_dma_end, drive);
+ stat = GET_STAT(); /* get drive status */
+ if (OK_STAT(stat,DRIVE_READY,drive->bad_wstat|DRQ_STAT)) {
+ if (!dma_stat) {
+ struct request *rq = HWGROUP(drive)->rq;
+ rq = HWGROUP(drive)->rq;
+ for (i = rq->nr_sectors; i > 0;) {
+ i -= rq->current_nr_sectors;
+ ide_end_request(1, HWGROUP(drive));
+ }
+ return ide_stopped;
+ }
+ printk("%s: dma_intr: bad DMA status\n", drive->name);
+ }
+ return ide_error(drive, "dma_intr", stat);
+}
+
+static int ide_build_sglist (ide_hwif_t *hwif, struct request *rq)
+{
+ struct buffer_head *bh;
+ struct scatterlist *sg = hwif->sg_table;
+ int nents = 0;
+
+ if (rq->cmd == READ)
+ hwif->sg_dma_direction = PCI_DMA_FROMDEVICE;
+ else
+ hwif->sg_dma_direction = PCI_DMA_TODEVICE;
+ bh = rq->bh;
+ do {
+ unsigned char *virt_addr = bh->b_data;
+ unsigned int size = bh->b_size;
+
+ while ((bh = bh->b_reqnext) != NULL) {
+ if ((virt_addr + size) != (unsigned char *) bh->b_data)
+ break;
+ size += bh->b_size;
+ }
+ memset(&sg[nents], 0, sizeof(*sg));
+ sg[nents].address = virt_addr;
+ sg[nents].length = size;
+ nents++;
+ } while (bh != NULL);
+
+ return pci_map_sg(hwif->pci_dev, sg, nents, hwif->sg_dma_direction);
+}
+
+/*
+ * ide_build_dmatable() prepares a dma request.
+ * Returns 0 if all went okay, returns 1 otherwise.
+ * May also be invoked from trm290.c
+ */
+int ide_build_dmatable (ide_drive_t *drive, ide_dma_action_t func)
+{
+ unsigned int *table = HWIF(drive)->dmatable_cpu;
+#ifdef CONFIG_BLK_DEV_TRM290
+ unsigned int is_trm290_chipset = (HWIF(drive)->chipset == ide_trm290);
+#else
+ const int is_trm290_chipset = 0;
+#endif
+ unsigned int count = 0;
+ int i;
+ struct scatterlist *sg;
+
+ HWIF(drive)->sg_nents = i = ide_build_sglist(HWIF(drive), HWGROUP(drive)->rq);
+
+ sg = HWIF(drive)->sg_table;
+ while (i && sg_dma_len(sg)) {
+ u32 cur_addr;
+ u32 cur_len;
+
+ cur_addr = sg_dma_address(sg);
+ cur_len = sg_dma_len(sg);
+
+ while (cur_len) {
+ if (++count >= PRD_ENTRIES) {
+ printk("%s: DMA table too small\n", drive->name);
+ pci_unmap_sg(HWIF(drive)->pci_dev,
+ HWIF(drive)->sg_table,
+ HWIF(drive)->sg_nents,
+ HWIF(drive)->sg_dma_direction);
+ return 0; /* revert to PIO for this request */
+ } else {
+ u32 xcount, bcount = 0x10000 - (cur_addr & 0xffff);
+
+ if (bcount > cur_len)
+ bcount = cur_len;
+ *table++ = cpu_to_le32(cur_addr);
+ xcount = bcount & 0xffff;
+ if (is_trm290_chipset)
+ xcount = ((xcount >> 2) - 1) << 16;
+ *table++ = cpu_to_le32(xcount);
+ cur_addr += bcount;
+ cur_len -= bcount;
+ }
+ }
+
+ sg++;
+ i--;
+ }
+
+ if (!count)
+ printk("%s: empty DMA table?\n", drive->name);
+ else if (!is_trm290_chipset)
+ *--table |= cpu_to_le32(0x80000000);
+
+ return count;
+}
+
+/* Teardown mappings after DMA has completed. */
+void ide_destroy_dmatable (ide_drive_t *drive)
+{
+ struct pci_dev *dev = HWIF(drive)->pci_dev;
+ struct scatterlist *sg = HWIF(drive)->sg_table;
+ int nents = HWIF(drive)->sg_nents;
+
+ pci_unmap_sg(dev, sg, nents, HWIF(drive)->sg_dma_direction);
+}
+
+/*
+ * For both Blacklisted and Whitelisted drives.
+ * This is setup to be called as an extern for future support
+ * to other special driver code.
+ */
+int check_drive_lists (ide_drive_t *drive, int good_bad)
+{
+ struct hd_driveid *id = drive->id;
+
+#ifdef CONFIG_IDEDMA_NEW_DRIVE_LISTINGS
+ if (good_bad) {
+ return in_drive_list(id, drive_whitelist);
+ } else {
+ int blacklist = in_drive_list(id, drive_blacklist);
+ if (blacklist)
+ printk("%s: Disabling (U)DMA for %s\n", drive->name, id->model);
+ return(blacklist);
+ }
+#else /* !CONFIG_IDEDMA_NEW_DRIVE_LISTINGS */
+ const char **list;
+
+ if (good_bad) {
+ /* Consult the list of known "good" drives */
+ list = good_dma_drives;
+ while (*list) {
+ if (!strcmp(*list++,id->model))
+ return 1;
+ }
+ } else {
+ /* Consult the list of known "bad" drives */
+ list = bad_dma_drives;
+ while (*list) {
+ if (!strcmp(*list++,id->model)) {
+ printk("%s: Disabling (U)DMA for %s\n",
+ drive->name, id->model);
+ return 1;
+ }
+ }
+ }
+#endif /* CONFIG_IDEDMA_NEW_DRIVE_LISTINGS */
+ return 0;
+}
+
+static int config_drive_for_dma (ide_drive_t *drive)
+{
+ struct hd_driveid *id = drive->id;
+ ide_hwif_t *hwif = HWIF(drive);
+
+ if (id && (id->capability & 1) && hwif->autodma) {
+ /* Consult the list of known "bad" drives */
+ if (ide_dmaproc(ide_dma_bad_drive, drive))
+ return hwif->dmaproc(ide_dma_off, drive);
+
+ /* Enable DMA on any drive that has UltraDMA (mode 3/4) enabled */
+ if ((id->field_valid & 4) && (hwif->udma_four) && (id->hw_config & 0x2000))
+ if ((id->dma_ultra & (id->dma_ultra >> 11) & 3))
+ return hwif->dmaproc(ide_dma_on, drive);
+ /* Enable DMA on any drive that has UltraDMA (mode 0/1/2) enabled */
+ if (id->field_valid & 4) /* UltraDMA */
+ if ((id->dma_ultra & (id->dma_ultra >> 8) & 7))
+ return hwif->dmaproc(ide_dma_on, drive);
+ /* Enable DMA on any drive that has mode2 DMA (multi or single) enabled */
+ if (id->field_valid & 2) /* regular DMA */
+ if ((id->dma_mword & 0x404) == 0x404 || (id->dma_1word & 0x404) == 0x404)
+ return hwif->dmaproc(ide_dma_on, drive);
+ /* Consult the list of known "good" drives */
+ if (ide_dmaproc(ide_dma_good_drive, drive))
+ return hwif->dmaproc(ide_dma_on, drive);
+ }
+ return hwif->dmaproc(ide_dma_off_quietly, drive);
+}
+
+/*
+ * ide_dmaproc() initiates/aborts DMA read/write operations on a drive.
+ *
+ * The caller is assumed to have selected the drive and programmed the drive's
+ * sector address using CHS or LBA. All that remains is to prepare for DMA
+ * and then issue the actual read/write DMA/PIO command to the drive.
+ *
+ * For ATAPI devices, we just prepare for DMA and return. The caller should
+ * then issue the packet command to the drive and call us again with
+ * ide_dma_begin afterwards.
+ *
+ * Returns 0 if all went well.
+ * Returns 1 if DMA read/write could not be started, in which case
+ * the caller should revert to PIO for the current request.
+ * May also be invoked from trm290.c
+ */
+int ide_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
+{
+ ide_hwif_t *hwif = HWIF(drive);
+ unsigned long dma_base = hwif->dma_base;
+ byte unit = (drive->select.b.unit & 0x01);
+ unsigned int count, reading = 0;
+ byte dma_stat;
+
+ switch (func) {
+ case ide_dma_off:
+ printk("%s: DMA disabled\n", drive->name);
+ case ide_dma_off_quietly:
+ outb(inb(dma_base+2) & ~(1<<(5+unit)), dma_base+2);
+ case ide_dma_on:
+ drive->using_dma = (func == ide_dma_on);
+ if (drive->using_dma)
+ outb(inb(dma_base+2)|(1<<(5+unit)), dma_base+2);
+ return 0;
+ case ide_dma_check:
+ return config_drive_for_dma (drive);
+ case ide_dma_read:
+ reading = 1 << 3;
+ case ide_dma_write:
+ if (!(count = ide_build_dmatable(drive, func)))
+ return 1; /* try PIO instead of DMA */
+ outl(hwif->dmatable_dma, dma_base + 4); /* PRD table */
+ outb(reading, dma_base); /* specify r/w */
+ outb(inb(dma_base+2)|6, dma_base+2); /* clear INTR & ERROR flags */
+ drive->waiting_for_dma = 1;
+ if (drive->media != ide_disk)
+ return 0;
+ ide_set_handler(drive, &ide_dma_intr, WAIT_CMD, NULL); /* issue cmd to drive */
+ OUT_BYTE(reading ? WIN_READDMA : WIN_WRITEDMA, IDE_COMMAND_REG);
+ case ide_dma_begin:
+ /* Note that this is done *after* the cmd has
+ * been issued to the drive, as per the BM-IDE spec.
+ * The Promise Ultra33 doesn't work correctly when
+ * we do this part before issuing the drive cmd.
+ */
+ outb(inb(dma_base)|1, dma_base); /* start DMA */
+ return 0;
+ case ide_dma_end: /* returns 1 on error, 0 otherwise */
+ drive->waiting_for_dma = 0;
+ outb(inb(dma_base)&~1, dma_base); /* stop DMA */
+ dma_stat = inb(dma_base+2); /* get DMA status */
+ outb(dma_stat|6, dma_base+2); /* clear the INTR & ERROR bits */
+ ide_destroy_dmatable(drive); /* purge DMA mappings */
+ return (dma_stat & 7) != 4; /* verify good DMA status */
+ case ide_dma_test_irq: /* returns 1 if dma irq issued, 0 otherwise */
+ dma_stat = inb(dma_base+2);
+ return (dma_stat & 4) == 4; /* return 1 if INTR asserted */
+ case ide_dma_bad_drive:
+ case ide_dma_good_drive:
+ return check_drive_lists(drive, (func == ide_dma_good_drive));
+ case ide_dma_lostirq:
+ case ide_dma_timeout:
+ printk("ide_dmaproc: chipset supported %s func only: %d\n", ide_dmafunc_verbose(func), func);
+ return 1;
+ default:
+ printk("ide_dmaproc: unsupported %s func: %d\n", ide_dmafunc_verbose(func), func);
+ return 1;
+ }
+}
+
+/*
+ * Needed for allowing full modular support of ide-driver
+ */
+int ide_release_dma (ide_hwif_t *hwif)
+{
+ if (hwif->dmatable_cpu) {
+ pci_free_consistent(hwif->pci_dev,
+ PRD_ENTRIES * PRD_BYTES,
+ hwif->dmatable_cpu,
+ hwif->dmatable_dma);
+ hwif->dmatable_cpu = NULL;
+ }
+ if (hwif->sg_table) {
+ kfree(hwif->sg_table);
+ hwif->sg_table = NULL;
+ }
+ if ((hwif->dma_extra) && (hwif->channel == 0))
+ release_region((hwif->dma_base + 16), hwif->dma_extra);
+ release_region(hwif->dma_base, 8);
+ return 1;
+}
+
+/*
+ * This can be called for a dynamically installed interface. Don't __init it
+ */
+
+void ide_setup_dma (ide_hwif_t *hwif, unsigned long dma_base, unsigned int num_ports)
+{
+ printk(" %s: BM-DMA at 0x%04lx-0x%04lx", hwif->name, dma_base, dma_base + num_ports - 1);
+ if (check_region(dma_base, num_ports)) {
+ printk(" -- ERROR, PORT ADDRESSES ALREADY IN USE\n");
+ return;
+ }
+ request_region(dma_base, num_ports, hwif->name);
+ hwif->dma_base = dma_base;
+ hwif->dmatable_cpu = pci_alloc_consistent(hwif->pci_dev,
+ PRD_ENTRIES * PRD_BYTES,
+ &hwif->dmatable_dma);
+ if (hwif->dmatable_cpu == NULL)
+ goto dma_alloc_failure;
+
+ hwif->sg_table = kmalloc(sizeof(struct scatterlist) * PRD_ENTRIES,
+ GFP_KERNEL);
+ if (hwif->sg_table == NULL) {
+ pci_free_consistent(hwif->pci_dev, PRD_ENTRIES * PRD_BYTES,
+ hwif->dmatable_cpu, hwif->dmatable_dma);
+ goto dma_alloc_failure;
+ }
+
+ hwif->dmaproc = &ide_dmaproc;
+
+ if (hwif->chipset != ide_trm290) {
+ byte dma_stat = inb(dma_base+2);
+ printk(", BIOS settings: %s:%s, %s:%s",
+ hwif->drives[0].name, (dma_stat & 0x20) ? "DMA" : "pio",
+ hwif->drives[1].name, (dma_stat & 0x40) ? "DMA" : "pio");
+ }
+ printk("\n");
+ return;
+
+dma_alloc_failure:
+ printk(" -- ERROR, UNABLE TO ALLOCATE DMA TABLES\n");
+}
+
+/*
+ * Fetch the DMA Bus-Master-I/O-Base-Address (BMIBA) from PCI space:
+ */
+unsigned long __init ide_get_or_set_dma_base (ide_hwif_t *hwif, int extra, const char *name)
+{
+ unsigned long dma_base = 0;
+ struct pci_dev *dev = hwif->pci_dev;
+
+ if (hwif->mate && hwif->mate->dma_base) {
+ dma_base = hwif->mate->dma_base - (hwif->channel ? 0 : 8);
+ } else {
+ dma_base = dev->resource[4].start;
+ if (!dma_base || dma_base == PCI_BASE_ADDRESS_IO_MASK) {
+ printk("%s: dma_base is invalid (0x%04lx)\n", name, dma_base);
+ dma_base = 0;
+ }
+ }
+ if (dma_base) {
+ if (extra) /* PDC20246, PDC20262, HPT343, & HPT366 */
+ request_region(dma_base+16, extra, name);
+ dma_base += hwif->channel ? 8 : 0;
+ hwif->dma_extra = extra;
+
+ switch(dev->device) {
+ case PCI_DEVICE_ID_AL_M5219:
+ case PCI_DEVICE_ID_AMD_VIPER_7409:
+ case PCI_DEVICE_ID_CMD_643:
+ outb(inb(dma_base+2) & 0x60, dma_base+2);
+ if (inb(dma_base+2) & 0x80) {
+ printk("%s: simplex device: DMA forced\n", name);
+ }
+ break;
+ default:
+ /*
+ * If the device claims "simplex" DMA,
+ * this means only one of the two interfaces
+ * can be trusted with DMA at any point in time.
+ * So we should enable DMA only on one of the
+ * two interfaces.
+ */
+ if ((inb(dma_base+2) & 0x80)) { /* simplex device? */
+ if ((!hwif->drives[0].present && !hwif->drives[1].present) ||
+ (hwif->mate && hwif->mate->dma_base)) {
+ printk("%s: simplex device: DMA disabled\n", name);
+ dma_base = 0;
+ }
+ }
+ }
+ }
+ return dma_base;
+}
diff --git a/drivers/ide/ide-features.c b/drivers/ide/ide-features.c
new file mode 100644
index 000000000..3f29ed591
--- /dev/null
+++ b/drivers/ide/ide-features.c
@@ -0,0 +1,312 @@
+/*
+ * linux/drivers/block/ide-features.c Version 0.03 Feb. 10, 2000
+ *
+ * Copyright (C) 1999-2000 Linus Torvalds & authors (see below)
+ *
+ * Copyright (C) 1999-2000 Andre Hedrick <andre@suse.com>
+ *
+ * Extracts if ide.c to address the evolving transfer rate code for
+ * the SETFEATURES_XFER callouts. Various parts of any given function
+ * are credited to previous ATA-IDE maintainers.
+ *
+ * May be copied or modified under the terms of the GNU General Public License
+ */
+
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/major.h>
+#include <linux/errno.h>
+#include <linux/genhd.h>
+#include <linux/blkpg.h>
+#include <linux/malloc.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/ide.h>
+
+#include <asm/byteorder.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/bitops.h>
+
+#define SETFEATURES_CONTROL_REG (0) /* some arch's may need */
+
+/*
+ * A Verbose noise maker for debugging on the attempted transfer rates.
+ */
+char *ide_xfer_verbose (byte xfer_rate)
+{
+ switch(xfer_rate) {
+ case XFER_UDMA_7: return("UDMA 7");
+ case XFER_UDMA_6: return("UDMA 6");
+ case XFER_UDMA_5: return("UDMA 5");
+ case XFER_UDMA_4: return("UDMA 4");
+ case XFER_UDMA_3: return("UDMA 3");
+ case XFER_UDMA_2: return("UDMA 2");
+ case XFER_UDMA_1: return("UDMA 1");
+ case XFER_UDMA_0: return("UDMA 0");
+ case XFER_MW_DMA_2: return("MW DMA 2");
+ case XFER_MW_DMA_1: return("MW DMA 1");
+ case XFER_MW_DMA_0: return("MW DMA 0");
+ case XFER_SW_DMA_2: return("SW DMA 2");
+ case XFER_SW_DMA_1: return("SW DMA 1");
+ case XFER_SW_DMA_0: return("SW DMA 0");
+ case XFER_PIO_4: return("PIO 4");
+ case XFER_PIO_3: return("PIO 3");
+ case XFER_PIO_2: return("PIO 2");
+ case XFER_PIO_1: return("PIO 1");
+ case XFER_PIO_0: return("PIO 0");
+ case XFER_PIO_SLOW: return("PIO SLOW");
+ default: return("XFER ERROR");
+ }
+}
+
+/*
+ *
+ */
+char *ide_media_verbose (ide_drive_t *drive)
+{
+ switch (drive->media) {
+ case ide_scsi: return("scsi ");
+ case ide_disk: return("disk ");
+ case ide_optical: return("optical");
+ case ide_cdrom: return("cdrom ");
+ case ide_tape: return("tape ");
+ case ide_floppy: return("floppy ");
+ default: return("???????");
+ }
+}
+
+/*
+ * A Verbose noise maker for debugging on the attempted dmaing calls.
+ */
+char *ide_dmafunc_verbose (ide_dma_action_t dmafunc)
+{
+ switch (dmafunc) {
+ case ide_dma_read: return("ide_dma_read");
+ case ide_dma_write: return("ide_dma_write");
+ case ide_dma_begin: return("ide_dma_begin");
+ case ide_dma_end: return("ide_dma_end:");
+ case ide_dma_check: return("ide_dma_check");
+ case ide_dma_on: return("ide_dma_on");
+ case ide_dma_off: return("ide_dma_off");
+ case ide_dma_off_quietly: return("ide_dma_off_quietly");
+ case ide_dma_test_irq: return("ide_dma_test_irq");
+ case ide_dma_bad_drive: return("ide_dma_bad_drive");
+ case ide_dma_good_drive: return("ide_dma_good_drive");
+ case ide_dma_lostirq: return("ide_dma_lostirq");
+ case ide_dma_timeout: return("ide_dma_timeout");
+ default: return("unknown");
+ }
+}
+
+/*
+ * Update the
+ */
+int ide_driveid_update (ide_drive_t *drive)
+{
+ /*
+ * Re-read drive->id for possible DMA mode
+ * change (copied from ide-probe.c)
+ */
+ struct hd_driveid *id;
+ unsigned long timeout, irqs, flags;
+
+ probe_irq_off(probe_irq_on());
+ irqs = probe_irq_on();
+ if (IDE_CONTROL_REG)
+ OUT_BYTE(drive->ctl,IDE_CONTROL_REG);
+ ide_delay_50ms();
+ OUT_BYTE(WIN_IDENTIFY, IDE_COMMAND_REG);
+ timeout = jiffies + WAIT_WORSTCASE;
+ do {
+ if (0 < (signed long)(jiffies - timeout)) {
+ if (irqs)
+ (void) probe_irq_off(irqs);
+ return 0; /* drive timed-out */
+ }
+ ide_delay_50ms(); /* give drive a breather */
+ } while (IN_BYTE(IDE_ALTSTATUS_REG) & BUSY_STAT);
+ ide_delay_50ms(); /* wait for IRQ and DRQ_STAT */
+ if (!OK_STAT(GET_STAT(),DRQ_STAT,BAD_R_STAT))
+ return 0;
+ __save_flags(flags); /* local CPU only */
+ __cli(); /* local CPU only; some systems need this */
+ id = kmalloc(SECTOR_WORDS*4, GFP_ATOMIC);
+ ide_input_data(drive, id, SECTOR_WORDS);
+ (void) GET_STAT(); /* clear drive IRQ */
+ ide__sti(); /* local CPU only */
+ __restore_flags(flags); /* local CPU only */
+ ide_fix_driveid(id);
+ if (id && id->cyls) {
+ drive->id->dma_ultra = id->dma_ultra;
+ drive->id->dma_mword = id->dma_mword;
+ drive->id->dma_1word = id->dma_1word;
+ /* anything more ? */
+#ifdef DEBUG
+ printk("%s: dma_ultra=%04X, dma_mword=%04X, dma_1word=%04X\n",
+ drive->name, id->dma_ultra, id->dma_mword, id->dma_1word);
+#endif
+ kfree(id);
+ }
+ return 1;
+}
+
+/*
+ * Verify that we are doing an approved SETFEATURES_XFER with respect
+ * to the hardware being able to support request. Since some hardware
+ * can improperly report capabilties, we check to see if the host adapter
+ * in combination with the device (usually a disk) properly detect
+ * and acknowledge each end of the ribbon.
+ */
+int ide_ata66_check (ide_drive_t *drive, int cmd, int nsect, int feature)
+{
+ if ((cmd == WIN_SETFEATURES) &&
+ (nsect > XFER_UDMA_2) &&
+ (feature == SETFEATURES_XFER)) {
+ if (!HWIF(drive)->udma_four) {
+ printk("%s: Speed warnings UDMA 3/4 is not functional.\n", HWIF(drive)->name);
+ return 1;
+ }
+ if ((drive->id->hw_config & 0x2000) == 0) {
+ printk("%s: Speed warnings UDMA 3/4 is not functional.\n", drive->name);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Backside of HDIO_DRIVE_CMD call of SETFEATURES_XFER.
+ * 1 : Safe to update drive->id DMA registers.
+ * 0 : OOPs not allowed.
+ */
+int set_transfer (ide_drive_t *drive, int cmd, int nsect, int feature)
+{
+ struct hd_driveid *id = drive->id;
+
+ if ((cmd == WIN_SETFEATURES) &&
+ (nsect >= XFER_SW_DMA_0) &&
+ (feature == SETFEATURES_XFER) &&
+ (id->dma_ultra || id->dma_mword || id->dma_1word))
+ return 1;
+ return 0;
+}
+
+/*
+ * Similar to ide_wait_stat(), except it never calls ide_error internally.
+ * This is a kludge to handle the new ide_config_drive_speed() function,
+ * and should not otherwise be used anywhere. Eventually, the tuneproc's
+ * should be updated to return ide_startstop_t, in which case we can get
+ * rid of this abomination again. :) -ml
+ *
+ * It is gone..........
+ *
+ * const char *msg == consider adding for verbose errors.
+ */
+int ide_config_drive_speed (ide_drive_t *drive, byte speed)
+{
+ ide_hwif_t *hwif = HWIF(drive);
+ int i, error = 1;
+ byte unit = (drive->select.b.unit & 0x01);
+ byte stat;
+
+ /*
+ * Don't use ide_wait_cmd here - it will
+ * attempt to set_geometry and recalibrate,
+ * but for some reason these don't work at
+ * this point (lost interrupt).
+ */
+ /*
+ * Select the drive, and issue the SETFEATURES command
+ */
+ disable_irq(hwif->irq); /* disable_irq_nosync ?? */
+ udelay(1);
+ SELECT_DRIVE(HWIF(drive), drive);
+ udelay(1);
+ if (IDE_CONTROL_REG)
+ OUT_BYTE(drive->ctl | 2, IDE_CONTROL_REG);
+ OUT_BYTE(speed, IDE_NSECTOR_REG);
+ OUT_BYTE(SETFEATURES_XFER, IDE_FEATURE_REG);
+ OUT_BYTE(WIN_SETFEATURES, IDE_COMMAND_REG);
+ if ((IDE_CONTROL_REG) && (SETFEATURES_CONTROL_REG))
+ OUT_BYTE(drive->ctl, IDE_CONTROL_REG);
+ udelay(1);
+ /*
+ * Wait for drive to become non-BUSY
+ */
+ if ((stat = GET_STAT()) & BUSY_STAT) {
+ unsigned long flags, timeout;
+ __save_flags(flags); /* local CPU only */
+ ide__sti(); /* local CPU only -- for jiffies */
+ timeout = jiffies + WAIT_CMD;
+ while ((stat = GET_STAT()) & BUSY_STAT) {
+ if (0 < (signed long)(jiffies - timeout))
+ break;
+ }
+ __restore_flags(flags); /* local CPU only */
+ }
+
+ /*
+ * Allow status to settle, then read it again.
+ * A few rare drives vastly violate the 400ns spec here,
+ * so we'll wait up to 10usec for a "good" status
+ * rather than expensively fail things immediately.
+ * This fix courtesy of Matthew Faupel & Niccolo Rigacci.
+ */
+ for (i = 0; i < 10; i++) {
+ udelay(1);
+ if (OK_STAT((stat = GET_STAT()), DRIVE_READY, BUSY_STAT|DRQ_STAT|ERR_STAT)) {
+ error = 0;
+ break;
+ }
+ }
+
+ enable_irq(hwif->irq);
+
+ if (error) {
+ (void) ide_dump_status(drive, "set_drive_speed_status", stat);
+ return error;
+ }
+
+ drive->id->dma_ultra &= ~0xFF00;
+ drive->id->dma_mword &= ~0x0F00;
+ drive->id->dma_1word &= ~0x0F00;
+
+ if (speed > XFER_PIO_4) {
+ outb(inb(hwif->dma_base+2)|(1<<(5+unit)), hwif->dma_base+2);
+ } else {
+ outb(inb(hwif->dma_base+2) & ~(1<<(5+unit)), hwif->dma_base+2);
+ }
+
+ switch(speed) {
+ case XFER_UDMA_7: drive->id->dma_ultra |= 0x8080; break;
+ case XFER_UDMA_6: drive->id->dma_ultra |= 0x4040; break;
+ case XFER_UDMA_5: drive->id->dma_ultra |= 0x2020; break;
+ case XFER_UDMA_4: drive->id->dma_ultra |= 0x1010; break;
+ case XFER_UDMA_3: drive->id->dma_ultra |= 0x0808; break;
+ case XFER_UDMA_2: drive->id->dma_ultra |= 0x0404; break;
+ case XFER_UDMA_1: drive->id->dma_ultra |= 0x0202; break;
+ case XFER_UDMA_0: drive->id->dma_ultra |= 0x0101; break;
+ case XFER_MW_DMA_2: drive->id->dma_mword |= 0x0404; break;
+ case XFER_MW_DMA_1: drive->id->dma_mword |= 0x0202; break;
+ case XFER_MW_DMA_0: drive->id->dma_mword |= 0x0101; break;
+ case XFER_SW_DMA_2: drive->id->dma_1word |= 0x0404; break;
+ case XFER_SW_DMA_1: drive->id->dma_1word |= 0x0202; break;
+ case XFER_SW_DMA_0: drive->id->dma_1word |= 0x0101; break;
+ default: break;
+ }
+ return error;
+}
+
+EXPORT_SYMBOL(ide_driveid_update);
+EXPORT_SYMBOL(ide_ata66_check);
+EXPORT_SYMBOL(set_transfer);
+EXPORT_SYMBOL(ide_config_drive_speed);
+
diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c
new file mode 100644
index 000000000..58eb2411d
--- /dev/null
+++ b/drivers/ide/ide-floppy.c
@@ -0,0 +1,1680 @@
+/*
+ * linux/drivers/block/ide-floppy.c Version 0.9 Jul 4, 1999
+ *
+ * Copyright (C) 1996 - 1999 Gadi Oxman <gadio@netvision.net.il>
+ */
+
+/*
+ * IDE ATAPI floppy driver.
+ *
+ * The driver currently doesn't have any fancy features, just the bare
+ * minimum read/write support.
+ *
+ * Many thanks to Lode Leroy <Lode.Leroy@www.ibase.be>, who tested so many
+ * ALPHA patches to this driver on an EASYSTOR LS-120 ATAPI floppy drive.
+ *
+ * Ver 0.1 Oct 17 96 Initial test version, mostly based on ide-tape.c.
+ * Ver 0.2 Oct 31 96 Minor changes.
+ * Ver 0.3 Dec 2 96 Fixed error recovery bug.
+ * Ver 0.4 Jan 26 97 Add support for the HDIO_GETGEO ioctl.
+ * Ver 0.5 Feb 21 97 Add partitions support.
+ * Use the minimum of the LBA and CHS capacities.
+ * Avoid hwgroup->rq == NULL on the last irq.
+ * Fix potential null dereferencing with DEBUG_LOG.
+ * Ver 0.8 Dec 7 97 Increase irq timeout from 10 to 50 seconds.
+ * Add media write-protect detection.
+ * Issue START command only if TEST UNIT READY fails.
+ * Add work-around for IOMEGA ZIP revision 21.D.
+ * Remove idefloppy_get_capabilities().
+ * Ver 0.9 Jul 4 99 Fix a bug which might have caused the number of
+ * bytes requested on each interrupt to be zero.
+ * Thanks to <shanos@es.co.nz> for pointing this out.
+ */
+
+#define IDEFLOPPY_VERSION "0.9"
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/major.h>
+#include <linux/errno.h>
+#include <linux/genhd.h>
+#include <linux/malloc.h>
+#include <linux/cdrom.h>
+#include <linux/ide.h>
+
+#include <asm/byteorder.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/unaligned.h>
+#include <asm/bitops.h>
+
+/*
+ * The following are used to debug the driver.
+ */
+#define IDEFLOPPY_DEBUG_LOG 0
+#define IDEFLOPPY_DEBUG_INFO 0
+#define IDEFLOPPY_DEBUG_BUGS 1
+
+/*
+ * Some drives require a longer irq timeout.
+ */
+#define IDEFLOPPY_WAIT_CMD (5 * WAIT_CMD)
+
+/*
+ * After each failed packet command we issue a request sense command
+ * and retry the packet command IDEFLOPPY_MAX_PC_RETRIES times.
+ */
+#define IDEFLOPPY_MAX_PC_RETRIES 3
+
+/*
+ * With each packet command, we allocate a buffer of
+ * IDEFLOPPY_PC_BUFFER_SIZE bytes.
+ */
+#define IDEFLOPPY_PC_BUFFER_SIZE 256
+
+/*
+ * In various places in the driver, we need to allocate storage
+ * for packet commands and requests, which will remain valid while
+ * we leave the driver to wait for an interrupt or a timeout event.
+ */
+#define IDEFLOPPY_PC_STACK (10 + IDEFLOPPY_MAX_PC_RETRIES)
+
+/*
+ * Our view of a packet command.
+ */
+typedef struct idefloppy_packet_command_s {
+ u8 c[12]; /* Actual packet bytes */
+ int retries; /* On each retry, we increment retries */
+ int error; /* Error code */
+ int request_transfer; /* Bytes to transfer */
+ int actually_transferred; /* Bytes actually transferred */
+ int buffer_size; /* Size of our data buffer */
+ char *b_data; /* Pointer which runs on the buffers */
+ int b_count; /* Missing/Available data on the current buffer */
+ struct request *rq; /* The corresponding request */
+ byte *buffer; /* Data buffer */
+ byte *current_position; /* Pointer into the above buffer */
+ void (*callback) (ide_drive_t *); /* Called when this packet command is completed */
+ byte pc_buffer[IDEFLOPPY_PC_BUFFER_SIZE]; /* Temporary buffer */
+ unsigned int flags; /* Status/Action bit flags */
+} idefloppy_pc_t;
+
+/*
+ * Packet command flag bits.
+ */
+#define PC_ABORT 0 /* Set when an error is considered normal - We won't retry */
+#define PC_DMA_RECOMMENDED 2 /* 1 when we prefer to use DMA if possible */
+#define PC_DMA_IN_PROGRESS 3 /* 1 while DMA in progress */
+#define PC_DMA_ERROR 4 /* 1 when encountered problem during DMA */
+#define PC_WRITING 5 /* Data direction */
+
+/*
+ * Removable Block Access Capabilities Page
+ */
+typedef struct {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ unsigned page_code :6; /* Page code - Should be 0x1b */
+ unsigned reserved1_6 :1; /* Reserved */
+ unsigned ps :1; /* Should be 0 */
+#elif defined(__BIG_ENDIAN_BITFIELD)
+ unsigned ps :1; /* Should be 0 */
+ unsigned reserved1_6 :1; /* Reserved */
+ unsigned page_code :6; /* Page code - Should be 0x1b */
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
+ u8 page_length; /* Page Length - Should be 0xa */
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ unsigned reserved2 :6;
+ unsigned srfp :1; /* Supports reporting progress of format */
+ unsigned sflp :1; /* System floppy type device */
+ unsigned tlun :3; /* Total logical units supported by the device */
+ unsigned reserved3 :3;
+ unsigned sml :1; /* Single / Multiple lun supported */
+ unsigned ncd :1; /* Non cd optical device */
+#elif defined(__BIG_ENDIAN_BITFIELD)
+ unsigned sflp :1; /* System floppy type device */
+ unsigned srfp :1; /* Supports reporting progress of format */
+ unsigned reserved2 :6;
+ unsigned ncd :1; /* Non cd optical device */
+ unsigned sml :1; /* Single / Multiple lun supported */
+ unsigned reserved3 :3;
+ unsigned tlun :3; /* Total logical units supported by the device */
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
+ u8 reserved[8];
+} idefloppy_capabilities_page_t;
+
+/*
+ * Flexible disk page.
+ */
+typedef struct {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ unsigned page_code :6; /* Page code - Should be 0x5 */
+ unsigned reserved1_6 :1; /* Reserved */
+ unsigned ps :1; /* The device is capable of saving the page */
+#elif defined(__BIG_ENDIAN_BITFIELD)
+ unsigned ps :1; /* The device is capable of saving the page */
+ unsigned reserved1_6 :1; /* Reserved */
+ unsigned page_code :6; /* Page code - Should be 0x5 */
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
+ u8 page_length; /* Page Length - Should be 0x1e */
+ u16 transfer_rate; /* In kilobits per second */
+ u8 heads, sectors; /* Number of heads, Number of sectors per track */
+ u16 sector_size; /* Byes per sector */
+ u16 cyls; /* Number of cylinders */
+ u8 reserved10[10];
+ u8 motor_delay; /* Motor off delay */
+ u8 reserved21[7];
+ u16 rpm; /* Rotations per minute */
+ u8 reserved30[2];
+} idefloppy_flexible_disk_page_t;
+
+/*
+ * Format capacity
+ */
+typedef struct {
+ u8 reserved[3];
+ u8 length; /* Length of the following descriptors in bytes */
+} idefloppy_capacity_header_t;
+
+typedef struct {
+ u32 blocks; /* Number of blocks */
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ unsigned dc :2; /* Descriptor Code */
+ unsigned reserved :6;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+ unsigned reserved :6;
+ unsigned dc :2; /* Descriptor Code */
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
+ u8 length_msb; /* Block Length (MSB)*/
+ u16 length; /* Block Length */
+} idefloppy_capacity_descriptor_t;
+
+#define CAPACITY_INVALID 0x00
+#define CAPACITY_UNFORMATTED 0x01
+#define CAPACITY_CURRENT 0x02
+#define CAPACITY_NO_CARTRIDGE 0x03
+
+/*
+ * Most of our global data which we need to save even as we leave the
+ * driver due to an interrupt or a timer event is stored in a variable
+ * of type idefloppy_floppy_t, defined below.
+ */
+typedef struct {
+ ide_drive_t *drive;
+
+ idefloppy_pc_t *pc; /* Current packet command */
+ idefloppy_pc_t *failed_pc; /* Last failed packet command */
+ idefloppy_pc_t pc_stack[IDEFLOPPY_PC_STACK];/* Packet command stack */
+ int pc_stack_index; /* Next free packet command storage space */
+ struct request rq_stack[IDEFLOPPY_PC_STACK];
+ int rq_stack_index; /* We implement a circular array */
+
+ /*
+ * Last error information
+ */
+ byte sense_key, asc, ascq;
+
+ /*
+ * Device information
+ */
+ int blocks, block_size, bs_factor; /* Current format */
+ idefloppy_capacity_descriptor_t capacity; /* Last format capacity */
+ idefloppy_flexible_disk_page_t flexible_disk_page; /* Copy of the flexible disk page */
+ int wp; /* Write protect */
+
+ unsigned int flags; /* Status/Action flags */
+} idefloppy_floppy_t;
+
+/*
+ * Floppy flag bits values.
+ */
+#define IDEFLOPPY_DRQ_INTERRUPT 0 /* DRQ interrupt device */
+#define IDEFLOPPY_MEDIA_CHANGED 1 /* Media may have changed */
+#define IDEFLOPPY_USE_READ12 2 /* Use READ12/WRITE12 or READ10/WRITE10 */
+
+/*
+ * ATAPI floppy drive packet commands
+ */
+#define IDEFLOPPY_FORMAT_UNIT_CMD 0x04
+#define IDEFLOPPY_INQUIRY_CMD 0x12
+#define IDEFLOPPY_MODE_SELECT_CMD 0x55
+#define IDEFLOPPY_MODE_SENSE_CMD 0x5a
+#define IDEFLOPPY_READ10_CMD 0x28
+#define IDEFLOPPY_READ12_CMD 0xa8
+#define IDEFLOPPY_READ_CAPACITY_CMD 0x23
+#define IDEFLOPPY_REQUEST_SENSE_CMD 0x03
+#define IDEFLOPPY_PREVENT_REMOVAL_CMD 0x1e
+#define IDEFLOPPY_SEEK_CMD 0x2b
+#define IDEFLOPPY_START_STOP_CMD 0x1b
+#define IDEFLOPPY_TEST_UNIT_READY_CMD 0x00
+#define IDEFLOPPY_VERIFY_CMD 0x2f
+#define IDEFLOPPY_WRITE10_CMD 0x2a
+#define IDEFLOPPY_WRITE12_CMD 0xaa
+#define IDEFLOPPY_WRITE_VERIFY_CMD 0x2e
+
+/*
+ * Defines for the mode sense command
+ */
+#define MODE_SENSE_CURRENT 0x00
+#define MODE_SENSE_CHANGEABLE 0x01
+#define MODE_SENSE_DEFAULT 0x02
+#define MODE_SENSE_SAVED 0x03
+
+/*
+ * Special requests for our block device strategy routine.
+ */
+#define IDEFLOPPY_FIRST_RQ 90
+
+/*
+ * IDEFLOPPY_PC_RQ is used to queue a packet command in the request queue.
+ */
+#define IDEFLOPPY_PC_RQ 90
+
+#define IDEFLOPPY_LAST_RQ 90
+
+/*
+ * A macro which can be used to check if a given request command
+ * originated in the driver or in the buffer cache layer.
+ */
+#define IDEFLOPPY_RQ_CMD(cmd) ((cmd >= IDEFLOPPY_FIRST_RQ) && (cmd <= IDEFLOPPY_LAST_RQ))
+
+/*
+ * Error codes which are returned in rq->errors to the higher part
+ * of the driver.
+ */
+#define IDEFLOPPY_ERROR_GENERAL 101
+
+/*
+ * The ATAPI Status Register.
+ */
+typedef union {
+ unsigned all :8;
+ struct {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ unsigned check :1; /* Error occurred */
+ unsigned idx :1; /* Reserved */
+ unsigned corr :1; /* Correctable error occurred */
+ unsigned drq :1; /* Data is request by the device */
+ unsigned dsc :1; /* Media access command finished */
+ unsigned reserved5 :1; /* Reserved */
+ unsigned drdy :1; /* Ignored for ATAPI commands (ready to accept ATA command) */
+ unsigned bsy :1; /* The device has access to the command block */
+#elif defined(__BIG_ENDIAN_BITFIELD)
+ unsigned bsy :1; /* The device has access to the command block */
+ unsigned drdy :1; /* Ignored for ATAPI commands (ready to accept ATA command) */
+ unsigned reserved5 :1; /* Reserved */
+ unsigned dsc :1; /* Media access command finished */
+ unsigned drq :1; /* Data is request by the device */
+ unsigned corr :1; /* Correctable error occurred */
+ unsigned idx :1; /* Reserved */
+ unsigned check :1; /* Error occurred */
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
+ } b;
+} idefloppy_status_reg_t;
+
+/*
+ * The ATAPI error register.
+ */
+typedef union {
+ unsigned all :8;
+ struct {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ unsigned ili :1; /* Illegal Length Indication */
+ unsigned eom :1; /* End Of Media Detected */
+ unsigned abrt :1; /* Aborted command - As defined by ATA */
+ unsigned mcr :1; /* Media Change Requested - As defined by ATA */
+ unsigned sense_key :4; /* Sense key of the last failed packet command */
+#elif defined(__BIG_ENDIAN_BITFIELD)
+ unsigned sense_key :4; /* Sense key of the last failed packet command */
+ unsigned mcr :1; /* Media Change Requested - As defined by ATA */
+ unsigned abrt :1; /* Aborted command - As defined by ATA */
+ unsigned eom :1; /* End Of Media Detected */
+ unsigned ili :1; /* Illegal Length Indication */
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
+ } b;
+} idefloppy_error_reg_t;
+
+/*
+ * ATAPI Feature Register
+ */
+typedef union {
+ unsigned all :8;
+ struct {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ unsigned dma :1; /* Using DMA or PIO */
+ unsigned reserved321 :3; /* Reserved */
+ unsigned reserved654 :3; /* Reserved (Tag Type) */
+ unsigned reserved7 :1; /* Reserved */
+#elif defined(__BIG_ENDIAN_BITFIELD)
+ unsigned reserved7 :1; /* Reserved */
+ unsigned reserved654 :3; /* Reserved (Tag Type) */
+ unsigned reserved321 :3; /* Reserved */
+ unsigned dma :1; /* Using DMA or PIO */
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
+ } b;
+} idefloppy_feature_reg_t;
+
+/*
+ * ATAPI Byte Count Register.
+ */
+typedef union {
+ unsigned all :16;
+ struct {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ unsigned low :8; /* LSB */
+ unsigned high :8; /* MSB */
+#elif defined(__BIG_ENDIAN_BITFIELD)
+ unsigned high :8; /* MSB */
+ unsigned low :8; /* LSB */
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
+ } b;
+} idefloppy_bcount_reg_t;
+
+/*
+ * ATAPI Interrupt Reason Register.
+ */
+typedef union {
+ unsigned all :8;
+ struct {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ unsigned cod :1; /* Information transferred is command (1) or data (0) */
+ unsigned io :1; /* The device requests us to read (1) or write (0) */
+ unsigned reserved :6; /* Reserved */
+#elif defined(__BIG_ENDIAN_BITFIELD)
+ unsigned reserved :6; /* Reserved */
+ unsigned io :1; /* The device requests us to read (1) or write (0) */
+ unsigned cod :1; /* Information transferred is command (1) or data (0) */
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
+ } b;
+} idefloppy_ireason_reg_t;
+
+/*
+ * ATAPI floppy Drive Select Register
+ */
+typedef union {
+ unsigned all :8;
+ struct {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ unsigned sam_lun :3; /* Logical unit number */
+ unsigned reserved3 :1; /* Reserved */
+ unsigned drv :1; /* The responding drive will be drive 0 (0) or drive 1 (1) */
+ unsigned one5 :1; /* Should be set to 1 */
+ unsigned reserved6 :1; /* Reserved */
+ unsigned one7 :1; /* Should be set to 1 */
+#elif defined(__BIG_ENDIAN_BITFIELD)
+ unsigned one7 :1; /* Should be set to 1 */
+ unsigned reserved6 :1; /* Reserved */
+ unsigned one5 :1; /* Should be set to 1 */
+ unsigned drv :1; /* The responding drive will be drive 0 (0) or drive 1 (1) */
+ unsigned reserved3 :1; /* Reserved */
+ unsigned sam_lun :3; /* Logical unit number */
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
+ } b;
+} idefloppy_drivesel_reg_t;
+
+/*
+ * ATAPI Device Control Register
+ */
+typedef union {
+ unsigned all :8;
+ struct {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ unsigned zero0 :1; /* Should be set to zero */
+ unsigned nien :1; /* Device interrupt is disabled (1) or enabled (0) */
+ unsigned srst :1; /* ATA software reset. ATAPI devices should use the new ATAPI srst. */
+ unsigned one3 :1; /* Should be set to 1 */
+ unsigned reserved4567 :4; /* Reserved */
+#elif defined(__BIG_ENDIAN_BITFIELD)
+ unsigned reserved4567 :4; /* Reserved */
+ unsigned one3 :1; /* Should be set to 1 */
+ unsigned srst :1; /* ATA software reset. ATAPI devices should use the new ATAPI srst. */
+ unsigned nien :1; /* Device interrupt is disabled (1) or enabled (0) */
+ unsigned zero0 :1; /* Should be set to zero */
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
+ } b;
+} idefloppy_control_reg_t;
+
+/*
+ * The following is used to format the general configuration word of
+ * the ATAPI IDENTIFY DEVICE command.
+ */
+struct idefloppy_id_gcw {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ unsigned packet_size :2; /* Packet Size */
+ unsigned reserved234 :3; /* Reserved */
+ unsigned drq_type :2; /* Command packet DRQ type */
+ unsigned removable :1; /* Removable media */
+ unsigned device_type :5; /* Device type */
+ unsigned reserved13 :1; /* Reserved */
+ unsigned protocol :2; /* Protocol type */
+#elif defined(__BIG_ENDIAN_BITFIELD)
+ unsigned protocol :2; /* Protocol type */
+ unsigned reserved13 :1; /* Reserved */
+ unsigned device_type :5; /* Device type */
+ unsigned removable :1; /* Removable media */
+ unsigned drq_type :2; /* Command packet DRQ type */
+ unsigned reserved234 :3; /* Reserved */
+ unsigned packet_size :2; /* Packet Size */
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
+};
+
+/*
+ * INQUIRY packet command - Data Format
+ */
+typedef struct {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ unsigned device_type :5; /* Peripheral Device Type */
+ unsigned reserved0_765 :3; /* Peripheral Qualifier - Reserved */
+ unsigned reserved1_6t0 :7; /* Reserved */
+ unsigned rmb :1; /* Removable Medium Bit */
+ unsigned ansi_version :3; /* ANSI Version */
+ unsigned ecma_version :3; /* ECMA Version */
+ unsigned iso_version :2; /* ISO Version */
+ unsigned response_format :4; /* Response Data Format */
+ unsigned reserved3_45 :2; /* Reserved */
+ unsigned reserved3_6 :1; /* TrmIOP - Reserved */
+ unsigned reserved3_7 :1; /* AENC - Reserved */
+#elif defined(__BIG_ENDIAN_BITFIELD)
+ unsigned reserved0_765 :3; /* Peripheral Qualifier - Reserved */
+ unsigned device_type :5; /* Peripheral Device Type */
+ unsigned rmb :1; /* Removable Medium Bit */
+ unsigned reserved1_6t0 :7; /* Reserved */
+ unsigned iso_version :2; /* ISO Version */
+ unsigned ecma_version :3; /* ECMA Version */
+ unsigned ansi_version :3; /* ANSI Version */
+ unsigned reserved3_7 :1; /* AENC - Reserved */
+ unsigned reserved3_6 :1; /* TrmIOP - Reserved */
+ unsigned reserved3_45 :2; /* Reserved */
+ unsigned response_format :4; /* Response Data Format */
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
+ u8 additional_length; /* Additional Length (total_length-4) */
+ u8 rsv5, rsv6, rsv7; /* Reserved */
+ u8 vendor_id[8]; /* Vendor Identification */
+ u8 product_id[16]; /* Product Identification */
+ u8 revision_level[4]; /* Revision Level */
+ u8 vendor_specific[20]; /* Vendor Specific - Optional */
+ u8 reserved56t95[40]; /* Reserved - Optional */
+ /* Additional information may be returned */
+} idefloppy_inquiry_result_t;
+
+/*
+ * REQUEST SENSE packet command result - Data Format.
+ */
+typedef struct {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ unsigned error_code :7; /* Current error (0x70) */
+ unsigned valid :1; /* The information field conforms to SFF-8070i */
+ u8 reserved1 :8; /* Reserved */
+ unsigned sense_key :4; /* Sense Key */
+ unsigned reserved2_4 :1; /* Reserved */
+ unsigned ili :1; /* Incorrect Length Indicator */
+ unsigned reserved2_67 :2;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+ unsigned valid :1; /* The information field conforms to SFF-8070i */
+ unsigned error_code :7; /* Current error (0x70) */
+ u8 reserved1 :8; /* Reserved */
+ unsigned reserved2_67 :2;
+ unsigned ili :1; /* Incorrect Length Indicator */
+ unsigned reserved2_4 :1; /* Reserved */
+ unsigned sense_key :4; /* Sense Key */
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
+ u32 information __attribute__ ((packed));
+ u8 asl; /* Additional sense length (n-7) */
+ u32 command_specific; /* Additional command specific information */
+ u8 asc; /* Additional Sense Code */
+ u8 ascq; /* Additional Sense Code Qualifier */
+ u8 replaceable_unit_code; /* Field Replaceable Unit Code */
+ u8 reserved[3];
+ u8 pad[2]; /* Padding to 20 bytes */
+} idefloppy_request_sense_result_t;
+
+/*
+ * Pages of the SELECT SENSE / MODE SENSE packet commands.
+ */
+#define IDEFLOPPY_CAPABILITIES_PAGE 0x1b
+#define IDEFLOPPY_FLEXIBLE_DISK_PAGE 0x05
+
+/*
+ * Mode Parameter Header for the MODE SENSE packet command
+ */
+typedef struct {
+ u16 mode_data_length; /* Length of the following data transfer */
+ u8 medium_type; /* Medium Type */
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ unsigned reserved3 :7;
+ unsigned wp :1; /* Write protect */
+#elif defined(__BIG_ENDIAN_BITFIELD)
+ unsigned wp :1; /* Write protect */
+ unsigned reserved3 :7;
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
+ u8 reserved[4];
+} idefloppy_mode_parameter_header_t;
+
+#define IDEFLOPPY_MIN(a,b) ((a)<(b) ? (a):(b))
+#define IDEFLOPPY_MAX(a,b) ((a)>(b) ? (a):(b))
+
+/*
+ * Too bad. The drive wants to send us data which we are not ready to accept.
+ * Just throw it away.
+ */
+static void idefloppy_discard_data (ide_drive_t *drive, unsigned int bcount)
+{
+ while (bcount--)
+ IN_BYTE (IDE_DATA_REG);
+}
+
+#if IDEFLOPPY_DEBUG_BUGS
+static void idefloppy_write_zeros (ide_drive_t *drive, unsigned int bcount)
+{
+ while (bcount--)
+ OUT_BYTE (0, IDE_DATA_REG);
+}
+#endif /* IDEFLOPPY_DEBUG_BUGS */
+
+/*
+ * idefloppy_end_request is used to finish servicing a request.
+ *
+ * For read/write requests, we will call ide_end_request to pass to the
+ * next buffer.
+ */
+static void idefloppy_end_request (byte uptodate, ide_hwgroup_t *hwgroup)
+{
+ ide_drive_t *drive = hwgroup->drive;
+ idefloppy_floppy_t *floppy = drive->driver_data;
+ struct request *rq = hwgroup->rq;
+ int error;
+
+#if IDEFLOPPY_DEBUG_LOG
+ printk (KERN_INFO "Reached idefloppy_end_request\n");
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+ switch (uptodate) {
+ case 0: error = IDEFLOPPY_ERROR_GENERAL; break;
+ case 1: error = 0; break;
+ default: error = uptodate;
+ }
+ if (error)
+ floppy->failed_pc = NULL;
+ /* Why does this happen? */
+ if (!rq)
+ return;
+ if (!IDEFLOPPY_RQ_CMD (rq->cmd)) {
+ ide_end_request (uptodate, hwgroup);
+ return;
+ }
+ rq->errors = error;
+ ide_end_drive_cmd (drive, 0, 0);
+}
+
+static void idefloppy_input_buffers (ide_drive_t *drive, idefloppy_pc_t *pc, unsigned int bcount)
+{
+ struct request *rq = pc->rq;
+ struct buffer_head *bh = rq->bh;
+ int count;
+
+ while (bcount) {
+ if (pc->b_count == bh->b_size) {
+ rq->sector += rq->current_nr_sectors;
+ rq->nr_sectors -= rq->current_nr_sectors;
+ idefloppy_end_request (1, HWGROUP(drive));
+ if ((bh = rq->bh) != NULL)
+ pc->b_count = 0;
+ }
+ if (bh == NULL) {
+ printk (KERN_ERR "%s: bh == NULL in idefloppy_input_buffers, bcount == %d\n", drive->name, bcount);
+ idefloppy_discard_data (drive, bcount);
+ return;
+ }
+ count = IDEFLOPPY_MIN (bh->b_size - pc->b_count, bcount);
+ atapi_input_bytes (drive, bh->b_data + pc->b_count, count);
+ bcount -= count; pc->b_count += count;
+ }
+}
+
+static void idefloppy_output_buffers (ide_drive_t *drive, idefloppy_pc_t *pc, unsigned int bcount)
+{
+ struct request *rq = pc->rq;
+ struct buffer_head *bh = rq->bh;
+ int count;
+
+ while (bcount) {
+ if (!pc->b_count) {
+ rq->sector += rq->current_nr_sectors;
+ rq->nr_sectors -= rq->current_nr_sectors;
+ idefloppy_end_request (1, HWGROUP(drive));
+ if ((bh = rq->bh) != NULL) {
+ pc->b_data = bh->b_data;
+ pc->b_count = bh->b_size;
+ }
+ }
+ if (bh == NULL) {
+ printk (KERN_ERR "%s: bh == NULL in idefloppy_output_buffers, bcount == %d\n", drive->name, bcount);
+ idefloppy_write_zeros (drive, bcount);
+ return;
+ }
+ count = IDEFLOPPY_MIN (pc->b_count, bcount);
+ atapi_output_bytes (drive, pc->b_data, count);
+ bcount -= count; pc->b_data += count; pc->b_count -= count;
+ }
+}
+
+#ifdef CONFIG_BLK_DEV_IDEDMA
+static void idefloppy_update_buffers (ide_drive_t *drive, idefloppy_pc_t *pc)
+{
+ struct request *rq = pc->rq;
+ struct buffer_head *bh = rq->bh;
+
+ while ((bh = rq->bh) != NULL)
+ idefloppy_end_request (1, HWGROUP(drive));
+}
+#endif /* CONFIG_BLK_DEV_IDEDMA */
+
+/*
+ * idefloppy_queue_pc_head generates a new packet command request in front
+ * of the request queue, before the current request, so that it will be
+ * processed immediately, on the next pass through the driver.
+ */
+static void idefloppy_queue_pc_head (ide_drive_t *drive,idefloppy_pc_t *pc,struct request *rq)
+{
+ ide_init_drive_cmd (rq);
+ rq->buffer = (char *) pc;
+ rq->cmd = IDEFLOPPY_PC_RQ;
+ (void) ide_do_drive_cmd (drive, rq, ide_preempt);
+}
+
+static idefloppy_pc_t *idefloppy_next_pc_storage (ide_drive_t *drive)
+{
+ idefloppy_floppy_t *floppy = drive->driver_data;
+
+ if (floppy->pc_stack_index==IDEFLOPPY_PC_STACK)
+ floppy->pc_stack_index=0;
+ return (&floppy->pc_stack[floppy->pc_stack_index++]);
+}
+
+static struct request *idefloppy_next_rq_storage (ide_drive_t *drive)
+{
+ idefloppy_floppy_t *floppy = drive->driver_data;
+
+ if (floppy->rq_stack_index==IDEFLOPPY_PC_STACK)
+ floppy->rq_stack_index=0;
+ return (&floppy->rq_stack[floppy->rq_stack_index++]);
+}
+
+/*
+ * idefloppy_analyze_error is called on each failed packet command retry
+ * to analyze the request sense.
+ */
+static void idefloppy_analyze_error (ide_drive_t *drive,idefloppy_request_sense_result_t *result)
+{
+ idefloppy_floppy_t *floppy = drive->driver_data;
+
+ floppy->sense_key = result->sense_key; floppy->asc = result->asc; floppy->ascq = result->ascq;
+#if IDEFLOPPY_DEBUG_LOG
+ if (floppy->failed_pc)
+ printk (KERN_INFO "ide-floppy: pc = %x, sense key = %x, asc = %x, ascq = %x\n",floppy->failed_pc->c[0],result->sense_key,result->asc,result->ascq);
+ else
+ printk (KERN_INFO "ide-floppy: sense key = %x, asc = %x, ascq = %x\n",result->sense_key,result->asc,result->ascq);
+#endif /* IDEFLOPPY_DEBUG_LOG */
+}
+
+static void idefloppy_request_sense_callback (ide_drive_t *drive)
+{
+ idefloppy_floppy_t *floppy = drive->driver_data;
+
+#if IDEFLOPPY_DEBUG_LOG
+ printk (KERN_INFO "ide-floppy: Reached idefloppy_request_sense_callback\n");
+#endif /* IDEFLOPPY_DEBUG_LOG */
+ if (!floppy->pc->error) {
+ idefloppy_analyze_error (drive,(idefloppy_request_sense_result_t *) floppy->pc->buffer);
+ idefloppy_end_request (1,HWGROUP (drive));
+ } else {
+ printk (KERN_ERR "Error in REQUEST SENSE itself - Aborting request!\n");
+ idefloppy_end_request (0,HWGROUP (drive));
+ }
+}
+
+/*
+ * General packet command callback function.
+ */
+static void idefloppy_pc_callback (ide_drive_t *drive)
+{
+ idefloppy_floppy_t *floppy = drive->driver_data;
+
+#if IDEFLOPPY_DEBUG_LOG
+ printk (KERN_INFO "ide-floppy: Reached idefloppy_pc_callback\n");
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+ idefloppy_end_request (floppy->pc->error ? 0:1, HWGROUP(drive));
+}
+
+/*
+ * idefloppy_init_pc initializes a packet command.
+ */
+static void idefloppy_init_pc (idefloppy_pc_t *pc)
+{
+ memset (pc->c, 0, 12);
+ pc->retries = 0;
+ pc->flags = 0;
+ pc->request_transfer = 0;
+ pc->buffer = pc->pc_buffer;
+ pc->buffer_size = IDEFLOPPY_PC_BUFFER_SIZE;
+ pc->b_data = NULL;
+ pc->callback = &idefloppy_pc_callback;
+}
+
+static void idefloppy_create_request_sense_cmd (idefloppy_pc_t *pc)
+{
+ idefloppy_init_pc (pc);
+ pc->c[0] = IDEFLOPPY_REQUEST_SENSE_CMD;
+ pc->c[4] = 255;
+ pc->request_transfer = 18;
+ pc->callback = &idefloppy_request_sense_callback;
+}
+
+/*
+ * idefloppy_retry_pc is called when an error was detected during the
+ * last packet command. We queue a request sense packet command in
+ * the head of the request list.
+ */
+static void idefloppy_retry_pc (ide_drive_t *drive)
+{
+ idefloppy_pc_t *pc;
+ struct request *rq;
+ idefloppy_error_reg_t error;
+
+ error.all = IN_BYTE (IDE_ERROR_REG);
+ pc = idefloppy_next_pc_storage (drive);
+ rq = idefloppy_next_rq_storage (drive);
+ idefloppy_create_request_sense_cmd (pc);
+ idefloppy_queue_pc_head (drive, pc, rq);
+}
+
+/*
+ * idefloppy_pc_intr is the usual interrupt handler which will be called
+ * during a packet command.
+ */
+static ide_startstop_t idefloppy_pc_intr (ide_drive_t *drive)
+{
+ idefloppy_floppy_t *floppy = drive->driver_data;
+ idefloppy_status_reg_t status;
+ idefloppy_bcount_reg_t bcount;
+ idefloppy_ireason_reg_t ireason;
+ idefloppy_pc_t *pc=floppy->pc;
+ struct request *rq = pc->rq;
+ unsigned int temp;
+
+#if IDEFLOPPY_DEBUG_LOG
+ printk (KERN_INFO "ide-floppy: Reached idefloppy_pc_intr interrupt handler\n");
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+#ifdef CONFIG_BLK_DEV_IDEDMA
+ if (test_bit (PC_DMA_IN_PROGRESS, &pc->flags)) {
+ if (HWIF(drive)->dmaproc(ide_dma_end, drive)) {
+ set_bit (PC_DMA_ERROR, &pc->flags);
+ } else {
+ pc->actually_transferred=pc->request_transfer;
+ idefloppy_update_buffers (drive, pc);
+ }
+#if IDEFLOPPY_DEBUG_LOG
+ printk (KERN_INFO "ide-floppy: DMA finished\n");
+#endif /* IDEFLOPPY_DEBUG_LOG */
+ }
+#endif /* CONFIG_BLK_DEV_IDEDMA */
+
+ status.all = GET_STAT(); /* Clear the interrupt */
+
+ if (!status.b.drq) { /* No more interrupts */
+#if IDEFLOPPY_DEBUG_LOG
+ printk (KERN_INFO "Packet command completed, %d bytes transferred\n", pc->actually_transferred);
+#endif /* IDEFLOPPY_DEBUG_LOG */
+ clear_bit (PC_DMA_IN_PROGRESS, &pc->flags);
+
+ ide__sti(); /* local CPU only */
+
+ if (status.b.check || test_bit (PC_DMA_ERROR, &pc->flags)) { /* Error detected */
+#if IDEFLOPPY_DEBUG_LOG
+ printk (KERN_INFO "ide-floppy: %s: I/O error\n",drive->name);
+#endif /* IDEFLOPPY_DEBUG_LOG */
+ rq->errors++;
+ if (pc->c[0] == IDEFLOPPY_REQUEST_SENSE_CMD) {
+ printk (KERN_ERR "ide-floppy: I/O error in request sense command\n");
+ return ide_do_reset (drive);
+ }
+ idefloppy_retry_pc (drive); /* Retry operation */
+ return ide_stopped; /* queued, but not started */
+ }
+ pc->error = 0;
+ if (floppy->failed_pc == pc)
+ floppy->failed_pc=NULL;
+ pc->callback(drive); /* Command finished - Call the callback function */
+ return ide_stopped;
+ }
+#ifdef CONFIG_BLK_DEV_IDEDMA
+ if (test_and_clear_bit (PC_DMA_IN_PROGRESS, &pc->flags)) {
+ printk (KERN_ERR "ide-floppy: The floppy wants to issue more interrupts in DMA mode\n");
+ (void) HWIF(drive)->dmaproc(ide_dma_off, drive);
+ return ide_do_reset (drive);
+ }
+#endif /* CONFIG_BLK_DEV_IDEDMA */
+ bcount.b.high=IN_BYTE (IDE_BCOUNTH_REG); /* Get the number of bytes to transfer */
+ bcount.b.low=IN_BYTE (IDE_BCOUNTL_REG); /* on this interrupt */
+ ireason.all=IN_BYTE (IDE_IREASON_REG);
+
+ if (ireason.b.cod) {
+ printk (KERN_ERR "ide-floppy: CoD != 0 in idefloppy_pc_intr\n");
+ return ide_do_reset (drive);
+ }
+ if (ireason.b.io == test_bit (PC_WRITING, &pc->flags)) { /* Hopefully, we will never get here */
+ printk (KERN_ERR "ide-floppy: We wanted to %s, ", ireason.b.io ? "Write":"Read");
+ printk (KERN_ERR "but the floppy wants us to %s !\n",ireason.b.io ? "Read":"Write");
+ return ide_do_reset (drive);
+ }
+ if (!test_bit (PC_WRITING, &pc->flags)) { /* Reading - Check that we have enough space */
+ temp = pc->actually_transferred + bcount.all;
+ if ( temp > pc->request_transfer) {
+ if (temp > pc->buffer_size) {
+ printk (KERN_ERR "ide-floppy: The floppy wants to send us more data than expected - discarding data\n");
+ idefloppy_discard_data (drive,bcount.all);
+ ide_set_handler (drive,&idefloppy_pc_intr,IDEFLOPPY_WAIT_CMD, NULL);
+ return ide_started;
+ }
+#if IDEFLOPPY_DEBUG_LOG
+ printk (KERN_NOTICE "ide-floppy: The floppy wants to send us more data than expected - allowing transfer\n");
+#endif /* IDEFLOPPY_DEBUG_LOG */
+ }
+ }
+ if (test_bit (PC_WRITING, &pc->flags)) {
+ if (pc->buffer != NULL)
+ atapi_output_bytes (drive,pc->current_position,bcount.all); /* Write the current buffer */
+ else
+ idefloppy_output_buffers (drive, pc, bcount.all);
+ } else {
+ if (pc->buffer != NULL)
+ atapi_input_bytes (drive,pc->current_position,bcount.all); /* Read the current buffer */
+ else
+ idefloppy_input_buffers (drive, pc, bcount.all);
+ }
+ pc->actually_transferred+=bcount.all; /* Update the current position */
+ pc->current_position+=bcount.all;
+
+ ide_set_handler (drive,&idefloppy_pc_intr,IDEFLOPPY_WAIT_CMD, NULL); /* And set the interrupt handler again */
+ return ide_started;
+}
+
+static ide_startstop_t idefloppy_transfer_pc (ide_drive_t *drive)
+{
+ ide_startstop_t startstop;
+ idefloppy_floppy_t *floppy = drive->driver_data;
+ idefloppy_ireason_reg_t ireason;
+
+ if (ide_wait_stat (&startstop,drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) {
+ printk (KERN_ERR "ide-floppy: Strange, packet command initiated yet DRQ isn't asserted\n");
+ return startstop;
+ }
+ ireason.all=IN_BYTE (IDE_IREASON_REG);
+ if (!ireason.b.cod || ireason.b.io) {
+ printk (KERN_ERR "ide-floppy: (IO,CoD) != (0,1) while issuing a packet command\n");
+ return ide_do_reset (drive);
+ }
+ ide_set_handler (drive, &idefloppy_pc_intr, IDEFLOPPY_WAIT_CMD, NULL); /* Set the interrupt routine */
+ atapi_output_bytes (drive, floppy->pc->c, 12); /* Send the actual packet */
+ return ide_started;
+}
+
+/*
+ * Issue a packet command
+ */
+static ide_startstop_t idefloppy_issue_pc (ide_drive_t *drive, idefloppy_pc_t *pc)
+{
+ idefloppy_floppy_t *floppy = drive->driver_data;
+ idefloppy_bcount_reg_t bcount;
+ int dma_ok = 0;
+
+#if IDEFLOPPY_DEBUG_BUGS
+ if (floppy->pc->c[0] == IDEFLOPPY_REQUEST_SENSE_CMD && pc->c[0] == IDEFLOPPY_REQUEST_SENSE_CMD) {
+ printk (KERN_ERR "ide-floppy: possible ide-floppy.c bug - Two request sense in serial were issued\n");
+ }
+#endif /* IDEFLOPPY_DEBUG_BUGS */
+
+ if (floppy->failed_pc == NULL && pc->c[0] != IDEFLOPPY_REQUEST_SENSE_CMD)
+ floppy->failed_pc=pc;
+ floppy->pc=pc; /* Set the current packet command */
+
+ if (pc->retries > IDEFLOPPY_MAX_PC_RETRIES || test_bit (PC_ABORT, &pc->flags)) {
+ /*
+ * We will "abort" retrying a packet command in case
+ * a legitimate error code was received.
+ */
+ if (!test_bit (PC_ABORT, &pc->flags)) {
+ printk (KERN_ERR "ide-floppy: %s: I/O error, pc = %2x, key = %2x, asc = %2x, ascq = %2x\n",
+ drive->name, pc->c[0], floppy->sense_key, floppy->asc, floppy->ascq);
+ pc->error = IDEFLOPPY_ERROR_GENERAL; /* Giving up */
+ }
+ floppy->failed_pc=NULL;
+ pc->callback(drive);
+ return ide_stopped;
+ }
+#if IDEFLOPPY_DEBUG_LOG
+ printk (KERN_INFO "Retry number - %d\n",pc->retries);
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+ pc->retries++;
+ pc->actually_transferred=0; /* We haven't transferred any data yet */
+ pc->current_position=pc->buffer;
+ bcount.all = IDE_MIN(pc->request_transfer, 63 * 1024);
+
+#ifdef CONFIG_BLK_DEV_IDEDMA
+ if (test_and_clear_bit (PC_DMA_ERROR, &pc->flags)) {
+ (void) HWIF(drive)->dmaproc(ide_dma_off, drive);
+ }
+ if (test_bit (PC_DMA_RECOMMENDED, &pc->flags) && drive->using_dma)
+ dma_ok=!HWIF(drive)->dmaproc(test_bit (PC_WRITING, &pc->flags) ? ide_dma_write : ide_dma_read, drive);
+#endif /* CONFIG_BLK_DEV_IDEDMA */
+
+ if (IDE_CONTROL_REG)
+ OUT_BYTE (drive->ctl,IDE_CONTROL_REG);
+ OUT_BYTE (dma_ok ? 1:0,IDE_FEATURE_REG); /* Use PIO/DMA */
+ OUT_BYTE (bcount.b.high,IDE_BCOUNTH_REG);
+ OUT_BYTE (bcount.b.low,IDE_BCOUNTL_REG);
+ OUT_BYTE (drive->select.all,IDE_SELECT_REG);
+
+#ifdef CONFIG_BLK_DEV_IDEDMA
+ if (dma_ok) { /* Begin DMA, if necessary */
+ set_bit (PC_DMA_IN_PROGRESS, &pc->flags);
+ (void) (HWIF(drive)->dmaproc(ide_dma_begin, drive));
+ }
+#endif /* CONFIG_BLK_DEV_IDEDMA */
+
+ if (test_bit (IDEFLOPPY_DRQ_INTERRUPT, &floppy->flags)) {
+ ide_set_handler (drive, &idefloppy_transfer_pc, IDEFLOPPY_WAIT_CMD, NULL);
+ OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* Issue the packet command */
+ return ide_started;
+ } else {
+ OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG);
+ return idefloppy_transfer_pc (drive);
+ }
+}
+
+static void idefloppy_rw_callback (ide_drive_t *drive)
+{
+#if IDEFLOPPY_DEBUG_LOG
+ printk (KERN_INFO "ide-floppy: Reached idefloppy_rw_callback\n");
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+ idefloppy_end_request(1, HWGROUP(drive));
+ return;
+}
+
+static void idefloppy_create_prevent_cmd (idefloppy_pc_t *pc, int prevent)
+{
+#if IDEFLOPPY_DEBUG_LOG
+ printk (KERN_INFO "ide-floppy: creating prevent removal command, prevent = %d\n", prevent);
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+ idefloppy_init_pc (pc);
+ pc->c[0] = IDEFLOPPY_PREVENT_REMOVAL_CMD;
+ pc->c[4] = prevent;
+}
+
+static void idefloppy_create_read_capacity_cmd (idefloppy_pc_t *pc)
+{
+ idefloppy_init_pc (pc);
+ pc->c[0] = IDEFLOPPY_READ_CAPACITY_CMD;
+ pc->c[7] = 255;
+ pc->c[8] = 255;
+ pc->request_transfer = 255;
+}
+
+/*
+ * A mode sense command is used to "sense" floppy parameters.
+ */
+static void idefloppy_create_mode_sense_cmd (idefloppy_pc_t *pc, byte page_code, byte type)
+{
+ unsigned short length = sizeof (idefloppy_mode_parameter_header_t);
+
+ idefloppy_init_pc (pc);
+ pc->c[0] = IDEFLOPPY_MODE_SENSE_CMD;
+ pc->c[1] = 0;
+ pc->c[2] = page_code + (type << 6);
+
+ switch (page_code) {
+ case IDEFLOPPY_CAPABILITIES_PAGE:
+ length += 12;
+ break;
+ case IDEFLOPPY_FLEXIBLE_DISK_PAGE:
+ length += 32;
+ break;
+ default:
+ printk (KERN_ERR "ide-floppy: unsupported page code in create_mode_sense_cmd\n");
+ }
+ put_unaligned (htons (length), (unsigned short *) &pc->c[7]);
+ pc->request_transfer = length;
+}
+
+static void idefloppy_create_start_stop_cmd (idefloppy_pc_t *pc, int start)
+{
+ idefloppy_init_pc (pc);
+ pc->c[0] = IDEFLOPPY_START_STOP_CMD;
+ pc->c[4] = start;
+}
+
+static void idefloppy_create_test_unit_ready_cmd(idefloppy_pc_t *pc)
+{
+ idefloppy_init_pc(pc);
+ pc->c[0] = IDEFLOPPY_TEST_UNIT_READY_CMD;
+}
+
+static void idefloppy_create_rw_cmd (idefloppy_floppy_t *floppy, idefloppy_pc_t *pc, struct request *rq, unsigned long sector)
+{
+ int block = sector / floppy->bs_factor;
+ int blocks = rq->nr_sectors / floppy->bs_factor;
+
+#if IDEFLOPPY_DEBUG_LOG
+ printk ("create_rw1%d_cmd: block == %d, blocks == %d\n",
+ 2 * test_bit (IDEFLOPPY_USE_READ12, &floppy->flags), block, blocks);
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+ idefloppy_init_pc (pc);
+ if (test_bit (IDEFLOPPY_USE_READ12, &floppy->flags)) {
+ pc->c[0] = rq->cmd == READ ? IDEFLOPPY_READ12_CMD : IDEFLOPPY_WRITE12_CMD;
+ put_unaligned (htonl (blocks), (unsigned int *) &pc->c[6]);
+ } else {
+ pc->c[0] = rq->cmd == READ ? IDEFLOPPY_READ10_CMD : IDEFLOPPY_WRITE10_CMD;
+ put_unaligned (htons (blocks), (unsigned short *) &pc->c[7]);
+ }
+ put_unaligned (htonl (block), (unsigned int *) &pc->c[2]);
+ pc->callback = &idefloppy_rw_callback;
+ pc->rq = rq;
+ pc->b_data = rq->buffer;
+ pc->b_count = rq->cmd == READ ? 0 : rq->bh->b_size;
+ if (rq->cmd == WRITE)
+ set_bit (PC_WRITING, &pc->flags);
+ pc->buffer = NULL;
+ pc->request_transfer = pc->buffer_size = blocks * floppy->block_size;
+ set_bit (PC_DMA_RECOMMENDED, &pc->flags);
+}
+
+/*
+ * idefloppy_do_request is our request handling function.
+ */
+static ide_startstop_t idefloppy_do_request (ide_drive_t *drive, struct request *rq, unsigned long block)
+{
+ idefloppy_floppy_t *floppy = drive->driver_data;
+ idefloppy_pc_t *pc;
+
+#if IDEFLOPPY_DEBUG_LOG
+ printk (KERN_INFO "rq_status: %d, rq_dev: %u, cmd: %d, errors: %d\n",rq->rq_status,(unsigned int) rq->rq_dev,rq->cmd,rq->errors);
+ printk (KERN_INFO "sector: %ld, nr_sectors: %ld, current_nr_sectors: %ld\n",rq->sector,rq->nr_sectors,rq->current_nr_sectors);
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+ if (rq->errors >= ERROR_MAX) {
+ if (floppy->failed_pc != NULL)
+ printk (KERN_ERR "ide-floppy: %s: I/O error, pc = %2x, key = %2x, asc = %2x, ascq = %2x\n",
+ drive->name, floppy->failed_pc->c[0], floppy->sense_key, floppy->asc, floppy->ascq);
+ else
+ printk (KERN_ERR "ide-floppy: %s: I/O error\n", drive->name);
+ idefloppy_end_request (0, HWGROUP(drive));
+ return ide_stopped;
+ }
+ switch (rq->cmd) {
+ case READ:
+ case WRITE:
+ if (rq->sector % floppy->bs_factor || rq->nr_sectors % floppy->bs_factor) {
+ printk ("%s: unsupported r/w request size\n", drive->name);
+ idefloppy_end_request (0, HWGROUP(drive));
+ return ide_stopped;
+ }
+ pc = idefloppy_next_pc_storage (drive);
+ idefloppy_create_rw_cmd (floppy, pc, rq, block);
+ break;
+ case IDEFLOPPY_PC_RQ:
+ pc = (idefloppy_pc_t *) rq->buffer;
+ break;
+ default:
+ printk (KERN_ERR "ide-floppy: unsupported command %x in request queue\n", rq->cmd);
+ idefloppy_end_request (0,HWGROUP (drive));
+ return ide_stopped;
+ }
+ pc->rq = rq;
+ return idefloppy_issue_pc (drive, pc);
+}
+
+/*
+ * idefloppy_queue_pc_tail adds a special packet command request to the
+ * tail of the request queue, and waits for it to be serviced.
+ */
+static int idefloppy_queue_pc_tail (ide_drive_t *drive,idefloppy_pc_t *pc)
+{
+ struct request rq;
+
+ ide_init_drive_cmd (&rq);
+ rq.buffer = (char *) pc;
+ rq.cmd = IDEFLOPPY_PC_RQ;
+ return ide_do_drive_cmd (drive, &rq, ide_wait);
+}
+
+/*
+ * Look at the flexible disk page parameters. We will ignore the CHS
+ * capacity parameters and use the LBA parameters instead.
+ */
+static int idefloppy_get_flexible_disk_page (ide_drive_t *drive)
+{
+ idefloppy_floppy_t *floppy = drive->driver_data;
+ idefloppy_pc_t pc;
+ idefloppy_mode_parameter_header_t *header;
+ idefloppy_flexible_disk_page_t *page;
+ int capacity, lba_capacity;
+
+ idefloppy_create_mode_sense_cmd (&pc, IDEFLOPPY_FLEXIBLE_DISK_PAGE, MODE_SENSE_CURRENT);
+ if (idefloppy_queue_pc_tail (drive,&pc)) {
+ printk (KERN_ERR "ide-floppy: Can't get flexible disk page parameters\n");
+ return 1;
+ }
+ header = (idefloppy_mode_parameter_header_t *) pc.buffer;
+ floppy->wp = header->wp;
+ page = (idefloppy_flexible_disk_page_t *) (header + 1);
+
+ page->transfer_rate = ntohs (page->transfer_rate);
+ page->sector_size = ntohs (page->sector_size);
+ page->cyls = ntohs (page->cyls);
+ page->rpm = ntohs (page->rpm);
+ capacity = page->cyls * page->heads * page->sectors * page->sector_size;
+ if (memcmp (page, &floppy->flexible_disk_page, sizeof (idefloppy_flexible_disk_page_t)))
+ printk (KERN_INFO "%s: %dkB, %d/%d/%d CHS, %d kBps, %d sector size, %d rpm\n",
+ drive->name, capacity / 1024, page->cyls, page->heads, page->sectors,
+ page->transfer_rate / 8, page->sector_size, page->rpm);
+
+ floppy->flexible_disk_page = *page;
+ drive->bios_cyl = page->cyls;
+ drive->bios_head = page->heads;
+ drive->bios_sect = page->sectors;
+ lba_capacity = floppy->blocks * floppy->block_size;
+ if (capacity < lba_capacity) {
+ printk (KERN_NOTICE "%s: The disk reports a capacity of %d bytes, "
+ "but the drive only handles %d\n",
+ drive->name, lba_capacity, capacity);
+ floppy->blocks = floppy->block_size ? capacity / floppy->block_size : 0;
+ }
+ return 0;
+}
+
+/*
+ * Determine if a media is present in the floppy drive, and if so,
+ * its LBA capacity.
+ */
+static int idefloppy_get_capacity (ide_drive_t *drive)
+{
+ idefloppy_floppy_t *floppy = drive->driver_data;
+ idefloppy_pc_t pc;
+ idefloppy_capacity_header_t *header;
+ idefloppy_capacity_descriptor_t *descriptor;
+ int i, descriptors, rc = 1, blocks, length;
+
+ drive->bios_cyl = 0;
+ drive->bios_head = drive->bios_sect = 0;
+ floppy->blocks = floppy->bs_factor = 0;
+ drive->part[0].nr_sects = 0;
+
+ idefloppy_create_read_capacity_cmd (&pc);
+ if (idefloppy_queue_pc_tail (drive, &pc)) {
+ printk (KERN_ERR "ide-floppy: Can't get floppy parameters\n");
+ return 1;
+ }
+ header = (idefloppy_capacity_header_t *) pc.buffer;
+ descriptors = header->length / sizeof (idefloppy_capacity_descriptor_t);
+ descriptor = (idefloppy_capacity_descriptor_t *) (header + 1);
+ for (i = 0; i < descriptors; i++, descriptor++) {
+ blocks = descriptor->blocks = ntohl (descriptor->blocks);
+ length = descriptor->length = ntohs (descriptor->length);
+ if (!i && descriptor->dc == CAPACITY_CURRENT) {
+ if (memcmp (descriptor, &floppy->capacity, sizeof (idefloppy_capacity_descriptor_t))) {
+ printk (KERN_INFO "%s: %dkB, %d blocks, %d sector size, %s \n",
+ drive->name, blocks * length / 1024, blocks, length,
+ drive->using_dma ? ", DMA":"");
+ }
+ floppy->capacity = *descriptor;
+ if (!length || length % 512)
+ printk (KERN_ERR "%s: %d bytes block size not supported\n", drive->name, length);
+ else {
+ floppy->blocks = blocks;
+ floppy->block_size = length;
+ if ((floppy->bs_factor = length / 512) != 1)
+ printk (KERN_NOTICE "%s: warning: non 512 bytes block size not fully supported\n", drive->name);
+ rc = 0;
+ }
+ }
+#if IDEFLOPPY_DEBUG_INFO
+ if (!i) printk (KERN_INFO "Descriptor 0 Code: %d\n", descriptor->dc);
+ printk (KERN_INFO "Descriptor %d: %dkB, %d blocks, %d sector size\n", i, blocks * length / 1024, blocks, length);
+#endif /* IDEFLOPPY_DEBUG_INFO */
+ }
+ (void) idefloppy_get_flexible_disk_page (drive);
+ drive->part[0].nr_sects = floppy->blocks * floppy->bs_factor;
+ return rc;
+}
+
+/*
+ * Our special ide-floppy ioctl's.
+ *
+ * Currently there aren't any ioctl's.
+ */
+static int idefloppy_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ idefloppy_pc_t pc;
+
+ if (cmd == CDROMEJECT) {
+ if (drive->usage > 1)
+ return -EBUSY;
+ idefloppy_create_prevent_cmd (&pc, 0);
+ (void) idefloppy_queue_pc_tail (drive, &pc);
+ idefloppy_create_start_stop_cmd (&pc, 2);
+ (void) idefloppy_queue_pc_tail (drive, &pc);
+ return 0;
+ }
+ return -EIO;
+}
+
+/*
+ * Our open/release functions
+ */
+static int idefloppy_open (struct inode *inode, struct file *filp, ide_drive_t *drive)
+{
+ idefloppy_floppy_t *floppy = drive->driver_data;
+ idefloppy_pc_t pc;
+
+#if IDEFLOPPY_DEBUG_LOG
+ printk (KERN_INFO "Reached idefloppy_open\n");
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+ MOD_INC_USE_COUNT;
+ if (drive->usage == 1) {
+ idefloppy_create_test_unit_ready_cmd(&pc);
+ if (idefloppy_queue_pc_tail(drive, &pc)) {
+ idefloppy_create_start_stop_cmd (&pc, 1);
+ (void) idefloppy_queue_pc_tail (drive, &pc);
+ }
+ if (idefloppy_get_capacity (drive)) {
+ drive->usage--;
+ MOD_DEC_USE_COUNT;
+ return -EIO;
+ }
+ if (floppy->wp && (filp->f_mode & 2)) {
+ drive->usage--;
+ MOD_DEC_USE_COUNT;
+ return -EROFS;
+ }
+ set_bit (IDEFLOPPY_MEDIA_CHANGED, &floppy->flags);
+ idefloppy_create_prevent_cmd (&pc, 1);
+ (void) idefloppy_queue_pc_tail (drive, &pc);
+ check_disk_change(inode->i_rdev);
+ }
+ return 0;
+}
+
+static void idefloppy_release (struct inode *inode, struct file *filp, ide_drive_t *drive)
+{
+ idefloppy_pc_t pc;
+
+#if IDEFLOPPY_DEBUG_LOG
+ printk (KERN_INFO "Reached idefloppy_release\n");
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+ if (!drive->usage) {
+ invalidate_buffers (inode->i_rdev);
+ idefloppy_create_prevent_cmd (&pc, 0);
+ (void) idefloppy_queue_pc_tail (drive, &pc);
+ }
+ MOD_DEC_USE_COUNT;
+}
+
+/*
+ * Check media change. Use a simple algorithm for now.
+ */
+static int idefloppy_media_change (ide_drive_t *drive)
+{
+ idefloppy_floppy_t *floppy = drive->driver_data;
+
+ return test_and_clear_bit (IDEFLOPPY_MEDIA_CHANGED, &floppy->flags);
+}
+
+/*
+ * Return the current floppy capacity to ide.c.
+ */
+static unsigned long idefloppy_capacity (ide_drive_t *drive)
+{
+ idefloppy_floppy_t *floppy = drive->driver_data;
+ unsigned long capacity = floppy->blocks * floppy->bs_factor;
+
+ return capacity;
+}
+
+/*
+ * idefloppy_identify_device checks if we can support a drive,
+ * based on the ATAPI IDENTIFY command results.
+ */
+static int idefloppy_identify_device (ide_drive_t *drive,struct hd_driveid *id)
+{
+ struct idefloppy_id_gcw gcw;
+#if IDEFLOPPY_DEBUG_INFO
+ unsigned short mask,i;
+ char buffer[80];
+#endif /* IDEFLOPPY_DEBUG_INFO */
+
+ *((unsigned short *) &gcw) = id->config;
+
+#ifdef CONFIG_PPC
+ /* kludge for Apple PowerBook internal zip */
+ if ((gcw.device_type == 5) && !strstr(id->model, "CD-ROM")
+ && strstr(id->model, "ZIP"))
+ gcw.device_type = 0;
+#endif
+
+#if IDEFLOPPY_DEBUG_INFO
+ printk (KERN_INFO "Dumping ATAPI Identify Device floppy parameters\n");
+ switch (gcw.protocol) {
+ case 0: case 1: sprintf (buffer, "ATA");break;
+ case 2: sprintf (buffer, "ATAPI");break;
+ case 3: sprintf (buffer, "Reserved (Unknown to ide-floppy)");break;
+ }
+ printk (KERN_INFO "Protocol Type: %s\n", buffer);
+ switch (gcw.device_type) {
+ case 0: sprintf (buffer, "Direct-access Device");break;
+ case 1: sprintf (buffer, "Streaming Tape Device");break;
+ case 2: case 3: case 4: sprintf (buffer, "Reserved");break;
+ case 5: sprintf (buffer, "CD-ROM Device");break;
+ case 6: sprintf (buffer, "Reserved");
+ case 7: sprintf (buffer, "Optical memory Device");break;
+ case 0x1f: sprintf (buffer, "Unknown or no Device type");break;
+ default: sprintf (buffer, "Reserved");
+ }
+ printk (KERN_INFO "Device Type: %x - %s\n", gcw.device_type, buffer);
+ printk (KERN_INFO "Removable: %s\n",gcw.removable ? "Yes":"No");
+ switch (gcw.drq_type) {
+ case 0: sprintf (buffer, "Microprocessor DRQ");break;
+ case 1: sprintf (buffer, "Interrupt DRQ");break;
+ case 2: sprintf (buffer, "Accelerated DRQ");break;
+ case 3: sprintf (buffer, "Reserved");break;
+ }
+ printk (KERN_INFO "Command Packet DRQ Type: %s\n", buffer);
+ switch (gcw.packet_size) {
+ case 0: sprintf (buffer, "12 bytes");break;
+ case 1: sprintf (buffer, "16 bytes");break;
+ default: sprintf (buffer, "Reserved");break;
+ }
+ printk (KERN_INFO "Command Packet Size: %s\n", buffer);
+ printk (KERN_INFO "Model: %.40s\n",id->model);
+ printk (KERN_INFO "Firmware Revision: %.8s\n",id->fw_rev);
+ printk (KERN_INFO "Serial Number: %.20s\n",id->serial_no);
+ printk (KERN_INFO "Write buffer size(?): %d bytes\n",id->buf_size*512);
+ printk (KERN_INFO "DMA: %s",id->capability & 0x01 ? "Yes\n":"No\n");
+ printk (KERN_INFO "LBA: %s",id->capability & 0x02 ? "Yes\n":"No\n");
+ printk (KERN_INFO "IORDY can be disabled: %s",id->capability & 0x04 ? "Yes\n":"No\n");
+ printk (KERN_INFO "IORDY supported: %s",id->capability & 0x08 ? "Yes\n":"Unknown\n");
+ printk (KERN_INFO "ATAPI overlap supported: %s",id->capability & 0x20 ? "Yes\n":"No\n");
+ printk (KERN_INFO "PIO Cycle Timing Category: %d\n",id->tPIO);
+ printk (KERN_INFO "DMA Cycle Timing Category: %d\n",id->tDMA);
+ printk (KERN_INFO "Single Word DMA supported modes:\n");
+ for (i=0,mask=1;i<8;i++,mask=mask << 1) {
+ if (id->dma_1word & mask)
+ printk (KERN_INFO " Mode %d%s\n", i, (id->dma_1word & (mask << 8)) ? " (active)" : "");
+ }
+ printk (KERN_INFO "Multi Word DMA supported modes:\n");
+ for (i=0,mask=1;i<8;i++,mask=mask << 1) {
+ if (id->dma_mword & mask)
+ printk (KERN_INFO " Mode %d%s\n", i, (id->dma_mword & (mask << 8)) ? " (active)" : "");
+ }
+ if (id->field_valid & 0x0002) {
+ printk (KERN_INFO "Enhanced PIO Modes: %s\n",id->eide_pio_modes & 1 ? "Mode 3":"None");
+ if (id->eide_dma_min == 0)
+ sprintf (buffer, "Not supported");
+ else
+ sprintf (buffer, "%d ns",id->eide_dma_min);
+ printk (KERN_INFO "Minimum Multi-word DMA cycle per word: %s\n", buffer);
+ if (id->eide_dma_time == 0)
+ sprintf (buffer, "Not supported");
+ else
+ sprintf (buffer, "%d ns",id->eide_dma_time);
+ printk (KERN_INFO "Manufacturer\'s Recommended Multi-word cycle: %s\n", buffer);
+ if (id->eide_pio == 0)
+ sprintf (buffer, "Not supported");
+ else
+ sprintf (buffer, "%d ns",id->eide_pio);
+ printk (KERN_INFO "Minimum PIO cycle without IORDY: %s\n", buffer);
+ if (id->eide_pio_iordy == 0)
+ sprintf (buffer, "Not supported");
+ else
+ sprintf (buffer, "%d ns",id->eide_pio_iordy);
+ printk (KERN_INFO "Minimum PIO cycle with IORDY: %s\n", buffer);
+ } else
+ printk (KERN_INFO "According to the device, fields 64-70 are not valid.\n");
+#endif /* IDEFLOPPY_DEBUG_INFO */
+
+ if (gcw.protocol != 2)
+ printk (KERN_ERR "ide-floppy: Protocol is not ATAPI\n");
+ else if (gcw.device_type != 0)
+ printk (KERN_ERR "ide-floppy: Device type is not set to floppy\n");
+ else if (!gcw.removable)
+ printk (KERN_ERR "ide-floppy: The removable flag is not set\n");
+ else if (gcw.drq_type == 3) {
+ printk (KERN_ERR "ide-floppy: Sorry, DRQ type %d not supported\n", gcw.drq_type);
+ } else if (gcw.packet_size != 0) {
+ printk (KERN_ERR "ide-floppy: Packet size is not 12 bytes long\n");
+ } else
+ return 1;
+ return 0;
+}
+
+static void idefloppy_add_settings(ide_drive_t *drive)
+{
+ int major = HWIF(drive)->major;
+ int minor = drive->select.b.unit << PARTN_BITS;
+
+ ide_add_setting(drive, "bios_cyl", SETTING_RW, -1, -1, TYPE_INT, 0, 1023, 1, 1, &drive->bios_cyl, NULL);
+ ide_add_setting(drive, "bios_head", SETTING_RW, -1, -1, TYPE_BYTE, 0, 255, 1, 1, &drive->bios_head, NULL);
+ ide_add_setting(drive, "bios_sect", SETTING_RW, -1, -1, TYPE_BYTE, 0, 63, 1, 1, &drive->bios_sect, NULL);
+ ide_add_setting(drive, "breada_readahead", SETTING_RW, BLKRAGET, BLKRASET, TYPE_INT, 0, 255, 1, 2, &read_ahead[major], NULL);
+ ide_add_setting(drive, "file_readahead", SETTING_RW, BLKFRAGET, BLKFRASET, TYPE_INTA, 0, INT_MAX, 1, 1024, &max_readahead[major][minor], NULL);
+ ide_add_setting(drive, "max_kb_per_request", SETTING_RW, BLKSECTGET, BLKSECTSET, TYPE_INTA, 1, 255, 1, 2, &max_sectors[major][minor], NULL);
+
+}
+
+/*
+ * Driver initialization.
+ */
+static void idefloppy_setup (ide_drive_t *drive, idefloppy_floppy_t *floppy)
+{
+ struct idefloppy_id_gcw gcw;
+ int major = HWIF(drive)->major, i;
+ int minor = drive->select.b.unit << PARTN_BITS;
+
+ *((unsigned short *) &gcw) = drive->id->config;
+ drive->driver_data = floppy;
+ drive->ready_stat = 0;
+ memset (floppy, 0, sizeof (idefloppy_floppy_t));
+ floppy->drive = drive;
+ floppy->pc = floppy->pc_stack;
+ if (gcw.drq_type == 1)
+ set_bit (IDEFLOPPY_DRQ_INTERRUPT, &floppy->flags);
+ /*
+ * We used to check revisions here. At this point however
+ * I'm giving up. Just assume they are all broken, its easier.
+ *
+ * The actual reason for the workarounds was likely
+ * a driver bug after all rather than a firmware bug,
+ * and the workaround below used to hide it. It should
+ * be fixed as of version 1.9, but to be on the safe side
+ * we'll leave the limitation below for the 2.2.x tree.
+ */
+
+ if (strcmp(drive->id->model, "IOMEGA ZIP 100 ATAPI") == 0)
+ {
+ for (i = 0; i < 1 << PARTN_BITS; i++)
+ max_sectors[major][minor + i] = 64;
+ }
+
+ (void) idefloppy_get_capacity (drive);
+ idefloppy_add_settings(drive);
+ for (i = 0; i < MAX_DRIVES; ++i) {
+ ide_hwif_t *hwif = HWIF(drive);
+
+ if (drive != &hwif->drives[i]) continue;
+ hwif->gd->de_arr[i] = drive->de;
+ if (drive->removable)
+ hwif->gd->flags[i] |= GENHD_FL_REMOVABLE;
+ break;
+ }
+}
+
+static int idefloppy_cleanup (ide_drive_t *drive)
+{
+ idefloppy_floppy_t *floppy = drive->driver_data;
+
+ if (ide_unregister_subdriver (drive))
+ return 1;
+ drive->driver_data = NULL;
+ kfree (floppy);
+ return 0;
+}
+
+#ifdef CONFIG_PROC_FS
+
+static ide_proc_entry_t idefloppy_proc[] = {
+ { "geometry", S_IFREG|S_IRUGO, proc_ide_read_geometry, NULL },
+ { NULL, 0, NULL, NULL }
+};
+
+#else
+
+#define idefloppy_proc NULL
+
+#endif /* CONFIG_PROC_FS */
+
+/*
+ * IDE subdriver functions, registered with ide.c
+ */
+static ide_driver_t idefloppy_driver = {
+ "ide-floppy", /* name */
+ IDEFLOPPY_VERSION, /* version */
+ ide_floppy, /* media */
+ 0, /* busy */
+ 1, /* supports_dma */
+ 0, /* supports_dsc_overlap */
+ idefloppy_cleanup, /* cleanup */
+ idefloppy_do_request, /* do_request */
+ idefloppy_end_request, /* end_request */
+ idefloppy_ioctl, /* ioctl */
+ idefloppy_open, /* open */
+ idefloppy_release, /* release */
+ idefloppy_media_change, /* media_change */
+ NULL, /* pre_reset */
+ idefloppy_capacity, /* capacity */
+ NULL, /* special */
+ idefloppy_proc /* proc */
+};
+
+int idefloppy_init (void);
+static ide_module_t idefloppy_module = {
+ IDE_DRIVER_MODULE,
+ idefloppy_init,
+ &idefloppy_driver,
+ NULL
+};
+
+/*
+ * idefloppy_init will register the driver for each floppy.
+ */
+int idefloppy_init (void)
+{
+ ide_drive_t *drive;
+ idefloppy_floppy_t *floppy;
+ int failed = 0;
+
+ MOD_INC_USE_COUNT;
+ while ((drive = ide_scan_devices (ide_floppy, idefloppy_driver.name, NULL, failed++)) != NULL) {
+ if (!idefloppy_identify_device (drive, drive->id)) {
+ printk (KERN_ERR "ide-floppy: %s: not supported by this version of ide-floppy\n", drive->name);
+ continue;
+ }
+ if (drive->scsi) {
+ printk("ide-floppy: passing drive %s to ide-scsi emulation.\n", drive->name);
+ continue;
+ }
+ if ((floppy = (idefloppy_floppy_t *) kmalloc (sizeof (idefloppy_floppy_t), GFP_KERNEL)) == NULL) {
+ printk (KERN_ERR "ide-floppy: %s: Can't allocate a floppy structure\n", drive->name);
+ continue;
+ }
+ if (ide_register_subdriver (drive, &idefloppy_driver, IDE_SUBDRIVER_VERSION)) {
+ printk (KERN_ERR "ide-floppy: %s: Failed to register the driver with ide.c\n", drive->name);
+ kfree (floppy);
+ continue;
+ }
+ idefloppy_setup (drive, floppy);
+ failed--;
+ }
+ ide_register_module(&idefloppy_module);
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+#ifdef MODULE
+int init_module (void)
+{
+ return idefloppy_init ();
+}
+
+void cleanup_module (void)
+{
+ ide_drive_t *drive;
+ int failed = 0;
+
+ while ((drive = ide_scan_devices (ide_floppy, idefloppy_driver.name, &idefloppy_driver, failed)) != NULL) {
+ if (idefloppy_cleanup (drive)) {
+ printk ("%s: cleanup_module() called while still busy\n", drive->name);
+ failed++;
+ }
+ /* We must remove proc entries defined in this module.
+ Otherwise we oops while accessing these entries */
+ if (drive->proc)
+ ide_remove_proc_entries(drive->proc, idefloppy_proc);
+ }
+ ide_unregister_module(&idefloppy_module);
+}
+#endif /* MODULE */
diff --git a/drivers/ide/ide-geometry.c b/drivers/ide/ide-geometry.c
new file mode 100644
index 000000000..6ebf20fe1
--- /dev/null
+++ b/drivers/ide/ide-geometry.c
@@ -0,0 +1,214 @@
+/*
+ * linux/drivers/block/ide-geometry.c
+ */
+#include <linux/config.h>
+
+#ifdef CONFIG_BLK_DEV_IDE
+#include <linux/ide.h>
+
+#include <asm/io.h>
+
+extern ide_drive_t * get_info_ptr(kdev_t);
+extern unsigned long current_capacity (ide_drive_t *);
+
+/*
+ * We query CMOS about hard disks : it could be that we have a SCSI/ESDI/etc
+ * controller that is BIOS compatible with ST-506, and thus showing up in our
+ * BIOS table, but not register compatible, and therefore not present in CMOS.
+ *
+ * Furthermore, we will assume that our ST-506 drives <if any> are the primary
+ * drives in the system -- the ones reflected as drive 1 or 2. The first
+ * drive is stored in the high nibble of CMOS byte 0x12, the second in the low
+ * nibble. This will be either a 4 bit drive type or 0xf indicating use byte
+ * 0x19 for an 8 bit type, drive 1, 0x1a for drive 2 in CMOS. A non-zero value
+ * means we have an AT controller hard disk for that drive.
+ *
+ * Of course, there is no guarantee that either drive is actually on the
+ * "primary" IDE interface, but we don't bother trying to sort that out here.
+ * If a drive is not actually on the primary interface, then these parameters
+ * will be ignored. This results in the user having to supply the logical
+ * drive geometry as a boot parameter for each drive not on the primary i/f.
+ */
+/*
+ * The only "perfect" way to handle this would be to modify the setup.[cS] code
+ * to do BIOS calls Int13h/Fn08h and Int13h/Fn48h to get all of the drive info
+ * for us during initialization. I have the necessary docs -- any takers? -ml
+ */
+/*
+ * I did this, but it doesnt work - there is no reasonable way to find the
+ * correspondence between the BIOS numbering of the disks and the Linux
+ * numbering. -aeb
+ *
+ * The code below is bad. One of the problems is that drives 1 and 2
+ * may be SCSI disks (even when IDE disks are present), so that
+ * the geometry we read here from BIOS is attributed to the wrong disks.
+ * Consequently, also the "drive->present = 1" below is a mistake.
+ *
+ * Eventually the entire routine below should be removed.
+ */
+void probe_cmos_for_drives (ide_hwif_t *hwif)
+{
+#ifdef __i386__
+ extern struct drive_info_struct drive_info;
+ byte cmos_disks, *BIOS = (byte *) &drive_info;
+ int unit;
+
+#ifdef CONFIG_BLK_DEV_PDC4030
+ if (hwif->chipset == ide_pdc4030 && hwif->channel != 0)
+ return;
+#endif /* CONFIG_BLK_DEV_PDC4030 */
+ outb_p(0x12,0x70); /* specify CMOS address 0x12 */
+ cmos_disks = inb_p(0x71); /* read the data from 0x12 */
+ /* Extract drive geometry from CMOS+BIOS if not already setup */
+ for (unit = 0; unit < MAX_DRIVES; ++unit) {
+ ide_drive_t *drive = &hwif->drives[unit];
+
+ if ((cmos_disks & (0xf0 >> (unit*4)))
+ && !drive->present && !drive->nobios) {
+ unsigned short cyl = *(unsigned short *)BIOS;
+ unsigned char head = *(BIOS+2);
+ unsigned char sect = *(BIOS+14);
+ if (cyl > 0 && head > 0 && sect > 0 && sect < 64) {
+ drive->cyl = drive->bios_cyl = cyl;
+ drive->head = drive->bios_head = head;
+ drive->sect = drive->bios_sect = sect;
+ drive->ctl = *(BIOS+8);
+ } else {
+ printk("hd%d: C/H/S=%d/%d/%d from BIOS ignored\n", unit, cyl, head, sect);
+ }
+ }
+
+ BIOS += 16;
+ }
+#endif
+}
+
+
+/*
+ * If heads is nonzero: find a translation with this many heads and S=63.
+ * Otherwise: find out how OnTrack Disk Manager would translate the disk.
+ */
+static void
+ontrack(ide_drive_t *drive, int heads, unsigned int *c, int *h, int *s) {
+ static const byte dm_head_vals[] = {4, 8, 16, 32, 64, 128, 255, 0};
+ const byte *headp = dm_head_vals;
+ unsigned long total;
+
+ /*
+ * The specs say: take geometry as obtained from Identify,
+ * compute total capacity C*H*S from that, and truncate to
+ * 1024*255*63. Now take S=63, H the first in the sequence
+ * 4, 8, 16, 32, 64, 128, 255 such that 63*H*1024 >= total.
+ * [Please tell aeb@cwi.nl in case this computes a
+ * geometry different from what OnTrack uses.]
+ */
+ total = DRIVER(drive)->capacity(drive);
+
+ *s = 63;
+
+ if (heads) {
+ *h = heads;
+ *c = total / (63 * heads);
+ return;
+ }
+
+ while (63 * headp[0] * 1024 < total && headp[1] != 0)
+ headp++;
+ *h = headp[0];
+ *c = total / (63 * headp[0]);
+}
+
+/*
+ * This routine is called from the partition-table code in pt/msdos.c.
+ * It has two tasks:
+ * (i) to handle Ontrack DiskManager by offsetting everything by 63 sectors,
+ * or to handle EZdrive by remapping sector 0 to sector 1.
+ * (ii) to invent a translated geometry.
+ * Part (i) is suppressed if the user specifies the "noremap" option
+ * on the command line.
+ * Part (ii) is suppressed if the user specifies an explicit geometry.
+ *
+ * The ptheads parameter is either 0 or tells about the number of
+ * heads shown by the end of the first nonempty partition.
+ * If this is either 16, 32, 64, 128, 240 or 255 we'll believe it.
+ *
+ * The xparm parameter has the following meaning:
+ * 0 = convert to CHS with fewer than 1024 cyls
+ * using the same method as Ontrack DiskManager.
+ * 1 = same as "0", plus offset everything by 63 sectors.
+ * -1 = similar to "0", plus redirect sector 0 to sector 1.
+ * 2 = convert to a CHS geometry with "ptheads" heads.
+ *
+ * Returns 0 if the translation was not possible, if the device was not
+ * an IDE disk drive, or if a geometry was "forced" on the commandline.
+ * Returns 1 if the geometry translation was successful.
+ */
+int ide_xlate_1024 (kdev_t i_rdev, int xparm, int ptheads, const char *msg)
+{
+ ide_drive_t *drive;
+ const char *msg1 = "";
+ int heads = 0;
+ int c, h, s;
+ int transl = 1; /* try translation */
+ int ret = 0;
+
+ drive = get_info_ptr(i_rdev);
+ if (!drive)
+ return 0;
+
+ /* remap? */
+ if (drive->remap_0_to_1 != 2) {
+ if (xparm == 1) { /* DM */
+ drive->sect0 = 63;
+ msg1 = " [remap +63]";
+ ret = 1;
+ } else if (xparm == -1) { /* EZ-Drive */
+ if (drive->remap_0_to_1 == 0) {
+ drive->remap_0_to_1 = 1;
+ msg1 = " [remap 0->1]";
+ ret = 1;
+ }
+ }
+ }
+
+ /* There used to be code here that assigned drive->id->CHS
+ to drive->CHS and that to drive->bios_CHS. However,
+ some disks have id->C/H/S = 4092/16/63 but are larger than 2.1 GB.
+ In such cases that code was wrong. Moreover,
+ there seems to be no reason to do any of these things. */
+
+ /* translate? */
+ if (drive->forced_geom)
+ transl = 0;
+
+ /* does ptheads look reasonable? */
+ if (ptheads == 32 || ptheads == 64 || ptheads == 128 ||
+ ptheads == 240 || ptheads == 255)
+ heads = ptheads;
+
+ if (xparm == 2) {
+ if (!heads ||
+ (drive->bios_head >= heads && drive->bios_sect == 63))
+ transl = 0;
+ }
+ if (xparm == -1) {
+ if (drive->bios_head > 16)
+ transl = 0; /* we already have a translation */
+ }
+
+ if (transl) {
+ ontrack(drive, heads, &c, &h, &s);
+ drive->bios_cyl = c;
+ drive->bios_head = h;
+ drive->bios_sect = s;
+ ret = 1;
+ }
+
+ drive->part[0].nr_sects = current_capacity(drive);
+
+ if (ret)
+ printk("%s%s [%d/%d/%d]", msg, msg1,
+ drive->bios_cyl, drive->bios_head, drive->bios_sect);
+ return ret;
+}
+#endif /* CONFIG_BLK_DEV_IDE */
diff --git a/drivers/ide/ide-pci.c b/drivers/ide/ide-pci.c
new file mode 100644
index 000000000..f667ee6dd
--- /dev/null
+++ b/drivers/ide/ide-pci.c
@@ -0,0 +1,728 @@
+/*
+ * linux/drivers/block/ide-pci.c Version 1.04 July 27, 1999
+ *
+ * Copyright (c) 1998-1999 Andre Hedrick
+ *
+ * Copyright (c) 1995-1998 Mark Lord
+ * May be copied or modified under the terms of the GNU General Public License
+ */
+
+/*
+ * This module provides support for automatic detection and
+ * configuration of all PCI IDE interfaces present in a system.
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#define DEVID_PIIXa ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371FB_0})
+#define DEVID_PIIXb ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371FB_1})
+#define DEVID_PIIX3 ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_1})
+#define DEVID_PIIX4 ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB})
+#define DEVID_PIIX4E ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AB_1})
+#define DEVID_PIIX4U ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_1})
+#define DEVID_PIIX4U2 ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82372FB_1})
+#define DEVID_VIA_IDE ((ide_pci_devid_t){PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C561})
+#define DEVID_VP_IDE ((ide_pci_devid_t){PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_1})
+#define DEVID_PDC20246 ((ide_pci_devid_t){PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20246})
+#define DEVID_PDC20262 ((ide_pci_devid_t){PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20262})
+#define DEVID_RZ1000 ((ide_pci_devid_t){PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1000})
+#define DEVID_RZ1001 ((ide_pci_devid_t){PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1001})
+#define DEVID_SAMURAI ((ide_pci_devid_t){PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_SAMURAI_IDE})
+#define DEVID_CMD640 ((ide_pci_devid_t){PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_640})
+#define DEVID_CMD643 ((ide_pci_devid_t){PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_643})
+#define DEVID_CMD646 ((ide_pci_devid_t){PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_646})
+#define DEVID_CMD648 ((ide_pci_devid_t){PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_648})
+#define DEVID_SIS5513 ((ide_pci_devid_t){PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_5513})
+#define DEVID_OPTI621 ((ide_pci_devid_t){PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C621})
+#define DEVID_OPTI621V ((ide_pci_devid_t){PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C558})
+#define DEVID_OPTI621X ((ide_pci_devid_t){PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C825})
+#define DEVID_TRM290 ((ide_pci_devid_t){PCI_VENDOR_ID_TEKRAM, PCI_DEVICE_ID_TEKRAM_DC290})
+#define DEVID_NS87410 ((ide_pci_devid_t){PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87410})
+#define DEVID_NS87415 ((ide_pci_devid_t){PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87415})
+#define DEVID_HT6565 ((ide_pci_devid_t){PCI_VENDOR_ID_HOLTEK, PCI_DEVICE_ID_HOLTEK_6565})
+#define DEVID_AEC6210 ((ide_pci_devid_t){PCI_VENDOR_ID_ARTOP, PCI_DEVICE_ID_ARTOP_ATP850UF})
+#define DEVID_W82C105 ((ide_pci_devid_t){PCI_VENDOR_ID_WINBOND, PCI_DEVICE_ID_WINBOND_82C105})
+#define DEVID_UM8673F ((ide_pci_devid_t){PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8673F})
+#define DEVID_UM8886A ((ide_pci_devid_t){PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8886A})
+#define DEVID_UM8886BF ((ide_pci_devid_t){PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8886BF})
+#define DEVID_HPT34X ((ide_pci_devid_t){PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT343})
+#define DEVID_HPT366 ((ide_pci_devid_t){PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT366})
+#define DEVID_ALI15X3 ((ide_pci_devid_t){PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M5229})
+#define DEVID_CY82C693 ((ide_pci_devid_t){PCI_VENDOR_ID_CONTAQ, PCI_DEVICE_ID_CONTAQ_82C693})
+#define DEVID_HINT ((ide_pci_devid_t){0x3388, 0x8013})
+#define DEVID_CS5530 ((ide_pci_devid_t){PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_IDE})
+#define DEVID_AMD7409 ((ide_pci_devid_t){PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7409})
+
+#define IDE_IGNORE ((void *)-1)
+
+#ifdef CONFIG_BLK_DEV_AEC6210
+extern unsigned int pci_init_aec6210(struct pci_dev *, const char *);
+extern void ide_init_aec6210(ide_hwif_t *);
+extern void ide_dmacapable_aec6210(ide_hwif_t *, unsigned long);
+#define PCI_AEC6210 &pci_init_aec6210
+#define INIT_AEC6210 &ide_init_aec6210
+#define DMA_AEC6210 &ide_dmacapable_aec6210
+#else
+#define PCI_AEC6210 NULL
+#define INIT_AEC6210 NULL
+#define DMA_AEC6210 NULL
+#endif
+
+#ifdef CONFIG_BLK_DEV_ALI15X3
+extern unsigned int pci_init_ali15x3(struct pci_dev *, const char *);
+extern unsigned int ata66_ali15x3(ide_hwif_t *);
+extern void ide_init_ali15x3(ide_hwif_t *);
+extern void ide_dmacapable_ali15x3(ide_hwif_t *, unsigned long);
+#define PCI_ALI15X3 &pci_init_ali15x3
+#define ATA66_ALI15X3 &ata66_ali15x3
+#define INIT_ALI15X3 &ide_init_ali15x3
+#define DMA_ALI15X3 &ide_dmacapable_ali15x3
+#else
+#define PCI_ALI15X3 NULL
+#define ATA66_ALI15X3 NULL
+#define INIT_ALI15X3 NULL
+#define DMA_ALI15X3 NULL
+#endif
+
+#ifdef CONFIG_BLK_DEV_AMD7409
+extern unsigned int pci_init_amd7409(struct pci_dev *, const char *);
+extern unsigned int ata66_amd7409(ide_hwif_t *);
+extern void ide_init_amd7409(ide_hwif_t *);
+extern void ide_dmacapable_amd7409(ide_hwif_t *, unsigned long);
+#define PCI_AMD7409 &pci_init_amd7409
+#define ATA66_AMD7409 &ata66_amd7409
+#define INIT_AMD7409 &ide_init_amd7409
+#define DMA_AMD7409 &ide_dmacapable_amd7409
+#else
+#define PCI_AMD7409 NULL
+#define ATA66_AMD7409 NULL
+#define INIT_AMD7409 NULL
+#define DMA_AMD7409 NULL
+#endif
+
+#ifdef CONFIG_BLK_DEV_CMD64X
+extern unsigned int pci_init_cmd64x(struct pci_dev *, const char *);
+extern unsigned int ata66_cmd64x(ide_hwif_t *);
+extern void ide_init_cmd64x(ide_hwif_t *);
+extern void ide_dmacapable_cmd64x(ide_hwif_t *, unsigned long);
+#define PCI_CMD64X &pci_init_cmd64x
+#define ATA66_CMD64X &ata66_cmd64x
+#define INIT_CMD64X &ide_init_cmd64x
+#else
+#define PCI_CMD64X NULL
+#define ATA66_CMD64X NULL
+#ifdef __sparc_v9__
+#define INIT_CMD64X IDE_IGNORE
+#else
+#define INIT_CMD64X NULL
+#endif
+#endif
+
+#ifdef CONFIG_BLK_DEV_CY82C693
+extern unsigned int pci_init_cy82c693(struct pci_dev *, const char *);
+extern void ide_init_cy82c693(ide_hwif_t *);
+#define PCI_CY82C693 &pci_init_cy82c693
+#define INIT_CY82C693 &ide_init_cy82c693
+#else
+#define PCI_CY82C693 NULL
+#define INIT_CY82C693 NULL
+#endif
+
+#ifdef CONFIG_BLK_DEV_CS5530
+extern unsigned int pci_init_cs5530(struct pci_dev *, const char *);
+extern void ide_init_cs5530(ide_hwif_t *);
+#define PCI_CS5530 &pci_init_cs5530
+#define INIT_CS5530 &ide_init_cs5530
+#else
+#define PCI_CS5530 NULL
+#define INIT_CS5530 NULL
+#endif
+
+#ifdef CONFIG_BLK_DEV_HPT34X
+extern unsigned int pci_init_hpt34x(struct pci_dev *, const char *);
+extern void ide_init_hpt34x(ide_hwif_t *);
+#define PCI_HPT34X &pci_init_hpt34x
+#define INIT_HPT34X &ide_init_hpt34x
+#else
+#define PCI_HPT34X NULL
+#define INIT_HPT34X IDE_IGNORE
+#endif
+
+#ifdef CONFIG_BLK_DEV_HPT366
+extern byte hpt363_shared_irq;
+extern byte hpt363_shared_pin;
+extern unsigned int pci_init_hpt366(struct pci_dev *, const char *);
+extern unsigned int ata66_hpt366(ide_hwif_t *);
+extern void ide_init_hpt366(ide_hwif_t *);
+extern void ide_dmacapable_hpt366(ide_hwif_t *, unsigned long);
+#define PCI_HPT366 &pci_init_hpt366
+#define ATA66_HPT366 &ata66_hpt366
+#define INIT_HPT366 &ide_init_hpt366
+#define DMA_HPT366 &ide_dmacapable_hpt366
+#else
+static byte hpt363_shared_irq = 0;
+static byte hpt363_shared_pin = 0;
+#define PCI_HPT366 NULL
+#define ATA66_HPT366 NULL
+#define INIT_HPT366 NULL
+#define DMA_HPT366 NULL
+#endif
+
+#ifdef CONFIG_BLK_DEV_NS87415
+extern void ide_init_ns87415(ide_hwif_t *);
+#define INIT_NS87415 &ide_init_ns87415
+#else
+#define INIT_NS87415 IDE_IGNORE
+#endif
+
+#ifdef CONFIG_BLK_DEV_OPTI621
+extern void ide_init_opti621(ide_hwif_t *);
+#define INIT_OPTI621 &ide_init_opti621
+#else
+#define INIT_OPTI621 NULL
+#endif
+
+#ifdef CONFIG_BLK_DEV_PDC202XX
+extern unsigned int pci_init_pdc202xx(struct pci_dev *, const char *);
+extern unsigned int ata66_pdc202xx(ide_hwif_t *);
+extern void ide_init_pdc202xx(ide_hwif_t *);
+#define PCI_PDC202XX &pci_init_pdc202xx
+#define ATA66_PDC202XX &ata66_pdc202xx
+#define INIT_PDC202XX &ide_init_pdc202xx
+#else
+#define PCI_PDC202XX NULL
+#define ATA66_PDC202XX NULL
+#define INIT_PDC202XX NULL
+#endif
+
+#ifdef CONFIG_BLK_DEV_PIIX
+extern unsigned int pci_init_piix(struct pci_dev *, const char *);
+extern unsigned int ata66_piix(ide_hwif_t *);
+extern void ide_init_piix(ide_hwif_t *);
+#define PCI_PIIX &pci_init_piix
+#define ATA66_PIIX &ata66_piix
+#define INIT_PIIX &ide_init_piix
+#else
+#define PCI_PIIX NULL
+#define ATA66_PIIX NULL
+#define INIT_PIIX NULL
+#endif
+
+#ifdef CONFIG_BLK_DEV_RZ1000
+extern void ide_init_rz1000(ide_hwif_t *);
+#define INIT_RZ1000 &ide_init_rz1000
+#else
+#define INIT_RZ1000 IDE_IGNORE
+#endif
+
+#define INIT_SAMURAI NULL
+
+#ifdef CONFIG_BLK_DEV_SIS5513
+extern unsigned int pci_init_sis5513(struct pci_dev *, const char *);
+extern unsigned int ata66_sis5513(ide_hwif_t *);
+extern void ide_init_sis5513(ide_hwif_t *);
+#define PCI_SIS5513 &pci_init_sis5513
+#define ATA66_SIS5513 &ata66_sis5513
+#define INIT_SIS5513 &ide_init_sis5513
+#else
+#define PCI_SIS5513 NULL
+#define ATA66_SIS5513 NULL
+#define INIT_SIS5513 NULL
+#endif
+
+#ifdef CONFIG_BLK_DEV_SL82C105
+extern void ide_init_sl82c105(ide_hwif_t *);
+extern void ide_dmacapable_sl82c105(ide_hwif_t *, unsigned long);
+#define INIT_W82C105 &ide_init_sl82c105
+#define DMA_W82C105 &ide_dmacapable_sl82c105
+#else
+#define INIT_W82C105 IDE_IGNORE
+#define DMA_W82C105 NULL
+#endif
+
+#ifdef CONFIG_BLK_DEV_TRM290
+extern void ide_init_trm290(ide_hwif_t *);
+#define INIT_TRM290 &ide_init_trm290
+#else
+#define INIT_TRM290 IDE_IGNORE
+#endif
+
+#ifdef CONFIG_BLK_DEV_VIA82CXXX
+extern unsigned int pci_init_via82cxxx(struct pci_dev *, const char *);
+extern unsigned int ata66_via82cxxx(ide_hwif_t *);
+extern void ide_init_via82cxxx(ide_hwif_t *);
+extern void ide_dmacapable_via82cxxx(ide_hwif_t *, unsigned long);
+#define PCI_VIA82CXXX &pci_init_via82cxxx
+#define ATA66_VIA82CXXX &ata66_via82cxxx
+#define INIT_VIA82CXXX &ide_init_via82cxxx
+#define DMA_VIA82CXXX &ide_dmacapable_via82cxxx
+#else
+#define PCI_VIA82CXXX NULL
+#define ATA66_VIA82CXXX NULL
+#define INIT_VIA82CXXX NULL
+#define DMA_VIA82CXXX NULL
+#endif
+
+typedef struct ide_pci_enablebit_s {
+ byte reg; /* byte pci reg holding the enable-bit */
+ byte mask; /* mask to isolate the enable-bit */
+ byte val; /* value of masked reg when "enabled" */
+} ide_pci_enablebit_t;
+
+typedef struct ide_pci_device_s {
+ ide_pci_devid_t devid;
+ const char *name;
+ unsigned int (*init_chipset)(struct pci_dev *dev, const char *name);
+ unsigned int (*ata66_check)(ide_hwif_t *hwif);
+ void (*init_hwif)(ide_hwif_t *hwif);
+ void (*dma_init)(ide_hwif_t *hwif, unsigned long dmabase);
+ ide_pci_enablebit_t enablebits[2];
+ byte bootable;
+ unsigned int extra;
+} ide_pci_device_t;
+
+static ide_pci_device_t ide_pci_chipsets[] __initdata = {
+ {DEVID_PIIXa, "PIIX", NULL, NULL, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0 },
+ {DEVID_PIIXb, "PIIX", NULL, NULL, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0 },
+ {DEVID_PIIX3, "PIIX3", PCI_PIIX, NULL, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0 },
+ {DEVID_PIIX4, "PIIX4", PCI_PIIX, NULL, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0 },
+ {DEVID_PIIX4E, "PIIX4", PCI_PIIX, NULL, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0 },
+ {DEVID_PIIX4U, "PIIX4", PCI_PIIX, ATA66_PIIX, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0 },
+ {DEVID_PIIX4U2, "PIIX4", PCI_PIIX, ATA66_PIIX, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0 },
+ {DEVID_VIA_IDE, "VIA_IDE", NULL, NULL, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 },
+ {DEVID_VP_IDE, "VP_IDE", PCI_VIA82CXXX, ATA66_VIA82CXXX,INIT_VIA82CXXX, DMA_VIA82CXXX, {{0x40,0x02,0x02}, {0x40,0x01,0x01}}, ON_BOARD, 0 },
+ {DEVID_PDC20246,"PDC20246", PCI_PDC202XX, NULL, INIT_PDC202XX, NULL, {{0x50,0x02,0x02}, {0x50,0x04,0x04}}, OFF_BOARD, 16 },
+ {DEVID_PDC20262,"PDC20262", PCI_PDC202XX, ATA66_PDC202XX, INIT_PDC202XX, NULL, {{0x50,0x02,0x02}, {0x50,0x04,0x04}}, OFF_BOARD, 48 },
+ {DEVID_RZ1000, "RZ1000", NULL, NULL, INIT_RZ1000, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 },
+ {DEVID_RZ1001, "RZ1001", NULL, NULL, INIT_RZ1000, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 },
+ {DEVID_SAMURAI, "SAMURAI", NULL, NULL, INIT_SAMURAI, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 },
+ {DEVID_CMD640, "CMD640", NULL, NULL, IDE_IGNORE, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 },
+ {DEVID_NS87410, "NS87410", NULL, NULL, NULL, NULL, {{0x43,0x08,0x08}, {0x47,0x08,0x08}}, ON_BOARD, 0 },
+ {DEVID_SIS5513, "SIS5513", PCI_SIS5513, ATA66_SIS5513, INIT_SIS5513, NULL, {{0x4a,0x02,0x02}, {0x4a,0x04,0x04}}, ON_BOARD, 0 },
+ {DEVID_CMD643, "CMD643", PCI_CMD64X, NULL, INIT_CMD64X, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 },
+ {DEVID_CMD646, "CMD646", PCI_CMD64X, NULL, INIT_CMD64X, NULL, {{0x00,0x00,0x00}, {0x51,0x80,0x80}}, ON_BOARD, 0 },
+ {DEVID_CMD648, "CMD648", PCI_CMD64X, ATA66_CMD64X, INIT_CMD64X, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 },
+ {DEVID_HT6565, "HT6565", NULL, NULL, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 },
+ {DEVID_OPTI621, "OPTI621", NULL, NULL, INIT_OPTI621, NULL, {{0x45,0x80,0x00}, {0x40,0x08,0x00}}, ON_BOARD, 0 },
+ {DEVID_OPTI621X,"OPTI621X", NULL, NULL, INIT_OPTI621, NULL, {{0x45,0x80,0x00}, {0x40,0x08,0x00}}, ON_BOARD, 0 },
+ {DEVID_TRM290, "TRM290", NULL, NULL, INIT_TRM290, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 },
+ {DEVID_NS87415, "NS87415", NULL, NULL, INIT_NS87415, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 },
+ {DEVID_AEC6210, "AEC6210", PCI_AEC6210, NULL, INIT_AEC6210, DMA_AEC6210, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, OFF_BOARD, 0 },
+ {DEVID_W82C105, "W82C105", NULL, NULL, INIT_W82C105, DMA_W82C105, {{0x40,0x01,0x01}, {0x40,0x10,0x10}}, ON_BOARD, 0 },
+ {DEVID_UM8673F, "UM8673F", NULL, NULL, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 },
+ {DEVID_UM8886A, "UM8886A", NULL, NULL, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 },
+ {DEVID_UM8886BF,"UM8886BF", NULL, NULL, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 },
+ {DEVID_HPT34X, "HPT34X", PCI_HPT34X, NULL, INIT_HPT34X, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, NEVER_BOARD, 16 },
+ {DEVID_HPT366, "HPT366", PCI_HPT366, ATA66_HPT366, INIT_HPT366, DMA_HPT366, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, OFF_BOARD, 240 },
+ {DEVID_ALI15X3, "ALI15X3", PCI_ALI15X3, ATA66_ALI15X3, INIT_ALI15X3, DMA_ALI15X3, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 },
+ {DEVID_CY82C693,"CY82C693", PCI_CY82C693, NULL, INIT_CY82C693, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 },
+ {DEVID_HINT, "HINT_IDE", NULL, NULL, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 },
+ {DEVID_CS5530, "CS5530", PCI_CS5530, NULL, INIT_CS5530, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 },
+ {DEVID_AMD7409, "AMD7409", PCI_AMD7409, ATA66_AMD7409, INIT_AMD7409, DMA_AMD7409, {{0x40,0x01,0x01}, {0x40,0x02,0x02}}, ON_BOARD, 0 },
+ {IDE_PCI_DEVID_NULL, "PCI_IDE", NULL, NULL, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }};
+
+/*
+ * This allows offboard ide-pci cards the enable a BIOS, verify interrupt
+ * settings of split-mirror pci-config space, place chipset into init-mode,
+ * and/or preserve an interrupt if the card is not native ide support.
+ */
+static unsigned int __init ide_special_settings (struct pci_dev *dev, const char *name)
+{
+ switch(dev->device) {
+ case PCI_DEVICE_ID_TTI_HPT366:
+ case PCI_DEVICE_ID_PROMISE_20246:
+ case PCI_DEVICE_ID_PROMISE_20262:
+ case PCI_DEVICE_ID_ARTOP_ATP850UF:
+ return dev->irq;
+ default:
+ break;
+ }
+ return 0;
+}
+
+/*
+ * Match a PCI IDE port against an entry in ide_hwifs[],
+ * based on io_base port if possible.
+ */
+static ide_hwif_t __init *ide_match_hwif (unsigned long io_base, byte bootable, const char *name)
+{
+ int h;
+ ide_hwif_t *hwif;
+
+ /*
+ * Look for a hwif with matching io_base specified using
+ * parameters to ide_setup().
+ */
+ for (h = 0; h < MAX_HWIFS; ++h) {
+ hwif = &ide_hwifs[h];
+ if (hwif->io_ports[IDE_DATA_OFFSET] == io_base) {
+ if (hwif->chipset == ide_generic)
+ return hwif; /* a perfect match */
+ }
+ }
+ /*
+ * Look for a hwif with matching io_base default value.
+ * If chipset is "ide_unknown", then claim that hwif slot.
+ * Otherwise, some other chipset has already claimed it.. :(
+ */
+ for (h = 0; h < MAX_HWIFS; ++h) {
+ hwif = &ide_hwifs[h];
+ if (hwif->io_ports[IDE_DATA_OFFSET] == io_base) {
+ if (hwif->chipset == ide_unknown)
+ return hwif; /* match */
+ printk("%s: port 0x%04lx already claimed by %s\n", name, io_base, hwif->name);
+ return NULL; /* already claimed */
+ }
+ }
+ /*
+ * Okay, there is no hwif matching our io_base,
+ * so we'll just claim an unassigned slot.
+ * Give preference to claiming other slots before claiming ide0/ide1,
+ * just in case there's another interface yet-to-be-scanned
+ * which uses ports 1f0/170 (the ide0/ide1 defaults).
+ *
+ * Unless there is a bootable card that does not use the standard
+ * ports 1f0/170 (the ide0/ide1 defaults). The (bootable) flag.
+ */
+ if (bootable) {
+ for (h = 0; h < MAX_HWIFS; ++h) {
+ hwif = &ide_hwifs[h];
+ if (hwif->chipset == ide_unknown)
+ return hwif; /* pick an unused entry */
+ }
+ } else {
+ for (h = 2; h < MAX_HWIFS; ++h) {
+ hwif = ide_hwifs + h;
+ if (hwif->chipset == ide_unknown)
+ return hwif; /* pick an unused entry */
+ }
+ }
+ for (h = 0; h < 2; ++h) {
+ hwif = ide_hwifs + h;
+ if (hwif->chipset == ide_unknown)
+ return hwif; /* pick an unused entry */
+ }
+ printk("%s: too many IDE interfaces, no room in table\n", name);
+ return NULL;
+}
+
+static int __init ide_setup_pci_baseregs (struct pci_dev *dev, const char *name)
+{
+ byte reg, progif = 0;
+
+ /*
+ * Place both IDE interfaces into PCI "native" mode:
+ */
+ if (pci_read_config_byte(dev, PCI_CLASS_PROG, &progif) || (progif & 5) != 5) {
+ if ((progif & 0xa) != 0xa) {
+ printk("%s: device not capable of full native PCI mode\n", name);
+ return 1;
+ }
+ printk("%s: placing both ports into native PCI mode\n", name);
+ (void) pci_write_config_byte(dev, PCI_CLASS_PROG, progif|5);
+ if (pci_read_config_byte(dev, PCI_CLASS_PROG, &progif) || (progif & 5) != 5) {
+ printk("%s: rewrite of PROGIF failed, wanted 0x%04x, got 0x%04x\n", name, progif|5, progif);
+ return 1;
+ }
+ }
+ /*
+ * Setup base registers for IDE command/control spaces for each interface:
+ */
+ for (reg = 0; reg < 4; reg++) {
+ struct resource *res = dev->resource + reg;
+ if (!(res->flags & PCI_BASE_ADDRESS_SPACE_IO))
+ continue;
+ if (!res->start) {
+ printk("%s: Missing I/O address #%d\n", name, reg);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * ide_setup_pci_device() looks at the primary/secondary interfaces
+ * on a PCI IDE device and, if they are enabled, prepares the IDE driver
+ * for use with them. This generic code works for most PCI chipsets.
+ *
+ * One thing that is not standardized is the location of the
+ * primary/secondary interface "enable/disable" bits. For chipsets that
+ * we "know" about, this information is in the ide_pci_device_t struct;
+ * for all other chipsets, we just assume both interfaces are enabled.
+ */
+static void __init ide_setup_pci_device (struct pci_dev *dev, ide_pci_device_t *d)
+{
+ unsigned int port, at_least_one_hwif_enabled = 0, autodma = 0, pciirq = 0;
+ unsigned short pcicmd = 0, tried_config = 0;
+ byte tmp = 0;
+ ide_hwif_t *hwif, *mate = NULL;
+
+#ifdef CONFIG_IDEDMA_AUTO
+ autodma = 1;
+#endif
+check_if_enabled:
+ if (pci_read_config_word(dev, PCI_COMMAND, &pcicmd)) {
+ printk("%s: error accessing PCI regs\n", d->name);
+ return;
+ }
+ if (!(pcicmd & PCI_COMMAND_IO)) { /* is device disabled? */
+ /*
+ * PnP BIOS was *supposed* to have set this device up for us,
+ * but we can do it ourselves, so long as the BIOS has assigned an IRQ
+ * (or possibly the device is using a "legacy header" for IRQs).
+ * Maybe the user deliberately *disabled* the device,
+ * but we'll eventually ignore it again if no drives respond.
+ */
+ if (tried_config++
+ || ide_setup_pci_baseregs(dev, d->name)
+ || pci_write_config_word(dev, PCI_COMMAND, pcicmd | PCI_COMMAND_IO)) {
+ printk("%s: device disabled (BIOS)\n", d->name);
+ return;
+ }
+ autodma = 0; /* default DMA off if we had to configure it here */
+ goto check_if_enabled;
+ }
+ if (tried_config)
+ printk("%s: device enabled (Linux)\n", d->name);
+ /*
+ * Can we trust the reported IRQ?
+ */
+ pciirq = dev->irq;
+ if ((dev->class & ~(0xfa)) != ((PCI_CLASS_STORAGE_IDE << 8) | 5)) {
+ printk("%s: not 100%% native mode: will probe irqs later\n", d->name);
+ /*
+ * This allows offboard ide-pci cards the enable a BIOS,
+ * verify interrupt settings of split-mirror pci-config
+ * space, place chipset into init-mode, and/or preserve
+ * an interrupt if the card is not native ide support.
+ */
+ pciirq = (d->init_chipset) ? d->init_chipset(dev, d->name) : ide_special_settings(dev, d->name);
+ } else if (tried_config) {
+ printk("%s: will probe irqs later\n", d->name);
+ pciirq = 0;
+ } else if (!pciirq) {
+ printk("%s: bad irq (%d): will probe later\n", d->name, pciirq);
+ pciirq = 0;
+ } else {
+ if (d->init_chipset)
+ (void) d->init_chipset(dev, d->name);
+#ifdef __sparc__
+ printk("%s: 100%% native mode on irq %s\n",
+ d->name, __irq_itoa(pciirq));
+#else
+ printk("%s: 100%% native mode on irq %d\n", d->name, pciirq);
+#endif
+ }
+ if (IDE_PCI_DEVID_EQ(d->devid, DEVID_HPT34X)) {
+ /* see comments in hpt34x.c on why..... */
+ d->bootable = (pcicmd & PCI_COMMAND_MEMORY) ? OFF_BOARD : NEVER_BOARD;
+ }
+ /*
+ * Set up the IDE ports
+ */
+ for (port = 0; port <= 1; ++port) {
+ unsigned long base = 0, ctl = 0;
+ ide_pci_enablebit_t *e = &(d->enablebits[port]);
+ if (e->reg && (pci_read_config_byte(dev, e->reg, &tmp) || (tmp & e->mask) != e->val))
+ continue; /* port not enabled */
+ if (IDE_PCI_DEVID_EQ(d->devid, DEVID_HPT366) && (port))
+ return;
+ if ((dev->class >> 8) != PCI_CLASS_STORAGE_IDE || (dev->class & (port ? 4 : 1)) != 0) {
+ ctl = dev->resource[(2*port)+1].start;
+ base = dev->resource[2*port].start;
+ if (!(ctl & PCI_BASE_ADDRESS_IO_MASK) ||
+ !(base & PCI_BASE_ADDRESS_IO_MASK)) {
+ printk("%s: IO baseregs (BIOS) are reported as MEM, report to <andre@suse.com>.\n", d->name);
+#if 0
+ /* FIXME! This really should check that it really gets the IO/MEM part right! */
+ continue;
+#endif
+ }
+ }
+ if ((ctl && !base) || (base && !ctl)) {
+ printk("%s: inconsistent baseregs (BIOS) for port %d, skipping\n", d->name, port);
+ continue;
+ }
+ if (!ctl)
+ ctl = port ? 0x374 : 0x3f4; /* use default value */
+ if (!base)
+ base = port ? 0x170 : 0x1f0; /* use default value */
+ if ((hwif = ide_match_hwif(base, d->bootable, d->name)) == NULL)
+ continue; /* no room in ide_hwifs[] */
+ if (hwif->io_ports[IDE_DATA_OFFSET] != base) {
+ ide_init_hwif_ports(&hwif->hw, base, (ctl | 2), NULL);
+ memcpy(hwif->io_ports, hwif->hw.io_ports, sizeof(hwif->io_ports));
+ hwif->noprobe = !hwif->io_ports[IDE_DATA_OFFSET];
+ }
+ hwif->chipset = ide_pci;
+ hwif->pci_dev = dev;
+ hwif->pci_devid = d->devid;
+ hwif->channel = port;
+ if (!hwif->irq)
+ hwif->irq = pciirq;
+ if (mate) {
+ hwif->mate = mate;
+ mate->mate = hwif;
+ if (IDE_PCI_DEVID_EQ(d->devid, DEVID_AEC6210)) {
+ hwif->serialized = 1;
+ mate->serialized = 1;
+ }
+ }
+ if (IDE_PCI_DEVID_EQ(d->devid, DEVID_UM8886A) ||
+ IDE_PCI_DEVID_EQ(d->devid, DEVID_UM8886BF) ||
+ IDE_PCI_DEVID_EQ(d->devid, DEVID_UM8673F)) {
+ hwif->irq = hwif->channel ? 15 : 14;
+ goto bypass_umc_dma;
+ }
+ if (hwif->udma_four) {
+ printk("%s: ATA-66 forced bit set (WARNING)!!\n", d->name);
+ } else {
+ hwif->udma_four = (d->ata66_check) ? d->ata66_check(hwif) : 0;
+ }
+#ifdef CONFIG_BLK_DEV_IDEDMA
+ if (IDE_PCI_DEVID_EQ(d->devid, DEVID_SIS5513) ||
+ IDE_PCI_DEVID_EQ(d->devid, DEVID_HPT34X))
+ autodma = 0;
+ if (autodma)
+ hwif->autodma = 1;
+ if (IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20246) ||
+ IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20262) ||
+ IDE_PCI_DEVID_EQ(d->devid, DEVID_AEC6210) ||
+ IDE_PCI_DEVID_EQ(d->devid, DEVID_HPT34X) ||
+ IDE_PCI_DEVID_EQ(d->devid, DEVID_HPT366) ||
+ IDE_PCI_DEVID_EQ(d->devid, DEVID_CS5530) ||
+ IDE_PCI_DEVID_EQ(d->devid, DEVID_CY82C693) ||
+ ((dev->class >> 8) == PCI_CLASS_STORAGE_IDE && (dev->class & 0x80))) {
+ unsigned long dma_base = ide_get_or_set_dma_base(hwif, (!mate && d->extra) ? d->extra : 0, d->name);
+ if (dma_base && !(pcicmd & PCI_COMMAND_MASTER)) {
+ /*
+ * Set up BM-DMA capability (PnP BIOS should have done this)
+ */
+ hwif->autodma = 0; /* default DMA off if we had to configure it here */
+ (void) pci_write_config_word(dev, PCI_COMMAND, pcicmd | PCI_COMMAND_MASTER);
+ if (pci_read_config_word(dev, PCI_COMMAND, &pcicmd) || !(pcicmd & PCI_COMMAND_MASTER)) {
+ printk("%s: %s error updating PCICMD\n", hwif->name, d->name);
+ dma_base = 0;
+ }
+ }
+ if (dma_base) {
+ if (d->dma_init) {
+ d->dma_init(hwif, dma_base);
+ } else {
+ ide_setup_dma(hwif, dma_base, 8);
+ }
+ } else {
+ printk("%s: %s Bus-Master DMA disabled (BIOS)\n", hwif->name, d->name);
+ }
+ }
+#endif /* CONFIG_BLK_DEV_IDEDMA */
+bypass_umc_dma:
+ if (d->init_hwif) /* Call chipset-specific routine for each enabled hwif */
+ d->init_hwif(hwif);
+ mate = hwif;
+ at_least_one_hwif_enabled = 1;
+ }
+ if (!at_least_one_hwif_enabled)
+ printk("%s: neither IDE port enabled (BIOS)\n", d->name);
+}
+
+static void __init hpt366_device_order_fixup (struct pci_dev *dev, ide_pci_device_t *d)
+{
+ struct pci_dev *dev2 = NULL, *findev;
+ ide_pci_device_t *d2;
+ unsigned char pin1 = 0, pin2 = 0;
+
+ if (PCI_FUNC(dev->devfn) & 1)
+ return;
+ pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin1);
+ pci_for_each_dev(findev) {
+ if ((findev->vendor == dev->vendor) &&
+ (findev->device == dev->device) &&
+ ((findev->devfn - dev->devfn) == 1) &&
+ (PCI_FUNC(findev->devfn) & 1)) {
+ dev2 = findev;
+ pci_read_config_byte(dev2, PCI_INTERRUPT_PIN, &pin2);
+ hpt363_shared_pin = (pin1 != pin2) ? 1 : 0;
+ hpt363_shared_irq = (dev->irq == dev2->irq) ? 1 : 0;
+ if (hpt363_shared_pin && hpt363_shared_irq) {
+ d->bootable = ON_BOARD;
+ printk("%s: onboard version of chipset, pin1=%d pin2=%d\n", d->name, pin1, pin2);
+ }
+ break;
+ }
+ }
+ printk("%s: IDE controller on PCI bus %02x dev %02x\n", d->name, dev->bus->number, dev->devfn);
+ ide_setup_pci_device(dev, d);
+ if (!dev2)
+ return;
+ d2 = d;
+ printk("%s: IDE controller on PCI bus %02x dev %02x\n", d2->name, dev2->bus->number, dev2->devfn);
+ if (hpt363_shared_pin && !hpt363_shared_irq) {
+ printk("%s: IDE controller run unsupported mode three!!!\n", d2->name);
+#ifndef CONFIG_HPT366_MODE3
+ printk("%s: IDE controller report to <andre@suse.com>\n", d->name);
+ return;
+#else /* CONFIG_HPT366_MODE3 */
+ printk("%s: OVERRIDE IDE controller not advisable this mode!!!\n", d2->name);
+#endif /* CONFIG_HPT366_MODE3 */
+ }
+ ide_setup_pci_device(dev2, d2);
+}
+
+/*
+ * ide_scan_pcibus() gets invoked at boot time from ide.c.
+ * It finds all PCI IDE controllers and calls ide_setup_pci_device for them.
+ */
+void __init ide_scan_pcidev (struct pci_dev *dev)
+{
+ ide_pci_devid_t devid;
+ ide_pci_device_t *d;
+
+ devid.vid = dev->vendor;
+ devid.did = dev->device;
+ for (d = ide_pci_chipsets; d->devid.vid && !IDE_PCI_DEVID_EQ(d->devid, devid); ++d);
+ if (d->init_hwif == IDE_IGNORE)
+ printk("%s: ignored by ide_scan_pci_device() (uses own driver)\n", d->name);
+ else if (IDE_PCI_DEVID_EQ(d->devid, DEVID_OPTI621V) && !(PCI_FUNC(dev->devfn) & 1))
+ return;
+ else if (IDE_PCI_DEVID_EQ(d->devid, DEVID_CY82C693) && (!(PCI_FUNC(dev->devfn) & 1) || !((dev->class >> 8) == PCI_CLASS_STORAGE_IDE)))
+ return; /* CY82C693 is more than only a IDE controller */
+ else if (IDE_PCI_DEVID_EQ(d->devid, DEVID_UM8886A) && !(PCI_FUNC(dev->devfn) & 1))
+ return; /* UM8886A/BF pair */
+ else if (IDE_PCI_DEVID_EQ(d->devid, DEVID_HPT366))
+ hpt366_device_order_fixup(dev, d);
+ else if (!IDE_PCI_DEVID_EQ(d->devid, IDE_PCI_DEVID_NULL) || (dev->class >> 8) == PCI_CLASS_STORAGE_IDE) {
+ if (IDE_PCI_DEVID_EQ(d->devid, IDE_PCI_DEVID_NULL))
+ printk("%s: unknown IDE controller on PCI bus %02x device %02x, VID=%04x, DID=%04x\n",
+ d->name, dev->bus->number, dev->devfn, devid.vid, devid.did);
+ else
+ printk("%s: IDE controller on PCI bus %02x dev %02x\n", d->name, dev->bus->number, dev->devfn);
+ ide_setup_pci_device(dev, d);
+ }
+}
+
+void __init ide_scan_pcibus (int scan_direction)
+{
+ struct pci_dev *dev;
+
+ if (!scan_direction) {
+ pci_for_each_dev(dev) {
+ ide_scan_pcidev(dev);
+ }
+ } else {
+ pci_for_each_dev_reverse(dev) {
+ ide_scan_pcidev(dev);
+ }
+ }
+}
diff --git a/drivers/ide/ide-pmac.c b/drivers/ide/ide-pmac.c
new file mode 100644
index 000000000..e0803e6fa
--- /dev/null
+++ b/drivers/ide/ide-pmac.c
@@ -0,0 +1,969 @@
+/*
+ * Support for IDE interfaces on PowerMacs.
+ * These IDE interfaces are memory-mapped and have a DBDMA channel
+ * for doing DMA.
+ *
+ * Copyright (C) 1998 Paul Mackerras.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Some code taken from drivers/block/ide-dma.c:
+ *
+ * Copyright (c) 1995-1998 Mark Lord
+ *
+ * BenH: I began adding more complete timing setup code, mostly because DMA
+ * won't work on new machines unless timings are setup correctly. This
+ * code was mainly stolen from Cmd646 driver and should be completed to
+ * include real timing calc. instead of hard coded values. The format of
+ * the timing register can be found in Darwin's source code, except for
+ * Keylargo ATA-4 controller.
+ */
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/ide.h>
+
+#include <asm/prom.h>
+#include <asm/io.h>
+#include <asm/dbdma.h>
+#include <asm/ide.h>
+#include <asm/mediabay.h>
+#include <asm/feature.h>
+#ifdef CONFIG_PMAC_PBOOK
+#include <linux/adb.h>
+#include <linux/pmu.h>
+#include <asm/irq.h>
+#endif
+#include "ide_modes.h"
+
+#undef IDE_PMAC_DEBUG
+
+#define IDE_SYSCLK_NS 30
+
+struct pmac_ide_hwif {
+ ide_ioreg_t regbase;
+ int irq;
+ int kind;
+ struct device_node* node;
+ u32 timings[2];
+#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC
+ volatile struct dbdma_regs* dma_regs;
+ struct dbdma_cmd* dma_table;
+#endif
+
+} pmac_ide[MAX_HWIFS];
+
+static int pmac_ide_count;
+
+enum {
+ controller_ohare, /* OHare based */
+ controller_heathrow, /* Heathrow/Paddington */
+ controller_kl_ata3, /* KeyLargo ATA-3 */
+ controller_kl_ata4 /* KeyLargo ATA-4 */
+};
+
+
+#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC
+
+typedef struct {
+ int accessTime;
+ int cycleTime;
+} pmac_ide_timing;
+
+/* Multiword DMA timings */
+static pmac_ide_timing mdma_timings[] =
+{
+ { 215, 480 }, /* Mode 0 */
+ { 80, 150 }, /* 1 */
+ { 70, 120 } /* 2 */
+};
+
+/* Ultra DMA timings (for use when I know how to calculate them */
+static pmac_ide_timing udma_timings[] =
+{
+ { 0, 114 }, /* Mode 0 */
+ { 0, 73 }, /* 1 */
+ { 0, 54 }, /* 2 */
+ { 0, 39 }, /* 3 */
+ { 0, 25 } /* 4 */
+};
+
+#define MAX_DCMDS 256 /* allow up to 256 DBDMA commands per xfer */
+
+static void pmac_ide_setup_dma(struct device_node *np, int ix);
+static int pmac_ide_dmaproc(ide_dma_action_t func, ide_drive_t *drive);
+static int pmac_ide_build_dmatable(ide_drive_t *drive, int ix, int wr);
+static void pmac_ide_tuneproc(ide_drive_t *drive, byte pio);
+static void pmac_ide_selectproc(ide_drive_t *drive);
+
+#endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */
+
+#ifdef CONFIG_PMAC_PBOOK
+static int idepmac_notify_sleep(struct pmu_sleep_notifier *self, int when);
+struct pmu_sleep_notifier idepmac_sleep_notifier = {
+ idepmac_notify_sleep, SLEEP_LEVEL_BLOCK,
+};
+#endif /* CONFIG_PMAC_PBOOK */
+
+static int
+pmac_ide_find(ide_drive_t *drive)
+{
+ ide_hwif_t *hwif = HWIF(drive);
+ ide_ioreg_t base;
+ int i;
+
+ for (i=0; i<pmac_ide_count; i++) {
+ base = pmac_ide[i].regbase;
+ if (base && base == hwif->io_ports[0])
+ return i;
+ }
+ return -1;
+}
+
+/*
+ * N.B. this can't be an initfunc, because the media-bay task can
+ * call ide_[un]register at any time.
+ */
+void pmac_ide_init_hwif_ports(hw_regs_t *hw,
+ ide_ioreg_t data_port, ide_ioreg_t ctrl_port,
+ int *irq)
+{
+ int i, ix;
+
+ if (data_port == 0)
+ return;
+
+ for (ix = 0; ix < MAX_HWIFS; ++ix)
+ if (data_port == pmac_ide[ix].regbase)
+ break;
+
+ if (ix >= MAX_HWIFS) {
+ /* Probably a PCI interface... */
+ for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; ++i)
+ hw->io_ports[i] = data_port + i - IDE_DATA_OFFSET;
+ /* XXX is this right? */
+ hw->io_ports[IDE_CONTROL_OFFSET] = 0;
+ if (irq != 0)
+ *irq = 0;
+ return;
+ }
+
+ /* we check only for -EINVAL meaning that we have found a matching
+ bay but with the wrong device type */
+ i = check_media_bay_by_base(data_port, MB_CD);
+ if (i == -EINVAL) {
+ hw->io_ports[IDE_DATA_OFFSET] = 0;
+ return;
+ }
+
+ for (i = 0; i < 8; ++i)
+ hw->io_ports[i] = data_port + i * 0x10;
+ hw->io_ports[8] = data_port + 0x160;
+
+ if (irq != NULL)
+ *irq = pmac_ide[ix].irq;
+
+ ide_hwifs[ix].tuneproc = pmac_ide_tuneproc;
+ ide_hwifs[ix].selectproc = pmac_ide_selectproc;
+ if (pmac_ide[ix].dma_regs && pmac_ide[ix].dma_table) {
+ ide_hwifs[ix].dmaproc = &pmac_ide_dmaproc;
+#ifdef CONFIG_PMAC_IDEDMA_AUTO
+ ide_hwifs[ix].autodma = 1;
+#endif
+ }
+}
+
+#if 0
+/* This one could be later extended to handle CMD IDE and be used by some kind
+ * of /proc interface. I want to be able to get the devicetree path of a block
+ * device for yaboot configuration
+ */
+struct device_node*
+pmac_ide_get_devnode(ide_drive_t *drive)
+{
+ int i = pmac_ide_find(drive);
+ if (i < 0)
+ return NULL;
+ return pmac_ide[i].node;
+}
+#endif
+
+/* Setup timings for the selected drive (master/slave). I still need to verify if this
+ * is enough, I beleive selectproc will be called whenever an IDE command is started,
+ * but... */
+static void
+pmac_ide_selectproc(ide_drive_t *drive)
+{
+ int i = pmac_ide_find(drive);
+ if (i < 0)
+ return;
+
+ if (drive->select.all & 0x10)
+ out_le32((unsigned *)(IDE_DATA_REG + 0x200 + _IO_BASE), pmac_ide[i].timings[1]);
+ else
+ out_le32((unsigned *)(IDE_DATA_REG + 0x200 + _IO_BASE), pmac_ide[i].timings[0]);
+}
+
+/* Number of IDE_SYSCLK_NS ticks, argument is in nanoseconds */
+#define SYSCLK_TICKS(t) (((t) + IDE_SYSCLK_NS - 1) / IDE_SYSCLK_NS)
+
+static void
+pmac_ide_tuneproc(ide_drive_t *drive, byte pio)
+{
+ ide_pio_data_t d;
+ int i;
+ u32 *timings;
+ int accessTicks, recTicks;
+
+ i = pmac_ide_find(drive);
+ if (i < 0)
+ return;
+
+ /* The "ata-4" IDE controller of UMA machines is a bit different.
+ * We don't do anything for PIO modes until we know how to do the
+ * calculation.
+ */
+ if (pmac_ide[i].kind == controller_kl_ata4)
+ return;
+
+ pio = ide_get_best_pio_mode(drive, pio, 4, &d);
+ accessTicks = SYSCLK_TICKS(ide_pio_timings[pio].active_time);
+ if (accessTicks < 4)
+ accessTicks = 4;
+ recTicks = SYSCLK_TICKS(d.cycle_time) - accessTicks - 4;
+ if (recTicks < 1)
+ recTicks = 1;
+ if (drive->select.all & 0x10)
+ timings = &pmac_ide[i].timings[1];
+ else
+ timings = &pmac_ide[i].timings[0];
+
+ *timings = ((*timings) & 0xFFFFFF800) | accessTicks | (recTicks << 5);
+#ifdef IDE_PMAC_DEBUG
+ printk("ide_pmac: Set PIO timing for mode %d, reg: 0x%08x\n",
+ pio, *timings);
+#endif
+
+ if (drive->select.all == IN_BYTE(IDE_SELECT_REG))
+ pmac_ide_selectproc(drive);
+}
+
+ide_ioreg_t
+pmac_ide_get_base(int index)
+{
+ return pmac_ide[index].regbase;
+}
+
+static int ide_majors[] = { 3, 22, 33, 34, 56, 57 };
+
+kdev_t __init
+pmac_find_ide_boot(char *bootdevice, int n)
+{
+ int i;
+
+ /*
+ * Look through the list of IDE interfaces for this one.
+ */
+ for (i = 0; i < pmac_ide_count; ++i) {
+ char *name;
+ if (!pmac_ide[i].node || !pmac_ide[i].node->full_name)
+ continue;
+ name = pmac_ide[i].node->full_name;
+ if (memcmp(name, bootdevice, n) == 0 && name[n] == 0) {
+ /* XXX should cope with the 2nd drive as well... */
+ return MKDEV(ide_majors[i], 0);
+ }
+ }
+
+ return 0;
+}
+
+void __init
+pmac_ide_probe(void)
+{
+ struct device_node *np;
+ int i;
+ struct device_node *atas;
+ struct device_node *p, **pp, *removables, **rp;
+ unsigned long base;
+ int irq;
+ ide_hwif_t *hwif;
+
+ if (_machine != _MACH_Pmac)
+ return;
+ pp = &atas;
+ rp = &removables;
+ p = find_devices("ATA");
+ if (p == NULL)
+ p = find_devices("IDE");
+ if (p == NULL)
+ p = find_type_devices("ide");
+ if (p == NULL)
+ p = find_type_devices("ata");
+ /* Move removable devices such as the media-bay CDROM
+ on the PB3400 to the end of the list. */
+ for (; p != NULL; p = p->next) {
+ if (p->parent && p->parent->type
+ && strcasecmp(p->parent->type, "media-bay") == 0) {
+ *rp = p;
+ rp = &p->next;
+ } else {
+ *pp = p;
+ pp = &p->next;
+ }
+ }
+ *rp = NULL;
+ *pp = removables;
+
+ for (i = 0, np = atas; i < MAX_HWIFS && np != NULL; np = np->next) {
+ struct device_node *tp;
+
+ /*
+ * If this node is not under a mac-io or dbdma node,
+ * leave it to the generic PCI driver.
+ */
+ for (tp = np->parent; tp != 0; tp = tp->parent)
+ if (tp->type && (strcmp(tp->type, "mac-io") == 0
+ || strcmp(tp->type, "dbdma") == 0))
+ break;
+ if (tp == 0)
+ continue;
+
+ if (np->n_addrs == 0) {
+ printk(KERN_WARNING "ide: no address for device %s\n",
+ np->full_name);
+ continue;
+ }
+
+ /*
+ * If this slot is taken (e.g. by ide-pci.c) try the next one.
+ */
+ while (i < MAX_HWIFS
+ && ide_hwifs[i].io_ports[IDE_DATA_OFFSET] != 0)
+ ++i;
+ if (i >= MAX_HWIFS)
+ break;
+
+ base = (unsigned long) ioremap(np->addrs[0].address, 0x200) - _IO_BASE;
+
+ /* XXX This is bogus. Should be fixed in the registry by checking
+ the kind of host interrupt controller, a bit like gatwick
+ fixes in irq.c
+ */
+ if (np->n_intrs == 0) {
+ printk("ide: no intrs for device %s, using 13\n",
+ np->full_name);
+ irq = 13;
+ } else {
+ irq = np->intrs[0].line;
+ }
+ pmac_ide[i].regbase = base;
+ pmac_ide[i].irq = irq;
+ pmac_ide[i].node = np;
+ if (device_is_compatible(np, "keylargo-ata")) {
+ if (strcmp(np->name, "ata-4") == 0)
+ pmac_ide[i].kind = controller_kl_ata4;
+ else
+ pmac_ide[i].kind = controller_kl_ata3;
+ } else if (device_is_compatible(np, "heathrow-ata"))
+ pmac_ide[i].kind = controller_heathrow;
+ else
+ pmac_ide[i].kind = controller_ohare;
+
+ if (np->parent && np->parent->name
+ && strcasecmp(np->parent->name, "media-bay") == 0) {
+ media_bay_set_ide_infos(np->parent,base,irq,i);
+ } else if (pmac_ide[i].kind == controller_ohare) {
+ /* The code below is having trouble on some ohare machines
+ * (timing related ?). Until I can put my hand on one of these
+ * units, I keep the old way
+ */
+ feature_set(np, FEATURE_IDE0_enable);
+ } else {
+ /* This is necessary to enable IDE when net-booting */
+ int *bidp = (int *)get_property(np, "AAPL,bus-id", NULL);
+ int bid = bidp ? *bidp : 0;
+ printk("pmac_ide: enabling IDE bus ID %d\n", bid);
+ switch(bid) {
+ case 0:
+ feature_set(np, FEATURE_IDE0_reset);
+ feature_set(np, FEATURE_IOBUS_enable);
+ mdelay(10);
+ feature_set(np, FEATURE_IDE0_enable);
+ mdelay(10);
+ feature_clear(np, FEATURE_IDE0_reset);
+ break;
+ case 1:
+ feature_set(np, FEATURE_Mediabay_IDE_reset);
+ mdelay(10);
+ feature_set(np, FEATURE_Mediabay_IDE_enable);
+ mdelay(10);
+ feature_clear(np, FEATURE_Mediabay_IDE_reset);
+ break;
+ case 2:
+ /* This one exists only for KL, I don't know about any
+ enable bit */
+ feature_set(np, FEATURE_IDE2_reset);
+ mdelay(10);
+ feature_clear(np, FEATURE_IDE2_reset);
+ break;
+ }
+ mdelay(1000);
+ }
+
+ hwif = &ide_hwifs[i];
+ pmac_ide_init_hwif_ports(&hwif->hw, base, 0, &hwif->irq);
+ memcpy(hwif->io_ports, hwif->hw.io_ports, sizeof(hwif->io_ports));
+ hwif->chipset = ide_pmac;
+ hwif->noprobe = !hwif->io_ports[IDE_DATA_OFFSET];
+
+#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC
+ if (np->n_addrs >= 2) {
+ /* has a DBDMA controller channel */
+ pmac_ide_setup_dma(np, i);
+ }
+#endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */
+
+ ++i;
+ }
+ pmac_ide_count = i;
+
+#ifdef CONFIG_PMAC_PBOOK
+ pmu_register_sleep_notifier(&idepmac_sleep_notifier);
+#endif /* CONFIG_PMAC_PBOOK */
+}
+
+#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC
+
+static void __init
+pmac_ide_setup_dma(struct device_node *np, int ix)
+{
+ pmac_ide[ix].dma_regs =
+ (volatile struct dbdma_regs*)ioremap(np->addrs[1].address, 0x200);
+
+ /*
+ * Allocate space for the DBDMA commands.
+ * The +2 is +1 for the stop command and +1 to allow for
+ * aligning the start address to a multiple of 16 bytes.
+ */
+ pmac_ide[ix].dma_table = (struct dbdma_cmd*)
+ kmalloc((MAX_DCMDS + 2) * sizeof(struct dbdma_cmd), GFP_KERNEL);
+ if (pmac_ide[ix].dma_table == 0) {
+ printk(KERN_ERR "%s: unable to allocate DMA command list\n",
+ ide_hwifs[ix].name);
+ return;
+ }
+
+ ide_hwifs[ix].dmaproc = &pmac_ide_dmaproc;
+#ifdef CONFIG_PMAC_IDEDMA_AUTO
+ ide_hwifs[ix].autodma = 1;
+#endif
+}
+
+/*
+ * pmac_ide_build_dmatable builds the DBDMA command list
+ * for a transfer and sets the DBDMA channel to point to it.
+ */
+static int
+pmac_ide_build_dmatable(ide_drive_t *drive, int ix, int wr)
+{
+ struct dbdma_cmd *table, *tstart;
+ int count = 0;
+ struct request *rq = HWGROUP(drive)->rq;
+ struct buffer_head *bh = rq->bh;
+ unsigned int size, addr;
+ volatile struct dbdma_regs *dma = pmac_ide[ix].dma_regs;
+
+ table = tstart = (struct dbdma_cmd *) DBDMA_ALIGN(pmac_ide[ix].dma_table);
+ out_le32(&dma->control, (RUN|PAUSE|FLUSH|WAKE|DEAD) << 16);
+ while (in_le32(&dma->status) & RUN)
+ udelay(1);
+
+ do {
+ /*
+ * Determine addr and size of next buffer area. We assume that
+ * individual virtual buffers are always composed linearly in
+ * physical memory. For example, we assume that any 8kB buffer
+ * is always composed of two adjacent physical 4kB pages rather
+ * than two possibly non-adjacent physical 4kB pages.
+ */
+ if (bh == NULL) { /* paging requests have (rq->bh == NULL) */
+ addr = virt_to_bus(rq->buffer);
+ size = rq->nr_sectors << 9;
+ } else {
+ /* group sequential buffers into one large buffer */
+ addr = virt_to_bus(bh->b_data);
+ size = bh->b_size;
+ while ((bh = bh->b_reqnext) != NULL) {
+ if ((addr + size) != virt_to_bus(bh->b_data))
+ break;
+ size += bh->b_size;
+ }
+ }
+
+ /*
+ * Fill in the next DBDMA command block.
+ * Note that one DBDMA command can transfer
+ * at most 65535 bytes.
+ */
+ while (size) {
+ unsigned int tc = (size < 0xfe00)? size: 0xfe00;
+
+ if (++count >= MAX_DCMDS) {
+ printk("%s: DMA table too small\n",
+ drive->name);
+ return 0; /* revert to PIO for this request */
+ }
+ st_le16(&table->command, wr? OUTPUT_MORE: INPUT_MORE);
+ st_le16(&table->req_count, tc);
+ st_le32(&table->phy_addr, addr);
+ table->cmd_dep = 0;
+ table->xfer_status = 0;
+ table->res_count = 0;
+ addr += tc;
+ size -= tc;
+ ++table;
+ }
+ } while (bh != NULL);
+
+ /* convert the last command to an input/output last command */
+ if (count)
+ st_le16(&table[-1].command, wr? OUTPUT_LAST: INPUT_LAST);
+ else
+ printk(KERN_DEBUG "%s: empty DMA table?\n", drive->name);
+
+ /* add the stop command to the end of the list */
+ memset(table, 0, sizeof(struct dbdma_cmd));
+ out_le16(&table->command, DBDMA_STOP);
+
+ out_le32(&dma->cmdptr, virt_to_bus(tstart));
+ return 1;
+}
+
+
+/* This is fun. -DaveM */
+#define IDE_SETXFER 0x03
+#define IDE_SETFEATURE 0xef
+#define IDE_DMA2_ENABLE 0x22
+#define IDE_DMA1_ENABLE 0x21
+#define IDE_DMA0_ENABLE 0x20
+#define IDE_UDMA4_ENABLE 0x44
+#define IDE_UDMA3_ENABLE 0x43
+#define IDE_UDMA2_ENABLE 0x42
+#define IDE_UDMA1_ENABLE 0x41
+#define IDE_UDMA0_ENABLE 0x40
+
+static __inline__ unsigned char
+dma_bits_to_command(unsigned char bits)
+{
+ if(bits & 0x04)
+ return IDE_DMA2_ENABLE;
+ if(bits & 0x02)
+ return IDE_DMA1_ENABLE;
+ return IDE_DMA0_ENABLE;
+}
+
+static __inline__ unsigned char
+udma_bits_to_command(unsigned char bits)
+{
+ if(bits & 0x10)
+ return IDE_UDMA4_ENABLE;
+ if(bits & 0x08)
+ return IDE_UDMA3_ENABLE;
+ if(bits & 0x04)
+ return IDE_UDMA2_ENABLE;
+ if(bits & 0x02)
+ return IDE_UDMA1_ENABLE;
+ if(bits & 0x01)
+ return IDE_UDMA0_ENABLE;
+ return 0;
+}
+
+static __inline__ int
+wait_for_ready(ide_drive_t *drive)
+{
+ /* Timeout bumped for some powerbooks */
+ int timeout = 2000;
+ byte stat;
+
+ while(--timeout) {
+ stat = GET_STAT();
+ if(!(stat & BUSY_STAT)) {
+ if (drive->ready_stat == 0)
+ break;
+ else if((stat & drive->ready_stat) || (stat & ERR_STAT))
+ break;
+ }
+ mdelay(1);
+ }
+ if((stat & ERR_STAT) || timeout <= 0) {
+ if (stat & ERR_STAT) {
+ printk("ide_pmace: wait_for_ready, error status: %x\n", stat);
+ }
+ return 1;
+ }
+ return 0;
+}
+
+static int
+pmac_ide_do_setfeature(ide_drive_t *drive, byte command)
+{
+ unsigned long flags;
+ byte old_select;
+ int result = 1;
+
+ save_flags(flags);
+ cli();
+ old_select = IN_BYTE(IDE_SELECT_REG);
+ OUT_BYTE(drive->select.all, IDE_SELECT_REG);
+ udelay(10);
+ OUT_BYTE(IDE_SETXFER, IDE_FEATURE_REG);
+ OUT_BYTE(command, IDE_NSECTOR_REG);
+ if(wait_for_ready(drive)) {
+ printk("pmac_ide_do_setfeature disk not ready before SET_FEATURE!\n");
+ goto out;
+ }
+ OUT_BYTE(IDE_SETFEATURE, IDE_COMMAND_REG);
+ result = wait_for_ready(drive);
+ if (result)
+ printk("pmac_ide_do_setfeature disk not ready after SET_FEATURE !\n");
+out:
+ OUT_BYTE(old_select, IDE_SELECT_REG);
+ restore_flags(flags);
+
+ return result;
+}
+
+static int
+pmac_ide_mdma_enable(ide_drive_t *drive, int idx)
+{
+ byte bits = drive->id->dma_mword & 0x07;
+ byte feature = dma_bits_to_command(bits);
+ u32 *timings;
+ int cycleTime, accessTime;
+ int accessTicks, recTicks;
+ struct hd_driveid *id = drive->id;
+
+ /* For now, we don't know these values */
+ if (pmac_ide[idx].kind == controller_kl_ata4 && feature != IDE_DMA2_ENABLE)
+ return 0;
+ if (pmac_ide[idx].kind != controller_kl_ata4 && feature == IDE_DMA0_ENABLE)
+ return 0;
+
+ /* Set feature on drive */
+ printk("%s: Enabling MultiWord DMA %d\n", drive->name, feature & 0xf);
+ if (pmac_ide_do_setfeature(drive, feature)) {
+ printk("%s: Failed !\n", drive->name);
+ return 0;
+ }
+
+ /* which drive is it ? */
+ if (drive->select.all & 0x10)
+ timings = &pmac_ide[idx].timings[1];
+ else
+ timings = &pmac_ide[idx].timings[0];
+
+ /* Calculate accesstime and cycle time */
+ cycleTime = mdma_timings[feature & 0xf].cycleTime;
+ accessTime = mdma_timings[feature & 0xf].accessTime;
+ if ((id->field_valid & 2) && (id->eide_dma_time))
+ cycleTime = id->eide_dma_time;
+ if ((pmac_ide[idx].kind == controller_ohare) && (cycleTime < 150))
+ cycleTime = 150;
+
+ /* For ata-4 controller, we don't know the calculation */
+ if (pmac_ide[idx].kind == controller_kl_ata4) {
+ *timings = 0x00019465; /* MDMA2 */
+ } else {
+ int halfTick = 0;
+ int origAccessTime = accessTime;
+ int origCycleTime = cycleTime;
+
+ accessTicks = SYSCLK_TICKS(accessTime);
+ if (accessTicks < 1)
+ accessTicks = 1;
+ accessTime = accessTicks * IDE_SYSCLK_NS;
+ recTicks = SYSCLK_TICKS(cycleTime - accessTime) - 1;
+ if (recTicks < 1)
+ recTicks = 1;
+ cycleTime = (recTicks + 1 + accessTicks) * IDE_SYSCLK_NS;
+
+ if ((accessTicks > 1) &&
+ ((accessTime - IDE_SYSCLK_NS/2) >= origAccessTime) &&
+ ((cycleTime - IDE_SYSCLK_NS) >= origCycleTime)) {
+ halfTick = 1;
+ accessTicks--;
+ }
+ *timings = ((*timings) & 0x7FF) |
+ (accessTicks | (recTicks << 5) | (halfTick << 10)) << 11;
+ }
+#ifdef IDE_PMAC_DEBUG
+ printk("ide_pmac: Set MDMA timing for mode %d, reg: 0x%08x\n",
+ feature & 0xf, *timings);
+#endif
+ return 1;
+}
+
+static int
+pmac_ide_udma_enable(ide_drive_t *drive, int idx)
+{
+ byte bits = drive->id->dma_ultra & 0x1f;
+ byte feature = udma_bits_to_command(bits);
+ u32 timings;
+
+ /* We support only those values */
+ if (feature != IDE_UDMA4_ENABLE && feature != IDE_UDMA2_ENABLE)
+ return 0;
+
+ /* Set feature on drive */
+ printk("%s: Enabling Ultra DMA %d\n", drive->name, feature & 0xf);
+ if (pmac_ide_do_setfeature(drive, feature)) {
+ printk("%s: Failed !\n", drive->name);
+ return 0;
+ }
+
+ /* Put this channel into UDMA mode.
+ * This value is set by MacOS on the iBook for U/DMA2
+ */
+ switch(feature) {
+ case IDE_UDMA4_ENABLE:
+ timings = 0x0cd00065;
+ break;
+ case IDE_UDMA2_ENABLE:
+ timings = 0x11100065;
+ break;
+ }
+
+ if (drive->select.all & 0x10)
+ pmac_ide[idx].timings[1] = timings;
+ else
+ pmac_ide[idx].timings[0] = timings;
+
+ return 1;
+}
+
+static int
+pmac_ide_dma_onoff(ide_drive_t *drive, int enable)
+{
+ int ata4, udma, idx;
+ struct hd_driveid *id = drive->id;
+
+ drive->using_dma = 0;
+
+ idx = pmac_ide_find(drive);
+ if (idx < 0)
+ return 0;
+
+ if (drive->media == ide_floppy)
+ enable = 0;
+ if (((id->capability & 1) == 0) && !check_drive_lists(drive, GOOD_DMA_DRIVE))
+ enable = 0;
+ if (check_drive_lists(drive, BAD_DMA_DRIVE))
+ enable = 0;
+
+ udma = 0;
+ ata4 = (pmac_ide[idx].kind == controller_kl_ata4);
+
+ if(enable) {
+ if (ata4 && (drive->media == ide_disk) &&
+ (id->field_valid & 0x0004) && (id->dma_ultra & 0x17)) {
+ /* UltraDMA modes. */
+ drive->using_dma = pmac_ide_udma_enable(drive, idx);
+ }
+ if (!drive->using_dma && (id->dma_mword & 0x0007)) {
+ /* Normal MultiWord DMA modes. */
+ drive->using_dma = pmac_ide_mdma_enable(drive, idx);
+ }
+ /* Without this, strange things will happen on Keylargo-based
+ * machines
+ */
+ OUT_BYTE(0, IDE_CONTROL_REG);
+ if (drive->select.all == IN_BYTE(IDE_SELECT_REG))
+ pmac_ide_selectproc(drive);
+ }
+ return 0;
+}
+
+int pmac_ide_dmaproc(ide_dma_action_t func, ide_drive_t *drive)
+{
+ ide_hwif_t *hwif = HWIF(drive);
+ int ix, dstat;
+ volatile struct dbdma_regs *dma;
+
+ /* Can we stuff a pointer to our intf structure in config_data
+ * or select_data in hwif ?
+ */
+ ix = pmac_ide_find(drive);
+ if (ix < 0)
+ return 0;
+ dma = pmac_ide[ix].dma_regs;
+
+ switch (func) {
+ case ide_dma_on:
+ case ide_dma_off:
+ case ide_dma_off_quietly:
+ pmac_ide_dma_onoff(drive, (func == ide_dma_on));
+ break;
+ case ide_dma_check:
+ if (hwif->autodma)
+ pmac_ide_dma_onoff(drive, 1);
+ break;
+ case ide_dma_read:
+ case ide_dma_write:
+ if (!pmac_ide_build_dmatable(drive, ix, func==ide_dma_write))
+ return 1;
+ drive->waiting_for_dma = 1;
+ if (drive->media != ide_disk)
+ return 0;
+ ide_set_handler(drive, &ide_dma_intr, WAIT_CMD, NULL);
+ OUT_BYTE(func==ide_dma_write? WIN_WRITEDMA: WIN_READDMA,
+ IDE_COMMAND_REG);
+ case ide_dma_begin:
+ out_le32(&dma->control, (RUN << 16) | RUN);
+ break;
+ case ide_dma_end:
+ drive->waiting_for_dma = 0;
+ dstat = in_le32(&dma->status);
+ out_le32(&dma->control, ((RUN|WAKE|DEAD) << 16));
+ /* verify good dma status */
+ return (dstat & (RUN|DEAD|ACTIVE)) != RUN;
+ case ide_dma_test_irq:
+ return (in_le32(&dma->status) & (RUN|ACTIVE)) == RUN;
+ default:
+ printk(KERN_ERR "pmac_ide_dmaproc: bad func %d\n", func);
+ }
+ return 0;
+}
+#endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */
+
+#ifdef CONFIG_PMAC_PBOOK
+static void idepmac_sleep_disk(int i, unsigned long base)
+{
+ struct device_node* np = pmac_ide[i].node;
+ int j;
+
+ /* FIXME: We only handle the master IDE */
+ if (ide_hwifs[i].drives[0].media == ide_disk) {
+ /* Spin down the drive */
+ outb(0xa0, base+0x60);
+ outb(0x0, base+0x30);
+ outb(0x0, base+0x20);
+ outb(0x0, base+0x40);
+ outb(0x0, base+0x50);
+ outb(0xe0, base+0x70);
+ outb(0x2, base+0x160);
+ for (j = 0; j < 10; j++) {
+ int status;
+ mdelay(100);
+ status = inb(base+0x70);
+ if (!(status & BUSY_STAT) && (status & DRQ_STAT))
+ break;
+ }
+ }
+ feature_set(np, FEATURE_IDE0_reset);
+ feature_clear(np, FEATURE_IOBUS_enable);
+ feature_clear(np, FEATURE_IDE0_enable);
+ pmac_ide[i].timings[0] = 0;
+ pmac_ide[i].timings[1] = 0;
+}
+
+static void idepmac_wake_disk(int i, unsigned long base)
+{
+ struct device_node* np = pmac_ide[i].node;
+ int j;
+
+ /* Revive IDE disk and controller */
+ feature_set(np, FEATURE_IOBUS_enable);
+ mdelay(10);
+ feature_set(np, FEATURE_IDE0_enable);
+ mdelay(10);
+ feature_clear(np, FEATURE_IDE0_reset);
+ mdelay(100);
+
+ /* Reset timings */
+ pmac_ide_selectproc(&ide_hwifs[i].drives[0]);
+ mdelay(10);
+
+ /* Wait up to 10 seconds (enough for recent drives) */
+ for (j = 0; j < 100; j++) {
+ int status;
+ mdelay(100);
+ status = inb(base + 0x70);
+ if (!(status & BUSY_STAT))
+ break;
+ }
+}
+
+/* Here we handle media bay devices */
+static void
+idepmac_wake_bay(int i, unsigned long base)
+{
+ int timeout;
+
+ /* Reset timings */
+ pmac_ide_selectproc(&ide_hwifs[i].drives[0]);
+ mdelay(10);
+
+ timeout = 10000;
+ while ((inb(base + 0x70) & BUSY_STAT) && timeout) {
+ mdelay(1);
+ --timeout;
+ }
+}
+
+/* Note: We support only master drives for now. This will have to be
+ * improved if we want to handle sleep on the iMacDV where the CD-ROM
+ * is a slave
+ */
+static int idepmac_notify_sleep(struct pmu_sleep_notifier *self, int when)
+{
+ int i, ret;
+ unsigned long base;
+
+ switch (when) {
+ case PBOOK_SLEEP_REQUEST:
+ break;
+ case PBOOK_SLEEP_REJECT:
+ break;
+ case PBOOK_SLEEP_NOW:
+ for (i = 0; i < pmac_ide_count; ++i) {
+ if ((base = pmac_ide[i].regbase) == 0)
+ continue;
+ /* Disable irq during sleep */
+ disable_irq(pmac_ide[i].irq);
+ ret = check_media_bay_by_base(base, MB_CD);
+ if (ret == -ENODEV)
+ /* not media bay - put the disk to sleep */
+ idepmac_sleep_disk(i, base);
+ }
+ break;
+ case PBOOK_WAKE:
+ for (i = 0; i < pmac_ide_count; ++i) {
+ ide_hwif_t *hwif;
+ if ((base = pmac_ide[i].regbase) == 0)
+ continue;
+ hwif = &ide_hwifs[i];
+ /* We don't handle media bay devices this way */
+ ret = check_media_bay_by_base(base, MB_CD);
+ if (ret == -ENODEV)
+ idepmac_wake_disk(i, base);
+ else if (ret == 0)
+ idepmac_wake_bay(i, base);
+ enable_irq(pmac_ide[i].irq);
+
+#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC
+ if (hwif->drives[0].present && hwif->drives[0].using_dma)
+ pmac_ide_dma_onoff(&hwif->drives[0], 1);
+#endif
+ }
+ break;
+ }
+ return PBOOK_SLEEP_OK;
+}
+#endif /* CONFIG_PMAC_PBOOK */
diff --git a/drivers/ide/ide-pnp.c b/drivers/ide/ide-pnp.c
new file mode 100644
index 000000000..ffa3ade56
--- /dev/null
+++ b/drivers/ide/ide-pnp.c
@@ -0,0 +1,158 @@
+/*
+ * linux/drivers/block/ide-pnp.c
+ *
+ * This file provides autodetection for ISA PnP IDE interfaces.
+ * It was tested with "ESS ES1868 Plug and Play AudioDrive" IDE interface.
+ *
+ * Copyright (C) 2000 Andrey Panin <pazke@orbita.don.sitek.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example /usr/src/linux/COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/ide.h>
+#include <linux/isapnp.h>
+
+#ifndef PREPARE_FUNC
+#define PREPARE_FUNC(dev) (dev->prepare)
+#define ACTIVATE_FUNC(dev) (dev->activate)
+#define DEACTIVATE_FUNC(dev) (dev->deactivate)
+#endif
+
+#define DEV_IO(dev, index) (dev->resource[index].start)
+#define DEV_IRQ(dev, index) (dev->irq_resource[index].start)
+
+#define DEV_NAME(dev) (dev->bus->name ? dev->bus->name : "ISA PnP")
+
+#define GENERIC_HD_DATA 0
+#define GENERIC_HD_ERROR 1
+#define GENERIC_HD_NSECTOR 2
+#define GENERIC_HD_SECTOR 3
+#define GENERIC_HD_LCYL 4
+#define GENERIC_HD_HCYL 5
+#define GENERIC_HD_SELECT 6
+#define GENERIC_HD_STATUS 7
+
+static int generic_ide_offsets[IDE_NR_PORTS] __initdata = {
+ GENERIC_HD_DATA, GENERIC_HD_ERROR, GENERIC_HD_NSECTOR,
+ GENERIC_HD_SECTOR, GENERIC_HD_LCYL, GENERIC_HD_HCYL,
+ GENERIC_HD_SELECT, GENERIC_HD_STATUS, -1, -1
+};
+
+/* ISA PnP device table entry */
+struct pnp_dev_t {
+ unsigned int vendor, device;
+ int (*init_fn)(struct pci_dev *dev, int enable);
+};
+
+/* Generic initialisation function for ISA PnP IDE interface */
+static int __init pnpide_generic_init(struct pci_dev *dev, int enable)
+{
+ hw_regs_t hw;
+ int index;
+
+ if (!enable)
+ return 0;
+
+ if (!(DEV_IO(dev, 0) && DEV_IO(dev, 1) && DEV_IRQ(dev, 0)))
+ return 1;
+
+ ide_setup_ports(&hw, (ide_ioreg_t) DEV_IO(dev, 0),
+ generic_ide_offsets, (ide_ioreg_t) DEV_IO(dev, 1),
+ 0, NULL, DEV_IRQ(dev, 0));
+
+ index = ide_register_hw(&hw, NULL);
+
+ if (index != -1) {
+ printk("ide%d: %s IDE interface\n", index, DEV_NAME(dev));
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Add your devices here :)) */
+struct pnp_dev_t idepnp_devices[] __initdata = {
+ /* Generic ESDI/IDE/ATA compatible hard disk controller */
+ { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0x0600),
+ pnpide_generic_init },
+ { 0 }
+};
+
+#ifdef MODULE
+#define NR_PNP_DEVICES 8
+struct pnp_dev_inst {
+ struct pci_dev *dev;
+ struct pnp_dev_t *dev_type;
+};
+static struct pnp_dev_inst devices[NR_PNP_DEVICES];
+static int pnp_ide_dev_idx = 0;
+#endif
+
+/*
+ * Probe for ISA PnP IDE interfaces.
+ */
+void pnpide_init(int enable)
+{
+ struct pci_dev *dev = NULL;
+ struct pnp_dev_t *dev_type;
+
+ if (!isapnp_present())
+ return;
+
+#ifdef MODULE
+ /* Module unload, deactivate all registered devices. */
+ if (!enable) {
+ int i;
+ for (i = 0; i < pnp_ide_dev_idx; i++) {
+ devices[i].dev_type->init_fn(dev, 0);
+
+ if (DEACTIVATE_FUNC(devices[i].dev))
+ DEACTIVATE_FUNC(devices[i].dev)(devices[i].dev);
+ }
+ return;
+ }
+#endif
+ for (dev_type = idepnp_devices; dev_type->vendor; dev_type++) {
+ while ((dev = isapnp_find_dev(NULL, dev_type->vendor,
+ dev_type->device, dev))) {
+
+ if (dev->active)
+ continue;
+
+ if (PREPARE_FUNC(dev) && (PREPARE_FUNC(dev))(dev) < 0) {
+ printk("ide: %s prepare failed\n", DEV_NAME(dev));
+ continue;
+ }
+
+ if (ACTIVATE_FUNC(dev) && (ACTIVATE_FUNC(dev))(dev) < 0) {
+ printk("ide: %s activate failed\n", DEV_NAME(dev));
+ continue;
+ }
+
+ /* Call device initialization function */
+ if (dev_type->init_fn(dev, 1)) {
+ if (DEACTIVATE_FUNC(dev))
+ DEACTIVATE_FUNC(dev)(dev);
+ } else {
+#ifdef MODULE
+ /*
+ * Register device in the array to
+ * deactivate it on a module unload.
+ */
+ if (pnp_ide_dev_idx >= NR_PNP_DEVICES)
+ return;
+ devices[pnp_ide_dev_idx].dev = dev;
+ devices[pnp_ide_dev_idx].dev_type = dev_type;
+ pnp_ide_dev_idx++;
+#endif
+ }
+ }
+ }
+}
diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c
new file mode 100644
index 000000000..311bcfa25
--- /dev/null
+++ b/drivers/ide/ide-probe.c
@@ -0,0 +1,929 @@
+/*
+ * linux/drivers/block/ide-probe.c Version 1.05 July 3, 1999
+ *
+ * Copyright (C) 1994-1998 Linus Torvalds & authors (see below)
+ */
+
+/*
+ * Mostly written by Mark Lord <mlord@pobox.com>
+ * and Gadi Oxman <gadio@netvision.net.il>
+ *
+ * See linux/MAINTAINERS for address of current maintainer.
+ *
+ * This is the IDE probe module, as evolved from hd.c and ide.c.
+ *
+ * Version 1.00 move drive probing code from ide.c to ide-probe.c
+ * Version 1.01 fix compilation problem for m68k
+ * Version 1.02 increase WAIT_PIDENTIFY to avoid CD-ROM locking at boot
+ * by Andrea Arcangeli
+ * Version 1.03 fix for (hwif->chipset == ide_4drives)
+ * Version 1.04 fixed buggy treatments of known flash memory cards
+ *
+ * Version 1.05 fix for (hwif->chipset == ide_pdc4030)
+ * added ide6/7/8/9
+ * allowed for secondary flash card to be detectable
+ * with new flag : drive->ata_flash : 1;
+ */
+
+#undef REALLY_SLOW_IO /* most systems can safely undef this */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/major.h>
+#include <linux/errno.h>
+#include <linux/genhd.h>
+#include <linux/malloc.h>
+#include <linux/delay.h>
+#include <linux/ide.h>
+
+#include <asm/byteorder.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+
+static inline void do_identify (ide_drive_t *drive, byte cmd)
+{
+ int bswap = 1;
+ struct hd_driveid *id;
+
+ id = drive->id = kmalloc (SECTOR_WORDS*4, GFP_ATOMIC); /* called with interrupts disabled! */
+ ide_input_data(drive, id, SECTOR_WORDS); /* read 512 bytes of id info */
+ ide__sti(); /* local CPU only */
+ ide_fix_driveid(id);
+ if (!drive->forced_lun)
+ drive->last_lun = id->last_lun & 0x7;
+#if defined (CONFIG_SCSI_EATA_DMA) || defined (CONFIG_SCSI_EATA_PIO) || defined (CONFIG_SCSI_EATA)
+ /*
+ * EATA SCSI controllers do a hardware ATA emulation:
+ * Ignore them if there is a driver for them available.
+ */
+ if ((id->model[0] == 'P' && id->model[1] == 'M')
+ || (id->model[0] == 'S' && id->model[1] == 'K')) {
+ printk("%s: EATA SCSI HBA %.10s\n", drive->name, id->model);
+ drive->present = 0;
+ return;
+ }
+#endif /* CONFIG_SCSI_EATA_DMA || CONFIG_SCSI_EATA_PIO */
+
+ /*
+ * WIN_IDENTIFY returns little-endian info,
+ * WIN_PIDENTIFY *usually* returns little-endian info.
+ */
+ if (cmd == WIN_PIDENTIFY) {
+ if ((id->model[0] == 'N' && id->model[1] == 'E') /* NEC */
+ || (id->model[0] == 'F' && id->model[1] == 'X') /* Mitsumi */
+ || (id->model[0] == 'P' && id->model[1] == 'i'))/* Pioneer */
+ bswap ^= 1; /* Vertos drives may still be weird */
+ }
+ ide_fixstring (id->model, sizeof(id->model), bswap);
+ ide_fixstring (id->fw_rev, sizeof(id->fw_rev), bswap);
+ ide_fixstring (id->serial_no, sizeof(id->serial_no), bswap);
+
+ if (strstr(id->model, "E X A B Y T E N E S T"))
+ return;
+
+ id->model[sizeof(id->model)-1] = '\0'; /* we depend on this a lot! */
+ printk("%s: %s, ", drive->name, id->model);
+ drive->present = 1;
+
+ /*
+ * Check for an ATAPI device
+ */
+ if (cmd == WIN_PIDENTIFY) {
+ byte type = (id->config >> 8) & 0x1f;
+ printk("ATAPI ");
+#ifdef CONFIG_BLK_DEV_PDC4030
+ if (HWIF(drive)->channel == 1 && HWIF(drive)->chipset == ide_pdc4030) {
+ printk(" -- not supported on 2nd Promise port\n");
+ drive->present = 0;
+ return;
+ }
+#endif /* CONFIG_BLK_DEV_PDC4030 */
+ switch (type) {
+ case ide_floppy:
+ if (!strstr(id->model, "CD-ROM")) {
+ if (!strstr(id->model, "oppy") && !strstr(id->model, "poyp") && !strstr(id->model, "ZIP"))
+ printk("cdrom or floppy?, assuming ");
+ if (drive->media != ide_cdrom) {
+ printk ("FLOPPY");
+ break;
+ }
+ }
+ type = ide_cdrom; /* Early cdrom models used zero */
+ case ide_cdrom:
+ drive->removable = 1;
+#ifdef CONFIG_PPC
+ /* kludge for Apple PowerBook internal zip */
+ if (!strstr(id->model, "CD-ROM") && strstr(id->model, "ZIP")) {
+ printk ("FLOPPY");
+ type = ide_floppy;
+ break;
+ }
+#endif
+ printk ("CDROM");
+ break;
+ case ide_tape:
+ printk ("TAPE");
+ break;
+ case ide_optical:
+ printk ("OPTICAL");
+ drive->removable = 1;
+ break;
+ default:
+ printk("UNKNOWN (type %d)", type);
+ break;
+ }
+ printk (" drive\n");
+ drive->media = type;
+ return;
+ }
+
+ /*
+ * Not an ATAPI device: looks like a "regular" hard disk
+ */
+ if (id->config & (1<<7))
+ drive->removable = 1;
+ /*
+ * Prevent long system lockup probing later for non-existant
+ * slave drive if the hwif is actually a flash memory card of some variety:
+ */
+ if (drive_is_flashcard(drive)) {
+ ide_drive_t *mate = &HWIF(drive)->drives[1^drive->select.b.unit];
+ if (!mate->ata_flash) {
+ mate->present = 0;
+ mate->noprobe = 1;
+ }
+ }
+ drive->media = ide_disk;
+ printk("ATA DISK drive\n");
+ return;
+}
+
+/*
+ * try_to_identify() sends an ATA(PI) IDENTIFY request to a drive
+ * and waits for a response. It also monitors irqs while this is
+ * happening, in hope of automatically determining which one is
+ * being used by the interface.
+ *
+ * Returns: 0 device was identified
+ * 1 device timed-out (no response to identify request)
+ * 2 device aborted the command (refused to identify itself)
+ */
+static int try_to_identify (ide_drive_t *drive, byte cmd)
+{
+ int rc;
+ ide_ioreg_t hd_status;
+ unsigned long timeout;
+ unsigned long irqs = 0;
+ byte s, a;
+
+ if (IDE_CONTROL_REG) {
+ if (!HWIF(drive)->irq) { /* already got an IRQ? */
+ probe_irq_off(probe_irq_on()); /* clear dangling irqs */
+ irqs = probe_irq_on(); /* start monitoring irqs */
+ OUT_BYTE(drive->ctl,IDE_CONTROL_REG); /* enable device irq */
+ }
+
+ ide_delay_50ms(); /* take a deep breath */
+ a = IN_BYTE(IDE_ALTSTATUS_REG);
+ s = IN_BYTE(IDE_STATUS_REG);
+ if ((a ^ s) & ~INDEX_STAT) {
+ printk("%s: probing with STATUS(0x%02x) instead of ALTSTATUS(0x%02x)\n", drive->name, s, a);
+ hd_status = IDE_STATUS_REG; /* ancient Seagate drives, broken interfaces */
+ } else {
+ hd_status = IDE_ALTSTATUS_REG; /* use non-intrusive polling */
+ }
+ } else {
+ ide_delay_50ms();
+ hd_status = IDE_STATUS_REG;
+ }
+
+#if CONFIG_BLK_DEV_PDC4030
+ if (HWIF(drive)->chipset == ide_pdc4030) {
+ /* DC4030 hosted drives need their own identify... */
+ extern int pdc4030_identify(ide_drive_t *);
+ if (pdc4030_identify(drive)) {
+ if (irqs)
+ (void) probe_irq_off(irqs);
+ return 1;
+ }
+ } else
+#endif /* CONFIG_BLK_DEV_PDC4030 */
+ OUT_BYTE(cmd,IDE_COMMAND_REG); /* ask drive for ID */
+ timeout = ((cmd == WIN_IDENTIFY) ? WAIT_WORSTCASE : WAIT_PIDENTIFY) / 2;
+ timeout += jiffies;
+ do {
+ if (0 < (signed long)(jiffies - timeout)) {
+ if (irqs)
+ (void) probe_irq_off(irqs);
+ return 1; /* drive timed-out */
+ }
+ ide_delay_50ms(); /* give drive a breather */
+ } while (IN_BYTE(hd_status) & BUSY_STAT);
+
+ ide_delay_50ms(); /* wait for IRQ and DRQ_STAT */
+ if (OK_STAT(GET_STAT(),DRQ_STAT,BAD_R_STAT)) {
+ unsigned long flags;
+ __save_flags(flags); /* local CPU only */
+ __cli(); /* local CPU only; some systems need this */
+ do_identify(drive, cmd); /* drive returned ID */
+ rc = 0; /* drive responded with ID */
+ (void) GET_STAT(); /* clear drive IRQ */
+ __restore_flags(flags); /* local CPU only */
+ } else
+ rc = 2; /* drive refused ID */
+ if (IDE_CONTROL_REG && !HWIF(drive)->irq) {
+ irqs = probe_irq_off(irqs); /* get our irq number */
+ if (irqs > 0) {
+ HWIF(drive)->irq = irqs; /* save it for later */
+ irqs = probe_irq_on();
+ OUT_BYTE(drive->ctl|2,IDE_CONTROL_REG); /* mask device irq */
+ udelay(5);
+ (void) probe_irq_off(irqs);
+ (void) probe_irq_off(probe_irq_on()); /* clear self-inflicted irq */
+ (void) GET_STAT(); /* clear drive IRQ */
+
+ } else { /* Mmmm.. multiple IRQs.. don't know which was ours */
+ printk("%s: IRQ probe failed (%ld)\n", drive->name, irqs);
+#ifdef CONFIG_BLK_DEV_CMD640
+#ifdef CMD640_DUMP_REGS
+ if (HWIF(drive)->chipset == ide_cmd640) {
+ printk("%s: Hmmm.. probably a driver problem.\n", drive->name);
+ CMD640_DUMP_REGS;
+ }
+#endif /* CMD640_DUMP_REGS */
+#endif /* CONFIG_BLK_DEV_CMD640 */
+ }
+ }
+ return rc;
+}
+
+/*
+ * do_probe() has the difficult job of finding a drive if it exists,
+ * without getting hung up if it doesn't exist, without trampling on
+ * ethernet cards, and without leaving any IRQs dangling to haunt us later.
+ *
+ * If a drive is "known" to exist (from CMOS or kernel parameters),
+ * but does not respond right away, the probe will "hang in there"
+ * for the maximum wait time (about 30 seconds), otherwise it will
+ * exit much more quickly.
+ *
+ * Returns: 0 device was identified
+ * 1 device timed-out (no response to identify request)
+ * 2 device aborted the command (refused to identify itself)
+ * 3 bad status from device (possible for ATAPI drives)
+ * 4 probe was not attempted because failure was obvious
+ */
+static int do_probe (ide_drive_t *drive, byte cmd)
+{
+ int rc;
+ ide_hwif_t *hwif = HWIF(drive);
+ if (drive->present) { /* avoid waiting for inappropriate probes */
+ if ((drive->media != ide_disk) && (cmd == WIN_IDENTIFY))
+ return 4;
+ }
+#ifdef DEBUG
+ printk("probing for %s: present=%d, media=%d, probetype=%s\n",
+ drive->name, drive->present, drive->media,
+ (cmd == WIN_IDENTIFY) ? "ATA" : "ATAPI");
+#endif
+ ide_delay_50ms(); /* needed for some systems (e.g. crw9624 as drive0 with disk as slave) */
+ SELECT_DRIVE(hwif,drive);
+ ide_delay_50ms();
+ if (IN_BYTE(IDE_SELECT_REG) != drive->select.all && !drive->present) {
+ if (drive->select.b.unit != 0) {
+ SELECT_DRIVE(hwif,&hwif->drives[0]); /* exit with drive0 selected */
+ ide_delay_50ms(); /* allow BUSY_STAT to assert & clear */
+ }
+ return 3; /* no i/f present: mmm.. this should be a 4 -ml */
+ }
+
+ if (OK_STAT(GET_STAT(),READY_STAT,BUSY_STAT)
+ || drive->present || cmd == WIN_PIDENTIFY)
+ {
+ if ((rc = try_to_identify(drive,cmd))) /* send cmd and wait */
+ rc = try_to_identify(drive,cmd); /* failed: try again */
+ if (rc == 1 && cmd == WIN_PIDENTIFY && drive->autotune != 2) {
+ unsigned long timeout;
+ printk("%s: no response (status = 0x%02x), resetting drive\n", drive->name, GET_STAT());
+ ide_delay_50ms();
+ OUT_BYTE (drive->select.all, IDE_SELECT_REG);
+ ide_delay_50ms();
+ OUT_BYTE(WIN_SRST, IDE_COMMAND_REG);
+ timeout = jiffies;
+ while ((GET_STAT() & BUSY_STAT) && time_before(jiffies, timeout + WAIT_WORSTCASE))
+ ide_delay_50ms();
+ rc = try_to_identify(drive, cmd);
+ }
+ if (rc == 1)
+ printk("%s: no response (status = 0x%02x)\n", drive->name, GET_STAT());
+ (void) GET_STAT(); /* ensure drive irq is clear */
+ } else {
+ rc = 3; /* not present or maybe ATAPI */
+ }
+ if (drive->select.b.unit != 0) {
+ SELECT_DRIVE(hwif,&hwif->drives[0]); /* exit with drive0 selected */
+ ide_delay_50ms();
+ (void) GET_STAT(); /* ensure drive irq is clear */
+ }
+ return rc;
+}
+
+/*
+ *
+ */
+static void enable_nest (ide_drive_t *drive)
+{
+ unsigned long timeout;
+
+ printk("%s: enabling %s -- ", HWIF(drive)->name, drive->id->model);
+ SELECT_DRIVE(HWIF(drive), drive);
+ ide_delay_50ms();
+ OUT_BYTE(EXABYTE_ENABLE_NEST, IDE_COMMAND_REG);
+ timeout = jiffies + WAIT_WORSTCASE;
+ do {
+ if (jiffies > timeout) {
+ printk("failed (timeout)\n");
+ return;
+ }
+ ide_delay_50ms();
+ } while (GET_STAT() & BUSY_STAT);
+ ide_delay_50ms();
+ if (!OK_STAT(GET_STAT(), 0, BAD_STAT))
+ printk("failed (status = 0x%02x)\n", GET_STAT());
+ else
+ printk("success\n");
+ if (do_probe(drive, WIN_IDENTIFY) >= 2) { /* if !(success||timed-out) */
+ (void) do_probe(drive, WIN_PIDENTIFY); /* look for ATAPI device */
+ }
+}
+
+/*
+ * probe_for_drive() tests for existence of a given drive using do_probe().
+ *
+ * Returns: 0 no device was found
+ * 1 device was found (note: drive->present might still be 0)
+ */
+static inline byte probe_for_drive (ide_drive_t *drive)
+{
+ if (drive->noprobe) /* skip probing? */
+ return drive->present;
+ if (do_probe(drive, WIN_IDENTIFY) >= 2) { /* if !(success||timed-out) */
+ (void) do_probe(drive, WIN_PIDENTIFY); /* look for ATAPI device */
+ }
+ if (drive->id && strstr(drive->id->model, "E X A B Y T E N E S T"))
+ enable_nest(drive);
+ if (!drive->present)
+ return 0; /* drive not found */
+ if (drive->id == NULL) { /* identification failed? */
+ if (drive->media == ide_disk) {
+ printk ("%s: non-IDE drive, CHS=%d/%d/%d\n",
+ drive->name, drive->cyl, drive->head, drive->sect);
+ } else if (drive->media == ide_cdrom) {
+ printk("%s: ATAPI cdrom (?)\n", drive->name);
+ } else {
+ drive->present = 0; /* nuke it */
+ }
+ }
+ return 1; /* drive was found */
+}
+
+/*
+ * Calculate the region that this interface occupies,
+ * handling interfaces where the registers may not be
+ * ordered sanely. We deal with the CONTROL register
+ * separately.
+ */
+static int hwif_check_regions (ide_hwif_t *hwif)
+{
+ int region_errors = 0;
+
+ hwif->straight8 = 0;
+ region_errors = ide_check_region(hwif->io_ports[IDE_DATA_OFFSET], 1);
+ region_errors += ide_check_region(hwif->io_ports[IDE_ERROR_OFFSET], 1);
+ region_errors += ide_check_region(hwif->io_ports[IDE_NSECTOR_OFFSET], 1);
+ region_errors += ide_check_region(hwif->io_ports[IDE_SECTOR_OFFSET], 1);
+ region_errors += ide_check_region(hwif->io_ports[IDE_LCYL_OFFSET], 1);
+ region_errors += ide_check_region(hwif->io_ports[IDE_HCYL_OFFSET], 1);
+ region_errors += ide_check_region(hwif->io_ports[IDE_SELECT_OFFSET], 1);
+ region_errors += ide_check_region(hwif->io_ports[IDE_STATUS_OFFSET], 1);
+
+ if (hwif->io_ports[IDE_CONTROL_OFFSET])
+ region_errors += ide_check_region(hwif->io_ports[IDE_CONTROL_OFFSET], 1);
+
+ if (hwif->io_ports[IDE_IRQ_OFFSET])
+ region_errors += ide_check_region(hwif->io_ports[IDE_IRQ_OFFSET], 1);
+
+ /*
+ * If any errors are return, we drop the hwif interface.
+ */
+ return(region_errors);
+}
+
+static void hwif_register (ide_hwif_t *hwif)
+{
+ if ((hwif->io_ports[IDE_DATA_OFFSET] | 7) ==
+ (hwif->io_ports[IDE_STATUS_OFFSET])) {
+ ide_request_region(hwif->io_ports[IDE_DATA_OFFSET], 8, hwif->name);
+ hwif->straight8 = 1;
+ goto jump_straight8;
+ }
+
+ if (hwif->io_ports[IDE_DATA_OFFSET])
+ ide_request_region(hwif->io_ports[IDE_DATA_OFFSET], 1, hwif->name);
+ if (hwif->io_ports[IDE_ERROR_OFFSET])
+ ide_request_region(hwif->io_ports[IDE_ERROR_OFFSET], 1, hwif->name);
+ if (hwif->io_ports[IDE_NSECTOR_OFFSET])
+ ide_request_region(hwif->io_ports[IDE_NSECTOR_OFFSET], 1, hwif->name);
+ if (hwif->io_ports[IDE_SECTOR_OFFSET])
+ ide_request_region(hwif->io_ports[IDE_SECTOR_OFFSET], 1, hwif->name);
+ if (hwif->io_ports[IDE_LCYL_OFFSET])
+ ide_request_region(hwif->io_ports[IDE_LCYL_OFFSET], 1, hwif->name);
+ if (hwif->io_ports[IDE_HCYL_OFFSET])
+ ide_request_region(hwif->io_ports[IDE_HCYL_OFFSET], 1, hwif->name);
+ if (hwif->io_ports[IDE_SELECT_OFFSET])
+ ide_request_region(hwif->io_ports[IDE_SELECT_OFFSET], 1, hwif->name);
+ if (hwif->io_ports[IDE_STATUS_OFFSET])
+ ide_request_region(hwif->io_ports[IDE_STATUS_OFFSET], 1, hwif->name);
+
+jump_straight8:
+ if (hwif->io_ports[IDE_CONTROL_OFFSET])
+ ide_request_region(hwif->io_ports[IDE_CONTROL_OFFSET], 1, hwif->name);
+ if (hwif->io_ports[IDE_IRQ_OFFSET])
+ ide_request_region(hwif->io_ports[IDE_IRQ_OFFSET], 1, hwif->name);
+}
+
+/*
+ * This routine only knows how to look for drive units 0 and 1
+ * on an interface, so any setting of MAX_DRIVES > 2 won't work here.
+ */
+static void probe_hwif (ide_hwif_t *hwif)
+{
+ unsigned int unit;
+ unsigned long flags;
+
+ if (hwif->noprobe)
+ return;
+ if (hwif->io_ports[IDE_DATA_OFFSET] == HD_DATA) {
+ extern void probe_cmos_for_drives(ide_hwif_t *);
+
+ probe_cmos_for_drives (hwif);
+ }
+
+ if ((hwif->chipset != ide_4drives || !hwif->mate->present) &&
+#if CONFIG_BLK_DEV_PDC4030
+ (hwif->chipset != ide_pdc4030 || hwif->channel == 0) &&
+#endif /* CONFIG_BLK_DEV_PDC4030 */
+ (hwif_check_regions(hwif))) {
+ int msgout = 0;
+ for (unit = 0; unit < MAX_DRIVES; ++unit) {
+ ide_drive_t *drive = &hwif->drives[unit];
+ if (drive->present) {
+ drive->present = 0;
+ printk("%s: ERROR, PORTS ALREADY IN USE\n", drive->name);
+ msgout = 1;
+ }
+ }
+ if (!msgout)
+ printk("%s: ports already in use, skipping probe\n", hwif->name);
+ return;
+ }
+
+ __save_flags(flags); /* local CPU only */
+ __sti(); /* local CPU only; needed for jiffies and irq probing */
+ /*
+ * Second drive should only exist if first drive was found,
+ * but a lot of cdrom drives are configured as single slaves.
+ */
+ for (unit = 0; unit < MAX_DRIVES; ++unit) {
+ ide_drive_t *drive = &hwif->drives[unit];
+ (void) probe_for_drive (drive);
+ if (drive->present && !hwif->present) {
+ hwif->present = 1;
+ if (hwif->chipset != ide_4drives || !hwif->mate->present) {
+ hwif_register(hwif);
+ }
+ }
+ }
+ if (hwif->io_ports[IDE_CONTROL_OFFSET] && hwif->reset) {
+ unsigned long timeout = jiffies + WAIT_WORSTCASE;
+ byte stat;
+
+ printk("%s: reset\n", hwif->name);
+ OUT_BYTE(12, hwif->io_ports[IDE_CONTROL_OFFSET]);
+ udelay(10);
+ OUT_BYTE(8, hwif->io_ports[IDE_CONTROL_OFFSET]);
+ do {
+ ide_delay_50ms();
+ stat = IN_BYTE(hwif->io_ports[IDE_STATUS_OFFSET]);
+ } while ((stat & BUSY_STAT) && 0 < (signed long)(timeout - jiffies));
+
+ }
+ __restore_flags(flags); /* local CPU only */
+ for (unit = 0; unit < MAX_DRIVES; ++unit) {
+ ide_drive_t *drive = &hwif->drives[unit];
+ if (drive->present) {
+ ide_tuneproc_t *tuneproc = HWIF(drive)->tuneproc;
+ if (tuneproc != NULL && drive->autotune == 1)
+ tuneproc(drive, 255); /* auto-tune PIO mode */
+ }
+ }
+}
+
+#if MAX_HWIFS > 1
+/*
+ * save_match() is used to simplify logic in init_irq() below.
+ *
+ * A loophole here is that we may not know about a particular
+ * hwif's irq until after that hwif is actually probed/initialized..
+ * This could be a problem for the case where an hwif is on a
+ * dual interface that requires serialization (eg. cmd640) and another
+ * hwif using one of the same irqs is initialized beforehand.
+ *
+ * This routine detects and reports such situations, but does not fix them.
+ */
+static void save_match (ide_hwif_t *hwif, ide_hwif_t *new, ide_hwif_t **match)
+{
+ ide_hwif_t *m = *match;
+
+ if (m && m->hwgroup && m->hwgroup != new->hwgroup) {
+ if (!new->hwgroup)
+ return;
+ printk("%s: potential irq problem with %s and %s\n", hwif->name, new->name, m->name);
+ }
+ if (!m || m->irq != hwif->irq) /* don't undo a prior perfect match */
+ *match = new;
+}
+#endif /* MAX_HWIFS > 1 */
+
+/*
+ * This routine sets up the irq for an ide interface, and creates a new
+ * hwgroup for the irq/hwif if none was previously assigned.
+ *
+ * Much of the code is for correctly detecting/handling irq sharing
+ * and irq serialization situations. This is somewhat complex because
+ * it handles static as well as dynamic (PCMCIA) IDE interfaces.
+ *
+ * The SA_INTERRUPT in sa_flags means ide_intr() is always entered with
+ * interrupts completely disabled. This can be bad for interrupt latency,
+ * but anything else has led to problems on some machines. We re-enable
+ * interrupts as much as we can safely do in most places.
+ */
+static int init_irq (ide_hwif_t *hwif)
+{
+ unsigned long flags;
+ unsigned int index;
+ ide_hwgroup_t *hwgroup;
+ ide_hwif_t *match = NULL;
+
+ save_flags(flags); /* all CPUs */
+ cli(); /* all CPUs */
+
+ hwif->hwgroup = NULL;
+#if MAX_HWIFS > 1
+ /*
+ * Group up with any other hwifs that share our irq(s).
+ */
+ for (index = 0; index < MAX_HWIFS; index++) {
+ ide_hwif_t *h = &ide_hwifs[index];
+ if (h->hwgroup) { /* scan only initialized hwif's */
+ if (hwif->irq == h->irq) {
+ hwif->sharing_irq = h->sharing_irq = 1;
+ if (hwif->chipset != ide_pci || h->chipset != ide_pci) {
+ save_match(hwif, h, &match);
+ }
+ }
+ if (hwif->serialized) {
+ if (hwif->mate && hwif->mate->irq == h->irq)
+ save_match(hwif, h, &match);
+ }
+ if (h->serialized) {
+ if (h->mate && hwif->irq == h->mate->irq)
+ save_match(hwif, h, &match);
+ }
+ }
+ }
+#endif /* MAX_HWIFS > 1 */
+ /*
+ * If we are still without a hwgroup, then form a new one
+ */
+ if (match) {
+ hwgroup = match->hwgroup;
+ } else {
+ hwgroup = kmalloc(sizeof(ide_hwgroup_t), GFP_KERNEL);
+ memset(hwgroup, 0, sizeof(ide_hwgroup_t));
+ hwgroup->hwif = hwif->next = hwif;
+ hwgroup->rq = NULL;
+ hwgroup->handler = NULL;
+ hwgroup->drive = NULL;
+ hwgroup->busy = 0;
+ init_timer(&hwgroup->timer);
+ hwgroup->timer.function = &ide_timer_expiry;
+ hwgroup->timer.data = (unsigned long) hwgroup;
+ }
+
+ /*
+ * Allocate the irq, if not already obtained for another hwif
+ */
+ if (!match || match->irq != hwif->irq) {
+#ifdef CONFIG_IDEPCI_SHARE_IRQ
+ int sa = (hwif->chipset == ide_pci) ? SA_SHIRQ : SA_INTERRUPT;
+#else /* !CONFIG_IDEPCI_SHARE_IRQ */
+ int sa = (hwif->chipset == ide_pci) ? SA_INTERRUPT|SA_SHIRQ : SA_INTERRUPT;
+#endif /* CONFIG_IDEPCI_SHARE_IRQ */
+ if (ide_request_irq(hwif->irq, &ide_intr, sa, hwif->name, hwgroup)) {
+ if (!match)
+ kfree(hwgroup);
+ restore_flags(flags); /* all CPUs */
+ return 1;
+ }
+ }
+
+ /*
+ * Everything is okay, so link us into the hwgroup
+ */
+ hwif->hwgroup = hwgroup;
+ hwif->next = hwgroup->hwif->next;
+ hwgroup->hwif->next = hwif;
+
+ for (index = 0; index < MAX_DRIVES; ++index) {
+ ide_drive_t *drive = &hwif->drives[index];
+ if (!drive->present)
+ continue;
+ if (!hwgroup->drive)
+ hwgroup->drive = drive;
+ drive->next = hwgroup->drive->next;
+ hwgroup->drive->next = drive;
+ }
+ if (!hwgroup->hwif) {
+ hwgroup->hwif = HWIF(hwgroup->drive);
+#ifdef DEBUG
+ printk("%s : Adding missed hwif to hwgroup!!\n", hwif->name);
+#endif
+ }
+ restore_flags(flags); /* all CPUs; safe now that hwif->hwgroup is set up */
+
+#if !defined(__mc68000__) && !defined(CONFIG_APUS) && !defined(__sparc__)
+ printk("%s at 0x%03x-0x%03x,0x%03x on irq %d", hwif->name,
+ hwif->io_ports[IDE_DATA_OFFSET],
+ hwif->io_ports[IDE_DATA_OFFSET]+7,
+ hwif->io_ports[IDE_CONTROL_OFFSET], hwif->irq);
+#elif defined(__sparc__)
+ printk("%s at 0x%03lx-0x%03lx,0x%03lx on irq %s", hwif->name,
+ hwif->io_ports[IDE_DATA_OFFSET],
+ hwif->io_ports[IDE_DATA_OFFSET]+7,
+ hwif->io_ports[IDE_CONTROL_OFFSET], __irq_itoa(hwif->irq));
+#else
+ printk("%s at %p on irq 0x%08x", hwif->name,
+ hwif->io_ports[IDE_DATA_OFFSET], hwif->irq);
+#endif /* __mc68000__ && CONFIG_APUS */
+ if (match)
+ printk(" (%sed with %s)",
+ hwif->sharing_irq ? "shar" : "serializ", match->name);
+ printk("\n");
+ return 0;
+}
+
+/*
+ * init_gendisk() (as opposed to ide_geninit) is called for each major device,
+ * after probing for drives, to allocate partition tables and other data
+ * structures needed for the routines in genhd.c. ide_geninit() gets called
+ * somewhat later, during the partition check.
+ */
+static void init_gendisk (ide_hwif_t *hwif)
+{
+ struct gendisk *gd, **gdp;
+ unsigned int unit, units, minors;
+ int *bs, *max_sect, *max_ra;
+ extern devfs_handle_t ide_devfs_handle;
+
+ /* figure out maximum drive number on the interface */
+ for (units = MAX_DRIVES; units > 0; --units) {
+ if (hwif->drives[units-1].present)
+ break;
+ }
+ minors = units * (1<<PARTN_BITS);
+ gd = kmalloc (sizeof(struct gendisk), GFP_KERNEL);
+ gd->sizes = kmalloc (minors * sizeof(int), GFP_KERNEL);
+ gd->part = kmalloc (minors * sizeof(struct hd_struct), GFP_KERNEL);
+ bs = kmalloc (minors*sizeof(int), GFP_KERNEL);
+ max_sect = kmalloc (minors*sizeof(int), GFP_KERNEL);
+ max_ra = kmalloc (minors*sizeof(int), GFP_KERNEL);
+
+ memset(gd->part, 0, minors * sizeof(struct hd_struct));
+
+ /* cdroms and msdos f/s are examples of non-1024 blocksizes */
+ blksize_size[hwif->major] = bs;
+ max_sectors[hwif->major] = max_sect;
+ max_readahead[hwif->major] = max_ra;
+ for (unit = 0; unit < minors; ++unit) {
+ *bs++ = BLOCK_SIZE;
+#ifdef CONFIG_BLK_DEV_PDC4030
+ *max_sect++ = ((hwif->chipset == ide_pdc4030) ? 127 : MAX_SECTORS);
+#else
+ *max_sect++ = MAX_SECTORS;
+#endif
+ *max_ra++ = MAX_READAHEAD;
+ }
+
+ for (unit = 0; unit < units; ++unit)
+ hwif->drives[unit].part = &gd->part[unit << PARTN_BITS];
+
+ gd->major = hwif->major; /* our major device number */
+ gd->major_name = IDE_MAJOR_NAME; /* treated special in genhd.c */
+ gd->minor_shift = PARTN_BITS; /* num bits for partitions */
+ gd->max_p = 1<<PARTN_BITS; /* 1 + max partitions / drive */
+ gd->nr_real = units; /* current num real drives */
+ gd->real_devices= hwif; /* ptr to internal data */
+ gd->next = NULL; /* linked list of major devs */
+ gd->fops = ide_fops; /* file operations */
+ gd->de_arr = kmalloc (sizeof *gd->de_arr * units, GFP_KERNEL);
+ gd->flags = kmalloc (sizeof *gd->flags * units, GFP_KERNEL);
+ if (gd->de_arr)
+ memset (gd->de_arr, 0, sizeof *gd->de_arr * units);
+ if (gd->flags)
+ memset (gd->flags, 0, sizeof *gd->flags * units);
+
+ for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next)) ;
+ hwif->gd = *gdp = gd; /* link onto tail of list */
+
+ for (unit = 0; unit < units; ++unit) {
+ if (hwif->drives[unit].present) {
+ char name[64];
+
+ ide_add_generic_settings(hwif->drives + unit);
+ sprintf (name, "host%d/bus%d/target%d/lun%d",
+ (hwif->channel && hwif->mate) ? hwif->mate->index : hwif->index,
+ hwif->channel, unit, 0);
+ hwif->drives[unit].de =
+ devfs_mk_dir (ide_devfs_handle, name, 0, NULL);
+ }
+ }
+}
+
+static int hwif_init (ide_hwif_t *hwif)
+{
+ ide_drive_t *drive;
+ void (*rfn)(request_queue_t *);
+
+ if (!hwif->present)
+ return 0;
+ if (!hwif->irq) {
+ if (!(hwif->irq = ide_default_irq(hwif->io_ports[IDE_DATA_OFFSET])))
+ {
+ printk("%s: DISABLED, NO IRQ\n", hwif->name);
+ return (hwif->present = 0);
+ }
+ }
+#ifdef CONFIG_BLK_DEV_HD
+ if (hwif->irq == HD_IRQ && hwif->io_ports[IDE_DATA_OFFSET] != HD_DATA) {
+ printk("%s: CANNOT SHARE IRQ WITH OLD HARDDISK DRIVER (hd.c)\n", hwif->name);
+ return (hwif->present = 0);
+ }
+#endif /* CONFIG_BLK_DEV_HD */
+
+ hwif->present = 0; /* we set it back to 1 if all is ok below */
+ switch (hwif->major) {
+ case IDE0_MAJOR: rfn = &do_ide0_request; break;
+#if MAX_HWIFS > 1
+ case IDE1_MAJOR: rfn = &do_ide1_request; break;
+#endif
+#if MAX_HWIFS > 2
+ case IDE2_MAJOR: rfn = &do_ide2_request; break;
+#endif
+#if MAX_HWIFS > 3
+ case IDE3_MAJOR: rfn = &do_ide3_request; break;
+#endif
+#if MAX_HWIFS > 4
+ case IDE4_MAJOR: rfn = &do_ide4_request; break;
+#endif
+#if MAX_HWIFS > 5
+ case IDE5_MAJOR: rfn = &do_ide5_request; break;
+#endif
+#if MAX_HWIFS > 6
+ case IDE6_MAJOR: rfn = &do_ide6_request; break;
+#endif
+#if MAX_HWIFS > 7
+ case IDE7_MAJOR: rfn = &do_ide7_request; break;
+#endif
+#if MAX_HWIFS > 8
+ case IDE8_MAJOR: rfn = &do_ide8_request; break;
+#endif
+#if MAX_HWIFS > 9
+ case IDE9_MAJOR: rfn = &do_ide9_request; break;
+#endif
+ default:
+ printk("%s: request_fn NOT DEFINED\n", hwif->name);
+ return (hwif->present = 0);
+ }
+ if (devfs_register_blkdev (hwif->major, hwif->name, ide_fops)) {
+ printk("%s: UNABLE TO GET MAJOR NUMBER %d\n", hwif->name, hwif->major);
+ return (hwif->present = 0);
+ }
+
+ if (init_irq(hwif)) {
+ int i = hwif->irq;
+ /*
+ * It failed to initialise. Find the default IRQ for
+ * this port and try that.
+ */
+ if (!(hwif->irq = ide_default_irq(hwif->io_ports[IDE_DATA_OFFSET]))) {
+ printk("%s: Disabled unable to get IRQ %d.\n", hwif->name, i);
+ (void) unregister_blkdev (hwif->major, hwif->name);
+ return (hwif->present = 0);
+ }
+ if (init_irq(hwif)) {
+ printk("%s: probed IRQ %d and default IRQ %d failed.\n",
+ hwif->name, i, hwif->irq);
+ (void) unregister_blkdev (hwif->major, hwif->name);
+ return (hwif->present = 0);
+ }
+ printk("%s: probed IRQ %d failed, using default.\n",
+ hwif->name, hwif->irq);
+ }
+
+ init_gendisk(hwif);
+ blk_dev[hwif->major].data = hwif;
+ blk_dev[hwif->major].queue = ide_get_queue;
+ read_ahead[hwif->major] = 8; /* (4kB) */
+ hwif->present = 1; /* success */
+
+ /*
+ * FIXME(eric) - This needs to be tested. I *think* that this
+ * is correct. Also, I believe that there is no longer any
+ * reason to have multiple functions (do_ide[0-7]_request)
+ * functions - the queuedata field could be used to indicate
+ * the correct hardware group - either this, or we could add
+ * a new field to request_queue_t to hold this information.
+ */
+ drive = &hwif->drives[0];
+ blk_init_queue(&drive->queue, rfn);
+
+ drive = &hwif->drives[1];
+ blk_init_queue(&drive->queue, rfn);
+
+#if (DEBUG_SPINLOCK > 0)
+{
+ static int done = 0;
+ if (!done++)
+ printk("io_request_lock is %p\n", &io_request_lock); /* FIXME */
+}
+#endif
+ return hwif->present;
+}
+
+int ideprobe_init (void);
+static ide_module_t ideprobe_module = {
+ IDE_PROBE_MODULE,
+ ideprobe_init,
+ NULL
+};
+
+int ideprobe_init (void)
+{
+ unsigned int index;
+ int probe[MAX_HWIFS];
+
+ MOD_INC_USE_COUNT;
+ memset(probe, 0, MAX_HWIFS * sizeof(int));
+ for (index = 0; index < MAX_HWIFS; ++index)
+ probe[index] = !ide_hwifs[index].present;
+
+ /*
+ * Probe for drives in the usual way.. CMOS/BIOS, then poke at ports
+ */
+ for (index = 0; index < MAX_HWIFS; ++index)
+ if (probe[index])
+ probe_hwif(&ide_hwifs[index]);
+ for (index = 0; index < MAX_HWIFS; ++index)
+ if (probe[index])
+ hwif_init(&ide_hwifs[index]);
+ if (!ide_probe)
+ ide_probe = &ideprobe_module;
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+#ifdef MODULE
+int init_module (void)
+{
+ unsigned int index;
+
+ for (index = 0; index < MAX_HWIFS; ++index)
+ ide_unregister(index);
+ ideprobe_init();
+ create_proc_ide_interfaces();
+ return 0;
+}
+
+void cleanup_module (void)
+{
+ ide_probe = NULL;
+}
+#endif /* MODULE */
diff --git a/drivers/ide/ide-proc.c b/drivers/ide/ide-proc.c
new file mode 100644
index 000000000..753597e9c
--- /dev/null
+++ b/drivers/ide/ide-proc.c
@@ -0,0 +1,904 @@
+/*
+ * linux/drivers/block/ide-proc.c Version 1.03 January 2, 1998
+ *
+ * Copyright (C) 1997-1998 Mark Lord
+ */
+
+/*
+ * This is the /proc/ide/ filesystem implementation.
+ *
+ * The major reason this exists is to provide sufficient access
+ * to driver and config data, such that user-mode programs can
+ * be developed to handle chipset tuning for most PCI interfaces.
+ * This should provide better utilities, and less kernel bloat.
+ *
+ * The entire pci config space for a PCI interface chipset can be
+ * retrieved by just reading it. e.g. "cat /proc/ide3/config"
+ *
+ * To modify registers *safely*, do something like:
+ * echo "P40:88" >/proc/ide/ide3/config
+ * That expression writes 0x88 to pci config register 0x40
+ * on the chip which controls ide3. Multiple tuples can be issued,
+ * and the writes will be completed as an atomic set:
+ * echo "P40:88 P41:35 P42:00 P43:00" >/proc/ide/ide3/config
+ *
+ * All numbers must be specified using pairs of ascii hex digits.
+ * It is important to note that these writes will be performed
+ * after waiting for the IDE controller (both interfaces)
+ * to be completely idle, to ensure no corruption of I/O in progress.
+ *
+ * Non-PCI registers can also be written, using "R" in place of "P"
+ * in the above examples. The size of the port transfer is determined
+ * by the number of pairs of hex digits given for the data. If a two
+ * digit value is given, the write will be a byte operation; if four
+ * digits are used, the write will be performed as a 16-bit operation;
+ * and if eight digits are specified, a 32-bit "dword" write will be
+ * performed. Odd numbers of digits are not permitted.
+ *
+ * If there is an error *anywhere* in the string of registers/data
+ * then *none* of the writes will be performed.
+ *
+ * Drive/Driver settings can be retrieved by reading the drive's
+ * "settings" files. e.g. "cat /proc/ide0/hda/settings"
+ * To write a new value "val" into a specific setting "name", use:
+ * echo "name:val" >/proc/ide/ide0/hda/settings
+ *
+ * Also useful, "cat /proc/ide0/hda/[identify, smart_values,
+ * smart_thresholds, capabilities]" will issue an IDENTIFY /
+ * PACKET_IDENTIFY / SMART_READ_VALUES / SMART_READ_THRESHOLDS /
+ * SENSE CAPABILITIES command to /dev/hda, and then dump out the
+ * returned data as 256 16-bit words. The "hdparm" utility will
+ * be updated someday soon to use this mechanism.
+ *
+ * Feel free to develop and distribute fancy GUI configuration
+ * utilities for your favorite PCI chipsets. I'll be working on
+ * one for the Promise 20246 someday soon. -ml
+ *
+ */
+
+#include <linux/config.h>
+#include <asm/uaccess.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/mm.h>
+#include <linux/pci.h>
+#include <linux/ctype.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+
+#ifndef MIN
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
+#ifdef CONFIG_BLK_DEV_AEC6210
+extern byte aec6210_proc;
+int (*aec6210_display_info)(char *, char **, off_t, int) = NULL;
+#endif /* CONFIG_BLK_DEV_AEC6210 */
+#ifdef CONFIG_BLK_DEV_ALI15X3
+extern byte ali_proc;
+int (*ali_display_info)(char *, char **, off_t, int) = NULL;
+#endif /* CONFIG_BLK_DEV_ALI15X3 */
+#ifdef CONFIG_BLK_DEV_AMD7409
+extern byte amd7409_proc;
+int (*amd7409_display_info)(char *, char **, off_t, int) = NULL;
+#endif /* CONFIG_BLK_DEV_AMD7409 */
+#ifdef CONFIG_BLK_DEV_CMD64X
+extern byte cmd64x_proc;
+int (*cmd64x_display_info)(char *, char **, off_t, int) = NULL;
+#endif /* CONFIG_BLK_DEV_CMD64X */
+#ifdef CONFIG_BLK_DEV_CS5530
+extern byte cs5530_proc;
+int (*cs5530_display_info)(char *, char **, off_t, int) = NULL;
+#endif /* CONFIG_BLK_DEV_CS5530 */
+#ifdef CONFIG_BLK_DEV_HPT34X
+extern byte hpt34x_proc;
+int (*hpt34x_display_info)(char *, char **, off_t, int) = NULL;
+#endif /* CONFIG_BLK_DEV_HPT34X */
+#ifdef CONFIG_BLK_DEV_HPT366
+extern byte hpt366_proc;
+int (*hpt366_display_info)(char *, char **, off_t, int) = NULL;
+#endif /* CONFIG_BLK_DEV_HPT366 */
+#ifdef CONFIG_BLK_DEV_PDC202XX
+extern byte pdc202xx_proc;
+int (*pdc202xx_display_info)(char *, char **, off_t, int) = NULL;
+#endif /* CONFIG_BLK_DEV_PDC202XX */
+#ifdef CONFIG_BLK_DEV_PIIX
+extern byte piix_proc;
+int (*piix_display_info)(char *, char **, off_t, int) = NULL;
+#endif /* CONFIG_BLK_DEV_PIIX */
+#ifdef CONFIG_BLK_DEV_SIS5513
+extern byte sis_proc;
+int (*sis_display_info)(char *, char **, off_t, int) = NULL;
+#endif /* CONFIG_BLK_DEV_SIS5513 */
+#ifdef CONFIG_BLK_DEV_VIA82CXXX
+extern byte via_proc;
+int (*via_display_info)(char *, char **, off_t, int) = NULL;
+#endif /* CONFIG_BLK_DEV_VIA82CXXX */
+
+static int ide_getxdigit(char c)
+{
+ int digit;
+ if (isdigit(c))
+ digit = c - '0';
+ else if (isxdigit(c))
+ digit = tolower(c) - 'a' + 10;
+ else
+ digit = -1;
+ return digit;
+}
+
+static int xx_xx_parse_error (const char *data, unsigned long len, const char *msg)
+{
+ char errbuf[16];
+ int i;
+ if (len >= sizeof(errbuf))
+ len = sizeof(errbuf) - 1;
+ for (i = 0; i < len; ++i) {
+ char c = data[i];
+ if (!c || c == '\n')
+ c = '\0';
+ else if (iscntrl(c))
+ c = '?';
+ errbuf[i] = c;
+ }
+ errbuf[i] = '\0';
+ printk("proc_ide: error: %s: '%s'\n", msg, errbuf);
+ return -EINVAL;
+}
+
+static struct proc_dir_entry * proc_ide_root = NULL;
+
+static int proc_ide_write_config
+ (struct file *file, const char *buffer, unsigned long count, void *data)
+{
+ ide_hwif_t *hwif = (ide_hwif_t *)data;
+ int for_real = 0;
+ unsigned long startn = 0, n, flags;
+ const char *start = NULL, *msg = NULL;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+ /*
+ * Skip over leading whitespace
+ */
+ while (count && isspace(*buffer)) {
+ --count;
+ ++buffer;
+ }
+ /*
+ * Do one full pass to verify all parameters,
+ * then do another to actually write the regs.
+ */
+ save_flags(flags); /* all CPUs */
+ do {
+ const char *p;
+ if (for_real) {
+ unsigned long timeout = jiffies + (3 * HZ);
+ ide_hwgroup_t *mygroup = (ide_hwgroup_t *)(hwif->hwgroup);
+ ide_hwgroup_t *mategroup = NULL;
+ if (hwif->mate && hwif->mate->hwgroup)
+ mategroup = (ide_hwgroup_t *)(hwif->mate->hwgroup);
+ cli(); /* all CPUs; ensure all writes are done together */
+ while (mygroup->busy || (mategroup && mategroup->busy)) {
+ sti(); /* all CPUs */
+ if (0 < (signed long)(jiffies - timeout)) {
+ printk("/proc/ide/%s/config: channel(s) busy, cannot write\n", hwif->name);
+ restore_flags(flags); /* all CPUs */
+ return -EBUSY;
+ }
+ cli(); /* all CPUs */
+ }
+ }
+ p = buffer;
+ n = count;
+ while (n > 0) {
+ int d, digits;
+ unsigned int reg = 0, val = 0, is_pci;
+ start = p;
+ startn = n--;
+ switch (*p++) {
+ case 'R': is_pci = 0;
+ break;
+ case 'P': is_pci = 1;
+#ifdef CONFIG_BLK_DEV_IDEPCI
+ if (hwif->pci_dev && !IDE_PCI_DEVID_EQ(hwif->pci_devid, IDE_PCI_DEVID_NULL))
+ break;
+#endif /* CONFIG_BLK_DEV_IDEPCI */
+ msg = "not a PCI device";
+ goto parse_error;
+ default: msg = "expected 'R' or 'P'";
+ goto parse_error;
+ }
+ digits = 0;
+ while (n > 0 && (d = ide_getxdigit(*p)) >= 0) {
+ reg = (reg << 4) | d;
+ --n;
+ ++p;
+ ++digits;
+ }
+ if (!digits || (digits > 4) || (is_pci && reg > 0xff)) {
+ msg = "bad/missing register number";
+ goto parse_error;
+ }
+ if (n-- == 0 || *p++ != ':') {
+ msg = "missing ':'";
+ goto parse_error;
+ }
+ digits = 0;
+ while (n > 0 && (d = ide_getxdigit(*p)) >= 0) {
+ val = (val << 4) | d;
+ --n;
+ ++p;
+ ++digits;
+ }
+ if (digits != 2 && digits != 4 && digits != 8) {
+ msg = "bad data, 2/4/8 digits required";
+ goto parse_error;
+ }
+ if (n > 0 && !isspace(*p)) {
+ msg = "expected whitespace after data";
+ goto parse_error;
+ }
+ while (n > 0 && isspace(*p)) {
+ --n;
+ ++p;
+ }
+#ifdef CONFIG_BLK_DEV_IDEPCI
+ if (is_pci && (reg & ((digits >> 1) - 1))) {
+ msg = "misaligned access";
+ goto parse_error;
+ }
+#endif /* CONFIG_BLK_DEV_IDEPCI */
+ if (for_real) {
+#if 0
+ printk("proc_ide_write_config: type=%c, reg=0x%x, val=0x%x, digits=%d\n", is_pci ? "PCI" : "non-PCI", reg, val, digits);
+#endif
+ if (is_pci) {
+#ifdef CONFIG_BLK_DEV_IDEPCI
+ int rc = 0;
+ struct pci_dev *dev = hwif->pci_dev;
+ switch (digits) {
+ case 2: msg = "byte";
+ rc = pci_write_config_byte(dev, reg, val);
+ break;
+ case 4: msg = "word";
+ rc = pci_write_config_word(dev, reg, val);
+ break;
+ case 8: msg = "dword";
+ rc = pci_write_config_dword(dev, reg, val);
+ break;
+ }
+ if (rc) {
+ restore_flags(flags); /* all CPUs */
+ printk("proc_ide_write_config: error writing %s at bus %02x dev %02x reg 0x%x value 0x%x\n",
+ msg, dev->bus->number, dev->devfn, reg, val);
+ printk("proc_ide_write_config: error %d\n", rc);
+ return -EIO;
+ }
+#endif /* CONFIG_BLK_DEV_IDEPCI */
+ } else { /* not pci */
+#if !defined(__mc68000__) && !defined(CONFIG_APUS)
+
+/*
+ * Geert Uytterhoeven
+ *
+ * unless you can explain me what it really does.
+ * On m68k, we don't have outw() and outl() yet,
+ * and I need a good reason to implement it.
+ *
+ * BTW, IMHO the main remaining portability problem with the IDE driver
+ * is that it mixes IO (ioport) and MMIO (iomem) access on different platforms.
+ *
+ * I think all accesses should be done using
+ *
+ * ide_in[bwl](ide_device_instance, offset)
+ * ide_out[bwl](ide_device_instance, value, offset)
+ *
+ * so the architecture specific code can #define ide_{in,out}[bwl] to the
+ * appropriate function.
+ *
+ */
+ switch (digits) {
+ case 2: outb(val, reg);
+ break;
+ case 4: outw(val, reg);
+ break;
+ case 8: outl(val, reg);
+ break;
+ }
+#endif /* !__mc68000__ && !CONFIG_APUS */
+ }
+ }
+ }
+ } while (!for_real++);
+ restore_flags(flags); /* all CPUs */
+ return count;
+parse_error:
+ restore_flags(flags); /* all CPUs */
+ printk("parse error\n");
+ return xx_xx_parse_error(start, startn, msg);
+}
+
+static int proc_ide_read_config
+ (char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+ char *out = page;
+ int len;
+
+#ifdef CONFIG_BLK_DEV_IDEPCI
+ ide_hwif_t *hwif = (ide_hwif_t *)data;
+ struct pci_dev *dev = hwif->pci_dev;
+ if (!IDE_PCI_DEVID_EQ(hwif->pci_devid, IDE_PCI_DEVID_NULL) && dev && dev->bus) {
+ int reg = 0;
+
+ out += sprintf(out, "pci bus %02x device %02x vid %04x did %04x channel %d\n",
+ dev->bus->number, dev->devfn, hwif->pci_devid.vid, hwif->pci_devid.did, hwif->channel);
+ do {
+ byte val;
+ int rc = pci_read_config_byte(dev, reg, &val);
+ if (rc) {
+ printk("proc_ide_read_config: error %d reading bus %02x dev %02x reg 0x%02x\n",
+ rc, dev->bus->number, dev->devfn, reg);
+ out += sprintf(out, "??%c", (++reg & 0xf) ? ' ' : '\n');
+ } else
+ out += sprintf(out, "%02x%c", val, (++reg & 0xf) ? ' ' : '\n');
+ } while (reg < 0x100);
+ } else
+#endif /* CONFIG_BLK_DEV_IDEPCI */
+ out += sprintf(out, "(none)\n");
+ len = out - page;
+ PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
+
+static int ide_getdigit(char c)
+{
+ int digit;
+ if (isdigit(c))
+ digit = c - '0';
+ else
+ digit = -1;
+ return digit;
+}
+
+static int proc_ide_read_drivers
+ (char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+ char *out = page;
+ int len;
+ ide_module_t *p = ide_modules;
+ ide_driver_t *driver;
+
+ while (p) {
+ driver = (ide_driver_t *) p->info;
+ if (driver)
+ out += sprintf(out, "%s version %s\n", driver->name, driver->version);
+ p = p->next;
+ }
+ len = out - page;
+ PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
+static int proc_ide_read_imodel
+ (char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+ ide_hwif_t *hwif = (ide_hwif_t *) data;
+ int len;
+ const char *name;
+
+ switch (hwif->chipset) {
+ case ide_unknown: name = "(none)"; break;
+ case ide_generic: name = "generic"; break;
+ case ide_pci: name = "pci"; break;
+ case ide_cmd640: name = "cmd640"; break;
+ case ide_dtc2278: name = "dtc2278"; break;
+ case ide_ali14xx: name = "ali14xx"; break;
+ case ide_qd6580: name = "qd6580"; break;
+ case ide_umc8672: name = "umc8672"; break;
+ case ide_ht6560b: name = "ht6560b"; break;
+ case ide_pdc4030: name = "pdc4030"; break;
+ case ide_rz1000: name = "rz1000"; break;
+ case ide_trm290: name = "trm290"; break;
+ case ide_cmd646: name = "cmd646"; break;
+ case ide_cy82c693: name = "cy82c693"; break;
+ case ide_4drives: name = "4drives"; break;
+ case ide_pmac: name = "mac-io"; break;
+ default: name = "(unknown)"; break;
+ }
+ len = sprintf(page, "%s\n", name);
+ PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
+static int proc_ide_read_mate
+ (char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+ ide_hwif_t *hwif = (ide_hwif_t *) data;
+ int len;
+
+ if (hwif && hwif->mate && hwif->mate->present)
+ len = sprintf(page, "%s\n", hwif->mate->name);
+ else
+ len = sprintf(page, "(none)\n");
+ PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
+static int proc_ide_read_channel
+ (char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+ ide_hwif_t *hwif = (ide_hwif_t *) data;
+ int len;
+
+ page[0] = hwif->channel ? '1' : '0';
+ page[1] = '\n';
+ len = 2;
+ PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
+static int proc_ide_get_identify(ide_drive_t *drive, byte *buf)
+{
+ return ide_wait_cmd(drive, (drive->media == ide_disk) ? WIN_IDENTIFY : WIN_PIDENTIFY, 0, 0, 1, buf);
+}
+
+static int proc_ide_read_identify
+ (char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+ ide_drive_t *drive = (ide_drive_t *)data;
+ int len = 0, i = 0;
+
+ if (drive && !proc_ide_get_identify(drive, page)) {
+ unsigned short *val = ((unsigned short *)page) + 2;
+ char *out = ((char *)val) + (SECTOR_WORDS * 4);
+ page = out;
+ do {
+ out += sprintf(out, "%04x%c", le16_to_cpu(*val), (++i & 7) ? ' ' : '\n');
+ val += 1;
+ } while (i < (SECTOR_WORDS * 2));
+ len = out - page;
+ }
+ else
+ len = sprintf(page, "\n");
+ PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
+static int proc_ide_read_settings
+ (char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+ ide_drive_t *drive = (ide_drive_t *) data;
+ ide_settings_t *setting = (ide_settings_t *) drive->settings;
+ char *out = page;
+ int len, rc, mul_factor, div_factor;
+
+ out += sprintf(out, "name\t\t\tvalue\t\tmin\t\tmax\t\tmode\n");
+ out += sprintf(out, "----\t\t\t-----\t\t---\t\t---\t\t----\n");
+ while(setting) {
+ mul_factor = setting->mul_factor;
+ div_factor = setting->div_factor;
+ out += sprintf(out, "%-24s", setting->name);
+ if ((rc = ide_read_setting(drive, setting)) >= 0)
+ out += sprintf(out, "%-16d", rc * mul_factor / div_factor);
+ else
+ out += sprintf(out, "%-16s", "write-only");
+ out += sprintf(out, "%-16d%-16d", (setting->min * mul_factor + div_factor - 1) / div_factor, setting->max * mul_factor / div_factor);
+ if (setting->rw & SETTING_READ)
+ out += sprintf(out, "r");
+ if (setting->rw & SETTING_WRITE)
+ out += sprintf(out, "w");
+ out += sprintf(out, "\n");
+ setting = setting->next;
+ }
+ len = out - page;
+ PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
+#define MAX_LEN 30
+
+static int proc_ide_write_settings
+ (struct file *file, const char *buffer, unsigned long count, void *data)
+{
+ ide_drive_t *drive = (ide_drive_t *) data;
+ char name[MAX_LEN + 1];
+ int for_real = 0, len;
+ unsigned long n;
+ const char *start = NULL;
+ ide_settings_t *setting;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+ /*
+ * Skip over leading whitespace
+ */
+ while (count && isspace(*buffer)) {
+ --count;
+ ++buffer;
+ }
+ /*
+ * Do one full pass to verify all parameters,
+ * then do another to actually write the new settings.
+ */
+ do {
+ const char *p;
+ p = buffer;
+ n = count;
+ while (n > 0) {
+ int d, digits;
+ unsigned int val = 0;
+ start = p;
+
+ while (n > 0 && *p != ':') {
+ --n;
+ p++;
+ }
+ if (*p != ':')
+ goto parse_error;
+ len = IDE_MIN(p - start, MAX_LEN);
+ strncpy(name, start, IDE_MIN(len, MAX_LEN));
+ name[len] = 0;
+
+ if (n > 0) {
+ --n;
+ p++;
+ } else
+ goto parse_error;
+
+ digits = 0;
+ while (n > 0 && (d = ide_getdigit(*p)) >= 0) {
+ val = (val * 10) + d;
+ --n;
+ ++p;
+ ++digits;
+ }
+ if (n > 0 && !isspace(*p))
+ goto parse_error;
+ while (n > 0 && isspace(*p)) {
+ --n;
+ ++p;
+ }
+ setting = ide_find_setting_by_name(drive, name);
+ if (!setting)
+ goto parse_error;
+
+ if (for_real)
+ ide_write_setting(drive, setting, val * setting->div_factor / setting->mul_factor);
+ }
+ } while (!for_real++);
+ return count;
+parse_error:
+ printk("proc_ide_write_settings(): parse error\n");
+ return -EINVAL;
+}
+
+int proc_ide_read_capacity
+ (char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+ ide_drive_t *drive = (ide_drive_t *) data;
+ ide_driver_t *driver = (ide_driver_t *) drive->driver;
+ int len;
+
+ if (!driver)
+ len = sprintf(page, "(none)\n");
+ else
+ len = sprintf(page,"%li\n", ((ide_driver_t *)drive->driver)->capacity(drive));
+ PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
+int proc_ide_read_geometry
+ (char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+ ide_drive_t *drive = (ide_drive_t *) data;
+ char *out = page;
+ int len;
+
+ out += sprintf(out,"physical %d/%d/%d\n", drive->cyl, drive->head, drive->sect);
+ out += sprintf(out,"logical %d/%d/%d\n", drive->bios_cyl, drive->bios_head, drive->bios_sect);
+ len = out - page;
+ PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
+static int proc_ide_read_dmodel
+ (char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+ ide_drive_t *drive = (ide_drive_t *) data;
+ struct hd_driveid *id = drive->id;
+ int len;
+
+ len = sprintf(page, "%.40s\n", (id && id->model[0]) ? (char *)id->model : "(none)");
+ PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
+static int proc_ide_read_driver
+ (char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+ ide_drive_t *drive = (ide_drive_t *) data;
+ ide_driver_t *driver = (ide_driver_t *) drive->driver;
+ int len;
+
+ if (!driver)
+ len = sprintf(page, "(none)\n");
+ else
+ len = sprintf(page, "%s version %s\n", driver->name, driver->version);
+ PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
+static int proc_ide_write_driver
+ (struct file *file, const char *buffer, unsigned long count, void *data)
+{
+ ide_drive_t *drive = (ide_drive_t *) data;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+ if (ide_replace_subdriver(drive, buffer))
+ return -EINVAL;
+ return count;
+}
+
+static int proc_ide_read_media
+ (char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+ ide_drive_t *drive = (ide_drive_t *) data;
+ const char *media;
+ int len;
+
+ switch (drive->media) {
+ case ide_disk: media = "disk\n";
+ break;
+ case ide_cdrom: media = "cdrom\n";
+ break;
+ case ide_tape: media = "tape\n";
+ break;
+ case ide_floppy:media = "floppy\n";
+ break;
+ default: media = "UNKNOWN\n";
+ break;
+ }
+ strcpy(page,media);
+ len = strlen(media);
+ PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
+static ide_proc_entry_t generic_drive_entries[] = {
+ { "driver", S_IFREG|S_IRUGO, proc_ide_read_driver, proc_ide_write_driver },
+ { "identify", S_IFREG|S_IRUSR, proc_ide_read_identify, NULL },
+ { "media", S_IFREG|S_IRUGO, proc_ide_read_media, NULL },
+ { "model", S_IFREG|S_IRUGO, proc_ide_read_dmodel, NULL },
+ { "settings", S_IFREG|S_IRUSR|S_IWUSR,proc_ide_read_settings, proc_ide_write_settings },
+ { NULL, 0, NULL, NULL }
+};
+
+void ide_add_proc_entries(struct proc_dir_entry *dir, ide_proc_entry_t *p, void *data)
+{
+ struct proc_dir_entry *ent;
+
+ if (!dir || !p)
+ return;
+ while (p->name != NULL) {
+ ent = create_proc_entry(p->name, p->mode, dir);
+ if (!ent) return;
+ ent->nlink = 1;
+ ent->data = data;
+ ent->read_proc = p->read_proc;
+ ent->write_proc = p->write_proc;
+ p++;
+ }
+}
+
+void ide_remove_proc_entries(struct proc_dir_entry *dir, ide_proc_entry_t *p)
+{
+ if (!dir || !p)
+ return;
+ while (p->name != NULL) {
+ remove_proc_entry(p->name, dir);
+ p++;
+ }
+}
+
+static void create_proc_ide_drives(ide_hwif_t *hwif)
+{
+ int d;
+ struct proc_dir_entry *ent;
+ struct proc_dir_entry *parent = hwif->proc;
+ char name[64];
+
+ for (d = 0; d < MAX_DRIVES; d++) {
+ ide_drive_t *drive = &hwif->drives[d];
+ ide_driver_t *driver = drive->driver;
+
+ if (!drive->present)
+ continue;
+ if (drive->proc)
+ continue;
+
+ drive->proc = proc_mkdir(drive->name, parent);
+ if (drive->proc) {
+ ide_add_proc_entries(drive->proc, generic_drive_entries, drive);
+ if (driver) {
+ ide_add_proc_entries(drive->proc, generic_subdriver_entries, drive);
+ ide_add_proc_entries(drive->proc, driver->proc, drive);
+ }
+ }
+ sprintf(name,"ide%d/%s", (drive->name[2]-'a')/2, drive->name);
+ ent = proc_symlink(drive->name, proc_ide_root, name);
+ if (!ent) return;
+ }
+}
+
+void destroy_proc_ide_drives(ide_hwif_t *hwif)
+{
+ int d;
+
+ for (d = 0; d < MAX_DRIVES; d++) {
+ ide_drive_t *drive = &hwif->drives[d];
+ ide_driver_t *driver = drive->driver;
+
+ if (!drive->proc)
+ continue;
+ if (driver)
+ ide_remove_proc_entries(drive->proc, driver->proc);
+ ide_remove_proc_entries(drive->proc, generic_drive_entries);
+ remove_proc_entry(drive->name, proc_ide_root);
+ remove_proc_entry(drive->name, hwif->proc);
+ drive->proc = NULL;
+ }
+}
+
+static ide_proc_entry_t hwif_entries[] = {
+ { "channel", S_IFREG|S_IRUGO, proc_ide_read_channel, NULL },
+ { "config", S_IFREG|S_IRUGO|S_IWUSR,proc_ide_read_config, proc_ide_write_config },
+ { "mate", S_IFREG|S_IRUGO, proc_ide_read_mate, NULL },
+ { "model", S_IFREG|S_IRUGO, proc_ide_read_imodel, NULL },
+ { NULL, 0, NULL, NULL }
+};
+
+void create_proc_ide_interfaces(void)
+{
+ int h;
+
+ for (h = 0; h < MAX_HWIFS; h++) {
+ ide_hwif_t *hwif = &ide_hwifs[h];
+
+ if (!hwif->present)
+ continue;
+ if (!hwif->proc) {
+ hwif->proc = proc_mkdir(hwif->name, proc_ide_root);
+ if (!hwif->proc)
+ return;
+ ide_add_proc_entries(hwif->proc, hwif_entries, hwif);
+ }
+ create_proc_ide_drives(hwif);
+ }
+}
+
+static void destroy_proc_ide_interfaces(void)
+{
+ int h;
+
+ for (h = 0; h < MAX_HWIFS; h++) {
+ ide_hwif_t *hwif = &ide_hwifs[h];
+ int exist = (hwif->proc != NULL);
+#if 0
+ if (!hwif->present)
+ continue;
+#endif
+ if (exist) {
+ destroy_proc_ide_drives(hwif);
+ ide_remove_proc_entries(hwif->proc, hwif_entries);
+ remove_proc_entry(hwif->name, proc_ide_root);
+ hwif->proc = NULL;
+ } else
+ continue;
+ }
+}
+
+void proc_ide_create(void)
+{
+ proc_ide_root = proc_mkdir("ide", 0);
+ if (!proc_ide_root) return;
+
+ create_proc_ide_interfaces();
+
+ create_proc_read_entry("drivers", 0, proc_ide_root,
+ proc_ide_read_drivers, NULL);
+
+#ifdef CONFIG_BLK_DEV_AEC6210
+ if ((aec6210_display_info) && (aec6210_proc))
+ create_proc_info_entry("aec6210", 0, proc_ide_root, aec6210_display_info);
+#endif /* CONFIG_BLK_DEV_AEC6210 */
+#ifdef CONFIG_BLK_DEV_ALI15X3
+ if ((ali_display_info) && (ali_proc))
+ create_proc_info_entry("ali", 0, proc_ide_root, ali_display_info);
+#endif /* CONFIG_BLK_DEV_ALI15X3 */
+#ifdef CONFIG_BLK_DEV_AMD7409
+ if ((amd7409_display_info) && (amd7409_proc))
+ create_proc_info_entry("amd7409", 0, proc_ide_root, amd7409_display_info);
+#endif /* CONFIG_BLK_DEV_AMD7409 */
+#ifdef CONFIG_BLK_DEV_CMD64X
+ if ((cmd64x_display_info) && (cmd64x_proc))
+ create_proc_info_entry("cmd64x", 0, proc_ide_root, cmd64x_display_info);
+#endif /* CONFIG_BLK_DEV_CMD64X */
+#ifdef CONFIG_BLK_DEV_CS5530
+ if ((cs5530_display_info) && (cs5530_proc))
+ create_proc_info_entry("cs5530", 0, proc_ide_root, cs5530_display_info);
+#endif /* CONFIG_BLK_DEV_CS5530 */
+#ifdef CONFIG_BLK_DEV_HPT34X
+ if ((hpt34x_display_info) && (hpt34x_proc))
+ create_proc_info_entry("hpt34x", 0, proc_ide_root, hpt34x_display_info);
+#endif /* CONFIG_BLK_DEV_HPT34X */
+#ifdef CONFIG_BLK_DEV_HPT366
+ if ((hpt366_display_info) && (hpt366_proc))
+ create_proc_info_entry("hpt366", 0, proc_ide_root, hpt366_display_info);
+#endif /* CONFIG_BLK_DEV_HPT366 */
+#ifdef CONFIG_BLK_DEV_PDC202XX
+ if ((pdc202xx_display_info) && (pdc202xx_proc))
+ create_proc_info_entry("pdc202xx", 0, proc_ide_root, pdc202xx_display_info);
+#endif /* CONFIG_BLK_DEV_PDC202XX */
+#ifdef CONFIG_BLK_DEV_PIIX
+ if ((piix_display_info) && (piix_proc))
+ create_proc_info_entry("piix", 0, proc_ide_root, piix_display_info);
+#endif /* CONFIG_BLK_DEV_PIIX */
+#ifdef CONFIG_BLK_DEV_SIS5513
+ if ((sis_display_info) && (sis_proc))
+ create_proc_info_entry("sis", 0, proc_ide_root, sis_display_info);
+#endif /* CONFIG_BLK_DEV_SIS5513 */
+#ifdef CONFIG_BLK_DEV_VIA82CXXX
+ if ((via_display_info) && (via_proc))
+ create_proc_info_entry("via", 0, proc_ide_root, via_display_info);
+#endif /* CONFIG_BLK_DEV_VIA82CXXX */
+}
+
+void proc_ide_destroy(void)
+{
+ /*
+ * Mmmm.. does this free up all resources,
+ * or do we need to do a more proper cleanup here ??
+ */
+#ifdef CONFIG_BLK_DEV_AEC6210
+ if ((aec6210_display_info) && (aec6210_proc))
+ remove_proc_entry("ide/aec6210",0);
+#endif /* CONFIG_BLK_DEV_AEC6210 */
+#ifdef CONFIG_BLK_DEV_ALI15X3
+ if ((ali_display_info) && (ali_proc))
+ remove_proc_entry("ide/ali",0);
+#endif /* CONFIG_BLK_DEV_ALI15X3 */
+#ifdef CONFIG_BLK_DEV_AMD7409
+ if ((amd7409_display_info) && (amd7409_proc))
+ remove_proc_entry("ide/amd7409",0);
+#endif /* CONFIG_BLK_DEV_AMD7409 */
+#ifdef CONFIG_BLK_DEV_CMD64X
+ if ((cmd64x_display_info) && (cmd64x_proc))
+ remove_proc_entry("ide/cmd64x",0);
+#endif /* CONFIG_BLK_DEV_CMD64X */
+#ifdef CONFIG_BLK_DEV_CS5530
+ if ((cs5530_display_info) && (cs5530_proc))
+ remove_proc_entry("ide/cs5530",0);
+#endif /* CONFIG_BLK_DEV_CS5530 */
+#ifdef CONFIG_BLK_DEV_HPT34X
+ if ((hpt34x_display_info) && (hpt34x_proc))
+ remove_proc_entry("ide/hpt34x",0);
+#endif /* CONFIG_BLK_DEV_HPT34X */
+#ifdef CONFIG_BLK_DEV_HPT366
+ if ((hpt366_display_info) && (hpt366_proc))
+ remove_proc_entry("ide/hpt366",0);
+#endif /* CONFIG_BLK_DEV_HPT366 */
+#ifdef CONFIG_BLK_DEV_PDC202XX
+ if ((pdc202xx_display_info) && (pdc202xx_proc))
+ remove_proc_entry("ide/pdc202xx",0);
+#endif /* CONFIG_BLK_DEV_PDC202XX */
+#ifdef CONFIG_BLK_DEV_PIIX
+ if ((piix_display_info) && (piix_proc))
+ remove_proc_entry("ide/piix",0);
+#endif /* CONFIG_BLK_DEV_PIIX */
+#ifdef CONFIG_BLK_DEV_SIS5513
+ if ((sis_display_info) && (sis_proc))
+ remove_proc_entry("ide/sis", 0);
+#endif /* CONFIG_BLK_DEV_SIS5513 */
+#ifdef CONFIG_BLK_DEV_VIA82CXXX
+ if ((via_display_info) && (via_proc))
+ remove_proc_entry("ide/via",0);
+#endif /* CONFIG_BLK_DEV_VIA82CXXX */
+
+ remove_proc_entry("ide/drivers", 0);
+ destroy_proc_ide_interfaces();
+ remove_proc_entry("ide", 0);
+}
diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c
new file mode 100644
index 000000000..95e780abf
--- /dev/null
+++ b/drivers/ide/ide-tape.c
@@ -0,0 +1,6031 @@
+/*
+ * linux/drivers/block/ide-tape.c Version 1.16f Dec 15, 1999
+ *
+ * Copyright (C) 1995 - 1999 Gadi Oxman <gadio@netvision.net.il>
+ *
+ * This driver was constructed as a student project in the software laboratory
+ * of the faculty of electrical engineering in the Technion - Israel's
+ * Institute Of Technology, with the guide of Avner Lottem and Dr. Ilana David.
+ *
+ * It is hereby placed under the terms of the GNU general public license.
+ * (See linux/COPYING).
+ */
+
+/*
+ * IDE ATAPI streaming tape driver.
+ *
+ * This driver is a part of the Linux ide driver and works in co-operation
+ * with linux/drivers/block/ide.c.
+ *
+ * The driver, in co-operation with ide.c, basically traverses the
+ * request-list for the block device interface. The character device
+ * interface, on the other hand, creates new requests, adds them
+ * to the request-list of the block device, and waits for their completion.
+ *
+ * Pipelined operation mode is now supported on both reads and writes.
+ *
+ * The block device major and minor numbers are determined from the
+ * tape's relative position in the ide interfaces, as explained in ide.c.
+ *
+ * The character device interface consists of the following devices:
+ *
+ * ht0 major 37, minor 0 first IDE tape, rewind on close.
+ * ht1 major 37, minor 1 second IDE tape, rewind on close.
+ * ...
+ * nht0 major 37, minor 128 first IDE tape, no rewind on close.
+ * nht1 major 37, minor 129 second IDE tape, no rewind on close.
+ * ...
+ *
+ * Run linux/scripts/MAKEDEV.ide to create the above entries.
+ *
+ * The general magnetic tape commands compatible interface, as defined by
+ * include/linux/mtio.h, is accessible through the character device.
+ *
+ * General ide driver configuration options, such as the interrupt-unmask
+ * flag, can be configured by issuing an ioctl to the block device interface,
+ * as any other ide device.
+ *
+ * Our own ide-tape ioctl's can be issued to either the block device or
+ * the character device interface.
+ *
+ * Maximal throughput with minimal bus load will usually be achieved in the
+ * following scenario:
+ *
+ * 1. ide-tape is operating in the pipelined operation mode.
+ * 2. No buffering is performed by the user backup program.
+ *
+ * Testing was done with a 2 GB CONNER CTMA 4000 IDE ATAPI Streaming Tape Drive.
+ *
+ * Ver 0.1 Nov 1 95 Pre-working code :-)
+ * Ver 0.2 Nov 23 95 A short backup (few megabytes) and restore procedure
+ * was successful ! (Using tar cvf ... on the block
+ * device interface).
+ * A longer backup resulted in major swapping, bad
+ * overall Linux performance and eventually failed as
+ * we received non serial read-ahead requests from the
+ * buffer cache.
+ * Ver 0.3 Nov 28 95 Long backups are now possible, thanks to the
+ * character device interface. Linux's responsiveness
+ * and performance doesn't seem to be much affected
+ * from the background backup procedure.
+ * Some general mtio.h magnetic tape operations are
+ * now supported by our character device. As a result,
+ * popular tape utilities are starting to work with
+ * ide tapes :-)
+ * The following configurations were tested:
+ * 1. An IDE ATAPI TAPE shares the same interface
+ * and irq with an IDE ATAPI CDROM.
+ * 2. An IDE ATAPI TAPE shares the same interface
+ * and irq with a normal IDE disk.
+ * Both configurations seemed to work just fine !
+ * However, to be on the safe side, it is meanwhile
+ * recommended to give the IDE TAPE its own interface
+ * and irq.
+ * The one thing which needs to be done here is to
+ * add a "request postpone" feature to ide.c,
+ * so that we won't have to wait for the tape to finish
+ * performing a long media access (DSC) request (such
+ * as a rewind) before we can access the other device
+ * on the same interface. This effect doesn't disturb
+ * normal operation most of the time because read/write
+ * requests are relatively fast, and once we are
+ * performing one tape r/w request, a lot of requests
+ * from the other device can be queued and ide.c will
+ * service all of them after this single tape request.
+ * Ver 1.0 Dec 11 95 Integrated into Linux 1.3.46 development tree.
+ * On each read / write request, we now ask the drive
+ * if we can transfer a constant number of bytes
+ * (a parameter of the drive) only to its buffers,
+ * without causing actual media access. If we can't,
+ * we just wait until we can by polling the DSC bit.
+ * This ensures that while we are not transferring
+ * more bytes than the constant referred to above, the
+ * interrupt latency will not become too high and
+ * we won't cause an interrupt timeout, as happened
+ * occasionally in the previous version.
+ * While polling for DSC, the current request is
+ * postponed and ide.c is free to handle requests from
+ * the other device. This is handled transparently to
+ * ide.c. The hwgroup locking method which was used
+ * in the previous version was removed.
+ * Use of new general features which are provided by
+ * ide.c for use with atapi devices.
+ * (Programming done by Mark Lord)
+ * Few potential bug fixes (Again, suggested by Mark)
+ * Single character device data transfers are now
+ * not limited in size, as they were before.
+ * We are asking the tape about its recommended
+ * transfer unit and send a larger data transfer
+ * as several transfers of the above size.
+ * For best results, use an integral number of this
+ * basic unit (which is shown during driver
+ * initialization). I will soon add an ioctl to get
+ * this important parameter.
+ * Our data transfer buffer is allocated on startup,
+ * rather than before each data transfer. This should
+ * ensure that we will indeed have a data buffer.
+ * Ver 1.1 Dec 14 95 Fixed random problems which occurred when the tape
+ * shared an interface with another device.
+ * (poll_for_dsc was a complete mess).
+ * Removed some old (non-active) code which had
+ * to do with supporting buffer cache originated
+ * requests.
+ * The block device interface can now be opened, so
+ * that general ide driver features like the unmask
+ * interrupts flag can be selected with an ioctl.
+ * This is the only use of the block device interface.
+ * New fast pipelined operation mode (currently only on
+ * writes). When using the pipelined mode, the
+ * throughput can potentially reach the maximum
+ * tape supported throughput, regardless of the
+ * user backup program. On my tape drive, it sometimes
+ * boosted performance by a factor of 2. Pipelined
+ * mode is enabled by default, but since it has a few
+ * downfalls as well, you may want to disable it.
+ * A short explanation of the pipelined operation mode
+ * is available below.
+ * Ver 1.2 Jan 1 96 Eliminated pipelined mode race condition.
+ * Added pipeline read mode. As a result, restores
+ * are now as fast as backups.
+ * Optimized shared interface behavior. The new behavior
+ * typically results in better IDE bus efficiency and
+ * higher tape throughput.
+ * Pre-calculation of the expected read/write request
+ * service time, based on the tape's parameters. In
+ * the pipelined operation mode, this allows us to
+ * adjust our polling frequency to a much lower value,
+ * and thus to dramatically reduce our load on Linux,
+ * without any decrease in performance.
+ * Implemented additional mtio.h operations.
+ * The recommended user block size is returned by
+ * the MTIOCGET ioctl.
+ * Additional minor changes.
+ * Ver 1.3 Feb 9 96 Fixed pipelined read mode bug which prevented the
+ * use of some block sizes during a restore procedure.
+ * The character device interface will now present a
+ * continuous view of the media - any mix of block sizes
+ * during a backup/restore procedure is supported. The
+ * driver will buffer the requests internally and
+ * convert them to the tape's recommended transfer
+ * unit, making performance almost independent of the
+ * chosen user block size.
+ * Some improvements in error recovery.
+ * By cooperating with ide-dma.c, bus mastering DMA can
+ * now sometimes be used with IDE tape drives as well.
+ * Bus mastering DMA has the potential to dramatically
+ * reduce the CPU's overhead when accessing the device,
+ * and can be enabled by using hdparm -d1 on the tape's
+ * block device interface. For more info, read the
+ * comments in ide-dma.c.
+ * Ver 1.4 Mar 13 96 Fixed serialize support.
+ * Ver 1.5 Apr 12 96 Fixed shared interface operation, broken in 1.3.85.
+ * Fixed pipelined read mode inefficiency.
+ * Fixed nasty null dereferencing bug.
+ * Ver 1.6 Aug 16 96 Fixed FPU usage in the driver.
+ * Fixed end of media bug.
+ * Ver 1.7 Sep 10 96 Minor changes for the CONNER CTT8000-A model.
+ * Ver 1.8 Sep 26 96 Attempt to find a better balance between good
+ * interactive response and high system throughput.
+ * Ver 1.9 Nov 5 96 Automatically cross encountered filemarks rather
+ * than requiring an explicit FSF command.
+ * Abort pending requests at end of media.
+ * MTTELL was sometimes returning incorrect results.
+ * Return the real block size in the MTIOCGET ioctl.
+ * Some error recovery bug fixes.
+ * Ver 1.10 Nov 5 96 Major reorganization.
+ * Reduced CPU overhead a bit by eliminating internal
+ * bounce buffers.
+ * Added module support.
+ * Added multiple tape drives support.
+ * Added partition support.
+ * Rewrote DSC handling.
+ * Some portability fixes.
+ * Removed ide-tape.h.
+ * Additional minor changes.
+ * Ver 1.11 Dec 2 96 Bug fix in previous DSC timeout handling.
+ * Use ide_stall_queue() for DSC overlap.
+ * Use the maximum speed rather than the current speed
+ * to compute the request service time.
+ * Ver 1.12 Dec 7 97 Fix random memory overwriting and/or last block data
+ * corruption, which could occur if the total number
+ * of bytes written to the tape was not an integral
+ * number of tape blocks.
+ * Add support for INTERRUPT DRQ devices.
+ * Ver 1.13 Jan 2 98 Add "speed == 0" work-around for HP COLORADO 5GB
+ * Ver 1.14 Dec 30 98 Partial fixes for the Sony/AIWA tape drives.
+ * Replace cli()/sti() with hwgroup spinlocks.
+ * Ver 1.15 Mar 25 99 Fix SMP race condition by replacing hwgroup
+ * spinlock with private per-tape spinlock.
+ * Ver 1.16 Sep 1 99 Add OnStream tape support.
+ * Abort read pipeline on EOD.
+ * Wait for the tape to become ready in case it returns
+ * "in the process of becoming ready" on open().
+ * Fix zero padding of the last written block in
+ * case the tape block size is larger than PAGE_SIZE.
+ * Decrease the default disconnection time to tn.
+ * Ver 1.16e Oct 3 99 Minor fixes.
+ * Ver 1.16e1 Oct 13 99 Patches by Arnold Niessen,
+ * niessen@iae.nl / arnold.niessen@philips.com
+ * GO-1) Undefined code in idetape_read_position
+ * according to Gadi's email
+ * AJN-1) Minor fix asc == 11 should be asc == 0x11
+ * in idetape_issue_packet_command (did effect
+ * debugging output only)
+ * AJN-2) Added more debugging output, and
+ * added ide-tape: where missing. I would also
+ * like to add tape->name where possible
+ * AJN-3) Added different debug_level's
+ * via /proc/ide/hdc/settings
+ * "debug_level" determines amount of debugging output;
+ * can be changed using /proc/ide/hdx/settings
+ * 0 : almost no debugging output
+ * 1 : 0+output errors only
+ * 2 : 1+output all sensekey/asc
+ * 3 : 2+follow all chrdev related procedures
+ * 4 : 3+follow all procedures
+ * 5 : 4+include pc_stack rq_stack info
+ * 6 : 5+USE_COUNT updates
+ * AJN-4) Fixed timeout for retension in idetape_queue_pc_tail
+ * from 5 to 10 minutes
+ * AJN-5) Changed maximum number of blocks to skip when
+ * reading tapes with multiple consecutive write
+ * errors from 100 to 1000 in idetape_get_logical_blk
+ * Proposed changes to code:
+ * 1) output "logical_blk_num" via /proc
+ * 2) output "current_operation" via /proc
+ * 3) Either solve or document the fact that `mt rewind' is
+ * required after reading from /dev/nhtx to be
+ * able to rmmod the idetape module;
+ * Also, sometimes an application finishes but the
+ * device remains `busy' for some time. Same cause ?
+ * Proposed changes to release-notes:
+ * 4) write a simple `quickstart' section in the
+ * release notes; I volunteer if you don't want to
+ * 5) include a pointer to video4linux in the doc
+ * to stimulate video applications
+ * 6) release notes lines 331 and 362: explain what happens
+ * if the application data rate is higher than 1100 KB/s;
+ * similar approach to lower-than-500 kB/s ?
+ * 7) 6.6 Comparison; wouldn't it be better to allow different
+ * strategies for read and write ?
+ * Wouldn't it be better to control the tape buffer
+ * contents instead of the bandwidth ?
+ * 8) line 536: replace will by would (if I understand
+ * this section correctly, a hypothetical and unwanted situation
+ * is being described)
+ * Ver 1.16f Dec 15 99 Change place of the secondary OnStream header frames.
+ *
+ *
+ * Here are some words from the first releases of hd.c, which are quoted
+ * in ide.c and apply here as well:
+ *
+ * | Special care is recommended. Have Fun!
+ *
+ */
+
+/*
+ * An overview of the pipelined operation mode.
+ *
+ * In the pipelined write mode, we will usually just add requests to our
+ * pipeline and return immediately, before we even start to service them. The
+ * user program will then have enough time to prepare the next request while
+ * we are still busy servicing previous requests. In the pipelined read mode,
+ * the situation is similar - we add read-ahead requests into the pipeline,
+ * before the user even requested them.
+ *
+ * The pipeline can be viewed as a "safety net" which will be activated when
+ * the system load is high and prevents the user backup program from keeping up
+ * with the current tape speed. At this point, the pipeline will get
+ * shorter and shorter but the tape will still be streaming at the same speed.
+ * Assuming we have enough pipeline stages, the system load will hopefully
+ * decrease before the pipeline is completely empty, and the backup program
+ * will be able to "catch up" and refill the pipeline again.
+ *
+ * When using the pipelined mode, it would be best to disable any type of
+ * buffering done by the user program, as ide-tape already provides all the
+ * benefits in the kernel, where it can be done in a more efficient way.
+ * As we will usually not block the user program on a request, the most
+ * efficient user code will then be a simple read-write-read-... cycle.
+ * Any additional logic will usually just slow down the backup process.
+ *
+ * Using the pipelined mode, I get a constant over 400 KBps throughput,
+ * which seems to be the maximum throughput supported by my tape.
+ *
+ * However, there are some downfalls:
+ *
+ * 1. We use memory (for data buffers) in proportional to the number
+ * of pipeline stages (each stage is about 26 KB with my tape).
+ * 2. In the pipelined write mode, we cheat and postpone error codes
+ * to the user task. In read mode, the actual tape position
+ * will be a bit further than the last requested block.
+ *
+ * Concerning (1):
+ *
+ * 1. We allocate stages dynamically only when we need them. When
+ * we don't need them, we don't consume additional memory. In
+ * case we can't allocate stages, we just manage without them
+ * (at the expense of decreased throughput) so when Linux is
+ * tight in memory, we will not pose additional difficulties.
+ *
+ * 2. The maximum number of stages (which is, in fact, the maximum
+ * amount of memory) which we allocate is limited by the compile
+ * time parameter IDETAPE_MAX_PIPELINE_STAGES.
+ *
+ * 3. The maximum number of stages is a controlled parameter - We
+ * don't start from the user defined maximum number of stages
+ * but from the lower IDETAPE_MIN_PIPELINE_STAGES (again, we
+ * will not even allocate this amount of stages if the user
+ * program can't handle the speed). We then implement a feedback
+ * loop which checks if the pipeline is empty, and if it is, we
+ * increase the maximum number of stages as necessary until we
+ * reach the optimum value which just manages to keep the tape
+ * busy with minimum allocated memory or until we reach
+ * IDETAPE_MAX_PIPELINE_STAGES.
+ *
+ * Concerning (2):
+ *
+ * In pipelined write mode, ide-tape can not return accurate error codes
+ * to the user program since we usually just add the request to the
+ * pipeline without waiting for it to be serviced. In case an error
+ * occurs, I will report it on the next user request.
+ *
+ * In the pipelined read mode, subsequent read requests or forward
+ * filemark spacing will perform correctly, as we preserve all blocks
+ * and filemarks which we encountered during our excess read-ahead.
+ *
+ * For accurate tape positioning and error reporting, disabling
+ * pipelined mode might be the best option.
+ *
+ * You can enable/disable/tune the pipelined operation mode by adjusting
+ * the compile time parameters below.
+ */
+
+/*
+ * Possible improvements.
+ *
+ * 1. Support for the ATAPI overlap protocol.
+ *
+ * In order to maximize bus throughput, we currently use the DSC
+ * overlap method which enables ide.c to service requests from the
+ * other device while the tape is busy executing a command. The
+ * DSC overlap method involves polling the tape's status register
+ * for the DSC bit, and servicing the other device while the tape
+ * isn't ready.
+ *
+ * In the current QIC development standard (December 1995),
+ * it is recommended that new tape drives will *in addition*
+ * implement the ATAPI overlap protocol, which is used for the
+ * same purpose - efficient use of the IDE bus, but is interrupt
+ * driven and thus has much less CPU overhead.
+ *
+ * ATAPI overlap is likely to be supported in most new ATAPI
+ * devices, including new ATAPI cdroms, and thus provides us
+ * a method by which we can achieve higher throughput when
+ * sharing a (fast) ATA-2 disk with any (slow) new ATAPI device.
+ */
+
+#define IDETAPE_VERSION "1.16f"
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/major.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/errno.h>
+#include <linux/genhd.h>
+#include <linux/malloc.h>
+#include <linux/pci.h>
+#include <linux/ide.h>
+
+#include <asm/byteorder.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/unaligned.h>
+#include <asm/bitops.h>
+
+
+#define NO_LONGER_REQUIRED (1)
+
+/*
+ * OnStream support
+ */
+#define ONSTREAM_DEBUG (0)
+#define OS_CONFIG_PARTITION (0xff)
+#define OS_DATA_PARTITION (0)
+#define OS_PARTITION_VERSION (1)
+
+/*
+ * partition
+ */
+typedef struct os_partition_s {
+ __u8 partition_num;
+ __u8 par_desc_ver;
+ __u16 wrt_pass_cntr;
+ __u32 first_frame_addr;
+ __u32 last_frame_addr;
+ __u32 eod_frame_addr;
+} os_partition_t;
+
+/*
+ * DAT entry
+ */
+typedef struct os_dat_entry_s {
+ __u32 blk_sz;
+ __u16 blk_cnt;
+ __u8 flags;
+ __u8 reserved;
+} os_dat_entry_t;
+
+/*
+ * DAT
+ */
+#define OS_DAT_FLAGS_DATA (0xc)
+#define OS_DAT_FLAGS_MARK (0x1)
+
+typedef struct os_dat_s {
+ __u8 dat_sz;
+ __u8 reserved1;
+ __u8 entry_cnt;
+ __u8 reserved3;
+ os_dat_entry_t dat_list[16];
+} os_dat_t;
+
+/*
+ * Frame types
+ */
+#define OS_FRAME_TYPE_FILL (0)
+#define OS_FRAME_TYPE_EOD (1 << 0)
+#define OS_FRAME_TYPE_MARKER (1 << 1)
+#define OS_FRAME_TYPE_HEADER (1 << 3)
+#define OS_FRAME_TYPE_DATA (1 << 7)
+
+/*
+ * AUX
+ */
+typedef struct os_aux_s {
+ __u32 format_id; /* hardware compability AUX is based on */
+ char application_sig[4]; /* driver used to write this media */
+ __u32 hdwr; /* reserved */
+ __u32 update_frame_cntr; /* for configuration frame */
+ __u8 frame_type;
+ __u8 frame_type_reserved;
+ __u8 reserved_18_19[2];
+ os_partition_t partition;
+ __u8 reserved_36_43[8];
+ __u32 frame_seq_num;
+ __u32 logical_blk_num_high;
+ __u32 logical_blk_num;
+ os_dat_t dat;
+ __u8 reserved188_191[4];
+ __u32 filemark_cnt;
+ __u32 phys_fm;
+ __u32 last_mark_addr;
+ __u8 reserved204_223[20];
+
+ /*
+ * __u8 app_specific[32];
+ *
+ * Linux specific fields:
+ */
+ __u32 next_mark_addr; /* when known, points to next marker */
+ __u8 linux_specific[28];
+
+ __u8 reserved_256_511[256];
+} os_aux_t;
+
+typedef struct os_header_s {
+ char ident_str[8];
+ __u8 major_rev;
+ __u8 minor_rev;
+ __u8 reserved10_15[6];
+ __u8 par_num;
+ __u8 reserved1_3[3];
+ os_partition_t partition;
+} os_header_t;
+
+/*
+ * OnStream ADRL frame
+ */
+#define OS_FRAME_SIZE (32 * 1024 + 512)
+#define OS_DATA_SIZE (32 * 1024)
+#define OS_AUX_SIZE (512)
+
+#include <linux/mtio.h>
+
+/**************************** Tunable parameters *****************************/
+
+
+/*
+ * Pipelined mode parameters.
+ *
+ * We try to use the minimum number of stages which is enough to
+ * keep the tape constantly streaming. To accomplish that, we implement
+ * a feedback loop around the maximum number of stages:
+ *
+ * We start from MIN maximum stages (we will not even use MIN stages
+ * if we don't need them), increment it by RATE*(MAX-MIN)
+ * whenever we sense that the pipeline is empty, until we reach
+ * the optimum value or until we reach MAX.
+ *
+ * Setting the following parameter to 0 will disable the pipelined mode.
+ */
+#define IDETAPE_MIN_PIPELINE_STAGES 200
+#define IDETAPE_MAX_PIPELINE_STAGES 400
+#define IDETAPE_INCREASE_STAGES_RATE 20
+
+/*
+ * The following are used to debug the driver:
+ *
+ * Setting IDETAPE_DEBUG_INFO to 1 will report device capabilities.
+ * Setting IDETAPE_DEBUG_LOG to 1 will log driver flow control.
+ * Setting IDETAPE_DEBUG_BUGS to 1 will enable self-sanity checks in
+ * some places.
+ *
+ * Setting them to 0 will restore normal operation mode:
+ *
+ * 1. Disable logging normal successful operations.
+ * 2. Disable self-sanity checks.
+ * 3. Errors will still be logged, of course.
+ *
+ * All the #if DEBUG code will be removed some day, when the driver
+ * is verified to be stable enough. This will make it much more
+ * esthetic.
+ */
+#define IDETAPE_DEBUG_INFO 0
+#define IDETAPE_DEBUG_LOG 1
+#define IDETAPE_DEBUG_LOG_VERBOSE 0
+#define IDETAPE_DEBUG_BUGS 1
+
+/*
+ * After each failed packet command we issue a request sense command
+ * and retry the packet command IDETAPE_MAX_PC_RETRIES times.
+ *
+ * Setting IDETAPE_MAX_PC_RETRIES to 0 will disable retries.
+ */
+#define IDETAPE_MAX_PC_RETRIES 3
+
+/*
+ * With each packet command, we allocate a buffer of
+ * IDETAPE_PC_BUFFER_SIZE bytes. This is used for several packet
+ * commands (Not for READ/WRITE commands).
+ */
+#define IDETAPE_PC_BUFFER_SIZE 256
+
+/*
+ * In various places in the driver, we need to allocate storage
+ * for packet commands and requests, which will remain valid while
+ * we leave the driver to wait for an interrupt or a timeout event.
+ */
+#define IDETAPE_PC_STACK (10 + IDETAPE_MAX_PC_RETRIES)
+
+/*
+ * Some tape drives require a long irq timeout
+ */
+#define IDETAPE_WAIT_CMD (60*HZ)
+
+/*
+ * The following parameter is used to select the point in the internal
+ * tape fifo in which we will start to refill the buffer. Decreasing
+ * the following parameter will improve the system's latency and
+ * interactive response, while using a high value might improve sytem
+ * throughput.
+ */
+#define IDETAPE_FIFO_THRESHOLD 2
+
+/*
+ * DSC polling parameters.
+ *
+ * Polling for DSC (a single bit in the status register) is a very
+ * important function in ide-tape. There are two cases in which we
+ * poll for DSC:
+ *
+ * 1. Before a read/write packet command, to ensure that we
+ * can transfer data from/to the tape's data buffers, without
+ * causing an actual media access. In case the tape is not
+ * ready yet, we take out our request from the device
+ * request queue, so that ide.c will service requests from
+ * the other device on the same interface meanwhile.
+ *
+ * 2. After the successful initialization of a "media access
+ * packet command", which is a command which can take a long
+ * time to complete (it can be several seconds or even an hour).
+ *
+ * Again, we postpone our request in the middle to free the bus
+ * for the other device. The polling frequency here should be
+ * lower than the read/write frequency since those media access
+ * commands are slow. We start from a "fast" frequency -
+ * IDETAPE_DSC_MA_FAST (one second), and if we don't receive DSC
+ * after IDETAPE_DSC_MA_THRESHOLD (5 minutes), we switch it to a
+ * lower frequency - IDETAPE_DSC_MA_SLOW (1 minute).
+ *
+ * We also set a timeout for the timer, in case something goes wrong.
+ * The timeout should be longer then the maximum execution time of a
+ * tape operation.
+ */
+
+/*
+ * DSC timings.
+ */
+#define IDETAPE_DSC_RW_MIN 5*HZ/100 /* 50 msec */
+#define IDETAPE_DSC_RW_MAX 40*HZ/100 /* 400 msec */
+#define IDETAPE_DSC_RW_TIMEOUT 2*60*HZ /* 2 minutes */
+#define IDETAPE_DSC_MA_FAST 2*HZ /* 2 seconds */
+#define IDETAPE_DSC_MA_THRESHOLD 5*60*HZ /* 5 minutes */
+#define IDETAPE_DSC_MA_SLOW 30*HZ /* 30 seconds */
+#define IDETAPE_DSC_MA_TIMEOUT 2*60*60*HZ /* 2 hours */
+
+/*************************** End of tunable parameters ***********************/
+
+/*
+ * Debugging/Performance analysis
+ *
+ * I/O trace support
+ */
+#define USE_IOTRACE 0
+#if USE_IOTRACE
+#include <linux/io_trace.h>
+#define IO_IDETAPE_FIFO 500
+#endif
+
+/*
+ * Read/Write error simulation
+ */
+#define SIMULATE_ERRORS 0
+
+/*
+ * For general magnetic tape device compatibility.
+ */
+typedef enum {
+ idetape_direction_none,
+ idetape_direction_read,
+ idetape_direction_write
+} idetape_chrdev_direction_t;
+
+/*
+ * Our view of a packet command.
+ */
+typedef struct idetape_packet_command_s {
+ u8 c[12]; /* Actual packet bytes */
+ int retries; /* On each retry, we increment retries */
+ int error; /* Error code */
+ int request_transfer; /* Bytes to transfer */
+ int actually_transferred; /* Bytes actually transferred */
+ int buffer_size; /* Size of our data buffer */
+ struct buffer_head *bh;
+ char *b_data;
+ int b_count;
+ byte *buffer; /* Data buffer */
+ byte *current_position; /* Pointer into the above buffer */
+ ide_startstop_t (*callback) (ide_drive_t *); /* Called when this packet command is completed */
+ byte pc_buffer[IDETAPE_PC_BUFFER_SIZE]; /* Temporary buffer */
+ unsigned int flags; /* Status/Action bit flags */
+} idetape_pc_t;
+
+/*
+ * Packet command flag bits.
+ */
+#define PC_ABORT 0 /* Set when an error is considered normal - We won't retry */
+#define PC_WAIT_FOR_DSC 1 /* 1 When polling for DSC on a media access command */
+#define PC_DMA_RECOMMENDED 2 /* 1 when we prefer to use DMA if possible */
+#define PC_DMA_IN_PROGRESS 3 /* 1 while DMA in progress */
+#define PC_DMA_ERROR 4 /* 1 when encountered problem during DMA */
+#define PC_WRITING 5 /* Data direction */
+
+/*
+ * Capabilities and Mechanical Status Page
+ */
+typedef struct {
+ unsigned page_code :6; /* Page code - Should be 0x2a */
+ __u8 reserved0_6 :1;
+ __u8 ps :1; /* parameters saveable */
+ __u8 page_length; /* Page Length - Should be 0x12 */
+ __u8 reserved2, reserved3;
+ unsigned ro :1; /* Read Only Mode */
+ unsigned reserved4_1234 :4;
+ unsigned sprev :1; /* Supports SPACE in the reverse direction */
+ unsigned reserved4_67 :2;
+ unsigned reserved5_012 :3;
+ unsigned efmt :1; /* Supports ERASE command initiated formatting */
+ unsigned reserved5_4 :1;
+ unsigned qfa :1; /* Supports the QFA two partition formats */
+ unsigned reserved5_67 :2;
+ unsigned lock :1; /* Supports locking the volume */
+ unsigned locked :1; /* The volume is locked */
+ unsigned prevent :1; /* The device defaults in the prevent state after power up */
+ unsigned eject :1; /* The device can eject the volume */
+ __u8 disconnect :1; /* The device can break request > ctl */
+ __u8 reserved6_5 :1;
+ unsigned ecc :1; /* Supports error correction */
+ unsigned cmprs :1; /* Supports data compression */
+ unsigned reserved7_0 :1;
+ unsigned blk512 :1; /* Supports 512 bytes block size */
+ unsigned blk1024 :1; /* Supports 1024 bytes block size */
+ unsigned reserved7_3_6 :4;
+ unsigned blk32768 :1; /* slowb - the device restricts the byte count for PIO */
+ /* transfers for slow buffer memory ??? */
+ /* Also 32768 block size in some cases */
+ __u16 max_speed; /* Maximum speed supported in KBps */
+ __u8 reserved10, reserved11;
+ __u16 ctl; /* Continuous Transfer Limit in blocks */
+ __u16 speed; /* Current Speed, in KBps */
+ __u16 buffer_size; /* Buffer Size, in 512 bytes */
+ __u8 reserved18, reserved19;
+} idetape_capabilities_page_t;
+
+/*
+ * Block Size Page
+ */
+typedef struct {
+ unsigned page_code :6; /* Page code - Should be 0x30 */
+ unsigned reserved1_6 :1;
+ unsigned ps :1;
+ __u8 page_length; /* Page Length - Should be 2 */
+ __u8 reserved2;
+ unsigned play32 :1;
+ unsigned play32_5 :1;
+ unsigned reserved2_23 :2;
+ unsigned record32 :1;
+ unsigned record32_5 :1;
+ unsigned reserved2_6 :1;
+ unsigned one :1;
+} idetape_block_size_page_t;
+
+/*
+ * A pipeline stage.
+ */
+typedef struct idetape_stage_s {
+ struct request rq; /* The corresponding request */
+ struct buffer_head *bh; /* The data buffers */
+ struct idetape_stage_s *next; /* Pointer to the next stage */
+ os_aux_t *aux; /* OnStream aux ptr */
+} idetape_stage_t;
+
+/*
+ * REQUEST SENSE packet command result - Data Format.
+ */
+typedef struct {
+ unsigned error_code :7; /* Current of deferred errors */
+ unsigned valid :1; /* The information field conforms to QIC-157C */
+ __u8 reserved1 :8; /* Segment Number - Reserved */
+ unsigned sense_key :4; /* Sense Key */
+ unsigned reserved2_4 :1; /* Reserved */
+ unsigned ili :1; /* Incorrect Length Indicator */
+ unsigned eom :1; /* End Of Medium */
+ unsigned filemark :1; /* Filemark */
+ __u32 information __attribute__ ((packed));
+ __u8 asl; /* Additional sense length (n-7) */
+ __u32 command_specific; /* Additional command specific information */
+ __u8 asc; /* Additional Sense Code */
+ __u8 ascq; /* Additional Sense Code Qualifier */
+ __u8 replaceable_unit_code; /* Field Replaceable Unit Code */
+ unsigned sk_specific1 :7; /* Sense Key Specific */
+ unsigned sksv :1; /* Sense Key Specific information is valid */
+ __u8 sk_specific2; /* Sense Key Specific */
+ __u8 sk_specific3; /* Sense Key Specific */
+ __u8 pad[2]; /* Padding to 20 bytes */
+} idetape_request_sense_result_t;
+
+
+/*
+ * Most of our global data which we need to save even as we leave the
+ * driver due to an interrupt or a timer event is stored in a variable
+ * of type idetape_tape_t, defined below.
+ */
+typedef struct {
+ ide_drive_t *drive;
+ devfs_handle_t de_r, de_n;
+
+ /*
+ * Since a typical character device operation requires more
+ * than one packet command, we provide here enough memory
+ * for the maximum of interconnected packet commands.
+ * The packet commands are stored in the circular array pc_stack.
+ * pc_stack_index points to the last used entry, and warps around
+ * to the start when we get to the last array entry.
+ *
+ * pc points to the current processed packet command.
+ *
+ * failed_pc points to the last failed packet command, or contains
+ * NULL if we do not need to retry any packet command. This is
+ * required since an additional packet command is needed before the
+ * retry, to get detailed information on what went wrong.
+ */
+ idetape_pc_t *pc; /* Current packet command */
+ idetape_pc_t *failed_pc; /* Last failed packet command */
+ idetape_pc_t pc_stack[IDETAPE_PC_STACK];/* Packet command stack */
+ int pc_stack_index; /* Next free packet command storage space */
+ struct request rq_stack[IDETAPE_PC_STACK];
+ int rq_stack_index; /* We implement a circular array */
+
+ /*
+ * DSC polling variables.
+ *
+ * While polling for DSC we use postponed_rq to postpone the
+ * current request so that ide.c will be able to service
+ * pending requests on the other device. Note that at most
+ * we will have only one DSC (usually data transfer) request
+ * in the device request queue. Additional requests can be
+ * queued in our internal pipeline, but they will be visible
+ * to ide.c only one at a time.
+ */
+ struct request *postponed_rq;
+ unsigned long dsc_polling_start; /* The time in which we started polling for DSC */
+ struct timer_list dsc_timer; /* Timer used to poll for dsc */
+ unsigned long best_dsc_rw_frequency; /* Read/Write dsc polling frequency */
+ unsigned long dsc_polling_frequency; /* The current polling frequency */
+ unsigned long dsc_timeout; /* Maximum waiting time */
+
+ /*
+ * Read position information
+ */
+ byte partition;
+ unsigned int first_frame_position; /* Current block */
+ unsigned int last_frame_position;
+ unsigned int blocks_in_buffer;
+
+ /*
+ * Last error information
+ */
+ byte sense_key, asc, ascq;
+
+ /*
+ * Character device operation
+ */
+ unsigned int minor;
+ char name[4]; /* device name */
+ idetape_chrdev_direction_t chrdev_direction; /* Current character device data transfer direction */
+
+ /*
+ * Device information
+ */
+ unsigned short tape_block_size; /* Usually 512 or 1024 bytes */
+ int user_bs_factor;
+ idetape_capabilities_page_t capabilities; /* Copy of the tape's Capabilities and Mechanical Page */
+
+ /*
+ * Active data transfer request parameters.
+ *
+ * At most, there is only one ide-tape originated data transfer
+ * request in the device request queue. This allows ide.c to
+ * easily service requests from the other device when we
+ * postpone our active request. In the pipelined operation
+ * mode, we use our internal pipeline structure to hold
+ * more data requests.
+ *
+ * The data buffer size is chosen based on the tape's
+ * recommendation.
+ */
+ struct request *active_data_request; /* Pointer to the request which is waiting in the device request queue */
+ int stage_size; /* Data buffer size (chosen based on the tape's recommendation */
+ idetape_stage_t *merge_stage;
+ int merge_stage_size;
+ struct buffer_head *bh;
+ char *b_data;
+ int b_count;
+
+ /*
+ * Pipeline parameters.
+ *
+ * To accomplish non-pipelined mode, we simply set the following
+ * variables to zero (or NULL, where appropriate).
+ */
+ int nr_stages; /* Number of currently used stages */
+ int nr_pending_stages; /* Number of pending stages */
+ int max_stages, min_pipeline, max_pipeline; /* We will not allocate more than this number of stages */
+ idetape_stage_t *first_stage; /* The first stage which will be removed from the pipeline */
+ idetape_stage_t *active_stage; /* The currently active stage */
+ idetape_stage_t *next_stage; /* Will be serviced after the currently active request */
+ idetape_stage_t *last_stage; /* New requests will be added to the pipeline here */
+ idetape_stage_t *cache_stage; /* Optional free stage which we can use */
+ int pages_per_stage;
+ int excess_bh_size; /* Wasted space in each stage */
+
+ unsigned int flags; /* Status/Action flags */
+ spinlock_t spinlock; /* protects the ide-tape queue */
+
+ /*
+ * Measures average tape speed
+ */
+ unsigned long avg_time;
+ int avg_size;
+ int avg_speed;
+
+ idetape_request_sense_result_t sense; /* last sense information */
+
+ char vendor_id[10];
+ char product_id[18];
+ char firmware_revision[6];
+ int firmware_revision_num;
+
+ int door_locked; /* the door is currently locked */
+
+ /*
+ * OnStream flags
+ */
+ int onstream; /* the tape is an OnStream tape */
+ int raw; /* OnStream raw access (32.5KB block size) */
+ int cur_frames; /* current number of frames in internal buffer */
+ int max_frames; /* max number of frames in internal buffer */
+ int logical_blk_num; /* logical block number */
+ __u16 wrt_pass_cntr; /* write pass counter */
+ __u32 update_frame_cntr; /* update frame counter */
+ struct semaphore *sem;
+ int onstream_write_error; /* write error recovery active */
+ int header_ok; /* header frame verified ok */
+ int linux_media; /* reading linux-specifc media */
+ int linux_media_version;
+ char application_sig[5]; /* application signature */
+ int filemark_cnt;
+ int first_mark_addr;
+ int last_mark_addr;
+ int eod_frame_addr;
+ unsigned long cmd_start_time;
+ unsigned long max_cmd_time;
+
+ /*
+ * Optimize the number of "buffer filling"
+ * mode sense commands.
+ */
+ unsigned long last_buffer_fill; /* last time in which we issued fill cmd */
+ int req_buffer_fill; /* buffer fill command requested */
+ int writes_since_buffer_fill;
+ int reads_since_buffer_fill;
+
+ /*
+ * Limit the number of times a request can
+ * be postponed, to avoid an infinite postpone
+ * deadlock.
+ */
+ int postpone_cnt; /* request postpone count limit */
+
+ /*
+ * Measures number of frames:
+ *
+ * 1. written/read to/from the driver pipeline (pipeline_head).
+ * 2. written/read to/from the tape buffers (buffer_head).
+ * 3. written/read by the tape to/from the media (tape_head).
+ */
+ int pipeline_head;
+ int buffer_head;
+ int tape_head;
+ int last_tape_head;
+
+ /*
+ * Speed control at the tape buffers input/output
+ */
+ unsigned long insert_time;
+ int insert_size;
+ int insert_speed;
+ int max_insert_speed;
+ int measure_insert_time;
+
+ /*
+ * Measure tape still time, in milliseconds
+ */
+ unsigned long tape_still_time_begin;
+ int tape_still_time;
+
+ /*
+ * Speed regulation negative feedback loop
+ */
+ int speed_control;
+ int pipeline_head_speed, controlled_pipeline_head_speed, uncontrolled_pipeline_head_speed;
+ int controlled_last_pipeline_head, uncontrolled_last_pipeline_head;
+ unsigned long uncontrolled_pipeline_head_time, controlled_pipeline_head_time;
+ int controlled_previous_pipeline_head, uncontrolled_previous_pipeline_head;
+ unsigned long controlled_previous_head_time, uncontrolled_previous_head_time;
+ int restart_speed_control_req;
+
+ /*
+ * Debug_level determines amount of debugging output;
+ * can be changed using /proc/ide/hdx/settings
+ * 0 : almost no debugging output
+ * 1 : 0+output errors only
+ * 2 : 1+output all sensekey/asc
+ * 3 : 2+follow all chrdev related procedures
+ * 4 : 3+follow all procedures
+ * 5 : 4+include pc_stack rq_stack info
+ * 6 : 5+USE_COUNT updates
+ */
+ int debug_level;
+} idetape_tape_t;
+
+/*
+ * Tape door status
+ */
+#define DOOR_UNLOCKED 0
+#define DOOR_LOCKED 1
+#define DOOR_EXPLICITLY_LOCKED 2
+
+/*
+ * Tape flag bits values.
+ */
+#define IDETAPE_IGNORE_DSC 0
+#define IDETAPE_ADDRESS_VALID 1 /* 0 When the tape position is unknown */
+#define IDETAPE_BUSY 2 /* Device already opened */
+#define IDETAPE_PIPELINE_ERROR 3 /* Error detected in a pipeline stage */
+#define IDETAPE_DETECT_BS 4 /* Attempt to auto-detect the current user block size */
+#define IDETAPE_FILEMARK 5 /* Currently on a filemark */
+#define IDETAPE_DRQ_INTERRUPT 6 /* DRQ interrupt device */
+#define IDETAPE_READ_ERROR 7
+#define IDETAPE_PIPELINE_ACTIVE 8 /* pipeline active */
+
+/*
+ * Supported ATAPI tape drives packet commands
+ */
+#define IDETAPE_TEST_UNIT_READY_CMD 0x00
+#define IDETAPE_REWIND_CMD 0x01
+#define IDETAPE_REQUEST_SENSE_CMD 0x03
+#define IDETAPE_READ_CMD 0x08
+#define IDETAPE_WRITE_CMD 0x0a
+#define IDETAPE_WRITE_FILEMARK_CMD 0x10
+#define IDETAPE_SPACE_CMD 0x11
+#define IDETAPE_INQUIRY_CMD 0x12
+#define IDETAPE_ERASE_CMD 0x19
+#define IDETAPE_MODE_SENSE_CMD 0x1a
+#define IDETAPE_MODE_SELECT_CMD 0x15
+#define IDETAPE_LOAD_UNLOAD_CMD 0x1b
+#define IDETAPE_PREVENT_CMD 0x1e
+#define IDETAPE_LOCATE_CMD 0x2b
+#define IDETAPE_READ_POSITION_CMD 0x34
+#define IDETAPE_READ_BUFFER_CMD 0x3c
+#define IDETAPE_SET_SPEED_CMD 0xbb
+
+/*
+ * Some defines for the READ BUFFER command
+ */
+#define IDETAPE_RETRIEVE_FAULTY_BLOCK 6
+
+/*
+ * Some defines for the SPACE command
+ */
+#define IDETAPE_SPACE_OVER_FILEMARK 1
+#define IDETAPE_SPACE_TO_EOD 3
+
+/*
+ * Some defines for the LOAD UNLOAD command
+ */
+#define IDETAPE_LU_LOAD_MASK 1
+#define IDETAPE_LU_RETENSION_MASK 2
+#define IDETAPE_LU_EOT_MASK 4
+
+/*
+ * Special requests for our block device strategy routine.
+ *
+ * In order to service a character device command, we add special
+ * requests to the tail of our block device request queue and wait
+ * for their completion.
+ *
+ */
+#define IDETAPE_FIRST_RQ 90
+
+/*
+ * IDETAPE_PC_RQ is used to queue a packet command in the request queue.
+ */
+#define IDETAPE_PC_RQ1 90
+#define IDETAPE_PC_RQ2 91
+
+/*
+ * IDETAPE_READ_RQ and IDETAPE_WRITE_RQ are used by our
+ * character device interface to request read/write operations from
+ * our block device interface.
+ */
+#define IDETAPE_READ_RQ 92
+#define IDETAPE_WRITE_RQ 93
+#define IDETAPE_ABORTED_WRITE_RQ 94
+#define IDETAPE_ABORTED_READ_RQ 95
+#define IDETAPE_READ_BUFFER_RQ 96
+
+#define IDETAPE_LAST_RQ 96
+
+/*
+ * A macro which can be used to check if a we support a given
+ * request command.
+ */
+#define IDETAPE_RQ_CMD(cmd) ((cmd >= IDETAPE_FIRST_RQ) && (cmd <= IDETAPE_LAST_RQ))
+
+/*
+ * Error codes which are returned in rq->errors to the higher part
+ * of the driver.
+ */
+#define IDETAPE_ERROR_GENERAL 101
+#define IDETAPE_ERROR_FILEMARK 102
+#define IDETAPE_ERROR_EOD 103
+
+/*
+ * The ATAPI Status Register.
+ */
+typedef union {
+ unsigned all :8;
+ struct {
+ unsigned check :1; /* Error occurred */
+ unsigned idx :1; /* Reserved */
+ unsigned corr :1; /* Correctable error occurred */
+ unsigned drq :1; /* Data is request by the device */
+ unsigned dsc :1; /* Buffer availability / Media access command finished */
+ unsigned reserved5 :1; /* Reserved */
+ unsigned drdy :1; /* Ignored for ATAPI commands (ready to accept ATA command) */
+ unsigned bsy :1; /* The device has access to the command block */
+ } b;
+} idetape_status_reg_t;
+
+/*
+ * The ATAPI error register.
+ */
+typedef union {
+ unsigned all :8;
+ struct {
+ unsigned ili :1; /* Illegal Length Indication */
+ unsigned eom :1; /* End Of Media Detected */
+ unsigned abrt :1; /* Aborted command - As defined by ATA */
+ unsigned mcr :1; /* Media Change Requested - As defined by ATA */
+ unsigned sense_key :4; /* Sense key of the last failed packet command */
+ } b;
+} idetape_error_reg_t;
+
+/*
+ * ATAPI Feature Register
+ */
+typedef union {
+ unsigned all :8;
+ struct {
+ unsigned dma :1; /* Using DMA of PIO */
+ unsigned reserved321 :3; /* Reserved */
+ unsigned reserved654 :3; /* Reserved (Tag Type) */
+ unsigned reserved7 :1; /* Reserved */
+ } b;
+} idetape_feature_reg_t;
+
+/*
+ * ATAPI Byte Count Register.
+ */
+typedef union {
+ unsigned all :16;
+ struct {
+ unsigned low :8; /* LSB */
+ unsigned high :8; /* MSB */
+ } b;
+} idetape_bcount_reg_t;
+
+/*
+ * ATAPI Interrupt Reason Register.
+ */
+typedef union {
+ unsigned all :8;
+ struct {
+ unsigned cod :1; /* Information transferred is command (1) or data (0) */
+ unsigned io :1; /* The device requests us to read (1) or write (0) */
+ unsigned reserved :6; /* Reserved */
+ } b;
+} idetape_ireason_reg_t;
+
+/*
+ * ATAPI Drive Select Register
+ */
+typedef union {
+ unsigned all :8;
+ struct {
+ unsigned sam_lun :4; /* Should be zero with ATAPI (not used) */
+ unsigned drv :1; /* The responding drive will be drive 0 (0) or drive 1 (1) */
+ unsigned one5 :1; /* Should be set to 1 */
+ unsigned reserved6 :1; /* Reserved */
+ unsigned one7 :1; /* Should be set to 1 */
+ } b;
+} idetape_drivesel_reg_t;
+
+/*
+ * ATAPI Device Control Register
+ */
+typedef union {
+ unsigned all :8;
+ struct {
+ unsigned zero0 :1; /* Should be set to zero */
+ unsigned nien :1; /* Device interrupt is disabled (1) or enabled (0) */
+ unsigned srst :1; /* ATA software reset. ATAPI devices should use the new ATAPI srst. */
+ unsigned one3 :1; /* Should be set to 1 */
+ unsigned reserved4567 :4; /* Reserved */
+ } b;
+} idetape_control_reg_t;
+
+/*
+ * idetape_chrdev_t provides the link between out character device
+ * interface and our block device interface and the corresponding
+ * ide_drive_t structure.
+ */
+typedef struct {
+ ide_drive_t *drive;
+} idetape_chrdev_t;
+
+/*
+ * The following is used to format the general configuration word of
+ * the ATAPI IDENTIFY DEVICE command.
+ */
+struct idetape_id_gcw {
+ unsigned packet_size :2; /* Packet Size */
+ unsigned reserved234 :3; /* Reserved */
+ unsigned drq_type :2; /* Command packet DRQ type */
+ unsigned removable :1; /* Removable media */
+ unsigned device_type :5; /* Device type */
+ unsigned reserved13 :1; /* Reserved */
+ unsigned protocol :2; /* Protocol type */
+};
+
+/*
+ * INQUIRY packet command - Data Format (From Table 6-8 of QIC-157C)
+ */
+typedef struct {
+ unsigned device_type :5; /* Peripheral Device Type */
+ unsigned reserved0_765 :3; /* Peripheral Qualifier - Reserved */
+ unsigned reserved1_6t0 :7; /* Reserved */
+ unsigned rmb :1; /* Removable Medium Bit */
+ unsigned ansi_version :3; /* ANSI Version */
+ unsigned ecma_version :3; /* ECMA Version */
+ unsigned iso_version :2; /* ISO Version */
+ unsigned response_format :4; /* Response Data Format */
+ unsigned reserved3_45 :2; /* Reserved */
+ unsigned reserved3_6 :1; /* TrmIOP - Reserved */
+ unsigned reserved3_7 :1; /* AENC - Reserved */
+ __u8 additional_length; /* Additional Length (total_length-4) */
+ __u8 rsv5, rsv6, rsv7; /* Reserved */
+ __u8 vendor_id[8]; /* Vendor Identification */
+ __u8 product_id[16]; /* Product Identification */
+ __u8 revision_level[4]; /* Revision Level */
+ __u8 vendor_specific[20]; /* Vendor Specific - Optional */
+ __u8 reserved56t95[40]; /* Reserved - Optional */
+ /* Additional information may be returned */
+} idetape_inquiry_result_t;
+
+/*
+ * READ POSITION packet command - Data Format (From Table 6-57)
+ */
+typedef struct {
+ unsigned reserved0_10 :2; /* Reserved */
+ unsigned bpu :1; /* Block Position Unknown */
+ unsigned reserved0_543 :3; /* Reserved */
+ unsigned eop :1; /* End Of Partition */
+ unsigned bop :1; /* Beginning Of Partition */
+ u8 partition; /* Partition Number */
+ u8 reserved2, reserved3; /* Reserved */
+ u32 first_block; /* First Block Location */
+ u32 last_block; /* Last Block Location (Optional) */
+ u8 reserved12; /* Reserved */
+ u8 blocks_in_buffer[3]; /* Blocks In Buffer - (Optional) */
+ u32 bytes_in_buffer; /* Bytes In Buffer (Optional) */
+} idetape_read_position_result_t;
+
+/*
+ * Follows structures which are related to the SELECT SENSE / MODE SENSE
+ * packet commands. Those packet commands are still not supported
+ * by ide-tape.
+ */
+#define IDETAPE_CAPABILITIES_PAGE 0x2a
+#define IDETAPE_BLOCK_SIZE_PAGE 0x30
+
+/*
+ * Mode Parameter Header for the MODE SENSE packet command
+ */
+typedef struct {
+ __u8 mode_data_length; /* Length of the following data transfer */
+ __u8 medium_type; /* Medium Type */
+ __u8 dsp; /* Device Specific Parameter */
+ __u8 bdl; /* Block Descriptor Length */
+#if 0
+ /* data transfer page */
+ __u8 page_code :6;
+ __u8 reserved0_6 :1;
+ __u8 ps :1; /* parameters saveable */
+ __u8 page_length; /* page Length == 0x02 */
+ __u8 reserved2;
+ __u8 read32k :1; /* 32k blk size (data only) */
+ __u8 read32k5 :1; /* 32.5k blk size (data&AUX) */
+ __u8 reserved3_23 :2;
+ __u8 write32k :1; /* 32k blk size (data only) */
+ __u8 write32k5 :1; /* 32.5k blk size (data&AUX) */
+ __u8 reserved3_6 :1;
+ __u8 streaming :1; /* streaming mode enable */
+#endif
+} idetape_mode_parameter_header_t;
+
+/*
+ * Mode Parameter Block Descriptor the MODE SENSE packet command
+ *
+ * Support for block descriptors is optional.
+ */
+typedef struct {
+ __u8 density_code; /* Medium density code */
+ __u8 blocks[3]; /* Number of blocks */
+ __u8 reserved4; /* Reserved */
+ __u8 length[3]; /* Block Length */
+} idetape_parameter_block_descriptor_t;
+
+/*
+ * The Data Compression Page, as returned by the MODE SENSE packet command.
+ */
+typedef struct {
+ unsigned page_code :6; /* Page Code - Should be 0xf */
+ unsigned reserved0 :1; /* Reserved */
+ unsigned ps :1;
+ __u8 page_length; /* Page Length - Should be 14 */
+ unsigned reserved2 :6; /* Reserved */
+ unsigned dcc :1; /* Data Compression Capable */
+ unsigned dce :1; /* Data Compression Enable */
+ unsigned reserved3 :5; /* Reserved */
+ unsigned red :2; /* Report Exception on Decompression */
+ unsigned dde :1; /* Data Decompression Enable */
+ __u32 ca; /* Compression Algorithm */
+ __u32 da; /* Decompression Algorithm */
+ __u8 reserved[4]; /* Reserved */
+} idetape_data_compression_page_t;
+
+/*
+ * The Medium Partition Page, as returned by the MODE SENSE packet command.
+ */
+typedef struct {
+ unsigned page_code :6; /* Page Code - Should be 0x11 */
+ unsigned reserved1_6 :1; /* Reserved */
+ unsigned ps :1;
+ __u8 page_length; /* Page Length - Should be 6 */
+ __u8 map; /* Maximum Additional Partitions - Should be 0 */
+ __u8 apd; /* Additional Partitions Defined - Should be 0 */
+ unsigned reserved4_012 :3; /* Reserved */
+ unsigned psum :2; /* Should be 0 */
+ unsigned idp :1; /* Should be 0 */
+ unsigned sdp :1; /* Should be 0 */
+ unsigned fdp :1; /* Fixed Data Partitions */
+ __u8 mfr; /* Medium Format Recognition */
+ __u8 reserved[2]; /* Reserved */
+} idetape_medium_partition_page_t;
+
+/*
+ * Run time configurable parameters.
+ */
+typedef struct {
+ int dsc_rw_frequency;
+ int dsc_media_access_frequency;
+ int nr_stages;
+} idetape_config_t;
+
+/*
+ * The variables below are used for the character device interface.
+ * Additional state variables are defined in our ide_drive_t structure.
+ */
+static idetape_chrdev_t idetape_chrdevs[MAX_HWIFS * MAX_DRIVES];
+static int idetape_chrdev_present = 0;
+
+#if IDETAPE_DEBUG_LOG_VERBOSE
+
+/*
+ * DO NOT REMOVE, BUILDING A VERBOSE DEBUG SCHEME FOR ATAPI
+ */
+
+char *idetape_sense_key_verbose (byte idetape_sense_key)
+{
+ switch (idetape_sense_key) {
+ default: {
+ char buf[22];
+ sprintf(buf, "IDETAPE_SENSE (0x%02x)", idetape_sense_key);
+ return(buf);
+ }
+
+ }
+}
+
+char *idetape_command_key_verbose (byte idetape_command_key)
+{
+ switch (idetape_command_key) {
+ case IDETAPE_TEST_UNIT_READY_CMD: return("TEST_UNIT_READY_CMD");
+ case IDETAPE_REWIND_CMD: return("REWIND_CMD");
+ case IDETAPE_REQUEST_SENSE_CMD: return("REQUEST_SENSE_CMD");
+ case IDETAPE_READ_CMD: return("READ_CMD");
+ case IDETAPE_WRITE_CMD: return("WRITE_CMD");
+ case IDETAPE_WRITE_FILEMARK_CMD: return("WRITE_FILEMARK_CMD");
+ case IDETAPE_SPACE_CMD: return("SPACE_CMD");
+ case IDETAPE_INQUIRY_CMD: return("INQUIRY_CMD");
+ case IDETAPE_ERASE_CMD: return("ERASE_CMD")
+ case IDETAPE_MODE_SENSE_CMD: return("MODE_SENSE_CMD");
+ case IDETAPE_MODE_SELECT_CMD: return("MODE_SELECT_CMD");
+ case IDETAPE_LOAD_UNLOAD_CMD: return("LOAD_UNLOAD_CMD");
+ case IDETAPE_PREVENT_CMD: return("PREVENT_CMD");
+ case IDETAPE_LOCATE_CMD: return("LOCATE_CMD");
+ case IDETAPE_READ_POSITION_CMD: return("READ_POSITION_CMD");
+ case IDETAPE_READ_BUFFER_CMD: return("READ_BUFFER_CMD");
+ case IDETAPE_SET_SPEED_CMD: return("SET_SPEED_CMD");
+ default: {
+ char buf[20];
+ sprintf(buf, "CMD (0x%02x)", idetape_command_key);
+ return(buf);
+ }
+ }
+}
+#endif /* IDETAPE_DEBUG_LOG_VERBOSE */
+
+/*
+ * Too bad. The drive wants to send us data which we are not ready to accept.
+ * Just throw it away.
+ */
+static void idetape_discard_data (ide_drive_t *drive, unsigned int bcount)
+{
+ while (bcount--)
+ IN_BYTE (IDE_DATA_REG);
+}
+
+static void idetape_input_buffers (ide_drive_t *drive, idetape_pc_t *pc, unsigned int bcount)
+{
+ struct buffer_head *bh = pc->bh;
+ int count;
+
+ while (bcount) {
+#if IDETAPE_DEBUG_BUGS
+ if (bh == NULL) {
+ printk (KERN_ERR "ide-tape: bh == NULL in idetape_input_buffers\n");
+ idetape_discard_data (drive, bcount);
+ return;
+ }
+#endif /* IDETAPE_DEBUG_BUGS */
+ count = IDE_MIN (bh->b_size - atomic_read(&bh->b_count), bcount);
+ atapi_input_bytes (drive, bh->b_data + atomic_read(&bh->b_count), count);
+ bcount -= count; atomic_add(count, &bh->b_count);
+ if (atomic_read(&bh->b_count) == bh->b_size) {
+ bh = bh->b_reqnext;
+ if (bh)
+ atomic_set(&bh->b_count, 0);
+ }
+ }
+ pc->bh = bh;
+}
+
+static void idetape_output_buffers (ide_drive_t *drive, idetape_pc_t *pc, unsigned int bcount)
+{
+ struct buffer_head *bh = pc->bh;
+ int count;
+
+ while (bcount) {
+#if IDETAPE_DEBUG_BUGS
+ if (bh == NULL) {
+ printk (KERN_ERR "ide-tape: bh == NULL in idetape_output_buffers\n");
+ return;
+ }
+#endif /* IDETAPE_DEBUG_BUGS */
+ count = IDE_MIN (pc->b_count, bcount);
+ atapi_output_bytes (drive, pc->b_data, count);
+ bcount -= count; pc->b_data += count; pc->b_count -= count;
+ if (!pc->b_count) {
+ pc->bh = bh = bh->b_reqnext;
+ if (bh) {
+ pc->b_data = bh->b_data;
+ pc->b_count = atomic_read(&bh->b_count);
+ }
+ }
+ }
+}
+
+#ifdef CONFIG_BLK_DEV_IDEDMA
+static void idetape_update_buffers (idetape_pc_t *pc)
+{
+ struct buffer_head *bh = pc->bh;
+ int count, bcount = pc->actually_transferred;
+
+ if (test_bit (PC_WRITING, &pc->flags))
+ return;
+ while (bcount) {
+#if IDETAPE_DEBUG_BUGS
+ if (bh == NULL) {
+ printk (KERN_ERR "ide-tape: bh == NULL in idetape_update_buffers\n");
+ return;
+ }
+#endif /* IDETAPE_DEBUG_BUGS */
+ count = IDE_MIN (bh->b_size, bcount);
+ atomic_set(&bh->b_count, count);
+ if (atomic_read(&bh->b_count) == bh->b_size)
+ bh = bh->b_reqnext;
+ bcount -= count;
+ }
+ pc->bh = bh;
+}
+#endif /* CONFIG_BLK_DEV_IDEDMA */
+
+/*
+ * idetape_next_pc_storage returns a pointer to a place in which we can
+ * safely store a packet command, even though we intend to leave the
+ * driver. A storage space for a maximum of IDETAPE_PC_STACK packet
+ * commands is allocated at initialization time.
+ */
+static idetape_pc_t *idetape_next_pc_storage (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+
+#if IDETAPE_DEBUG_LOG
+ if (tape->debug_level >= 5)
+ printk (KERN_INFO "ide-tape: pc_stack_index=%d\n",tape->pc_stack_index);
+#endif /* IDETAPE_DEBUG_LOG */
+ if (tape->pc_stack_index==IDETAPE_PC_STACK)
+ tape->pc_stack_index=0;
+ return (&tape->pc_stack[tape->pc_stack_index++]);
+}
+
+/*
+ * idetape_next_rq_storage is used along with idetape_next_pc_storage.
+ * Since we queue packet commands in the request queue, we need to
+ * allocate a request, along with the allocation of a packet command.
+ */
+
+/**************************************************************
+ * *
+ * This should get fixed to use kmalloc(GFP_ATOMIC, ..) *
+ * followed later on by kfree(). -ml *
+ * *
+ **************************************************************/
+
+static struct request *idetape_next_rq_storage (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+
+#if IDETAPE_DEBUG_LOG
+ if (tape->debug_level >= 5)
+ printk (KERN_INFO "ide-tape: rq_stack_index=%d\n",tape->rq_stack_index);
+#endif /* IDETAPE_DEBUG_LOG */
+ if (tape->rq_stack_index==IDETAPE_PC_STACK)
+ tape->rq_stack_index=0;
+ return (&tape->rq_stack[tape->rq_stack_index++]);
+}
+
+/*
+ * idetape_init_pc initializes a packet command.
+ */
+static void idetape_init_pc (idetape_pc_t *pc)
+{
+ memset (pc->c, 0, 12);
+ pc->retries = 0;
+ pc->flags = 0;
+ pc->request_transfer = 0;
+ pc->buffer = pc->pc_buffer;
+ pc->buffer_size = IDETAPE_PC_BUFFER_SIZE;
+ pc->bh = NULL;
+ pc->b_data = NULL;
+}
+
+/*
+ * idetape_analyze_error is called on each failed packet command retry
+ * to analyze the request sense. We currently do not utilize this
+ * information.
+ */
+static void idetape_analyze_error (ide_drive_t *drive,idetape_request_sense_result_t *result)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ idetape_pc_t *pc = tape->failed_pc;
+
+ tape->sense = *result;
+ tape->sense_key = result->sense_key; tape->asc = result->asc; tape->ascq = result->ascq;
+#if IDETAPE_DEBUG_LOG
+ /*
+ * Without debugging, we only log an error if we decided to
+ * give up retrying.
+ */
+ if (tape->debug_level >= 1)
+ printk (KERN_INFO "ide-tape: pc = %x, sense key = %x, asc = %x, ascq = %x\n",pc->c[0],result->sense_key,result->asc,result->ascq);
+#if IDETAPE_DEBUG_LOG_VERBOSE
+ if (tape->debug_level >= 1)
+ printk (KERN_INFO "ide-tape: pc = %s, sense key = %x, asc = %x, ascq = %x\n",
+ idetape_command_key_verbose((byte) pc->c[0]),
+ result->sense_key,
+ result->asc,
+ result->ascq);
+#endif /* IDETAPE_DEBUG_LOG_VERBOSE */
+#endif /* IDETAPE_DEBUG_LOG */
+
+ if (tape->onstream && result->sense_key == 2 && result->asc == 0x53 && result->ascq == 2) {
+ clear_bit(PC_DMA_ERROR, &pc->flags);
+ ide_stall_queue(drive, HZ / 2);
+ return;
+ }
+#ifdef CONFIG_BLK_DEV_IDEDMA
+
+ /*
+ * Correct pc->actually_transferred by asking the tape.
+ */
+ if (test_bit (PC_DMA_ERROR, &pc->flags)) {
+ pc->actually_transferred = pc->request_transfer - tape->tape_block_size * ntohl (get_unaligned (&result->information));
+ idetape_update_buffers (pc);
+ }
+#endif /* CONFIG_BLK_DEV_IDEDMA */
+ if (pc->c[0] == IDETAPE_READ_CMD && result->filemark) {
+ pc->error = IDETAPE_ERROR_FILEMARK;
+ set_bit (PC_ABORT, &pc->flags);
+ }
+ if (pc->c[0] == IDETAPE_WRITE_CMD) {
+ if (result->eom || (result->sense_key == 0xd && result->asc == 0x0 && result->ascq == 0x2)) {
+ pc->error = IDETAPE_ERROR_EOD;
+ set_bit (PC_ABORT, &pc->flags);
+ }
+ }
+ if (pc->c[0] == IDETAPE_READ_CMD || pc->c[0] == IDETAPE_WRITE_CMD) {
+ if (result->sense_key == 8) {
+ pc->error = IDETAPE_ERROR_EOD;
+ set_bit (PC_ABORT, &pc->flags);
+ }
+ if (!test_bit (PC_ABORT, &pc->flags) && (tape->onstream || pc->actually_transferred))
+ pc->retries = IDETAPE_MAX_PC_RETRIES + 1;
+ }
+}
+
+static void idetape_abort_pipeline (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ idetape_stage_t *stage = tape->next_stage;
+
+#if IDETAPE_DEBUG_LOG
+ if (tape->debug_level >= 4)
+ printk(KERN_INFO "ide-tape: %s: idetape_abort_pipeline called\n", tape->name);
+#endif
+ while (stage) {
+ if (stage->rq.cmd == IDETAPE_WRITE_RQ)
+ stage->rq.cmd = IDETAPE_ABORTED_WRITE_RQ;
+ else if (stage->rq.cmd == IDETAPE_READ_RQ)
+ stage->rq.cmd = IDETAPE_ABORTED_READ_RQ;
+ stage = stage->next;
+ }
+}
+
+/*
+ * idetape_active_next_stage will declare the next stage as "active".
+ */
+static void idetape_active_next_stage (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ idetape_stage_t *stage=tape->next_stage;
+ struct request *rq = &stage->rq;
+
+#if IDETAPE_DEBUG_LOG
+ if (tape->debug_level >= 4)
+ printk (KERN_INFO "ide-tape: Reached idetape_active_next_stage\n");
+#endif /* IDETAPE_DEBUG_LOG */
+#if IDETAPE_DEBUG_BUGS
+ if (stage == NULL) {
+ printk (KERN_ERR "ide-tape: bug: Trying to activate a non existing stage\n");
+ return;
+ }
+#endif /* IDETAPE_DEBUG_BUGS */
+
+ rq->buffer = NULL;
+ rq->bh = stage->bh;
+ tape->active_data_request=rq;
+ tape->active_stage=stage;
+ tape->next_stage=stage->next;
+}
+
+/*
+ * idetape_increase_max_pipeline_stages is a part of the feedback
+ * loop which tries to find the optimum number of stages. In the
+ * feedback loop, we are starting from a minimum maximum number of
+ * stages, and if we sense that the pipeline is empty, we try to
+ * increase it, until we reach the user compile time memory limit.
+ */
+static void idetape_increase_max_pipeline_stages (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ int increase = (tape->max_pipeline - tape->min_pipeline) / 10;
+
+#if IDETAPE_DEBUG_LOG
+ if (tape->debug_level >= 4)
+ printk (KERN_INFO "ide-tape: Reached idetape_increase_max_pipeline_stages\n");
+#endif /* IDETAPE_DEBUG_LOG */
+
+ tape->max_stages += increase;
+ tape->max_stages = IDE_MAX(tape->max_stages, tape->min_pipeline);
+ tape->max_stages = IDE_MIN(tape->max_stages, tape->max_pipeline);
+}
+
+/*
+ * idetape_kfree_stage calls kfree to completely free a stage, along with
+ * its related buffers.
+ */
+static void __idetape_kfree_stage (idetape_stage_t *stage)
+{
+ struct buffer_head *prev_bh, *bh = stage->bh;
+ int size;
+
+ while (bh != NULL) {
+ if (bh->b_data != NULL) {
+ size = (int) bh->b_size;
+ while (size > 0) {
+ free_page ((unsigned long) bh->b_data);
+ size -= PAGE_SIZE;
+ bh->b_data += PAGE_SIZE;
+ }
+ }
+ prev_bh = bh;
+ bh = bh->b_reqnext;
+ kfree (prev_bh);
+ }
+ kfree (stage);
+}
+
+static void idetape_kfree_stage (idetape_tape_t *tape, idetape_stage_t *stage)
+{
+ __idetape_kfree_stage (stage);
+}
+
+/*
+ * idetape_remove_stage_head removes tape->first_stage from the pipeline.
+ * The caller should avoid race conditions.
+ */
+static void idetape_remove_stage_head (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ idetape_stage_t *stage;
+
+#if IDETAPE_DEBUG_LOG
+ if (tape->debug_level >= 4)
+ printk (KERN_INFO "ide-tape: Reached idetape_remove_stage_head\n");
+#endif /* IDETAPE_DEBUG_LOG */
+#if IDETAPE_DEBUG_BUGS
+ if (tape->first_stage == NULL) {
+ printk (KERN_ERR "ide-tape: bug: tape->first_stage is NULL\n");
+ return;
+ }
+ if (tape->active_stage == tape->first_stage) {
+ printk (KERN_ERR "ide-tape: bug: Trying to free our active pipeline stage\n");
+ return;
+ }
+#endif /* IDETAPE_DEBUG_BUGS */
+ stage=tape->first_stage;
+ tape->first_stage=stage->next;
+ idetape_kfree_stage (tape, stage);
+ tape->nr_stages--;
+ if (tape->first_stage == NULL) {
+ tape->last_stage=NULL;
+#if IDETAPE_DEBUG_BUGS
+ if (tape->next_stage != NULL)
+ printk (KERN_ERR "ide-tape: bug: tape->next_stage != NULL\n");
+ if (tape->nr_stages)
+ printk (KERN_ERR "ide-tape: bug: nr_stages should be 0 now\n");
+#endif /* IDETAPE_DEBUG_BUGS */
+ }
+}
+
+/*
+ * idetape_end_request is used to finish servicing a request, and to
+ * insert a pending pipeline request into the main device queue.
+ */
+static void idetape_end_request (byte uptodate, ide_hwgroup_t *hwgroup)
+{
+ ide_drive_t *drive = hwgroup->drive;
+ struct request *rq = hwgroup->rq;
+ idetape_tape_t *tape = drive->driver_data;
+ unsigned long flags;
+ int error;
+ int remove_stage = 0;
+#if ONSTREAM_DEBUG
+ idetape_stage_t *stage;
+ os_aux_t *aux;
+ unsigned char *p;
+#endif
+
+#if IDETAPE_DEBUG_LOG
+ if (tape->debug_level >= 4)
+ printk (KERN_INFO "ide-tape: Reached idetape_end_request\n");
+#endif /* IDETAPE_DEBUG_LOG */
+
+ switch (uptodate) {
+ case 0: error = IDETAPE_ERROR_GENERAL; break;
+ case 1: error = 0; break;
+ default: error = uptodate;
+ }
+ rq->errors = error;
+ if (error)
+ tape->failed_pc = NULL;
+
+ spin_lock_irqsave(&tape->spinlock, flags);
+ if (tape->active_data_request == rq) { /* The request was a pipelined data transfer request */
+ tape->active_stage = NULL;
+ tape->active_data_request = NULL;
+ tape->nr_pending_stages--;
+ if (rq->cmd == IDETAPE_WRITE_RQ) {
+#if ONSTREAM_DEBUG
+ if (tape->debug_level >= 2) {
+ if (tape->onstream) {
+ stage = tape->first_stage;
+ aux = stage->aux;
+ p = stage->bh->b_data;
+ if (ntohl(aux->logical_blk_num) < 11300 && ntohl(aux->logical_blk_num) > 11100)
+ printk(KERN_INFO "ide-tape: finished writing logical blk %u (data %x %x %x %x)\n", ntohl(aux->logical_blk_num), *p++, *p++, *p++, *p++);
+ }
+ }
+#endif
+ if (tape->onstream && !tape->raw) {
+ if (tape->first_frame_position == 0xba4) {
+#if ONSTREAM_DEBUG
+ if (tape->debug_level >= 2)
+ printk("ide-tape: %s: skipping over config parition..\n", tape->name);
+#endif
+ tape->onstream_write_error = 2;
+ if (tape->sem)
+ up(tape->sem);
+ }
+ }
+ remove_stage = 1;
+ if (error) {
+ set_bit (IDETAPE_PIPELINE_ERROR, &tape->flags);
+ if (error == IDETAPE_ERROR_EOD)
+ idetape_abort_pipeline (drive);
+ if (tape->onstream && !tape->raw && error == IDETAPE_ERROR_GENERAL && tape->sense.sense_key == 3) {
+ clear_bit (IDETAPE_PIPELINE_ERROR, &tape->flags);
+ printk(KERN_ERR "ide-tape: %s: write error, enabling error recovery\n", tape->name);
+ tape->onstream_write_error = 1;
+ remove_stage = 0;
+ tape->nr_pending_stages++;
+ tape->next_stage = tape->first_stage;
+ rq->current_nr_sectors = rq->nr_sectors;
+ if (tape->sem)
+ up(tape->sem);
+ }
+ }
+ } else if (rq->cmd == IDETAPE_READ_RQ) {
+ if (error == IDETAPE_ERROR_EOD) {
+ set_bit (IDETAPE_PIPELINE_ERROR, &tape->flags);
+ idetape_abort_pipeline(drive);
+ }
+ }
+ if (tape->next_stage != NULL && !tape->onstream_write_error) {
+ idetape_active_next_stage (drive);
+
+ /*
+ * Insert the next request into the request queue.
+ */
+ (void) ide_do_drive_cmd (drive, tape->active_data_request, ide_end);
+ } else if (!error) {
+ if (!tape->onstream)
+ idetape_increase_max_pipeline_stages (drive);
+ }
+ }
+ ide_end_drive_cmd (drive, 0, 0);
+ if (remove_stage)
+ idetape_remove_stage_head (drive);
+ if (tape->active_data_request == NULL)
+ clear_bit(IDETAPE_PIPELINE_ACTIVE, &tape->flags);
+ spin_unlock_irqrestore(&tape->spinlock, flags);
+}
+
+static ide_startstop_t idetape_request_sense_callback (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+
+#if IDETAPE_DEBUG_LOG
+ if (tape->debug_level >= 4)
+ printk (KERN_INFO "ide-tape: Reached idetape_request_sense_callback\n");
+#endif /* IDETAPE_DEBUG_LOG */
+ if (!tape->pc->error) {
+ idetape_analyze_error (drive,(idetape_request_sense_result_t *) tape->pc->buffer);
+ idetape_end_request (1,HWGROUP (drive));
+ } else {
+ printk (KERN_ERR "ide-tape: Error in REQUEST SENSE itself - Aborting request!\n");
+ idetape_end_request (0,HWGROUP (drive));
+ }
+ return ide_stopped;
+}
+
+static void idetape_create_request_sense_cmd (idetape_pc_t *pc)
+{
+ idetape_init_pc (pc);
+ pc->c[0] = IDETAPE_REQUEST_SENSE_CMD;
+ pc->c[4] = 20;
+ pc->request_transfer = 18;
+ pc->callback = &idetape_request_sense_callback;
+}
+
+/*
+ * idetape_queue_pc_head generates a new packet command request in front
+ * of the request queue, before the current request, so that it will be
+ * processed immediately, on the next pass through the driver.
+ *
+ * idetape_queue_pc_head is called from the request handling part of
+ * the driver (the "bottom" part). Safe storage for the request should
+ * be allocated with idetape_next_pc_storage and idetape_next_rq_storage
+ * before calling idetape_queue_pc_head.
+ *
+ * Memory for those requests is pre-allocated at initialization time, and
+ * is limited to IDETAPE_PC_STACK requests. We assume that we have enough
+ * space for the maximum possible number of inter-dependent packet commands.
+ *
+ * The higher level of the driver - The ioctl handler and the character
+ * device handling functions should queue request to the lower level part
+ * and wait for their completion using idetape_queue_pc_tail or
+ * idetape_queue_rw_tail.
+ */
+static void idetape_queue_pc_head (ide_drive_t *drive,idetape_pc_t *pc,struct request *rq)
+{
+ ide_init_drive_cmd (rq);
+ rq->buffer = (char *) pc;
+ rq->cmd = IDETAPE_PC_RQ1;
+ (void) ide_do_drive_cmd (drive, rq, ide_preempt);
+}
+
+/*
+ * idetape_retry_pc is called when an error was detected during the
+ * last packet command. We queue a request sense packet command in
+ * the head of the request list.
+ */
+static ide_startstop_t idetape_retry_pc (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ idetape_pc_t *pc;
+ struct request *rq;
+ idetape_error_reg_t error;
+
+ error.all = IN_BYTE (IDE_ERROR_REG);
+ pc = idetape_next_pc_storage (drive);
+ rq = idetape_next_rq_storage (drive);
+ idetape_create_request_sense_cmd (pc);
+ set_bit (IDETAPE_IGNORE_DSC, &tape->flags);
+ idetape_queue_pc_head (drive, pc, rq);
+ return ide_stopped;
+}
+
+/*
+ * idetape_postpone_request postpones the current request so that
+ * ide.c will be able to service requests from another device on
+ * the same hwgroup while we are polling for DSC.
+ */
+static void idetape_postpone_request (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+
+#if IDETAPE_DEBUG_LOG
+ if (tape->debug_level >= 4)
+ printk(KERN_INFO "ide-tape: idetape_postpone_request\n");
+#endif
+ tape->postponed_rq = HWGROUP(drive)->rq;
+ ide_stall_queue(drive, tape->dsc_polling_frequency);
+}
+
+/*
+ * idetape_pc_intr is the usual interrupt handler which will be called
+ * during a packet command. We will transfer some of the data (as
+ * requested by the drive) and will re-point interrupt handler to us.
+ * When data transfer is finished, we will act according to the
+ * algorithm described before idetape_issue_packet_command.
+ *
+ */
+static ide_startstop_t idetape_pc_intr (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ idetape_status_reg_t status;
+ idetape_bcount_reg_t bcount;
+ idetape_ireason_reg_t ireason;
+ idetape_pc_t *pc=tape->pc;
+
+ unsigned int temp;
+ unsigned long cmd_time;
+#if SIMULATE_ERRORS
+ static int error_sim_count = 0;
+#endif
+
+#if IDETAPE_DEBUG_LOG
+ if (tape->debug_level >= 4)
+ printk (KERN_INFO "ide-tape: Reached idetape_pc_intr interrupt handler\n");
+#endif /* IDETAPE_DEBUG_LOG */
+
+ status.all = GET_STAT(); /* Clear the interrupt */
+
+#ifdef CONFIG_BLK_DEV_IDEDMA
+ if (test_bit (PC_DMA_IN_PROGRESS, &pc->flags)) {
+ if (HWIF(drive)->dmaproc(ide_dma_end, drive)) {
+ /*
+ * A DMA error is sometimes expected. For example,
+ * if the tape is crossing a filemark during a
+ * READ command, it will issue an irq and position
+ * itself before the filemark, so that only a partial
+ * data transfer will occur (which causes the DMA
+ * error). In that case, we will later ask the tape
+ * how much bytes of the original request were
+ * actually transferred (we can't receive that
+ * information from the DMA engine on most chipsets).
+ */
+ set_bit (PC_DMA_ERROR, &pc->flags);
+ } else if (!status.b.check) {
+ pc->actually_transferred=pc->request_transfer;
+ idetape_update_buffers (pc);
+ }
+#if IDETAPE_DEBUG_LOG
+ if (tape->debug_level >= 4)
+ printk (KERN_INFO "ide-tape: DMA finished\n");
+#endif /* IDETAPE_DEBUG_LOG */
+ }
+#endif /* CONFIG_BLK_DEV_IDEDMA */
+
+ if (!status.b.drq) { /* No more interrupts */
+ cmd_time = (jiffies - tape->cmd_start_time) * 1000 / HZ;
+ tape->max_cmd_time = IDE_MAX(cmd_time, tape->max_cmd_time);
+#if IDETAPE_DEBUG_LOG
+ if (tape->debug_level >= 2)
+ printk (KERN_INFO "ide-tape: Packet command completed, %d bytes transferred\n", pc->actually_transferred);
+#endif /* IDETAPE_DEBUG_LOG */
+ clear_bit (PC_DMA_IN_PROGRESS, &pc->flags);
+
+ ide__sti(); /* local CPU only */
+
+#if SIMULATE_ERRORS
+ if ((pc->c[0] == IDETAPE_WRITE_CMD || pc->c[0] == IDETAPE_READ_CMD) && (++error_sim_count % 100) == 0) {
+ printk(KERN_INFO "ide-tape: %s: simulating error\n", tape->name);
+ status.b.check = 1;
+ }
+#endif
+ if (status.b.check && pc->c[0] == IDETAPE_REQUEST_SENSE_CMD)
+ status.b.check = 0;
+ if (status.b.check || test_bit (PC_DMA_ERROR, &pc->flags)) { /* Error detected */
+#if IDETAPE_DEBUG_LOG
+ if (tape->debug_level >= 1)
+ printk (KERN_INFO "ide-tape: %s: I/O error, ",tape->name);
+#endif /* IDETAPE_DEBUG_LOG */
+ if (pc->c[0] == IDETAPE_REQUEST_SENSE_CMD) {
+ printk (KERN_ERR "ide-tape: I/O error in request sense command\n");
+ return ide_do_reset (drive);
+ }
+#if IDETAPE_DEBUG_LOG
+ if (tape->debug_level >= 1)
+ printk(KERN_INFO "ide-tape: [cmd %x]: check condition\n", pc->c[0]);
+#endif
+ return idetape_retry_pc (drive); /* Retry operation */
+ }
+ pc->error = 0;
+ if (!tape->onstream && test_bit (PC_WAIT_FOR_DSC, &pc->flags) && !status.b.dsc) { /* Media access command */
+ tape->dsc_polling_start = jiffies;
+ tape->dsc_polling_frequency = IDETAPE_DSC_MA_FAST;
+ tape->dsc_timeout = jiffies + IDETAPE_DSC_MA_TIMEOUT;
+ idetape_postpone_request (drive); /* Allow ide.c to handle other requests */
+ return ide_stopped;
+ }
+ if (tape->failed_pc == pc)
+ tape->failed_pc=NULL;
+ return pc->callback(drive); /* Command finished - Call the callback function */
+ }
+#ifdef CONFIG_BLK_DEV_IDEDMA
+ if (test_and_clear_bit (PC_DMA_IN_PROGRESS, &pc->flags)) {
+ printk (KERN_ERR "ide-tape: The tape wants to issue more interrupts in DMA mode\n");
+ printk (KERN_ERR "ide-tape: DMA disabled, reverting to PIO\n");
+ (void) HWIF(drive)->dmaproc(ide_dma_off, drive);
+ return ide_do_reset (drive);
+ }
+#endif /* CONFIG_BLK_DEV_IDEDMA */
+ bcount.b.high=IN_BYTE (IDE_BCOUNTH_REG); /* Get the number of bytes to transfer */
+ bcount.b.low=IN_BYTE (IDE_BCOUNTL_REG); /* on this interrupt */
+ ireason.all=IN_BYTE (IDE_IREASON_REG);
+
+ if (ireason.b.cod) {
+ printk (KERN_ERR "ide-tape: CoD != 0 in idetape_pc_intr\n");
+ return ide_do_reset (drive);
+ }
+ if (ireason.b.io == test_bit (PC_WRITING, &pc->flags)) { /* Hopefully, we will never get here */
+ printk (KERN_ERR "ide-tape: We wanted to %s, ", ireason.b.io ? "Write":"Read");
+ printk (KERN_ERR "ide-tape: but the tape wants us to %s !\n",ireason.b.io ? "Read":"Write");
+ return ide_do_reset (drive);
+ }
+ if (!test_bit (PC_WRITING, &pc->flags)) { /* Reading - Check that we have enough space */
+ temp = pc->actually_transferred + bcount.all;
+ if ( temp > pc->request_transfer) {
+ if (temp > pc->buffer_size) {
+ printk (KERN_ERR "ide-tape: The tape wants to send us more data than expected - discarding data\n");
+ idetape_discard_data (drive,bcount.all);
+ ide_set_handler (drive,&idetape_pc_intr,IDETAPE_WAIT_CMD,NULL);
+ return ide_started;
+ }
+#if IDETAPE_DEBUG_LOG
+ if (tape->debug_level >= 2)
+ printk (KERN_NOTICE "ide-tape: The tape wants to send us more data than expected - allowing transfer\n");
+#endif /* IDETAPE_DEBUG_LOG */
+ }
+ }
+ if (test_bit (PC_WRITING, &pc->flags)) {
+ if (pc->bh != NULL)
+ idetape_output_buffers (drive, pc, bcount.all);
+ else
+ atapi_output_bytes (drive,pc->current_position,bcount.all); /* Write the current buffer */
+ } else {
+ if (pc->bh != NULL)
+ idetape_input_buffers (drive, pc, bcount.all);
+ else
+ atapi_input_bytes (drive,pc->current_position,bcount.all); /* Read the current buffer */
+ }
+ pc->actually_transferred+=bcount.all; /* Update the current position */
+ pc->current_position+=bcount.all;
+#if IDETAPE_DEBUG_LOG
+ if (tape->debug_level >= 2)
+ printk(KERN_INFO "ide-tape: [cmd %x] transferred %d bytes on that interrupt\n", pc->c[0], bcount.all);
+#endif
+ ide_set_handler (drive,&idetape_pc_intr,IDETAPE_WAIT_CMD,NULL); /* And set the interrupt handler again */
+ return ide_started;
+}
+
+/*
+ * Packet Command Interface
+ *
+ * The current Packet Command is available in tape->pc, and will not
+ * change until we finish handling it. Each packet command is associated
+ * with a callback function that will be called when the command is
+ * finished.
+ *
+ * The handling will be done in three stages:
+ *
+ * 1. idetape_issue_packet_command will send the packet command to the
+ * drive, and will set the interrupt handler to idetape_pc_intr.
+ *
+ * 2. On each interrupt, idetape_pc_intr will be called. This step
+ * will be repeated until the device signals us that no more
+ * interrupts will be issued.
+ *
+ * 3. ATAPI Tape media access commands have immediate status with a
+ * delayed process. In case of a successful initiation of a
+ * media access packet command, the DSC bit will be set when the
+ * actual execution of the command is finished.
+ * Since the tape drive will not issue an interrupt, we have to
+ * poll for this event. In this case, we define the request as
+ * "low priority request" by setting rq_status to
+ * IDETAPE_RQ_POSTPONED, set a timer to poll for DSC and exit
+ * the driver.
+ *
+ * ide.c will then give higher priority to requests which
+ * originate from the other device, until will change rq_status
+ * to RQ_ACTIVE.
+ *
+ * 4. When the packet command is finished, it will be checked for errors.
+ *
+ * 5. In case an error was found, we queue a request sense packet command
+ * in front of the request queue and retry the operation up to
+ * IDETAPE_MAX_PC_RETRIES times.
+ *
+ * 6. In case no error was found, or we decided to give up and not
+ * to retry again, the callback function will be called and then
+ * we will handle the next request.
+ *
+ */
+static ide_startstop_t idetape_transfer_pc(ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ idetape_pc_t *pc = tape->pc;
+ idetape_ireason_reg_t ireason;
+ int retries = 100;
+ ide_startstop_t startstop;
+
+ if (ide_wait_stat (&startstop,drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) {
+ printk (KERN_ERR "ide-tape: Strange, packet command initiated yet DRQ isn't asserted\n");
+ return startstop;
+ }
+ ireason.all=IN_BYTE (IDE_IREASON_REG);
+ while (retries-- && (!ireason.b.cod || ireason.b.io)) {
+ printk(KERN_ERR "ide-tape: (IO,CoD != (0,1) while issuing a packet command, retrying\n");
+ udelay(100);
+ ireason.all = IN_BYTE(IDE_IREASON_REG);
+ if (retries == 0) {
+ printk(KERN_ERR "ide-tape: (IO,CoD != (0,1) while issuing a packet command, ignoring\n");
+ ireason.b.cod = 1;
+ ireason.b.io = 0;
+ }
+ }
+ if (!ireason.b.cod || ireason.b.io) {
+ printk (KERN_ERR "ide-tape: (IO,CoD) != (0,1) while issuing a packet command\n");
+ return ide_do_reset (drive);
+ }
+ tape->cmd_start_time = jiffies;
+ ide_set_handler(drive, &idetape_pc_intr, IDETAPE_WAIT_CMD, NULL); /* Set the interrupt routine */
+ atapi_output_bytes (drive,pc->c,12); /* Send the actual packet */
+ return ide_started;
+}
+
+static ide_startstop_t idetape_issue_packet_command (ide_drive_t *drive, idetape_pc_t *pc)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ idetape_bcount_reg_t bcount;
+ int dma_ok=0;
+
+#if IDETAPE_DEBUG_BUGS
+ if (tape->pc->c[0] == IDETAPE_REQUEST_SENSE_CMD && pc->c[0] == IDETAPE_REQUEST_SENSE_CMD) {
+ printk (KERN_ERR "ide-tape: possible ide-tape.c bug - Two request sense in serial were issued\n");
+ }
+#endif /* IDETAPE_DEBUG_BUGS */
+
+ if (tape->failed_pc == NULL && pc->c[0] != IDETAPE_REQUEST_SENSE_CMD)
+ tape->failed_pc=pc;
+ tape->pc=pc; /* Set the current packet command */
+
+ if (pc->retries > IDETAPE_MAX_PC_RETRIES || test_bit (PC_ABORT, &pc->flags)) {
+ /*
+ * We will "abort" retrying a packet command in case
+ * a legitimate error code was received (crossing a
+ * filemark, or DMA error in the end of media, for
+ * example).
+ */
+ if (!test_bit (PC_ABORT, &pc->flags)) {
+ if (!(pc->c[0] == 0 && tape->sense_key == 2 && tape->asc == 4 && (tape->ascq == 1 || tape->ascq == 8))) {
+ printk (KERN_ERR "ide-tape: %s: I/O error, pc = %2x, key = %2x, asc = %2x, ascq = %2x\n",
+ tape->name, pc->c[0], tape->sense_key, tape->asc, tape->ascq);
+ if (tape->onstream && pc->c[0] == 8 && tape->sense_key == 3 && tape->asc == 0x11) /* AJN-1: 11 should be 0x11 */
+ printk(KERN_ERR "ide-tape: %s: enabling read error recovery\n", tape->name);
+ }
+ pc->error = IDETAPE_ERROR_GENERAL; /* Giving up */
+ }
+ tape->failed_pc=NULL;
+ return pc->callback(drive);
+ }
+#if IDETAPE_DEBUG_LOG
+ if (tape->debug_level >= 2)
+ printk (KERN_INFO "ide-tape: Retry number - %d\n",pc->retries);
+#endif /* IDETAPE_DEBUG_LOG */
+
+ pc->retries++;
+ pc->actually_transferred=0; /* We haven't transferred any data yet */
+ pc->current_position=pc->buffer;
+ bcount.all=pc->request_transfer; /* Request to transfer the entire buffer at once */
+
+#ifdef CONFIG_BLK_DEV_IDEDMA
+ if (test_and_clear_bit (PC_DMA_ERROR, &pc->flags)) {
+ printk (KERN_WARNING "ide-tape: DMA disabled, reverting to PIO\n");
+ (void) HWIF(drive)->dmaproc(ide_dma_off, drive);
+ }
+ if (test_bit (PC_DMA_RECOMMENDED, &pc->flags) && drive->using_dma)
+ dma_ok=!HWIF(drive)->dmaproc(test_bit (PC_WRITING, &pc->flags) ? ide_dma_write : ide_dma_read, drive);
+#endif /* CONFIG_BLK_DEV_IDEDMA */
+
+ if (IDE_CONTROL_REG)
+ OUT_BYTE (drive->ctl,IDE_CONTROL_REG);
+ OUT_BYTE (dma_ok ? 1:0,IDE_FEATURE_REG); /* Use PIO/DMA */
+ OUT_BYTE (bcount.b.high,IDE_BCOUNTH_REG);
+ OUT_BYTE (bcount.b.low,IDE_BCOUNTL_REG);
+ OUT_BYTE (drive->select.all,IDE_SELECT_REG);
+#ifdef CONFIG_BLK_DEV_IDEDMA
+ if (dma_ok) { /* Begin DMA, if necessary */
+ set_bit (PC_DMA_IN_PROGRESS, &pc->flags);
+ (void) (HWIF(drive)->dmaproc(ide_dma_begin, drive));
+ }
+#endif /* CONFIG_BLK_DEV_IDEDMA */
+ if (test_bit(IDETAPE_DRQ_INTERRUPT, &tape->flags)) {
+ ide_set_handler(drive, &idetape_transfer_pc, IDETAPE_WAIT_CMD, NULL);
+ OUT_BYTE(WIN_PACKETCMD, IDE_COMMAND_REG);
+ return ide_started;
+ } else {
+ OUT_BYTE(WIN_PACKETCMD, IDE_COMMAND_REG);
+ return idetape_transfer_pc(drive);
+ }
+}
+
+/*
+ * General packet command callback function.
+ */
+static ide_startstop_t idetape_pc_callback (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+
+#if IDETAPE_DEBUG_LOG
+ if (tape->debug_level >= 4)
+ printk (KERN_INFO "ide-tape: Reached idetape_pc_callback\n");
+#endif /* IDETAPE_DEBUG_LOG */
+
+ idetape_end_request (tape->pc->error ? 0:1, HWGROUP(drive));
+ return ide_stopped;
+}
+
+/*
+ * A mode sense command is used to "sense" tape parameters.
+ */
+static void idetape_create_mode_sense_cmd (idetape_pc_t *pc, byte page_code)
+{
+ idetape_init_pc (pc);
+ pc->c[0] = IDETAPE_MODE_SENSE_CMD;
+ pc->c[1] = 8; /* DBD = 1 - Don't return block descriptors for now */
+ pc->c[2] = page_code;
+ pc->c[3] = 255; /* Don't limit the returned information */
+ pc->c[4] = 255; /* (We will just discard data in that case) */
+ if (page_code == IDETAPE_CAPABILITIES_PAGE)
+ pc->request_transfer = 24;
+ else
+ pc->request_transfer = 50;
+ pc->callback = &idetape_pc_callback;
+}
+
+static ide_startstop_t idetape_onstream_buffer_fill_callback (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+
+ tape->max_frames = tape->pc->buffer[4 + 2];
+ tape->cur_frames = tape->pc->buffer[4 + 3];
+ if (tape->chrdev_direction == idetape_direction_write)
+ tape->tape_head = tape->buffer_head - tape->cur_frames;
+ else
+ tape->tape_head = tape->buffer_head + tape->cur_frames;
+ if (tape->tape_head != tape->last_tape_head) {
+ tape->last_tape_head = tape->tape_head;
+ tape->tape_still_time_begin = jiffies;
+ if (tape->tape_still_time > 200)
+ tape->measure_insert_time = 1;
+ }
+ tape->tape_still_time = (jiffies - tape->tape_still_time_begin) * 1000 / HZ;
+#if USE_IOTRACE
+ IO_trace(IO_IDETAPE_FIFO, tape->pipeline_head, tape->buffer_head, tape->tape_head, tape->minor);
+#endif
+#if IDETAPE_DEBUG_LOG
+ if (tape->debug_level >= 1)
+ printk(KERN_INFO "ide-tape: buffer fill callback, %d/%d\n", tape->cur_frames, tape->max_frames);
+#endif
+ idetape_end_request (tape->pc->error ? 0:1, HWGROUP(drive));
+ return ide_stopped;
+}
+
+static void idetape_queue_onstream_buffer_fill (ide_drive_t *drive)
+{
+ idetape_pc_t *pc;
+ struct request *rq;
+
+ pc = idetape_next_pc_storage (drive);
+ rq = idetape_next_rq_storage (drive);
+ idetape_create_mode_sense_cmd (pc, 0x33);
+ pc->callback = idetape_onstream_buffer_fill_callback;
+ idetape_queue_pc_head (drive, pc, rq);
+}
+
+static void calculate_speeds(ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ int full = 125, empty = 75;
+
+ if (jiffies > tape->controlled_pipeline_head_time + 120 * HZ) {
+ tape->controlled_previous_pipeline_head = tape->controlled_last_pipeline_head;
+ tape->controlled_previous_head_time = tape->controlled_pipeline_head_time;
+ tape->controlled_last_pipeline_head = tape->pipeline_head;
+ tape->controlled_pipeline_head_time = jiffies;
+ }
+ if (jiffies > tape->controlled_pipeline_head_time + 60 * HZ)
+ tape->controlled_pipeline_head_speed = (tape->pipeline_head - tape->controlled_last_pipeline_head) * 32 * HZ / (jiffies - tape->controlled_pipeline_head_time);
+ else if (jiffies > tape->controlled_previous_head_time)
+ tape->controlled_pipeline_head_speed = (tape->pipeline_head - tape->controlled_previous_pipeline_head) * 32 * HZ / (jiffies - tape->controlled_previous_head_time);
+
+ if (tape->nr_pending_stages < tape->max_stages /*- 1 */) { /* -1 for read mode error recovery */
+ if (jiffies > tape->uncontrolled_previous_head_time + 10 * HZ) {
+ tape->uncontrolled_pipeline_head_time = jiffies;
+ tape->uncontrolled_pipeline_head_speed = (tape->pipeline_head - tape->uncontrolled_previous_pipeline_head) * 32 * HZ / (jiffies - tape->uncontrolled_previous_head_time);
+ }
+ } else {
+ tape->uncontrolled_previous_head_time = jiffies;
+ tape->uncontrolled_previous_pipeline_head = tape->pipeline_head;
+ if (jiffies > tape->uncontrolled_pipeline_head_time + 30 * HZ) {
+ tape->uncontrolled_pipeline_head_time = jiffies;
+ }
+ }
+ tape->pipeline_head_speed = IDE_MAX(tape->uncontrolled_pipeline_head_speed, tape->controlled_pipeline_head_speed);
+ if (tape->speed_control == 0) {
+ tape->max_insert_speed = 5000;
+ } else if (tape->speed_control == 1) {
+ if (tape->nr_pending_stages >= tape->max_stages / 2)
+ tape->max_insert_speed = tape->pipeline_head_speed +
+ (1100 - tape->pipeline_head_speed) * 2 * (tape->nr_pending_stages - tape->max_stages / 2) / tape->max_stages;
+ else
+ tape->max_insert_speed = 500 +
+ (tape->pipeline_head_speed - 500) * 2 * tape->nr_pending_stages / tape->max_stages;
+ if (tape->nr_pending_stages >= tape->max_stages * 99 / 100)
+ tape->max_insert_speed = 5000;
+ } else if (tape->speed_control == 2) {
+ tape->max_insert_speed = tape->pipeline_head_speed * empty / 100 +
+ (tape->pipeline_head_speed * full / 100 - tape->pipeline_head_speed * empty / 100) * tape->nr_pending_stages / tape->max_stages;
+ } else
+ tape->max_insert_speed = tape->speed_control;
+ tape->max_insert_speed = IDE_MAX(tape->max_insert_speed, 500);
+}
+
+static ide_startstop_t idetape_media_access_finished (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ idetape_pc_t *pc = tape->pc;
+ idetape_status_reg_t status;
+
+ if (tape->onstream)
+ printk(KERN_INFO "ide-tape: bug: onstream, media_access_finished\n");
+ status.all = GET_STAT();
+ if (status.b.dsc) {
+ if (status.b.check) { /* Error detected */
+ printk (KERN_ERR "ide-tape: %s: I/O error, ",tape->name);
+ return idetape_retry_pc (drive); /* Retry operation */
+ }
+ pc->error = 0;
+ if (tape->failed_pc == pc)
+ tape->failed_pc = NULL;
+ } else {
+ pc->error = IDETAPE_ERROR_GENERAL;
+ tape->failed_pc = NULL;
+ }
+ return pc->callback (drive);
+}
+
+static ide_startstop_t idetape_rw_callback (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ struct request *rq = HWGROUP(drive)->rq;
+ int blocks = tape->pc->actually_transferred / tape->tape_block_size;
+
+ tape->avg_size += blocks * tape->tape_block_size;
+ tape->insert_size += blocks * tape->tape_block_size;
+ if (tape->insert_size > 1024 * 1024)
+ tape->measure_insert_time = 1;
+ if (tape->measure_insert_time) {
+ tape->measure_insert_time = 0;
+ tape->insert_time = jiffies;
+ tape->insert_size = 0;
+ }
+ if (jiffies > tape->insert_time)
+ tape->insert_speed = tape->insert_size / 1024 * HZ / (jiffies - tape->insert_time);
+ if (jiffies - tape->avg_time >= HZ) {
+ tape->avg_speed = tape->avg_size * HZ / (jiffies - tape->avg_time) / 1024;
+ tape->avg_size = 0;
+ tape->avg_time = jiffies;
+ }
+
+#if IDETAPE_DEBUG_LOG
+ if (tape->debug_level >= 4)
+ printk (KERN_INFO "ide-tape: Reached idetape_rw_callback\n");
+#endif /* IDETAPE_DEBUG_LOG */
+
+ tape->first_frame_position += blocks;
+ rq->current_nr_sectors -= blocks;
+
+ if (!tape->pc->error)
+ idetape_end_request (1, HWGROUP (drive));
+ else
+ idetape_end_request (tape->pc->error, HWGROUP (drive));
+ return ide_stopped;
+}
+
+static void idetape_create_read_cmd (idetape_tape_t *tape, idetape_pc_t *pc, unsigned int length, struct buffer_head *bh)
+{
+ struct buffer_head *p = bh;
+ idetape_init_pc (pc);
+ pc->c[0] = IDETAPE_READ_CMD;
+ put_unaligned (htonl (length), (unsigned int *) &pc->c[1]);
+ pc->c[1] = 1;
+ pc->callback = &idetape_rw_callback;
+ pc->bh = bh;
+ atomic_set(&bh->b_count, 0);
+ pc->buffer = NULL;
+ if (tape->onstream) {
+ while (p) {
+ atomic_set(&p->b_count, 0);
+ p = p->b_reqnext;
+ }
+ }
+ if (!tape->onstream) {
+ pc->request_transfer = pc->buffer_size = length * tape->tape_block_size;
+ if (pc->request_transfer == tape->stage_size)
+ set_bit (PC_DMA_RECOMMENDED, &pc->flags);
+ } else {
+ if (length) {
+ pc->request_transfer = pc->buffer_size = 32768 + 512;
+ set_bit (PC_DMA_RECOMMENDED, &pc->flags);
+ } else
+ pc->request_transfer = 0;
+ }
+}
+
+static void idetape_create_read_buffer_cmd(idetape_tape_t *tape, idetape_pc_t *pc, unsigned int length, struct buffer_head *bh)
+{
+ int size = 32768;
+
+ struct buffer_head *p = bh;
+ idetape_init_pc (pc);
+ pc->c[0] = IDETAPE_READ_BUFFER_CMD;
+ pc->c[1] = IDETAPE_RETRIEVE_FAULTY_BLOCK;
+ pc->c[7] = size >> 8;
+ pc->c[8] = size & 0xff;
+ pc->callback = &idetape_pc_callback;
+ pc->bh = bh;
+ atomic_set(&bh->b_count, 0);
+ pc->buffer = NULL;
+ while (p) {
+ atomic_set(&p->b_count, 0);
+ p = p->b_reqnext;
+ }
+ pc->request_transfer = pc->buffer_size = size;
+}
+
+static void idetape_create_write_cmd (idetape_tape_t *tape, idetape_pc_t *pc, unsigned int length, struct buffer_head *bh)
+{
+ struct buffer_head *p = bh;
+ idetape_init_pc (pc);
+ pc->c[0] = IDETAPE_WRITE_CMD;
+ put_unaligned (htonl (length), (unsigned int *) &pc->c[1]);
+ pc->c[1] = 1;
+ pc->callback = &idetape_rw_callback;
+ set_bit (PC_WRITING, &pc->flags);
+ if (tape->onstream) {
+ while (p) {
+ atomic_set(&p->b_count, p->b_size);
+ p = p->b_reqnext;
+ }
+ }
+ pc->bh = bh;
+ pc->b_data = bh->b_data;
+ pc->b_count = atomic_read(&bh->b_count);
+ pc->buffer = NULL;
+ if (!tape->onstream) {
+ pc->request_transfer = pc->buffer_size = length * tape->tape_block_size;
+ if (pc->request_transfer == tape->stage_size)
+ set_bit (PC_DMA_RECOMMENDED, &pc->flags);
+ } else {
+ if (length) {
+ pc->request_transfer = pc->buffer_size = 32768 + 512;
+ set_bit (PC_DMA_RECOMMENDED, &pc->flags);
+ } else
+ pc->request_transfer = 0;
+ }
+}
+
+/*
+ * idetape_do_request is our request handling function.
+ */
+static ide_startstop_t idetape_do_request (ide_drive_t *drive, struct request *rq, unsigned long block)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ idetape_pc_t *pc;
+ struct request *postponed_rq = tape->postponed_rq;
+ idetape_status_reg_t status;
+
+#if IDETAPE_DEBUG_LOG
+ if (tape->debug_level >= 5)
+ printk (KERN_INFO "ide-tape: rq_status: %d, rq_dev: %u, cmd: %d, errors: %d\n",rq->rq_status,(unsigned int) rq->rq_dev,rq->cmd,rq->errors);
+ if (tape->debug_level >= 2)
+ printk (KERN_INFO "ide-tape: sector: %ld, nr_sectors: %ld, current_nr_sectors: %ld\n",rq->sector,rq->nr_sectors,rq->current_nr_sectors);
+#endif /* IDETAPE_DEBUG_LOG */
+
+ if (!IDETAPE_RQ_CMD (rq->cmd)) {
+ /*
+ * We do not support buffer cache originated requests.
+ */
+ printk (KERN_NOTICE "ide-tape: %s: Unsupported command in request queue (%d)\n", drive->name, rq->cmd);
+ ide_end_request (0,HWGROUP (drive)); /* Let the common code handle it */
+ return ide_stopped;
+ }
+
+ /*
+ * Retry a failed packet command
+ */
+ if (tape->failed_pc != NULL && tape->pc->c[0] == IDETAPE_REQUEST_SENSE_CMD) {
+ return idetape_issue_packet_command (drive, tape->failed_pc);
+ }
+#if IDETAPE_DEBUG_BUGS
+ if (postponed_rq != NULL)
+ if (rq != postponed_rq) {
+ printk (KERN_ERR "ide-tape: ide-tape.c bug - Two DSC requests were queued\n");
+ idetape_end_request (0,HWGROUP (drive));
+ return ide_stopped;
+ }
+#endif /* IDETAPE_DEBUG_BUGS */
+
+ tape->postponed_rq = NULL;
+
+ /*
+ * If the tape is still busy, postpone our request and service
+ * the other device meanwhile.
+ */
+ status.all = GET_STAT();
+
+ /*
+ * The OnStream tape drive doesn't support DSC. Assume
+ * that DSC is always set.
+ */
+ if (tape->onstream)
+ status.b.dsc = 1;
+ if (!drive->dsc_overlap && rq->cmd != IDETAPE_PC_RQ2)
+ set_bit (IDETAPE_IGNORE_DSC, &tape->flags);
+
+ /*
+ * For the OnStream tape, check the current status of the tape
+ * internal buffer using data gathered from the buffer fill
+ * mode page, and postpone our request, effectively "disconnecting"
+ * from the IDE bus, in case the buffer is full (writing) or
+ * empty (reading), and there is a danger that our request will
+ * hold the IDE bus during actual media access.
+ */
+ if (tape->tape_still_time > 100 && tape->tape_still_time < 200)
+ tape->measure_insert_time = 1;
+ if (tape->req_buffer_fill && (rq->cmd == IDETAPE_WRITE_RQ || rq->cmd == IDETAPE_READ_RQ)) {
+ tape->req_buffer_fill = 0;
+ tape->writes_since_buffer_fill = 0;
+ tape->reads_since_buffer_fill = 0;
+ tape->last_buffer_fill = jiffies;
+ idetape_queue_onstream_buffer_fill(drive);
+ if (jiffies > tape->insert_time)
+ tape->insert_speed = tape->insert_size / 1024 * HZ / (jiffies - tape->insert_time);
+ return ide_stopped;
+ }
+ if (jiffies > tape->insert_time)
+ tape->insert_speed = tape->insert_size / 1024 * HZ / (jiffies - tape->insert_time);
+ calculate_speeds(drive);
+ if (tape->onstream && tape->max_frames &&
+ ((rq->cmd == IDETAPE_WRITE_RQ && (tape->cur_frames == tape->max_frames || (tape->speed_control && tape->cur_frames > 5 && (tape->insert_speed > tape->max_insert_speed || (0 /* tape->cur_frames > 30 && tape->tape_still_time > 200 */))))) ||
+ (rq->cmd == IDETAPE_READ_RQ && (tape->cur_frames == 0 || (tape->speed_control && (tape->cur_frames < tape->max_frames - 5) && tape->insert_speed > tape->max_insert_speed)) && rq->nr_sectors))) {
+#if IDETAPE_DEBUG_LOG
+ if (tape->debug_level >= 4)
+ printk(KERN_INFO "ide-tape: postponing request, cmd %d, cur %d, max %d\n",
+ rq->cmd, tape->cur_frames, tape->max_frames);
+#endif
+ if (tape->postpone_cnt++ < 500) {
+ status.b.dsc = 0;
+ tape->req_buffer_fill = 1;
+ }
+#if ONSTREAM_DEBUG
+ else if (tape->debug_level >= 4)
+ printk(KERN_INFO "ide-tape: %s: postpone_cnt %d\n", tape->name, tape->postpone_cnt);
+#endif
+ }
+ if (!test_and_clear_bit (IDETAPE_IGNORE_DSC, &tape->flags) && !status.b.dsc) {
+ if (postponed_rq == NULL) {
+ tape->dsc_polling_start = jiffies;
+ tape->dsc_polling_frequency = tape->best_dsc_rw_frequency;
+ tape->dsc_timeout = jiffies + IDETAPE_DSC_RW_TIMEOUT;
+ } else if ((signed long) (jiffies - tape->dsc_timeout) > 0) {
+ printk (KERN_ERR "ide-tape: %s: DSC timeout\n", tape->name);
+ if (rq->cmd == IDETAPE_PC_RQ2) {
+ idetape_media_access_finished (drive);
+ return ide_stopped;
+ } else {
+ return ide_do_reset (drive);
+ }
+ } else if (jiffies - tape->dsc_polling_start > IDETAPE_DSC_MA_THRESHOLD)
+ tape->dsc_polling_frequency = IDETAPE_DSC_MA_SLOW;
+ idetape_postpone_request (drive);
+ return ide_stopped;
+ }
+ switch (rq->cmd) {
+ case IDETAPE_READ_RQ:
+ tape->buffer_head++;
+#if USE_IOTRACE
+ IO_trace(IO_IDETAPE_FIFO, tape->pipeline_head, tape->buffer_head, tape->tape_head, tape->minor);
+#endif
+ tape->postpone_cnt = 0;
+ tape->reads_since_buffer_fill++;
+ if (tape->onstream) {
+ if (tape->cur_frames - tape->reads_since_buffer_fill <= 0)
+ tape->req_buffer_fill = 1;
+ if (jiffies > tape->last_buffer_fill + 5 * HZ / 100)
+ tape->req_buffer_fill = 1;
+ }
+ pc=idetape_next_pc_storage (drive);
+ idetape_create_read_cmd (tape, pc, rq->current_nr_sectors, rq->bh);
+ break;
+ case IDETAPE_WRITE_RQ:
+ tape->buffer_head++;
+#if USE_IOTRACE
+ IO_trace(IO_IDETAPE_FIFO, tape->pipeline_head, tape->buffer_head, tape->tape_head, tape->minor);
+#endif
+ tape->postpone_cnt = 0;
+ tape->writes_since_buffer_fill++;
+ if (tape->onstream) {
+ if (tape->cur_frames + tape->writes_since_buffer_fill >= tape->max_frames)
+ tape->req_buffer_fill = 1;
+ if (jiffies > tape->last_buffer_fill + 5 * HZ / 100)
+ tape->req_buffer_fill = 1;
+ calculate_speeds(drive);
+ }
+ pc=idetape_next_pc_storage (drive);
+ idetape_create_write_cmd (tape, pc, rq->current_nr_sectors, rq->bh);
+ break;
+ case IDETAPE_READ_BUFFER_RQ:
+ tape->postpone_cnt = 0;
+ pc=idetape_next_pc_storage (drive);
+ idetape_create_read_buffer_cmd (tape, pc, rq->current_nr_sectors, rq->bh);
+ break;
+ case IDETAPE_ABORTED_WRITE_RQ:
+ rq->cmd = IDETAPE_WRITE_RQ;
+ idetape_end_request (IDETAPE_ERROR_EOD, HWGROUP(drive));
+ return ide_stopped;
+ case IDETAPE_ABORTED_READ_RQ:
+#if IDETAPE_DEBUG_LOG
+ if (tape->debug_level >= 2)
+ printk(KERN_INFO "ide-tape: %s: detected aborted read rq\n", tape->name);
+#endif
+ rq->cmd = IDETAPE_READ_RQ;
+ idetape_end_request (IDETAPE_ERROR_EOD, HWGROUP(drive));
+ return ide_stopped;
+ case IDETAPE_PC_RQ1:
+ pc=(idetape_pc_t *) rq->buffer;
+ rq->cmd = IDETAPE_PC_RQ2;
+ break;
+ case IDETAPE_PC_RQ2:
+ idetape_media_access_finished (drive);
+ return ide_stopped;
+ default:
+ printk (KERN_ERR "ide-tape: bug in IDETAPE_RQ_CMD macro\n");
+ idetape_end_request (0,HWGROUP (drive));
+ return ide_stopped;
+ }
+ return idetape_issue_packet_command (drive, pc);
+}
+
+/*
+ * Pipeline related functions
+ */
+static inline int idetape_pipeline_active (idetape_tape_t *tape)
+{
+ int rc1, rc2;
+
+ rc1 = test_bit(IDETAPE_PIPELINE_ACTIVE, &tape->flags);
+ rc2 = (tape->active_data_request != NULL);
+ return rc1;
+}
+
+/*
+ * idetape_kmalloc_stage uses __get_free_page to allocate a pipeline
+ * stage, along with all the necessary small buffers which together make
+ * a buffer of size tape->stage_size (or a bit more). We attempt to
+ * combine sequential pages as much as possible.
+ *
+ * Returns a pointer to the new allocated stage, or NULL if we
+ * can't (or don't want to) allocate a stage.
+ *
+ * Pipeline stages are optional and are used to increase performance.
+ * If we can't allocate them, we'll manage without them.
+ */
+static idetape_stage_t *__idetape_kmalloc_stage (idetape_tape_t *tape, int full, int clear)
+{
+ idetape_stage_t *stage;
+ struct buffer_head *prev_bh, *bh;
+ int pages = tape->pages_per_stage;
+ char *b_data;
+
+ if ((stage = (idetape_stage_t *) kmalloc (sizeof (idetape_stage_t),GFP_KERNEL)) == NULL)
+ return NULL;
+ stage->next = NULL;
+
+ bh = stage->bh = (struct buffer_head *) kmalloc (sizeof (struct buffer_head), GFP_KERNEL);
+ if (bh == NULL)
+ goto abort;
+ bh->b_reqnext = NULL;
+ if ((bh->b_data = (char *) __get_free_page (GFP_KERNEL)) == NULL)
+ goto abort;
+ if (clear)
+ memset(bh->b_data, 0, PAGE_SIZE);
+ bh->b_size = PAGE_SIZE;
+ atomic_set(&bh->b_count, full ? bh->b_size : 0);
+ set_bit (BH_Lock, &bh->b_state);
+
+ while (--pages) {
+ if ((b_data = (char *) __get_free_page (GFP_KERNEL)) == NULL)
+ goto abort;
+ if (clear)
+ memset(b_data, 0, PAGE_SIZE);
+ if (bh->b_data == b_data + PAGE_SIZE) {
+ bh->b_size += PAGE_SIZE;
+ bh->b_data -= PAGE_SIZE;
+ if (full)
+ atomic_add(PAGE_SIZE, &bh->b_count);
+ continue;
+ }
+ if (b_data == bh->b_data + bh->b_size) {
+ bh->b_size += PAGE_SIZE;
+ if (full)
+ atomic_add(PAGE_SIZE, &bh->b_count);
+ continue;
+ }
+ prev_bh = bh;
+ if ((bh = (struct buffer_head *) kmalloc (sizeof (struct buffer_head), GFP_KERNEL)) == NULL) {
+ free_page ((unsigned long) b_data);
+ goto abort;
+ }
+ bh->b_reqnext = NULL;
+ bh->b_data = b_data;
+ bh->b_size = PAGE_SIZE;
+ atomic_set(&bh->b_count, full ? bh->b_size : 0);
+ set_bit (BH_Lock, &bh->b_state);
+ prev_bh->b_reqnext = bh;
+ }
+ bh->b_size -= tape->excess_bh_size;
+ if (full)
+ atomic_sub(tape->excess_bh_size, &bh->b_count);
+ if (tape->onstream)
+ stage->aux = (os_aux_t *) (bh->b_data + bh->b_size - OS_AUX_SIZE);
+ return stage;
+abort:
+ __idetape_kfree_stage (stage);
+ return NULL;
+}
+
+static idetape_stage_t *idetape_kmalloc_stage (idetape_tape_t *tape)
+{
+ idetape_stage_t *cache_stage = tape->cache_stage;
+
+#if IDETAPE_DEBUG_LOG
+ if (tape->debug_level >= 4)
+ printk (KERN_INFO "ide-tape: Reached idetape_kmalloc_stage\n");
+#endif /* IDETAPE_DEBUG_LOG */
+
+ if (tape->nr_stages >= tape->max_stages)
+ return NULL;
+ if (cache_stage != NULL) {
+ tape->cache_stage = NULL;
+ return cache_stage;
+ }
+ return __idetape_kmalloc_stage (tape, 0, 0);
+}
+
+static void idetape_copy_stage_from_user (idetape_tape_t *tape, idetape_stage_t *stage, const char *buf, int n)
+{
+ struct buffer_head *bh = tape->bh;
+ int count;
+
+ while (n) {
+#if IDETAPE_DEBUG_BUGS
+ if (bh == NULL) {
+ printk (KERN_ERR "ide-tape: bh == NULL in idetape_copy_stage_from_user\n");
+ return;
+ }
+#endif /* IDETAPE_DEBUG_BUGS */
+ count = IDE_MIN (bh->b_size - atomic_read(&bh->b_count), n);
+ copy_from_user (bh->b_data + atomic_read(&bh->b_count), buf, count);
+ n -= count; atomic_add(count, &bh->b_count); buf += count;
+ if (atomic_read(&bh->b_count) == bh->b_size) {
+ bh = bh->b_reqnext;
+ if (bh)
+ atomic_set(&bh->b_count, 0);
+ }
+ }
+ tape->bh = bh;
+}
+
+static void idetape_copy_stage_to_user (idetape_tape_t *tape, char *buf, idetape_stage_t *stage, int n)
+{
+ struct buffer_head *bh = tape->bh;
+ int count;
+
+ while (n) {
+#if IDETAPE_DEBUG_BUGS
+ if (bh == NULL) {
+ printk (KERN_ERR "ide-tape: bh == NULL in idetape_copy_stage_to_user\n");
+ return;
+ }
+#endif /* IDETAPE_DEBUG_BUGS */
+ count = IDE_MIN (tape->b_count, n);
+ copy_to_user (buf, tape->b_data, count);
+ n -= count; tape->b_data += count; tape->b_count -= count; buf += count;
+ if (!tape->b_count) {
+ tape->bh = bh = bh->b_reqnext;
+ if (bh) {
+ tape->b_data = bh->b_data;
+ tape->b_count = atomic_read(&bh->b_count);
+ }
+ }
+ }
+}
+
+static void idetape_init_merge_stage (idetape_tape_t *tape)
+{
+ struct buffer_head *bh = tape->merge_stage->bh;
+
+ tape->bh = bh;
+ if (tape->chrdev_direction == idetape_direction_write)
+ atomic_set(&bh->b_count, 0);
+ else {
+ tape->b_data = bh->b_data;
+ tape->b_count = atomic_read(&bh->b_count);
+ }
+}
+
+static void idetape_switch_buffers (idetape_tape_t *tape, idetape_stage_t *stage)
+{
+ struct buffer_head *tmp;
+ os_aux_t *tmp_aux;
+
+ tmp = stage->bh; tmp_aux = stage->aux;
+ stage->bh = tape->merge_stage->bh; stage->aux = tape->merge_stage->aux;
+ tape->merge_stage->bh = tmp; tape->merge_stage->aux = tmp_aux;
+ idetape_init_merge_stage (tape);
+}
+
+/*
+ * idetape_add_stage_tail adds a new stage at the end of the pipeline.
+ */
+static void idetape_add_stage_tail (ide_drive_t *drive,idetape_stage_t *stage)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ unsigned long flags;
+
+#if IDETAPE_DEBUG_LOG
+ if (tape->debug_level >= 4)
+ printk (KERN_INFO "ide-tape: Reached idetape_add_stage_tail\n");
+#endif /* IDETAPE_DEBUG_LOG */
+ spin_lock_irqsave(&tape->spinlock, flags);
+ stage->next=NULL;
+ if (tape->last_stage != NULL)
+ tape->last_stage->next=stage;
+ else
+ tape->first_stage=tape->next_stage=stage;
+ tape->last_stage=stage;
+ if (tape->next_stage == NULL)
+ tape->next_stage=tape->last_stage;
+ tape->nr_stages++;
+ tape->nr_pending_stages++;
+ spin_unlock_irqrestore(&tape->spinlock, flags);
+}
+
+/*
+ * Initialize the OnStream AUX
+ */
+static void idetape_init_stage (ide_drive_t *drive, idetape_stage_t *stage, int frame_type, int logical_blk_num)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ os_aux_t *aux = stage->aux;
+ os_partition_t *par = &aux->partition;
+ os_dat_t *dat = &aux->dat;
+
+ if (!tape->onstream || tape->raw)
+ return;
+ memset(aux, 0, sizeof(*aux));
+ aux->format_id = htonl(0);
+ memcpy(aux->application_sig, "LIN3", 4);
+ aux->hdwr = htonl(0);
+ aux->frame_type = frame_type;
+
+ if (frame_type == OS_FRAME_TYPE_HEADER) {
+ aux->update_frame_cntr = htonl(tape->update_frame_cntr);
+ par->partition_num = OS_CONFIG_PARTITION;
+ par->par_desc_ver = OS_PARTITION_VERSION;
+ par->wrt_pass_cntr = htons(0xffff);
+ par->first_frame_addr = htonl(0);
+ par->last_frame_addr = htonl(0xbb7);
+ } else {
+ aux->update_frame_cntr = htonl(0);
+ par->partition_num = OS_DATA_PARTITION;
+ par->par_desc_ver = OS_PARTITION_VERSION;
+ par->wrt_pass_cntr = htons(tape->wrt_pass_cntr);
+ par->first_frame_addr = htonl(0x14);
+ par->last_frame_addr = htonl(19239 * 24);
+ }
+ if (frame_type != OS_FRAME_TYPE_HEADER) {
+ aux->frame_seq_num = htonl(logical_blk_num);
+ aux->logical_blk_num_high = htonl(0);
+ aux->logical_blk_num = htonl(logical_blk_num);
+ } else {
+ aux->frame_seq_num = htonl(0);
+ aux->logical_blk_num_high = htonl(0);
+ aux->logical_blk_num = htonl(0);
+ }
+
+ if (frame_type != OS_FRAME_TYPE_HEADER) {
+ dat->dat_sz = 8;
+ dat->reserved1 = 0;
+ dat->entry_cnt = 1;
+ dat->reserved3 = 0;
+ if (frame_type == OS_FRAME_TYPE_DATA)
+ dat->dat_list[0].blk_sz = htonl(32 * 1024);
+ else
+ dat->dat_list[0].blk_sz = 0;
+ dat->dat_list[0].blk_cnt = htons(1);
+ if (frame_type == OS_FRAME_TYPE_MARKER)
+ dat->dat_list[0].flags = OS_DAT_FLAGS_MARK;
+ else
+ dat->dat_list[0].flags = OS_DAT_FLAGS_DATA;
+ dat->dat_list[0].reserved = 0;
+ } else
+ aux->next_mark_addr = htonl(tape->first_mark_addr);
+ aux->filemark_cnt = ntohl(tape->filemark_cnt);
+ aux->phys_fm = ntohl(0xffffffff);
+ aux->last_mark_addr = ntohl(tape->last_mark_addr);
+}
+
+/*
+ * idetape_wait_for_request installs a semaphore in a pending request
+ * and sleeps until it is serviced.
+ *
+ * The caller should ensure that the request will not be serviced
+ * before we install the semaphore (usually by disabling interrupts).
+ */
+static void idetape_wait_for_request (ide_drive_t *drive, struct request *rq)
+{
+ DECLARE_MUTEX_LOCKED(sem);
+ idetape_tape_t *tape = drive->driver_data;
+
+#if IDETAPE_DEBUG_BUGS
+ if (rq == NULL || !IDETAPE_RQ_CMD (rq->cmd)) {
+ printk (KERN_ERR "ide-tape: bug: Trying to sleep on non-valid request\n");
+ return;
+ }
+#endif /* IDETAPE_DEBUG_BUGS */
+ rq->sem = &sem;
+ tape->sem = &sem;
+ spin_unlock(&tape->spinlock);
+ down(&sem);
+ rq->sem = NULL;
+ tape->sem = NULL;
+ spin_lock_irq(&tape->spinlock);
+}
+
+static ide_startstop_t idetape_read_position_callback (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ idetape_read_position_result_t *result;
+
+#if IDETAPE_DEBUG_LOG
+ if (tape->debug_level >= 4)
+ printk (KERN_INFO "ide-tape: Reached idetape_read_position_callback\n");
+#endif /* IDETAPE_DEBUG_LOG */
+
+ if (!tape->pc->error) {
+ result = (idetape_read_position_result_t *) tape->pc->buffer;
+#if IDETAPE_DEBUG_LOG
+ if (tape->debug_level >= 2)
+ printk (KERN_INFO "ide-tape: BOP - %s\n",result->bop ? "Yes":"No");
+ if (tape->debug_level >= 2)
+ printk (KERN_INFO "ide-tape: EOP - %s\n",result->eop ? "Yes":"No");
+#endif /* IDETAPE_DEBUG_LOG */
+ if (result->bpu) {
+ printk (KERN_INFO "ide-tape: Block location is unknown to the tape\n");
+ clear_bit (IDETAPE_ADDRESS_VALID, &tape->flags);
+ idetape_end_request (0,HWGROUP (drive));
+ } else {
+#if IDETAPE_DEBUG_LOG
+ if (tape->debug_level >= 2)
+ printk (KERN_INFO "ide-tape: Block Location - %u\n", ntohl (result->first_block));
+#endif /* IDETAPE_DEBUG_LOG */
+ tape->partition = result->partition;
+ tape->first_frame_position = ntohl (result->first_block);
+ tape->last_frame_position = ntohl (result->last_block);
+ tape->blocks_in_buffer = result->blocks_in_buffer[2];
+ set_bit (IDETAPE_ADDRESS_VALID, &tape->flags);
+ idetape_end_request (1,HWGROUP (drive));
+ }
+ } else {
+ idetape_end_request (0,HWGROUP (drive));
+ }
+ return ide_stopped;
+}
+
+/*
+ * idetape_create_write_filemark_cmd will:
+ *
+ * 1. Write a filemark if write_filemark=1.
+ * 2. Flush the device buffers without writing a filemark
+ * if write_filemark=0.
+ *
+ */
+static void idetape_create_write_filemark_cmd (ide_drive_t *drive, idetape_pc_t *pc,int write_filemark)
+{
+ idetape_tape_t *tape = drive->driver_data;
+
+ idetape_init_pc (pc);
+ pc->c[0] = IDETAPE_WRITE_FILEMARK_CMD;
+ if (tape->onstream)
+ pc->c[1] = 1;
+ pc->c[4] = write_filemark;
+ set_bit (PC_WAIT_FOR_DSC, &pc->flags);
+ pc->callback = &idetape_pc_callback;
+}
+
+static void idetape_create_test_unit_ready_cmd(idetape_pc_t *pc)
+{
+ idetape_init_pc(pc);
+ pc->c[0] = IDETAPE_TEST_UNIT_READY_CMD;
+ pc->callback = &idetape_pc_callback;
+}
+
+/*
+ * idetape_queue_pc_tail is based on the following functions:
+ *
+ * ide_do_drive_cmd from ide.c
+ * cdrom_queue_request and cdrom_queue_packet_command from ide-cd.c
+ *
+ * We add a special packet command request to the tail of the request queue,
+ * and wait for it to be serviced.
+ *
+ * This is not to be called from within the request handling part
+ * of the driver ! We allocate here data in the stack, and it is valid
+ * until the request is finished. This is not the case for the bottom
+ * part of the driver, where we are always leaving the functions to wait
+ * for an interrupt or a timer event.
+ *
+ * From the bottom part of the driver, we should allocate safe memory
+ * using idetape_next_pc_storage and idetape_next_rq_storage, and add
+ * the request to the request list without waiting for it to be serviced !
+ * In that case, we usually use idetape_queue_pc_head.
+ */
+static int __idetape_queue_pc_tail (ide_drive_t *drive,idetape_pc_t *pc)
+{
+ struct request rq;
+
+ ide_init_drive_cmd (&rq);
+ rq.buffer = (char *) pc;
+ rq.cmd = IDETAPE_PC_RQ1;
+ return ide_do_drive_cmd (drive, &rq, ide_wait);
+}
+
+static void idetape_create_load_unload_cmd (ide_drive_t *drive, idetape_pc_t *pc,int cmd)
+{
+ idetape_tape_t *tape = drive->driver_data;
+
+ idetape_init_pc (pc);
+ pc->c[0] = IDETAPE_LOAD_UNLOAD_CMD;
+ pc->c[4] = cmd;
+ if (tape->onstream)
+ pc->c[1] = 1;
+ set_bit (PC_WAIT_FOR_DSC, &pc->flags);
+ pc->callback = &idetape_pc_callback;
+}
+
+static int idetape_wait_ready (ide_drive_t *drive, unsigned long long timeout)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ idetape_pc_t pc;
+
+ /*
+ * Wait for the tape to become ready
+ */
+ timeout += jiffies;
+ while (jiffies < timeout) {
+ idetape_create_test_unit_ready_cmd(&pc);
+ if (!__idetape_queue_pc_tail(drive, &pc))
+ return 0;
+ if (tape->sense_key == 2 && tape->asc == 4 && tape->ascq == 2) {
+ idetape_create_load_unload_cmd (drive, &pc, IDETAPE_LU_LOAD_MASK);
+ __idetape_queue_pc_tail(drive,&pc);
+ idetape_create_test_unit_ready_cmd(&pc);
+ if (!__idetape_queue_pc_tail(drive, &pc))
+ return 0;
+ }
+ if (!(tape->sense_key == 2 && tape->asc == 4 && (tape->ascq == 1 || tape->ascq == 8)))
+ break;
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(HZ / 10);
+ }
+ return -EIO;
+}
+
+static int idetape_queue_pc_tail (ide_drive_t *drive,idetape_pc_t *pc)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ int rc;
+
+ rc = __idetape_queue_pc_tail(drive, pc);
+ if (rc) return rc;
+ if (tape->onstream && test_bit(PC_WAIT_FOR_DSC, &pc->flags))
+ rc = idetape_wait_ready(drive, 60 * 10 * HZ); /* AJN-4: Changed from 5 to 10 minutes;
+ because retension takes approx. 8:20 with Onstream 30GB tape */
+ return rc;
+}
+
+static int idetape_flush_tape_buffers (ide_drive_t *drive)
+{
+ idetape_pc_t pc;
+ int rc;
+
+ idetape_create_write_filemark_cmd(drive, &pc, 0);
+ if ((rc = idetape_queue_pc_tail (drive,&pc)))
+ return rc;
+ idetape_wait_ready(drive, 60 * 5 * HZ);
+ return 0;
+}
+
+static void idetape_create_read_position_cmd (idetape_pc_t *pc)
+{
+ idetape_init_pc (pc);
+ pc->c[0] = IDETAPE_READ_POSITION_CMD;
+ pc->request_transfer = 20;
+ pc->callback = &idetape_read_position_callback;
+}
+
+static int idetape_read_position (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ idetape_pc_t pc;
+ int position;
+
+#ifdef NO_LONGER_REQUIRED
+ idetape_flush_tape_buffers(drive);
+#endif
+ idetape_create_read_position_cmd(&pc);
+ if (idetape_queue_pc_tail (drive,&pc))
+ return -1;
+ position = tape->first_frame_position;
+#ifdef NO_LONGER_REQUIRED
+ if (tape->onstream) {
+ if ((position != tape->last_frame_position - tape->blocks_in_buffer) &&
+ (position != tape->last_frame_position + tape->blocks_in_buffer)) {
+ if (tape->blocks_in_buffer == 0) {
+ printk("ide-tape: %s: correcting read position %d, %d, %d\n", tape->name, position, tape->last_frame_position, tape->blocks_in_buffer);
+ position = tape->last_frame_position;
+ tape->first_frame_position = position;
+ }
+ }
+ }
+#endif
+ return position;
+}
+
+static void idetape_create_locate_cmd (ide_drive_t *drive, idetape_pc_t *pc, unsigned int block, byte partition, int skip)
+{
+ idetape_tape_t *tape = drive->driver_data;
+
+ idetape_init_pc (pc);
+ pc->c[0] = IDETAPE_LOCATE_CMD;
+ if (tape->onstream)
+ pc->c[1] = 1;
+ else
+ pc->c[1] = 2;
+ put_unaligned (htonl (block), (unsigned int *) &pc->c[3]);
+ pc->c[8] = partition;
+ if (tape->onstream)
+ pc->c[9] = skip << 7;
+ set_bit (PC_WAIT_FOR_DSC, &pc->flags);
+ pc->callback = &idetape_pc_callback;
+}
+
+static void idetape_create_prevent_cmd (ide_drive_t *drive, idetape_pc_t *pc, int prevent)
+{
+ idetape_init_pc(pc);
+ pc->c[0] = IDETAPE_PREVENT_CMD;
+ pc->c[4] = prevent;
+ pc->callback = &idetape_pc_callback;
+}
+
+static int __idetape_discard_read_pipeline (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ unsigned long flags;
+ int cnt;
+
+ if (tape->chrdev_direction != idetape_direction_read)
+ return 0;
+ tape->merge_stage_size = 0;
+ if (tape->merge_stage != NULL) {
+ __idetape_kfree_stage (tape->merge_stage);
+ tape->merge_stage = NULL;
+ }
+ tape->chrdev_direction = idetape_direction_none;
+
+ if (tape->first_stage == NULL)
+ return 0;
+
+ spin_lock_irqsave(&tape->spinlock, flags);
+ tape->next_stage = NULL;
+ if (idetape_pipeline_active (tape))
+ idetape_wait_for_request(drive, tape->active_data_request);
+ spin_unlock_irqrestore(&tape->spinlock, flags);
+
+ cnt = tape->nr_stages - tape->nr_pending_stages;
+ while (tape->first_stage != NULL)
+ idetape_remove_stage_head (drive);
+ tape->nr_pending_stages = 0;
+ tape->max_stages = tape->min_pipeline;
+ return cnt;
+}
+
+/*
+ * idetape_position_tape positions the tape to the requested block
+ * using the LOCATE packet command. A READ POSITION command is then
+ * issued to check where we are positioned.
+ *
+ * Like all higher level operations, we queue the commands at the tail
+ * of the request queue and wait for their completion.
+ *
+ */
+static int idetape_position_tape (ide_drive_t *drive, unsigned int block, byte partition, int skip)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ int retval;
+ idetape_pc_t pc;
+
+ if (tape->chrdev_direction == idetape_direction_read)
+ __idetape_discard_read_pipeline(drive);
+ idetape_wait_ready(drive, 60 * 5 * HZ);
+ idetape_create_locate_cmd (drive, &pc, block, partition, skip);
+ retval=idetape_queue_pc_tail (drive,&pc);
+ if (retval) return (retval);
+
+ idetape_create_read_position_cmd (&pc);
+ return (idetape_queue_pc_tail (drive,&pc));
+}
+
+static void idetape_discard_read_pipeline (ide_drive_t *drive, int restore_position)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ int cnt;
+ int seek, position;
+
+ cnt = __idetape_discard_read_pipeline(drive);
+ if (restore_position) {
+ position = idetape_read_position(drive);
+#if ONSTREAM_DEBUG
+ if (tape->debug_level >= 2)
+ printk(KERN_INFO "ide-tape: address %u, nr_stages %d\n", position, cnt);
+#endif
+ seek = position > cnt ? position - cnt : 0;
+ if (idetape_position_tape(drive, seek, 0, 0)) {
+ printk(KERN_INFO "ide-tape: %s: position_tape failed in discard_pipeline()\n", tape->name);
+ return;
+ }
+ }
+}
+
+static void idetape_update_stats (ide_drive_t *drive)
+{
+ idetape_pc_t pc;
+
+ idetape_create_mode_sense_cmd (&pc, 0x33);
+ pc.callback = idetape_onstream_buffer_fill_callback;
+ (void) idetape_queue_pc_tail(drive, &pc);
+}
+
+/*
+ * idetape_queue_rw_tail generates a read/write request for the block
+ * device interface and wait for it to be serviced.
+ */
+static int idetape_queue_rw_tail (ide_drive_t *drive, int cmd, int blocks, struct buffer_head *bh)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ struct request rq;
+
+#if IDETAPE_DEBUG_LOG
+ if (tape->debug_level >= 2)
+ printk (KERN_INFO "ide-tape: idetape_queue_rw_tail: cmd=%d\n",cmd);
+#endif /* IDETAPE_DEBUG_LOG */
+#if IDETAPE_DEBUG_BUGS
+ if (idetape_pipeline_active (tape)) {
+ printk (KERN_ERR "ide-tape: bug: the pipeline is active in idetape_queue_rw_tail\n");
+ return (0);
+ }
+#endif /* IDETAPE_DEBUG_BUGS */
+
+ ide_init_drive_cmd (&rq);
+ rq.bh = bh;
+ rq.cmd = cmd;
+ rq.sector = tape->first_frame_position;
+ rq.nr_sectors = rq.current_nr_sectors = blocks;
+ if (tape->onstream)
+ tape->postpone_cnt = 600;
+ (void) ide_do_drive_cmd (drive, &rq, ide_wait);
+
+ if (cmd != IDETAPE_READ_RQ && cmd != IDETAPE_WRITE_RQ)
+ return 0;
+
+ if (tape->merge_stage)
+ idetape_init_merge_stage (tape);
+ if (rq.errors == IDETAPE_ERROR_GENERAL)
+ return -EIO;
+ return (tape->tape_block_size * (blocks-rq.current_nr_sectors));
+}
+
+/*
+ * Read back the drive's internal buffer contents, as a part
+ * of the write error recovery mechanism for old OnStream
+ * firmware revisions.
+ */
+static void idetape_onstream_read_back_buffer (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ int frames, i, logical_blk_num;
+ idetape_stage_t *stage, *first = NULL, *last = NULL;
+ os_aux_t *aux;
+ struct request *rq;
+ unsigned char *p;
+ unsigned long flags;
+
+ idetape_update_stats(drive);
+ frames = tape->cur_frames;
+ logical_blk_num = ntohl(tape->first_stage->aux->logical_blk_num) - frames;
+ printk(KERN_INFO "ide-tape: %s: reading back %d frames from the drive's internal buffer\n", tape->name, frames);
+ for (i = 0; i < frames; i++) {
+ stage = __idetape_kmalloc_stage(tape, 0, 0);
+ if (!first)
+ first = stage;
+ aux = stage->aux;
+ p = stage->bh->b_data;
+ idetape_queue_rw_tail(drive, IDETAPE_READ_BUFFER_RQ, tape->capabilities.ctl, stage->bh);
+#if ONSTREAM_DEBUG
+ if (tape->debug_level >= 2)
+ printk(KERN_INFO "ide-tape: %s: read back logical block %d, data %x %x %x %x\n", tape->name, logical_blk_num, *p++, *p++, *p++, *p++);
+#endif
+ rq = &stage->rq;
+ ide_init_drive_cmd (rq);
+ rq->cmd = IDETAPE_WRITE_RQ;
+ rq->sector = tape->first_frame_position;
+ rq->nr_sectors = rq->current_nr_sectors = tape->capabilities.ctl;
+ idetape_init_stage(drive, stage, OS_FRAME_TYPE_DATA, logical_blk_num++);
+ stage->next = NULL;
+ if (last)
+ last->next = stage;
+ last = stage;
+ }
+ if (frames) {
+ spin_lock_irqsave(&tape->spinlock, flags);
+ last->next = tape->first_stage;
+ tape->next_stage = tape->first_stage = first;
+ tape->nr_stages += frames;
+ tape->nr_pending_stages += frames;
+ spin_unlock_irqrestore(&tape->spinlock, flags);
+ }
+ idetape_update_stats(drive);
+#if ONSTREAM_DEBUG
+ if (tape->debug_level >= 2)
+ printk(KERN_INFO "ide-tape: %s: frames left in buffer: %d\n", tape->name, tape->cur_frames);
+#endif
+}
+
+/*
+ * Error recovery algorithm for the OnStream tape.
+ */
+static void idetape_onstream_write_error_recovery (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ unsigned int block;
+
+ if (tape->onstream_write_error == 1) {
+ printk(KERN_ERR "ide-tape: %s: detected physical bad block at %u\n", tape->name, ntohl(tape->sense.information));
+ block = ntohl(tape->sense.information) + 80;
+ idetape_update_stats(drive);
+ printk(KERN_ERR "ide-tape: %s: relocating %d buffered logical blocks to physical block %u\n", tape->name, tape->cur_frames, block);
+ idetape_update_stats(drive);
+ if (tape->firmware_revision_num >= 106)
+ idetape_position_tape(drive, block, 0, 1);
+ else {
+ idetape_onstream_read_back_buffer(drive);
+ idetape_position_tape(drive, block, 0, 0);
+ }
+ idetape_read_position(drive);
+#if ONSTREAM_DEBUG
+ if (tape->debug_level >= 1)
+ printk(KERN_ERR "ide-tape: %s: positioning complete, cur_frames %d, pos %d, tape pos %d\n", tape->name, tape->cur_frames, tape->first_frame_position, tape->last_frame_position);
+#endif
+ } else if (tape->onstream_write_error == 2) {
+#if ONSTREAM_DEBUG
+ if (tape->debug_level >= 1)
+ printk(KERN_INFO "ide-tape: %s: skipping over config partition\n", tape->name);
+#endif
+ idetape_flush_tape_buffers(drive);
+ block = idetape_read_position(drive);
+ if (block != 0xba4)
+ printk(KERN_ERR "ide-tape: warning, current position %d, expected %d\n", block, 0xba4);
+ idetape_position_tape(drive, 0xbb8, 0, 0);
+ }
+ tape->onstream_write_error = 0;
+}
+
+/*
+ * idetape_insert_pipeline_into_queue is used to start servicing the
+ * pipeline stages, starting from tape->next_stage.
+ */
+static void idetape_insert_pipeline_into_queue (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+
+ if (tape->next_stage == NULL)
+ return;
+ if (!idetape_pipeline_active (tape)) {
+ if (tape->onstream_write_error)
+ idetape_onstream_write_error_recovery(drive);
+ set_bit(IDETAPE_PIPELINE_ACTIVE, &tape->flags);
+ idetape_active_next_stage (drive);
+ (void) ide_do_drive_cmd (drive, tape->active_data_request, ide_end);
+ }
+}
+
+static void idetape_create_inquiry_cmd (idetape_pc_t *pc)
+{
+ idetape_init_pc(pc);
+ pc->c[0] = IDETAPE_INQUIRY_CMD;
+ pc->c[4] = pc->request_transfer = 254;
+ pc->callback = &idetape_pc_callback;
+}
+
+static void idetape_create_rewind_cmd (ide_drive_t *drive, idetape_pc_t *pc)
+{
+ idetape_tape_t *tape = drive->driver_data;
+
+ idetape_init_pc (pc);
+ pc->c[0] = IDETAPE_REWIND_CMD;
+ if (tape->onstream)
+ pc->c[1] = 1;
+ set_bit (PC_WAIT_FOR_DSC, &pc->flags);
+ pc->callback = &idetape_pc_callback;
+}
+
+static void idetape_create_mode_select_cmd (idetape_pc_t *pc, int length)
+{
+ idetape_init_pc (pc);
+ set_bit (PC_WRITING, &pc->flags);
+ pc->c[0] = IDETAPE_MODE_SELECT_CMD;
+ pc->c[1] = 0x10;
+ put_unaligned (htons(length), (unsigned short *) &pc->c[3]);
+ pc->request_transfer = 255;
+ pc->callback = &idetape_pc_callback;
+}
+
+static void idetape_create_erase_cmd (idetape_pc_t *pc)
+{
+ idetape_init_pc (pc);
+ pc->c[0] = IDETAPE_ERASE_CMD;
+ pc->c[1] = 1;
+ set_bit (PC_WAIT_FOR_DSC, &pc->flags);
+ pc->callback = &idetape_pc_callback;
+}
+
+static void idetape_create_space_cmd (idetape_pc_t *pc,int count,byte cmd)
+{
+ idetape_init_pc (pc);
+ pc->c[0] = IDETAPE_SPACE_CMD;
+ put_unaligned (htonl (count), (unsigned int *) &pc->c[1]);
+ pc->c[1] = cmd;
+ set_bit (PC_WAIT_FOR_DSC, &pc->flags);
+ pc->callback = &idetape_pc_callback;
+}
+
+/*
+ * Verify that we have the correct tape frame
+ */
+static int idetape_verify_stage (ide_drive_t *drive, idetape_stage_t *stage, int logical_blk_num, int quiet)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ os_aux_t *aux = stage->aux;
+ os_partition_t *par = &aux->partition;
+ struct request *rq = &stage->rq;
+ struct buffer_head *bh;
+
+ if (!tape->onstream)
+ return 1;
+ if (tape->raw) {
+ if (rq->errors) {
+ bh = stage->bh;
+ while (bh) {
+ memset(bh->b_data, 0, bh->b_size);
+ bh = bh->b_reqnext;
+ }
+ strcpy(stage->bh->b_data, "READ ERROR ON FRAME");
+ }
+ return 1;
+ }
+ if (rq->errors == IDETAPE_ERROR_GENERAL) {
+ printk(KERN_INFO "ide-tape: %s: skipping frame, read error\n", tape->name);
+ return 0;
+ }
+ if (rq->errors == IDETAPE_ERROR_EOD) {
+ printk(KERN_INFO "ide-tape: %s: skipping frame, eod\n", tape->name);
+ return 0;
+ }
+ if (ntohl(aux->format_id) != 0) {
+ printk(KERN_INFO "ide-tape: %s: skipping frame, format_id %u\n", tape->name, ntohl(aux->format_id));
+ return 0;
+ }
+ if (memcmp(aux->application_sig, tape->application_sig, 4) != 0) {
+ printk(KERN_INFO "ide-tape: %s: skipping frame, incorrect application signature\n", tape->name);
+ return 0;
+ }
+ if (aux->frame_type != OS_FRAME_TYPE_DATA &&
+ aux->frame_type != OS_FRAME_TYPE_EOD &&
+ aux->frame_type != OS_FRAME_TYPE_MARKER) {
+ printk(KERN_INFO "ide-tape: %s: skipping frame, frame type %x\n", tape->name, aux->frame_type);
+ return 0;
+ }
+ if (par->partition_num != OS_DATA_PARTITION) {
+ if (!tape->linux_media || tape->linux_media_version != 2) {
+ printk(KERN_INFO "ide-tape: %s: skipping frame, partition num %d\n", tape->name, par->partition_num);
+ return 0;
+ }
+ }
+ if (par->par_desc_ver != OS_PARTITION_VERSION) {
+ printk(KERN_INFO "ide-tape: %s: skipping frame, partition version %d\n", tape->name, par->par_desc_ver);
+ return 0;
+ }
+ if (ntohs(par->wrt_pass_cntr) != tape->wrt_pass_cntr) {
+ printk(KERN_INFO "ide-tape: %s: skipping frame, wrt_pass_cntr %d (expected %d)(logical_blk_num %u)\n", tape->name, ntohs(par->wrt_pass_cntr), tape->wrt_pass_cntr, ntohl(aux->logical_blk_num));
+ return 0;
+ }
+ if (aux->frame_seq_num != aux->logical_blk_num) {
+ printk(KERN_INFO "ide-tape: %s: skipping frame, seq != logical\n", tape->name);
+ return 0;
+ }
+ if (logical_blk_num != -1 && ntohl(aux->logical_blk_num) != logical_blk_num) {
+ if (!quiet)
+ printk(KERN_INFO "ide-tape: %s: skipping frame, logical_blk_num %u (expected %d)\n", tape->name, ntohl(aux->logical_blk_num), logical_blk_num);
+ return 0;
+ }
+ if (aux->frame_type == OS_FRAME_TYPE_MARKER) {
+ rq->errors = IDETAPE_ERROR_FILEMARK;
+ rq->current_nr_sectors = rq->nr_sectors;
+ }
+ return 1;
+}
+
+static void idetape_wait_first_stage (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ unsigned long flags;
+
+ if (tape->first_stage == NULL)
+ return;
+ spin_lock_irqsave(&tape->spinlock, flags);
+ if (tape->active_stage == tape->first_stage)
+ idetape_wait_for_request(drive, tape->active_data_request);
+ spin_unlock_irqrestore(&tape->spinlock, flags);
+}
+
+/*
+ * idetape_add_chrdev_write_request tries to add a character device
+ * originated write request to our pipeline. In case we don't succeed,
+ * we revert to non-pipelined operation mode for this request.
+ *
+ * 1. Try to allocate a new pipeline stage.
+ * 2. If we can't, wait for more and more requests to be serviced
+ * and try again each time.
+ * 3. If we still can't allocate a stage, fallback to
+ * non-pipelined operation mode for this request.
+ */
+static int idetape_add_chrdev_write_request (ide_drive_t *drive, int blocks)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ idetape_stage_t *new_stage;
+ unsigned long flags;
+ struct request *rq;
+
+#if IDETAPE_DEBUG_LOG
+ if (tape->debug_level >= 3)
+ printk (KERN_INFO "ide-tape: Reached idetape_add_chrdev_write_request\n");
+#endif /* IDETAPE_DEBUG_LOG */
+
+ /*
+ * Attempt to allocate a new stage.
+ * Pay special attention to possible race conditions.
+ */
+ while ((new_stage = idetape_kmalloc_stage (tape)) == NULL) {
+ spin_lock_irqsave(&tape->spinlock, flags);
+ if (idetape_pipeline_active (tape)) {
+ idetape_wait_for_request(drive, tape->active_data_request);
+ spin_unlock_irqrestore(&tape->spinlock, flags);
+ } else {
+ spin_unlock_irqrestore(&tape->spinlock, flags);
+ idetape_insert_pipeline_into_queue (drive);
+ if (idetape_pipeline_active (tape))
+ continue;
+ /*
+ * Linux is short on memory. Fallback to
+ * non-pipelined operation mode for this request.
+ */
+ return idetape_queue_rw_tail (drive, IDETAPE_WRITE_RQ, blocks, tape->merge_stage->bh);
+ }
+ }
+ rq = &new_stage->rq;
+ ide_init_drive_cmd (rq);
+ rq->cmd = IDETAPE_WRITE_RQ;
+ rq->sector = tape->first_frame_position; /* Doesn't actually matter - We always assume sequential access */
+ rq->nr_sectors = rq->current_nr_sectors = blocks;
+
+ idetape_switch_buffers (tape, new_stage);
+ idetape_init_stage(drive, new_stage, OS_FRAME_TYPE_DATA, tape->logical_blk_num);
+ tape->logical_blk_num++;
+ idetape_add_stage_tail (drive,new_stage);
+ tape->pipeline_head++;
+#if USE_IOTRACE
+ IO_trace(IO_IDETAPE_FIFO, tape->pipeline_head, tape->buffer_head, tape->tape_head, tape->minor);
+#endif
+ calculate_speeds(drive);
+
+ /*
+ * Estimate whether the tape has stopped writing by checking
+ * if our write pipeline is currently empty. If we are not
+ * writing anymore, wait for the pipeline to be full enough
+ * (90%) before starting to service requests, so that we will
+ * be able to keep up with the higher speeds of the tape.
+ *
+ * For the OnStream drive, we can query the number of pending
+ * frames in the drive's internal buffer. As long as the tape
+ * is still writing, it is better to write frames immediately
+ * rather than gather them in the pipeline. This will give the
+ * tape's firmware the ability to sense the current incoming
+ * data rate more accurately, and since the OnStream tape
+ * supports variable speeds, it can try to adjust itself to the
+ * incoming data rate.
+ */
+ if (!idetape_pipeline_active(tape)) {
+ if (tape->nr_stages >= tape->max_stages * 9 / 10 ||
+ tape->nr_stages >= tape->max_stages - tape->uncontrolled_pipeline_head_speed * 3 * 1024 / tape->tape_block_size) {
+ tape->measure_insert_time = 1;
+ tape->insert_time = jiffies;
+ tape->insert_size = 0;
+ tape->insert_speed = 0;
+ idetape_insert_pipeline_into_queue (drive);
+ } else if (tape->onstream) {
+ idetape_update_stats(drive);
+ if (tape->cur_frames > 5)
+ idetape_insert_pipeline_into_queue (drive);
+ }
+ }
+ if (test_and_clear_bit (IDETAPE_PIPELINE_ERROR, &tape->flags)) /* Return a deferred error */
+ return -EIO;
+ return blocks;
+}
+
+/*
+ * idetape_wait_for_pipeline will wait until all pending pipeline
+ * requests are serviced. Typically called on device close.
+ */
+static void idetape_wait_for_pipeline (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ unsigned long flags;
+
+ while (tape->next_stage || idetape_pipeline_active(tape)) {
+ idetape_insert_pipeline_into_queue (drive);
+ spin_lock_irqsave(&tape->spinlock, flags);
+ if (idetape_pipeline_active(tape))
+ idetape_wait_for_request(drive, tape->active_data_request);
+ spin_unlock_irqrestore(&tape->spinlock, flags);
+ }
+}
+
+static void idetape_empty_write_pipeline (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ int blocks, i, min;
+ struct buffer_head *bh;
+
+#if IDETAPE_DEBUG_BUGS
+ if (tape->chrdev_direction != idetape_direction_write) {
+ printk (KERN_ERR "ide-tape: bug: Trying to empty write pipeline, but we are not writing.\n");
+ return;
+ }
+ if (tape->merge_stage_size > tape->stage_size) {
+ printk (KERN_ERR "ide-tape: bug: merge_buffer too big\n");
+ tape->merge_stage_size = tape->stage_size;
+ }
+#endif /* IDETAPE_DEBUG_BUGS */
+ if (tape->merge_stage_size) {
+ blocks=tape->merge_stage_size/tape->tape_block_size;
+ if (tape->merge_stage_size % tape->tape_block_size) {
+ blocks++;
+ i = tape->tape_block_size - tape->merge_stage_size % tape->tape_block_size;
+ bh = tape->bh->b_reqnext;
+ while (bh) {
+ atomic_set(&bh->b_count, 0);
+ bh = bh->b_reqnext;
+ }
+ bh = tape->bh;
+ while (i) {
+ if (bh == NULL) {
+ printk(KERN_INFO "ide-tape: bug, bh NULL\n");
+ break;
+ }
+ min = IDE_MIN(i, bh->b_size - atomic_read(&bh->b_count));
+ memset(bh->b_data + atomic_read(&bh->b_count), 0, min);
+ atomic_add(min, &bh->b_count);
+ i -= min;
+ bh = bh->b_reqnext;
+ }
+ }
+ (void) idetape_add_chrdev_write_request (drive, blocks);
+ tape->merge_stage_size = 0;
+ }
+ idetape_wait_for_pipeline (drive);
+ if (tape->merge_stage != NULL) {
+ __idetape_kfree_stage (tape->merge_stage);
+ tape->merge_stage = NULL;
+ }
+ clear_bit (IDETAPE_PIPELINE_ERROR, &tape->flags);
+ tape->chrdev_direction=idetape_direction_none;
+
+ /*
+ * On the next backup, perform the feedback loop again.
+ * (I don't want to keep sense information between backups,
+ * as some systems are constantly on, and the system load
+ * can be totally different on the next backup).
+ */
+ tape->max_stages = tape->min_pipeline;
+#if IDETAPE_DEBUG_BUGS
+ if (tape->first_stage != NULL || tape->next_stage != NULL || tape->last_stage != NULL || tape->nr_stages != 0) {
+ printk (KERN_ERR "ide-tape: ide-tape pipeline bug, "
+ "first_stage %p, next_stage %p, last_stage %p, nr_stages %d\n",
+ tape->first_stage, tape->next_stage, tape->last_stage, tape->nr_stages);
+ }
+#endif /* IDETAPE_DEBUG_BUGS */
+}
+
+static void idetape_restart_speed_control (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+
+ tape->restart_speed_control_req = 0;
+ tape->pipeline_head = 0;
+ tape->buffer_head = tape->tape_head = tape->cur_frames;
+ tape->controlled_last_pipeline_head = tape->uncontrolled_last_pipeline_head = 0;
+ tape->controlled_previous_pipeline_head = tape->uncontrolled_previous_pipeline_head = 0;
+ tape->pipeline_head_speed = tape->controlled_pipeline_head_speed = 5000;
+ tape->uncontrolled_pipeline_head_speed = 0;
+ tape->controlled_pipeline_head_time = tape->uncontrolled_pipeline_head_time = jiffies;
+ tape->controlled_previous_head_time = tape->uncontrolled_previous_head_time = jiffies;
+}
+
+static int idetape_initiate_read (ide_drive_t *drive, int max_stages)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ idetape_stage_t *new_stage;
+ struct request rq;
+ int bytes_read;
+ int blocks = tape->capabilities.ctl;
+
+ if (tape->chrdev_direction != idetape_direction_read) { /* Initialize read operation */
+ if (tape->chrdev_direction == idetape_direction_write) {
+ idetape_empty_write_pipeline (drive);
+ idetape_flush_tape_buffers (drive);
+ }
+#if IDETAPE_DEBUG_BUGS
+ if (tape->merge_stage || tape->merge_stage_size) {
+ printk (KERN_ERR "ide-tape: merge_stage_size should be 0 now\n");
+ tape->merge_stage_size = 0;
+ }
+#endif /* IDETAPE_DEBUG_BUGS */
+ if ((tape->merge_stage = __idetape_kmalloc_stage (tape, 0, 0)) == NULL)
+ return -ENOMEM;
+ tape->chrdev_direction = idetape_direction_read;
+ tape->logical_blk_num = 0;
+
+ /*
+ * Issue a read 0 command to ensure that DSC handshake
+ * is switched from completion mode to buffer available
+ * mode.
+ */
+ bytes_read = idetape_queue_rw_tail (drive, IDETAPE_READ_RQ, 0, tape->merge_stage->bh);
+ if (bytes_read < 0) {
+ kfree (tape->merge_stage);
+ tape->merge_stage = NULL;
+ tape->chrdev_direction = idetape_direction_none;
+ return bytes_read;
+ }
+ }
+ if (tape->restart_speed_control_req)
+ idetape_restart_speed_control(drive);
+ ide_init_drive_cmd (&rq);
+ rq.cmd = IDETAPE_READ_RQ;
+ rq.sector = tape->first_frame_position;
+ rq.nr_sectors = rq.current_nr_sectors = blocks;
+ if (!test_bit(IDETAPE_PIPELINE_ERROR, &tape->flags) && tape->nr_stages <= max_stages) {
+ new_stage=idetape_kmalloc_stage (tape);
+ while (new_stage != NULL) {
+ new_stage->rq=rq;
+ idetape_add_stage_tail (drive,new_stage);
+ if (tape->nr_stages >= max_stages)
+ break;
+ new_stage=idetape_kmalloc_stage (tape);
+ }
+ }
+ if (!idetape_pipeline_active(tape)) {
+ if (tape->nr_pending_stages >= 3 * max_stages / 4) {
+ tape->measure_insert_time = 1;
+ tape->insert_time = jiffies;
+ tape->insert_size = 0;
+ tape->insert_speed = 0;
+ idetape_insert_pipeline_into_queue (drive);
+ } else if (tape->onstream) {
+ idetape_update_stats(drive);
+ if (tape->cur_frames < tape->max_frames - 5)
+ idetape_insert_pipeline_into_queue (drive);
+ }
+ }
+ return 0;
+}
+
+static int idetape_get_logical_blk (ide_drive_t *drive, int logical_blk_num, int max_stages, int quiet)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ unsigned long flags;
+ int cnt = 0, x, position;
+
+ /*
+ * Search and wait for the next logical tape block
+ */
+ while (1) {
+ if (cnt++ > 1000) { /* AJN: was 100 */
+ printk(KERN_INFO "ide-tape: %s: couldn't find logical block %d, aborting\n", tape->name, logical_blk_num);
+ return 0;
+ }
+ idetape_initiate_read(drive, max_stages);
+ if (tape->first_stage == NULL) {
+ if (tape->onstream) {
+#if ONSTREAM_DEBUG
+ if (tape->debug_level >= 1)
+ printk(KERN_INFO "ide-tape: %s: first_stage == NULL, pipeline error %ld\n", tape->name, (long)test_bit(IDETAPE_PIPELINE_ERROR, &tape->flags));
+#endif
+ clear_bit(IDETAPE_PIPELINE_ERROR, &tape->flags);
+ position = idetape_read_position(drive);
+ printk(KERN_INFO "ide-tape: %s: blank block detected, positioning tape to block %d\n", tape->name, position + 60);
+ idetape_position_tape(drive, position + 60, 0, 1);
+ cnt += 40;
+ continue;
+ } else
+ return 0;
+ }
+ idetape_wait_first_stage(drive);
+ if (idetape_verify_stage(drive, tape->first_stage, logical_blk_num, quiet))
+ break;
+ if (tape->first_stage->rq.errors == IDETAPE_ERROR_EOD)
+ cnt--;
+ if (idetape_verify_stage(drive, tape->first_stage, -1, quiet)) {
+ x = ntohl(tape->first_stage->aux->logical_blk_num);
+ if (x > logical_blk_num) {
+ printk(KERN_ERR "ide-tape: %s: couldn't find logical block %d, aborting (block %d found)\n", tape->name, logical_blk_num, x);
+ return 0;
+ }
+ }
+ spin_lock_irqsave(&tape->spinlock, flags);
+ idetape_remove_stage_head(drive);
+ spin_unlock_irqrestore(&tape->spinlock, flags);
+ }
+ if (tape->onstream)
+ tape->logical_blk_num = ntohl(tape->first_stage->aux->logical_blk_num);
+ return 1;
+}
+
+/*
+ * idetape_add_chrdev_read_request is called from idetape_chrdev_read
+ * to service a character device read request and add read-ahead
+ * requests to our pipeline.
+ */
+static int idetape_add_chrdev_read_request (ide_drive_t *drive,int blocks)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ unsigned long flags;
+ struct request *rq_ptr;
+ int bytes_read;
+
+#if IDETAPE_DEBUG_LOG
+ if (tape->debug_level >= 4)
+ printk (KERN_INFO "ide-tape: Reached idetape_add_chrdev_read_request, %d blocks\n", blocks);
+#endif /* IDETAPE_DEBUG_LOG */
+
+ /*
+ * Wait for the next logical block to be available at the head
+ * of the pipeline
+ */
+ if (!idetape_get_logical_blk(drive, tape->logical_blk_num, tape->max_stages, 0)) {
+ if (tape->onstream) {
+ set_bit(IDETAPE_READ_ERROR, &tape->flags);
+ return 0;
+ }
+ if (test_bit(IDETAPE_PIPELINE_ERROR, &tape->flags))
+ return 0;
+ return idetape_queue_rw_tail (drive, IDETAPE_READ_RQ, blocks, tape->merge_stage->bh);
+ }
+ rq_ptr = &tape->first_stage->rq;
+ bytes_read = tape->tape_block_size * (rq_ptr->nr_sectors - rq_ptr->current_nr_sectors);
+ rq_ptr->nr_sectors = rq_ptr->current_nr_sectors = 0;
+
+
+ if (tape->onstream && !tape->raw && tape->first_stage->aux->frame_type == OS_FRAME_TYPE_EOD) {
+#if ONSTREAM_DEBUG
+ if (tape->debug_level >= 2)
+ printk(KERN_INFO "ide-tape: %s: EOD reached\n", tape->name);
+#endif
+ return 0;
+ }
+ if (rq_ptr->errors == IDETAPE_ERROR_EOD)
+ return 0;
+ else if (rq_ptr->errors == IDETAPE_ERROR_FILEMARK)
+ set_bit (IDETAPE_FILEMARK, &tape->flags);
+ else {
+ idetape_switch_buffers (tape, tape->first_stage);
+ if (rq_ptr->errors == IDETAPE_ERROR_GENERAL) {
+#if ONSTREAM_DEBUG
+ if (tape->debug_level >= 1)
+ printk(KERN_INFO "ide-tape: error detected, bytes_read %d\n", bytes_read);
+#endif
+ }
+ clear_bit (IDETAPE_FILEMARK, &tape->flags);
+ spin_lock_irqsave(&tape->spinlock, flags);
+ idetape_remove_stage_head (drive);
+ spin_unlock_irqrestore(&tape->spinlock, flags);
+ tape->logical_blk_num++;
+ tape->pipeline_head++;
+#if USE_IOTRACE
+ IO_trace(IO_IDETAPE_FIFO, tape->pipeline_head, tape->buffer_head, tape->tape_head, tape->minor);
+#endif
+ calculate_speeds(drive);
+ }
+#if IDETAPE_DEBUG_BUGS
+ if (bytes_read > blocks*tape->tape_block_size) {
+ printk (KERN_ERR "ide-tape: bug: trying to return more bytes than requested\n");
+ bytes_read=blocks*tape->tape_block_size;
+ }
+#endif /* IDETAPE_DEBUG_BUGS */
+ return (bytes_read);
+}
+
+static void idetape_pad_zeros (ide_drive_t *drive, int bcount)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ struct buffer_head *bh;
+ int count, blocks;
+
+ while (bcount) {
+ bh = tape->merge_stage->bh;
+ count = IDE_MIN (tape->stage_size, bcount);
+ bcount -= count;
+ blocks = count / tape->tape_block_size;
+ while (count) {
+ atomic_set(&bh->b_count, IDE_MIN (count, bh->b_size));
+ memset (bh->b_data, 0, atomic_read(&bh->b_count));
+ count -= atomic_read(&bh->b_count);
+ bh = bh->b_reqnext;
+ }
+ idetape_queue_rw_tail (drive, IDETAPE_WRITE_RQ, blocks, tape->merge_stage->bh);
+ }
+}
+
+static int idetape_pipeline_size (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ idetape_stage_t *stage;
+ struct request *rq;
+ int size = 0;
+
+ idetape_wait_for_pipeline (drive);
+ stage = tape->first_stage;
+ while (stage != NULL) {
+ rq = &stage->rq;
+ size += tape->tape_block_size * (rq->nr_sectors-rq->current_nr_sectors);
+ if (rq->errors == IDETAPE_ERROR_FILEMARK)
+ size += tape->tape_block_size;
+ stage = stage->next;
+ }
+ size += tape->merge_stage_size;
+ return size;
+}
+
+/*
+ * Rewinds the tape to the Beginning Of the current Partition (BOP).
+ *
+ * We currently support only one partition.
+ */
+static int idetape_rewind_tape (ide_drive_t *drive)
+{
+ int retval;
+ idetape_pc_t pc;
+ idetape_tape_t *tape = drive->driver_data;
+#if IDETAPE_DEBUG_LOG
+ if (tape->debug_level >= 2)
+ printk (KERN_INFO "ide-tape: Reached idetape_rewind_tape\n");
+#endif /* IDETAPE_DEBUG_LOG */
+
+ idetape_create_rewind_cmd (drive, &pc);
+ retval=idetape_queue_pc_tail (drive,&pc);
+ if (retval) return retval;
+
+ idetape_create_read_position_cmd (&pc);
+ retval = idetape_queue_pc_tail (drive,&pc);
+ if (retval) return retval;
+ tape->logical_blk_num = 0;
+ return 0;
+}
+
+/*
+ * Our special ide-tape ioctl's.
+ *
+ * Currently there aren't any ioctl's.
+ * mtio.h compatible commands should be issued to the character device
+ * interface.
+ */
+static int idetape_blkdev_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ idetape_config_t config;
+
+#if IDETAPE_DEBUG_LOG
+ if (tape->debug_level >= 4)
+ printk (KERN_INFO "ide-tape: Reached idetape_blkdev_ioctl\n");
+#endif /* IDETAPE_DEBUG_LOG */
+ switch (cmd) {
+ case 0x0340:
+ if (copy_from_user ((char *) &config, (char *) arg, sizeof (idetape_config_t)))
+ return -EFAULT;
+ tape->best_dsc_rw_frequency = config.dsc_rw_frequency;
+ tape->max_stages = config.nr_stages;
+ break;
+ case 0x0350:
+ config.dsc_rw_frequency = (int) tape->best_dsc_rw_frequency;
+ config.nr_stages = tape->max_stages;
+ if (copy_to_user ((char *) arg, (char *) &config, sizeof (idetape_config_t)))
+ return -EFAULT;
+ break;
+ default:
+ return -EIO;
+ }
+ return 0;
+}
+
+/*
+ * The block device interface should not be used for data transfers.
+ * However, we still allow opening it so that we can issue general
+ * ide driver configuration ioctl's, such as the interrupt unmask feature.
+ */
+static int idetape_blkdev_open (struct inode *inode, struct file *filp, ide_drive_t *drive)
+{
+ MOD_INC_USE_COUNT;
+#if ONSTREAM_DEBUG
+ printk(KERN_INFO "ide-tape: MOD_INC_USE_COUNT in idetape_blkdev_open\n");
+#endif
+ return 0;
+}
+
+static void idetape_blkdev_release (struct inode *inode, struct file *filp, ide_drive_t *drive)
+{
+ MOD_DEC_USE_COUNT;
+#if ONSTREAM_DEBUG
+ printk(KERN_INFO "ide-tape: MOD_DEC_USE_COUNT in idetape_blkdev_release\n");
+#endif
+}
+
+/*
+ * idetape_pre_reset is called before an ATAPI/ATA software reset.
+ */
+static void idetape_pre_reset (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ if (tape != NULL)
+ set_bit (IDETAPE_IGNORE_DSC, &tape->flags);
+}
+
+/*
+ * Character device interface functions
+ */
+static ide_drive_t *get_drive_ptr (kdev_t i_rdev)
+{
+ unsigned int i = MINOR(i_rdev) & ~0xc0;
+
+ if (i >= MAX_HWIFS * MAX_DRIVES)
+ return NULL;
+ return (idetape_chrdevs[i].drive);
+}
+
+static int idetape_onstream_space_over_filemarks_backward (ide_drive_t *drive,short mt_op,int mt_count)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ int cnt = 0;
+ int last_mark_addr;
+ unsigned long flags;
+
+ if (!idetape_get_logical_blk(drive, -1, 10, 0)) {
+ printk(KERN_INFO "ide-tape: %s: couldn't get logical blk num in space_filemarks_bwd\n", tape->name);
+ return -EIO;
+ }
+ while (cnt != mt_count) {
+ last_mark_addr = ntohl(tape->first_stage->aux->last_mark_addr);
+ if (last_mark_addr == -1)
+ return -EIO;
+#if ONSTREAM_DEBUG
+ if (tape->debug_level >= 2)
+ printk(KERN_INFO "ide-tape: positioning to last mark at %d\n", last_mark_addr);
+#endif
+ idetape_position_tape(drive, last_mark_addr, 0, 0);
+ cnt++;
+ if (!idetape_get_logical_blk(drive, -1, 10, 0)) {
+ printk(KERN_INFO "ide-tape: %s: couldn't get logical blk num in space_filemarks\n", tape->name);
+ return -EIO;
+ }
+ if (tape->first_stage->aux->frame_type != OS_FRAME_TYPE_MARKER) {
+ printk(KERN_INFO "ide-tape: %s: expected to find marker at block %d, not found\n", tape->name, last_mark_addr);
+ return -EIO;
+ }
+ }
+ if (mt_op == MTBSFM) {
+ spin_lock_irqsave(&tape->spinlock, flags);
+ idetape_remove_stage_head (drive);
+ tape->logical_blk_num++;
+ spin_unlock_irqrestore(&tape->spinlock, flags);
+ }
+ return 0;
+}
+
+/*
+ * ADRL 1.1 compatible "slow" space filemarks fwd version
+ *
+ * Just scans for the filemark sequentially.
+ */
+static int idetape_onstream_space_over_filemarks_forward_slow (ide_drive_t *drive,short mt_op,int mt_count)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ int cnt = 0;
+ unsigned long flags;
+
+ if (!idetape_get_logical_blk(drive, -1, 10, 0)) {
+ printk(KERN_INFO "ide-tape: %s: couldn't get logical blk num in space_filemarks_fwd\n", tape->name);
+ return -EIO;
+ }
+ while (1) {
+ if (!idetape_get_logical_blk(drive, -1, 10, 0)) {
+ printk(KERN_INFO "ide-tape: %s: couldn't get logical blk num in space_filemarks\n", tape->name);
+ return -EIO;
+ }
+ if (tape->first_stage->aux->frame_type == OS_FRAME_TYPE_MARKER)
+ cnt++;
+ if (tape->first_stage->aux->frame_type == OS_FRAME_TYPE_EOD) {
+#if ONSTREAM_DEBUG
+ if (tape->debug_level >= 2)
+ printk(KERN_INFO "ide-tape: %s: space_fwd: EOD reached\n", tape->name);
+#endif
+ return -EIO;
+ }
+ if (cnt == mt_count)
+ break;
+ spin_lock_irqsave(&tape->spinlock, flags);
+ idetape_remove_stage_head (drive);
+ spin_unlock_irqrestore(&tape->spinlock, flags);
+ }
+ if (mt_op == MTFSF) {
+ spin_lock_irqsave(&tape->spinlock, flags);
+ idetape_remove_stage_head (drive);
+ tape->logical_blk_num++;
+ spin_unlock_irqrestore(&tape->spinlock, flags);
+ }
+ return 0;
+}
+
+
+/*
+ * Fast linux specific version of OnStream FSF
+ */
+static int idetape_onstream_space_over_filemarks_forward_fast (ide_drive_t *drive,short mt_op,int mt_count)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ int cnt = 0, next_mark_addr;
+ unsigned long flags;
+
+ if (!idetape_get_logical_blk(drive, -1, 10, 0)) {
+ printk(KERN_INFO "ide-tape: %s: couldn't get logical blk num in space_filemarks_fwd\n", tape->name);
+ return -EIO;
+ }
+
+ /*
+ * Find nearest (usually previous) marker
+ */
+ while (1) {
+ if (tape->first_stage->aux->frame_type == OS_FRAME_TYPE_MARKER)
+ break;
+ if (tape->first_stage->aux->frame_type == OS_FRAME_TYPE_EOD) {
+#if ONSTREAM_DEBUG
+ if (tape->debug_level >= 2)
+ printk(KERN_INFO "ide-tape: %s: space_fwd: EOD reached\n", tape->name);
+#endif
+ return -EIO;
+ }
+ if (ntohl(tape->first_stage->aux->filemark_cnt) == 0) {
+ if (tape->first_mark_addr == -1) {
+ printk(KERN_INFO "ide-tape: %s: reverting to slow filemark space\n", tape->name);
+ return idetape_onstream_space_over_filemarks_forward_slow(drive, mt_op, mt_count);
+ }
+ idetape_position_tape(drive, tape->first_mark_addr, 0, 0);
+ if (!idetape_get_logical_blk(drive, -1, 10, 0)) {
+ printk(KERN_INFO "ide-tape: %s: couldn't get logical blk num in space_filemarks_fwd_fast\n", tape->name);
+ return -EIO;
+ }
+ if (tape->first_stage->aux->frame_type != OS_FRAME_TYPE_MARKER) {
+ printk(KERN_INFO "ide-tape: %s: expected to find filemark at %d\n", tape->name, tape->first_mark_addr);
+ return -EIO;
+ }
+ } else {
+ if (idetape_onstream_space_over_filemarks_backward(drive, MTBSF, 1) < 0)
+ return -EIO;
+ mt_count++;
+ }
+ }
+ cnt++;
+ while (cnt != mt_count) {
+ next_mark_addr = ntohl(tape->first_stage->aux->next_mark_addr);
+ if (!next_mark_addr || next_mark_addr > tape->eod_frame_addr) {
+ printk(KERN_INFO "ide-tape: %s: reverting to slow filemark space\n", tape->name);
+ return idetape_onstream_space_over_filemarks_forward_slow(drive, mt_op, mt_count - cnt);
+#if ONSTREAM_DEBUG
+ } else if (tape->debug_level >= 2) {
+ printk(KERN_INFO "ide-tape: positioning to next mark at %d\n", next_mark_addr);
+#endif
+ }
+ idetape_position_tape(drive, next_mark_addr, 0, 0);
+ cnt++;
+ if (!idetape_get_logical_blk(drive, -1, 10, 0)) {
+ printk(KERN_INFO "ide-tape: %s: couldn't get logical blk num in space_filemarks\n", tape->name);
+ return -EIO;
+ }
+ if (tape->first_stage->aux->frame_type != OS_FRAME_TYPE_MARKER) {
+ printk(KERN_INFO "ide-tape: %s: expected to find marker at block %d, not found\n", tape->name, next_mark_addr);
+ return -EIO;
+ }
+ }
+ if (mt_op == MTFSF) {
+ spin_lock_irqsave(&tape->spinlock, flags);
+ idetape_remove_stage_head (drive);
+ tape->logical_blk_num++;
+ spin_unlock_irqrestore(&tape->spinlock, flags);
+ }
+ return 0;
+}
+
+/*
+ * idetape_space_over_filemarks is now a bit more complicated than just
+ * passing the command to the tape since we may have crossed some
+ * filemarks during our pipelined read-ahead mode.
+ *
+ * As a minor side effect, the pipeline enables us to support MTFSFM when
+ * the filemark is in our internal pipeline even if the tape doesn't
+ * support spacing over filemarks in the reverse direction.
+ */
+static int idetape_space_over_filemarks (ide_drive_t *drive,short mt_op,int mt_count)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ idetape_pc_t pc;
+ unsigned long flags;
+ int retval,count=0;
+ int speed_control;
+
+ if (tape->onstream) {
+ if (tape->raw)
+ return -EIO;
+ speed_control = tape->speed_control;
+ tape->speed_control = 0;
+ if (mt_op == MTFSF || mt_op == MTFSFM) {
+ if (tape->linux_media)
+ retval = idetape_onstream_space_over_filemarks_forward_fast(drive, mt_op, mt_count);
+ else
+ retval = idetape_onstream_space_over_filemarks_forward_slow(drive, mt_op, mt_count);
+ } else
+ retval = idetape_onstream_space_over_filemarks_backward(drive, mt_op, mt_count);
+ tape->speed_control = speed_control;
+ tape->restart_speed_control_req = 1;
+ return retval;
+ }
+
+ if (tape->chrdev_direction == idetape_direction_read) {
+ /*
+ * We have a read-ahead buffer. Scan it for crossed
+ * filemarks.
+ */
+ tape->merge_stage_size = 0;
+ clear_bit (IDETAPE_FILEMARK, &tape->flags);
+ while (tape->first_stage != NULL) {
+ idetape_wait_first_stage(drive);
+ if (tape->first_stage->rq.errors == IDETAPE_ERROR_FILEMARK)
+ count++;
+ if (count == mt_count) {
+ switch (mt_op) {
+ case MTFSF:
+ spin_lock_irqsave(&tape->spinlock, flags);
+ idetape_remove_stage_head (drive);
+ spin_unlock_irqrestore(&tape->spinlock, flags);
+ case MTFSFM:
+ return (0);
+ default:
+ break;
+ }
+ }
+ spin_lock_irqsave(&tape->spinlock, flags);
+ idetape_remove_stage_head (drive);
+ spin_unlock_irqrestore(&tape->spinlock, flags);
+ }
+ idetape_discard_read_pipeline (drive, 1);
+ }
+
+ /*
+ * The filemark was not found in our internal pipeline.
+ * Now we can issue the space command.
+ */
+ switch (mt_op) {
+ case MTFSF:
+ idetape_create_space_cmd (&pc,mt_count-count,IDETAPE_SPACE_OVER_FILEMARK);
+ return (idetape_queue_pc_tail (drive,&pc));
+ case MTFSFM:
+ if (!tape->capabilities.sprev)
+ return (-EIO);
+ retval = idetape_space_over_filemarks (drive, MTFSF, mt_count-count);
+ if (retval) return (retval);
+ return (idetape_space_over_filemarks (drive, MTBSF, 1));
+ case MTBSF:
+ if (!tape->capabilities.sprev)
+ return (-EIO);
+ idetape_create_space_cmd (&pc,-(mt_count+count),IDETAPE_SPACE_OVER_FILEMARK);
+ return (idetape_queue_pc_tail (drive,&pc));
+ case MTBSFM:
+ if (!tape->capabilities.sprev)
+ return (-EIO);
+ retval = idetape_space_over_filemarks (drive, MTBSF, mt_count+count);
+ if (retval) return (retval);
+ return (idetape_space_over_filemarks (drive, MTFSF, 1));
+ default:
+ printk (KERN_ERR "ide-tape: MTIO operation %d not supported\n",mt_op);
+ return (-EIO);
+ }
+}
+
+
+/*
+ * Our character device read / write functions.
+ *
+ * The tape is optimized to maximize throughput when it is transferring
+ * an integral number of the "continuous transfer limit", which is
+ * a parameter of the specific tape (26 KB on my particular tape).
+ * (32 kB for Onstream)
+ *
+ * As of version 1.3 of the driver, the character device provides an
+ * abstract continuous view of the media - any mix of block sizes (even 1
+ * byte) on the same backup/restore procedure is supported. The driver
+ * will internally convert the requests to the recommended transfer unit,
+ * so that an unmatch between the user's block size to the recommended
+ * size will only result in a (slightly) increased driver overhead, but
+ * will no longer hit performance.
+ * This is not applicable to Onstream.
+ */
+static ssize_t idetape_chrdev_read (struct file *file, char *buf,
+ size_t count, loff_t *ppos)
+{
+ struct inode *inode = file->f_dentry->d_inode;
+ ide_drive_t *drive = get_drive_ptr (inode->i_rdev);
+ idetape_tape_t *tape = drive->driver_data;
+ ssize_t bytes_read,temp,actually_read=0, rc;
+
+ if (ppos != &file->f_pos) {
+ /* "A request was outside the capabilities of the device." */
+ return -ENXIO;
+ }
+ if (tape->onstream && (count != tape->tape_block_size)) {
+ printk(KERN_ERR "ide-tape: %s: use %d bytes as block size (%Zd used)\n", tape->name, tape->tape_block_size, count);
+ return -EINVAL;
+ }
+#if IDETAPE_DEBUG_LOG
+ if (tape->debug_level >= 3)
+ printk (KERN_INFO "ide-tape: Reached idetape_chrdev_read, count %Zd\n", count);
+#endif /* IDETAPE_DEBUG_LOG */
+
+ if (tape->chrdev_direction != idetape_direction_read) {
+ if (test_bit (IDETAPE_DETECT_BS, &tape->flags))
+ if (count > tape->tape_block_size && (count % tape->tape_block_size) == 0)
+ tape->user_bs_factor = count / tape->tape_block_size;
+ }
+ if ((rc = idetape_initiate_read(drive, tape->max_stages)) < 0)
+ return rc;
+ if (count==0)
+ return (0);
+ if (tape->merge_stage_size) {
+ actually_read=IDE_MIN (tape->merge_stage_size,count);
+ idetape_copy_stage_to_user (tape, buf, tape->merge_stage, actually_read);
+ buf += actually_read; tape->merge_stage_size -= actually_read; count-=actually_read;
+ }
+ while (count >= tape->stage_size) {
+ bytes_read=idetape_add_chrdev_read_request (drive, tape->capabilities.ctl);
+ if (bytes_read <= 0)
+ goto finish;
+ idetape_copy_stage_to_user (tape, buf, tape->merge_stage, bytes_read);
+ buf += bytes_read; count -= bytes_read; actually_read += bytes_read;
+ }
+ if (count) {
+ bytes_read=idetape_add_chrdev_read_request (drive, tape->capabilities.ctl);
+ if (bytes_read <= 0)
+ goto finish;
+ temp=IDE_MIN (count,bytes_read);
+ idetape_copy_stage_to_user (tape, buf, tape->merge_stage, temp);
+ actually_read+=temp;
+ tape->merge_stage_size=bytes_read-temp;
+ }
+finish:
+ if (!actually_read && test_bit (IDETAPE_FILEMARK, &tape->flags)) {
+#if IDETAPE_DEBUG_LOG
+ if (tape->debug_level >= 2)
+ printk(KERN_INFO "ide-tape: %s: spacing over filemark\n", tape->name);
+#endif
+ idetape_space_over_filemarks (drive, MTFSF, 1);
+ return 0;
+ }
+ if (tape->onstream && !actually_read && test_and_clear_bit(IDETAPE_READ_ERROR, &tape->flags)) {
+ printk(KERN_ERR "ide-tape: %s: unrecovered read error on logical block number %d, skipping\n", tape->name, tape->logical_blk_num);
+ tape->logical_blk_num++;
+ return -EIO;
+ }
+ return actually_read;
+}
+
+static void idetape_update_last_marker (ide_drive_t *drive, int last_mark_addr, int next_mark_addr)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ idetape_stage_t *stage;
+ os_aux_t *aux;
+ int position;
+
+ if (!tape->onstream || tape->raw)
+ return;
+ if (last_mark_addr == -1)
+ return;
+ stage = __idetape_kmalloc_stage(tape, 0, 0);
+ if (stage == NULL)
+ return;
+ idetape_flush_tape_buffers(drive);
+ position = idetape_read_position(drive);
+#if ONSTREAM_DEBUG
+ if (tape->debug_level >= 2)
+ printk(KERN_INFO "ide-tape: current position (2) %d, lblk %d\n", position, tape->logical_blk_num);
+ if (tape->debug_level >= 2)
+ printk(KERN_INFO "ide-tape: current position (2) tape block %d\n", tape->last_frame_position);
+#endif
+ idetape_position_tape(drive, last_mark_addr, 0, 0);
+ if (!idetape_queue_rw_tail (drive, IDETAPE_READ_RQ, 1, stage->bh)) {
+ printk(KERN_INFO "ide-tape: %s: couldn't read last marker\n", tape->name);
+ __idetape_kfree_stage (stage);
+ idetape_position_tape(drive, position, 0, 0);
+ return;
+ }
+ aux = stage->aux;
+ if (aux->frame_type != OS_FRAME_TYPE_MARKER) {
+ printk(KERN_INFO "ide-tape: %s: expected to find marker at addr %d\n", tape->name, last_mark_addr);
+ __idetape_kfree_stage (stage);
+ idetape_position_tape(drive, position, 0, 0);
+ return;
+ }
+#if ONSTREAM_DEBUG
+ if (tape->debug_level >= 2)
+ printk(KERN_INFO "ide-tape: writing back marker\n");
+#endif
+ aux->next_mark_addr = htonl(next_mark_addr);
+ idetape_position_tape(drive, last_mark_addr, 0, 0);
+ if (!idetape_queue_rw_tail (drive, IDETAPE_WRITE_RQ, 1, stage->bh)) {
+ printk(KERN_INFO "ide-tape: %s: couldn't write back marker frame at %d\n", tape->name, last_mark_addr);
+ __idetape_kfree_stage (stage);
+ idetape_position_tape(drive, position, 0, 0);
+ return;
+ }
+ __idetape_kfree_stage (stage);
+ idetape_flush_tape_buffers (drive);
+ idetape_position_tape(drive, position, 0, 0);
+ return;
+}
+
+static void __idetape_write_header (ide_drive_t *drive, int block, int cnt)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ idetape_stage_t *stage;
+ os_header_t header;
+
+ stage = __idetape_kmalloc_stage(tape, 1, 1);
+ if (stage == NULL)
+ return;
+ idetape_init_stage(drive, stage, OS_FRAME_TYPE_HEADER, tape->logical_blk_num);
+ idetape_wait_ready(drive, 60 * 5 * HZ);
+ idetape_position_tape(drive, block, 0, 0);
+ memset(&header, 0, sizeof(header));
+ strcpy(header.ident_str, "ADR_SEQ");
+ header.major_rev = 1;
+ header.minor_rev = 2;
+ header.par_num = 1;
+ header.partition.partition_num = OS_DATA_PARTITION;
+ header.partition.par_desc_ver = OS_PARTITION_VERSION;
+ header.partition.first_frame_addr = htonl(0x14);
+ header.partition.last_frame_addr = htonl(19239 * 24);
+ header.partition.wrt_pass_cntr = htons(tape->wrt_pass_cntr);
+ header.partition.eod_frame_addr = htonl(tape->eod_frame_addr);
+ memcpy(stage->bh->b_data, &header, sizeof(header));
+ while (cnt--) {
+ if (!idetape_queue_rw_tail (drive, IDETAPE_WRITE_RQ, 1, stage->bh)) {
+ printk(KERN_INFO "ide-tape: %s: couldn't write header frame\n", tape->name);
+ __idetape_kfree_stage (stage);
+ return;
+ }
+ }
+ __idetape_kfree_stage (stage);
+ idetape_flush_tape_buffers (drive);
+}
+
+static void idetape_write_header (ide_drive_t *drive, int locate_eod)
+{
+ idetape_tape_t *tape = drive->driver_data;
+
+#if ONSTREAM_DEBUG
+ if (tape->debug_level >= 2)
+ printk(KERN_INFO "ide-tape: %s: writing tape header\n", tape->name);
+#endif
+ if (!tape->onstream || tape->raw)
+ return;
+ tape->update_frame_cntr++;
+ __idetape_write_header(drive, 5, 5);
+ __idetape_write_header(drive, 0xbae, 5);
+ if (locate_eod) {
+#if ONSTREAM_DEBUG
+ if (tape->debug_level >= 2)
+ printk(KERN_INFO "ide-tape: %s: locating back to eod frame addr %d\n", tape->name, tape->eod_frame_addr);
+#endif
+ idetape_position_tape(drive, tape->eod_frame_addr, 0, 0);
+ }
+}
+
+static ssize_t idetape_chrdev_write (struct file *file, const char *buf,
+ size_t count, loff_t *ppos)
+{
+ struct inode *inode = file->f_dentry->d_inode;
+ ide_drive_t *drive = get_drive_ptr (inode->i_rdev);
+ idetape_tape_t *tape = drive->driver_data;
+ ssize_t retval,actually_written=0;
+ int position;
+
+ if (ppos != &file->f_pos) {
+ /* "A request was outside the capabilities of the device." */
+ return -ENXIO;
+ }
+ if (tape->onstream && (count != tape->tape_block_size)) {
+ printk(KERN_ERR "ide-tape: %s: use %d bytes as block size (%Zd used)\n", tape->name, tape->tape_block_size, count);
+ return -EINVAL;
+ }
+#if IDETAPE_DEBUG_LOG
+ if (tape->debug_level >= 3)
+ printk (KERN_INFO "ide-tape: Reached idetape_chrdev_write, count %Zd\n", count);
+#endif /* IDETAPE_DEBUG_LOG */
+
+ if (tape->chrdev_direction != idetape_direction_write) { /* Initialize write operation */
+ if (tape->chrdev_direction == idetape_direction_read)
+ idetape_discard_read_pipeline (drive, 1);
+#if IDETAPE_DEBUG_BUGS
+ if (tape->merge_stage || tape->merge_stage_size) {
+ printk (KERN_ERR "ide-tape: merge_stage_size should be 0 now\n");
+ tape->merge_stage_size = 0;
+ }
+#endif /* IDETAPE_DEBUG_BUGS */
+ if ((tape->merge_stage = __idetape_kmalloc_stage (tape, 0, 0)) == NULL)
+ return -ENOMEM;
+ tape->chrdev_direction = idetape_direction_write;
+ idetape_init_merge_stage (tape);
+
+ if (tape->onstream) {
+ position = idetape_read_position(drive);
+ if (position <= 20) {
+ tape->logical_blk_num = 0;
+ tape->wrt_pass_cntr++;
+#if ONSTREAM_DEBUG
+ if (tape->debug_level >= 2)
+ printk(KERN_INFO "ide-tape: %s: logical block num 0, setting eod to 20\n", tape->name);
+ if (tape->debug_level >= 2)
+ printk(KERN_INFO "ide-tape: %s: allocating new write pass counter %d\n", tape->name, tape->wrt_pass_cntr);
+#endif
+ tape->filemark_cnt = 0;
+ tape->eod_frame_addr = 20;
+ tape->first_mark_addr = tape->last_mark_addr = -1;
+ idetape_write_header(drive, 1);
+ }
+#if ONSTREAM_DEBUG
+ if (tape->debug_level >= 2)
+ printk(KERN_INFO "ide-tape: %s: positioning tape to eod at %d\n", tape->name, tape->eod_frame_addr);
+#endif
+ position = idetape_read_position(drive);
+ if (position != tape->eod_frame_addr)
+ idetape_position_tape(drive, tape->eod_frame_addr, 0, 0);
+#if ONSTREAM_DEBUG
+ if (tape->debug_level >= 2)
+ printk(KERN_INFO "ide-tape: %s: first_frame_position %d\n", tape->name, tape->first_frame_position);
+#endif
+ }
+
+ /*
+ * Issue a write 0 command to ensure that DSC handshake
+ * is switched from completion mode to buffer available
+ * mode.
+ */
+ retval = idetape_queue_rw_tail (drive, IDETAPE_WRITE_RQ, 0, tape->merge_stage->bh);
+ if (retval < 0) {
+ kfree (tape->merge_stage);
+ tape->merge_stage = NULL;
+ tape->chrdev_direction = idetape_direction_none;
+ return retval;
+ }
+#if ONSTREAM_DEBUG
+ if (tape->debug_level >= 2)
+ printk("ide-tape: first_frame_position %d\n", tape->first_frame_position);
+#endif
+ }
+ if (count==0)
+ return (0);
+ if (tape->restart_speed_control_req)
+ idetape_restart_speed_control(drive);
+ if (tape->merge_stage_size) {
+#if IDETAPE_DEBUG_BUGS
+ if (tape->merge_stage_size >= tape->stage_size) {
+ printk (KERN_ERR "ide-tape: bug: merge buffer too big\n");
+ tape->merge_stage_size=0;
+ }
+#endif /* IDETAPE_DEBUG_BUGS */
+ actually_written=IDE_MIN (tape->stage_size-tape->merge_stage_size,count);
+ idetape_copy_stage_from_user (tape, tape->merge_stage, buf, actually_written);
+ buf+=actually_written;tape->merge_stage_size+=actually_written;count-=actually_written;
+
+ if (tape->merge_stage_size == tape->stage_size) {
+ tape->merge_stage_size = 0;
+ retval=idetape_add_chrdev_write_request (drive, tape->capabilities.ctl);
+ if (retval <= 0)
+ return (retval);
+ }
+ }
+ while (count >= tape->stage_size) {
+ idetape_copy_stage_from_user (tape, tape->merge_stage, buf, tape->stage_size);
+ buf+=tape->stage_size;count-=tape->stage_size;
+ retval=idetape_add_chrdev_write_request (drive, tape->capabilities.ctl);
+ actually_written+=tape->stage_size;
+ if (retval <= 0)
+ return (retval);
+ }
+ if (count) {
+ actually_written+=count;
+ idetape_copy_stage_from_user (tape, tape->merge_stage, buf, count);
+ tape->merge_stage_size+=count;
+ }
+ return (actually_written);
+}
+
+static int idetape_write_filemark (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ int last_mark_addr;
+ idetape_pc_t pc;
+
+ if (!tape->onstream) {
+ idetape_create_write_filemark_cmd(drive, &pc,1); /* Write a filemark */
+ if (idetape_queue_pc_tail (drive,&pc)) {
+ printk (KERN_ERR "ide-tape: Couldn't write a filemark\n");
+ return -EIO;
+ }
+ } else if (!tape->raw) {
+ last_mark_addr = idetape_read_position(drive);
+ tape->merge_stage = __idetape_kmalloc_stage (tape, 1, 0);
+ if (tape->merge_stage != NULL) {
+ idetape_init_stage(drive, tape->merge_stage, OS_FRAME_TYPE_MARKER, tape->logical_blk_num);
+ idetape_pad_zeros (drive, tape->stage_size);
+ tape->logical_blk_num++;
+ __idetape_kfree_stage (tape->merge_stage);
+ tape->merge_stage = NULL;
+ }
+ if (tape->filemark_cnt)
+ idetape_update_last_marker(drive, tape->last_mark_addr, last_mark_addr);
+ tape->last_mark_addr = last_mark_addr;
+ if (tape->filemark_cnt++ == 0)
+ tape->first_mark_addr = last_mark_addr;
+ }
+ return 0;
+}
+
+static void idetape_write_eod (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+
+ if (!tape->onstream || tape->raw)
+ return;
+ tape->merge_stage = __idetape_kmalloc_stage (tape, 1, 0);
+ if (tape->merge_stage != NULL) {
+ tape->eod_frame_addr = idetape_read_position(drive);
+ idetape_init_stage(drive, tape->merge_stage, OS_FRAME_TYPE_EOD, tape->logical_blk_num);
+ idetape_pad_zeros (drive, tape->stage_size);
+ __idetape_kfree_stage (tape->merge_stage);
+ tape->merge_stage = NULL;
+ }
+ return;
+}
+
+int idetape_seek_logical_blk (ide_drive_t *drive, int logical_blk_num)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ int estimated_address = logical_blk_num + 20;
+ int retries = 0;
+ int speed_control;
+
+ speed_control = tape->speed_control;
+ tape->speed_control = 0;
+ if (logical_blk_num < 0)
+ logical_blk_num = 0;
+ if (idetape_get_logical_blk(drive, logical_blk_num, 10, 1))
+ goto ok;
+ while (++retries < 10) {
+ idetape_discard_read_pipeline(drive, 0);
+ idetape_position_tape(drive, estimated_address, 0, 0);
+ if (idetape_get_logical_blk(drive, logical_blk_num, 10, 1))
+ goto ok;
+ if (!idetape_get_logical_blk(drive, -1, 10, 1))
+ goto error;
+ if (tape->logical_blk_num < logical_blk_num)
+ estimated_address += logical_blk_num - tape->logical_blk_num;
+ else
+ break;
+ }
+error:
+ tape->speed_control = speed_control;
+ tape->restart_speed_control_req = 1;
+ printk(KERN_INFO "ide-tape: %s: couldn't seek to logical block %d (at %d), %d retries\n", tape->name, logical_blk_num, tape->logical_blk_num, retries);
+ return -EIO;
+ok:
+ tape->speed_control = speed_control;
+ tape->restart_speed_control_req = 1;
+ return 0;
+}
+
+/*
+ * idetape_mtioctop is called from idetape_chrdev_ioctl when
+ * the general mtio MTIOCTOP ioctl is requested.
+ *
+ * We currently support the following mtio.h operations:
+ *
+ * MTFSF - Space over mt_count filemarks in the positive direction.
+ * The tape is positioned after the last spaced filemark.
+ *
+ * MTFSFM - Same as MTFSF, but the tape is positioned before the
+ * last filemark.
+ *
+ * MTBSF - Steps background over mt_count filemarks, tape is
+ * positioned before the last filemark.
+ *
+ * MTBSFM - Like MTBSF, only tape is positioned after the last filemark.
+ *
+ * Note:
+ *
+ * MTBSF and MTBSFM are not supported when the tape doesn't
+ * supports spacing over filemarks in the reverse direction.
+ * In this case, MTFSFM is also usually not supported (it is
+ * supported in the rare case in which we crossed the filemark
+ * during our read-ahead pipelined operation mode).
+ *
+ * MTWEOF - Writes mt_count filemarks. Tape is positioned after
+ * the last written filemark.
+ *
+ * MTREW - Rewinds tape.
+ *
+ * MTLOAD - Loads the tape.
+ *
+ * MTOFFL - Puts the tape drive "Offline": Rewinds the tape and
+ * MTUNLOAD prevents further access until the media is replaced.
+ *
+ * MTNOP - Flushes tape buffers.
+ *
+ * MTRETEN - Retension media. This typically consists of one end
+ * to end pass on the media.
+ *
+ * MTEOM - Moves to the end of recorded data.
+ *
+ * MTERASE - Erases tape.
+ *
+ * MTSETBLK - Sets the user block size to mt_count bytes. If
+ * mt_count is 0, we will attempt to autodetect
+ * the block size.
+ *
+ * MTSEEK - Positions the tape in a specific block number, where
+ * each block is assumed to contain which user_block_size
+ * bytes.
+ *
+ * MTSETPART - Switches to another tape partition.
+ *
+ * MTLOCK - Locks the tape door.
+ *
+ * MTUNLOCK - Unlocks the tape door.
+ *
+ * The following commands are currently not supported:
+ *
+ * MTFSS, MTBSS, MTWSM, MTSETDENSITY,
+ * MTSETDRVBUFFER, MT_ST_BOOLEANS, MT_ST_WRITE_THRESHOLD.
+ */
+static int idetape_mtioctop (ide_drive_t *drive,short mt_op,int mt_count)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ idetape_pc_t pc;
+ int i,retval;
+
+#if IDETAPE_DEBUG_LOG
+ if (tape->debug_level >= 1)
+ printk (KERN_INFO "ide-tape: Handling MTIOCTOP ioctl: mt_op=%d, mt_count=%d\n",mt_op,mt_count);
+#endif /* IDETAPE_DEBUG_LOG */
+ /*
+ * Commands which need our pipelined read-ahead stages.
+ */
+ switch (mt_op) {
+ case MTFSF:
+ case MTFSFM:
+ case MTBSF:
+ case MTBSFM:
+ if (!mt_count)
+ return (0);
+ return (idetape_space_over_filemarks (drive,mt_op,mt_count));
+ default:
+ break;
+ }
+ switch (mt_op) {
+ case MTWEOF:
+ idetape_discard_read_pipeline (drive, 1);
+ for (i = 0; i < mt_count; i++) {
+ retval = idetape_write_filemark(drive);
+ if (retval) return retval;
+ }
+ return (0);
+ case MTREW:
+ idetape_discard_read_pipeline (drive, 0);
+ if (idetape_rewind_tape(drive))
+ return -EIO;
+ if (tape->onstream && !tape->raw)
+ return idetape_position_tape(drive, 20, 0, 0);
+ return 0;
+ case MTLOAD:
+ idetape_discard_read_pipeline (drive, 0);
+ idetape_create_load_unload_cmd (drive, &pc, IDETAPE_LU_LOAD_MASK);
+ return (idetape_queue_pc_tail (drive,&pc));
+ case MTUNLOAD:
+ case MTOFFL:
+ idetape_discard_read_pipeline (drive, 0);
+ idetape_create_load_unload_cmd (drive, &pc,!IDETAPE_LU_LOAD_MASK);
+ return (idetape_queue_pc_tail (drive,&pc));
+ case MTNOP:
+ idetape_discard_read_pipeline (drive, 0);
+ return (idetape_flush_tape_buffers (drive));
+ case MTRETEN:
+ idetape_discard_read_pipeline (drive, 0);
+ idetape_create_load_unload_cmd (drive, &pc,IDETAPE_LU_RETENSION_MASK | IDETAPE_LU_LOAD_MASK);
+ return (idetape_queue_pc_tail (drive,&pc));
+ case MTEOM:
+ if (tape->onstream) {
+#if ONSTREAM_DEBUG
+ if (tape->debug_level >= 2)
+ printk(KERN_INFO "ide-tape: %s: positioning tape to eod at %d\n", tape->name, tape->eod_frame_addr);
+#endif
+ idetape_position_tape(drive, tape->eod_frame_addr, 0, 0);
+ if (!idetape_get_logical_blk(drive, -1, 10, 0))
+ return -EIO;
+ if (tape->first_stage->aux->frame_type != OS_FRAME_TYPE_EOD)
+ return -EIO;
+ return 0;
+ }
+ idetape_create_space_cmd (&pc,0,IDETAPE_SPACE_TO_EOD);
+ return (idetape_queue_pc_tail (drive,&pc));
+ case MTERASE:
+ if (tape->onstream) {
+ tape->eod_frame_addr = 20;
+ tape->logical_blk_num = 0;
+ tape->first_mark_addr = tape->last_mark_addr = -1;
+ idetape_position_tape(drive, tape->eod_frame_addr, 0, 0);
+ idetape_write_eod(drive);
+ idetape_flush_tape_buffers (drive);
+ idetape_write_header(drive, 0);
+ idetape_flush_tape_buffers (drive);
+ (void) idetape_rewind_tape (drive);
+ return 0;
+ }
+ (void) idetape_rewind_tape (drive);
+ idetape_create_erase_cmd (&pc);
+ return (idetape_queue_pc_tail (drive,&pc));
+ case MTSETBLK:
+ if (tape->onstream) {
+ if (mt_count != tape->tape_block_size) {
+ printk(KERN_INFO "ide-tape: %s: MTSETBLK %d -- only %d bytes block size supported\n", tape->name, mt_count, tape->tape_block_size);
+ return -EINVAL;
+ }
+ return 0;
+ }
+ if (mt_count) {
+ if (mt_count < tape->tape_block_size || mt_count % tape->tape_block_size)
+ return -EIO;
+ tape->user_bs_factor = mt_count / tape->tape_block_size;
+ clear_bit (IDETAPE_DETECT_BS, &tape->flags);
+ } else
+ set_bit (IDETAPE_DETECT_BS, &tape->flags);
+ return 0;
+ case MTSEEK:
+ if (!tape->onstream || tape->raw) {
+ idetape_discard_read_pipeline (drive, 0);
+ return idetape_position_tape (drive, mt_count * tape->user_bs_factor, tape->partition, 0);
+ }
+ return idetape_seek_logical_blk(drive, mt_count);
+ case MTSETPART:
+ idetape_discard_read_pipeline (drive, 0);
+ if (tape->onstream)
+ return -EIO;
+ return (idetape_position_tape (drive, 0, mt_count, 0));
+ case MTFSR:
+ case MTBSR:
+ if (tape->onstream) {
+ if (!idetape_get_logical_blk(drive, -1, 10, 0))
+ return -EIO;
+ if (mt_op == MTFSR)
+ return idetape_seek_logical_blk(drive, tape->logical_blk_num + mt_count);
+ else {
+ idetape_discard_read_pipeline (drive, 0);
+ return idetape_seek_logical_blk(drive, tape->logical_blk_num - mt_count);
+ }
+ }
+ case MTLOCK:
+ idetape_create_prevent_cmd(drive, &pc, 1);
+ retval = idetape_queue_pc_tail (drive,&pc);
+ if (retval) return retval;
+ tape->door_locked = DOOR_EXPLICITLY_LOCKED;
+ return 0;
+ case MTUNLOCK:
+ idetape_create_prevent_cmd(drive, &pc, 0);
+ retval = idetape_queue_pc_tail (drive,&pc);
+ if (retval) return retval;
+ tape->door_locked = DOOR_UNLOCKED;
+ return 0;
+ default:
+ printk (KERN_ERR "ide-tape: MTIO operation %d not supported\n",mt_op);
+ return (-EIO);
+ }
+}
+
+/*
+ * Our character device ioctls.
+ *
+ * General mtio.h magnetic io commands are supported here, and not in
+ * the corresponding block interface.
+ *
+ * The following ioctls are supported:
+ *
+ * MTIOCTOP - Refer to idetape_mtioctop for detailed description.
+ *
+ * MTIOCGET - The mt_dsreg field in the returned mtget structure
+ * will be set to (user block size in bytes <<
+ * MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK.
+ *
+ * The mt_blkno is set to the current user block number.
+ * The other mtget fields are not supported.
+ *
+ * MTIOCPOS - The current tape "block position" is returned. We
+ * assume that each block contains user_block_size
+ * bytes.
+ *
+ * Our own ide-tape ioctls are supported on both interfaces.
+ */
+static int idetape_chrdev_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+ ide_drive_t *drive = get_drive_ptr (inode->i_rdev);
+ idetape_tape_t *tape = drive->driver_data;
+ struct mtop mtop;
+ struct mtget mtget;
+ struct mtpos mtpos;
+ int block_offset = 0, position = tape->first_frame_position;
+
+#if IDETAPE_DEBUG_LOG
+ if (tape->debug_level >= 3)
+ printk (KERN_INFO "ide-tape: Reached idetape_chrdev_ioctl, cmd=%u\n",cmd);
+#endif /* IDETAPE_DEBUG_LOG */
+
+ tape->restart_speed_control_req = 1;
+ if (tape->chrdev_direction == idetape_direction_write) {
+ idetape_empty_write_pipeline (drive);
+ idetape_flush_tape_buffers (drive);
+ }
+ if (cmd == MTIOCGET || cmd == MTIOCPOS) {
+ block_offset = idetape_pipeline_size (drive) / (tape->tape_block_size * tape->user_bs_factor);
+ if ((position = idetape_read_position(drive)) < 0)
+ return -EIO;
+ }
+ switch (cmd) {
+ case MTIOCTOP:
+ if (copy_from_user ((char *) &mtop, (char *) arg, sizeof (struct mtop)))
+ return -EFAULT;
+ return (idetape_mtioctop (drive,mtop.mt_op,mtop.mt_count));
+ case MTIOCGET:
+ memset (&mtget, 0, sizeof (struct mtget));
+ mtget.mt_type = MT_ISSCSI2;
+ if (!tape->onstream || tape->raw)
+ mtget.mt_blkno = position / tape->user_bs_factor - block_offset;
+ else {
+ if (!idetape_get_logical_blk(drive, -1, 10, 0))
+ mtget.mt_blkno = -1;
+ else
+ mtget.mt_blkno = tape->logical_blk_num;
+ }
+ mtget.mt_dsreg = ((tape->tape_block_size * tape->user_bs_factor) << MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK;
+ if (tape->onstream) {
+ mtget.mt_gstat |= GMT_ONLINE(0xffffffff);
+ if (tape->first_stage && tape->first_stage->aux->frame_type == OS_FRAME_TYPE_EOD)
+ mtget.mt_gstat |= GMT_EOD(0xffffffff);
+ if (position <= 20)
+ mtget.mt_gstat |= GMT_BOT(0xffffffff);
+ }
+ if (copy_to_user ((char *) arg,(char *) &mtget, sizeof (struct mtget)))
+ return -EFAULT;
+ return 0;
+ case MTIOCPOS:
+ if (tape->onstream && !tape->raw) {
+ if (!idetape_get_logical_blk(drive, -1, 10, 0))
+ return -EIO;
+ mtpos.mt_blkno = tape->logical_blk_num;
+ } else
+ mtpos.mt_blkno = position / tape->user_bs_factor - block_offset;
+ if (copy_to_user ((char *) arg,(char *) &mtpos, sizeof (struct mtpos)))
+ return -EFAULT;
+ return 0;
+ default:
+ if (tape->chrdev_direction == idetape_direction_read)
+ idetape_discard_read_pipeline (drive, 1);
+ return (idetape_blkdev_ioctl (drive,inode,file,cmd,arg));
+ }
+}
+
+static int __idetape_analyze_headers (ide_drive_t *drive, int block)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ idetape_stage_t *stage;
+ os_header_t *header;
+ os_aux_t *aux;
+
+ if (!tape->onstream || tape->raw) {
+ tape->header_ok = tape->linux_media = 1;
+ return 1;
+ }
+ tape->header_ok = tape->linux_media = 0;
+ tape->update_frame_cntr = 0;
+ tape->wrt_pass_cntr = 0;
+ tape->eod_frame_addr = 20;
+ tape->first_mark_addr = tape->last_mark_addr = -1;
+ stage = __idetape_kmalloc_stage (tape, 0, 0);
+ if (stage == NULL)
+ return 0;
+#if ONSTREAM_DEBUG
+ if (tape->debug_level >= 2)
+ printk(KERN_INFO "ide-tape: %s: reading header\n", tape->name);
+#endif
+ idetape_position_tape(drive, block, 0, 0);
+ if (!idetape_queue_rw_tail (drive, IDETAPE_READ_RQ, 1, stage->bh)) {
+ printk(KERN_INFO "ide-tape: %s: couldn't read header frame\n", tape->name);
+ __idetape_kfree_stage (stage);
+ return 0;
+ }
+ header = (os_header_t *) stage->bh->b_data;
+ aux = stage->aux;
+ if (strncmp(header->ident_str, "ADR_SEQ", 7) != 0) {
+ printk(KERN_INFO "ide-tape: %s: invalid header identification string\n", tape->name);
+ __idetape_kfree_stage (stage);
+ return 0;
+ }
+ if (header->major_rev != 1 || (header->minor_rev != 1 && header->minor_rev != 2))
+ printk(KERN_INFO "ide-tape: warning: revision %d.%d detected (1.1/1.2 supported)\n", header->major_rev, header->minor_rev);
+ if (header->par_num != 1)
+ printk(KERN_INFO "ide-tape: warning: %d partitions defined, only one supported\n", header->par_num);
+ tape->wrt_pass_cntr = ntohs(header->partition.wrt_pass_cntr);
+ tape->eod_frame_addr = ntohl(header->partition.eod_frame_addr);
+ tape->filemark_cnt = ntohl(aux->filemark_cnt);
+ tape->first_mark_addr = ntohl(aux->next_mark_addr);
+ tape->last_mark_addr = ntohl(aux->last_mark_addr);
+ tape->update_frame_cntr = ntohl(aux->update_frame_cntr);
+ memcpy(tape->application_sig, aux->application_sig, 4); tape->application_sig[4] = 0;
+ if (memcmp(tape->application_sig, "LIN", 3) == 0) {
+ tape->linux_media = 1;
+ tape->linux_media_version = tape->application_sig[3] - '0';
+ if (tape->linux_media_version != 3)
+ printk(KERN_INFO "ide-tape: %s: Linux media version %d detected (current 3)\n", tape->name, tape->linux_media_version);
+ } else {
+ printk(KERN_INFO "ide-tape: %s: non Linux media detected (%s)\n", tape->name, tape->application_sig);
+ tape->linux_media = 0;
+ }
+#if ONSTREAM_DEBUG
+ if (tape->debug_level >= 2)
+ printk(KERN_INFO "ide-tape: %s: detected write pass counter %d, eod frame addr %d\n", tape->name, tape->wrt_pass_cntr, tape->eod_frame_addr);
+#endif
+ __idetape_kfree_stage (stage);
+ return 1;
+}
+
+static int idetape_analyze_headers (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ int position, block;
+
+ if (!tape->onstream || tape->raw) {
+ tape->header_ok = tape->linux_media = 1;
+ return 1;
+ }
+ tape->header_ok = tape->linux_media = 0;
+ position = idetape_read_position(drive);
+ for (block = 5; block < 10; block++)
+ if (__idetape_analyze_headers(drive, block))
+ goto ok;
+#if 0
+ for (block = 0xbae; block < 0xbb8; block++)
+#else
+ for (block = 0xbae; block < 0xbb3; block++)
+#endif
+ if (__idetape_analyze_headers(drive, block))
+ goto ok;
+ printk(KERN_ERR "ide-tape: %s: failed to find valid ADRL header\n", tape->name);
+ return 0;
+ok:
+ if (position < 20)
+ position = 20;
+ idetape_position_tape(drive, position, 0, 0);
+ tape->header_ok = 1;
+ return 1;
+}
+
+/*
+ * Our character device open function.
+ */
+static int idetape_chrdev_open (struct inode *inode, struct file *filp)
+{
+ ide_drive_t *drive;
+ idetape_tape_t *tape;
+ idetape_pc_t pc;
+ unsigned int minor=MINOR (inode->i_rdev);
+
+#if IDETAPE_DEBUG_LOG
+ printk (KERN_INFO "ide-tape: Reached idetape_chrdev_open\n");
+#endif /* IDETAPE_DEBUG_LOG */
+
+ if ((drive = get_drive_ptr (inode->i_rdev)) == NULL)
+ return -ENXIO;
+ tape = drive->driver_data;
+
+ if (test_and_set_bit (IDETAPE_BUSY, &tape->flags))
+ return -EBUSY;
+ MOD_INC_USE_COUNT;
+#if ONSTREAM_DEBUG
+ if (tape->debug_level >= 6)
+ printk(KERN_INFO "ide-tape: MOD_INC_USE_COUNT in idetape_chrdev_open-1\n");
+#endif
+ if (!tape->onstream) {
+ idetape_read_position(drive);
+ if (!test_bit (IDETAPE_ADDRESS_VALID, &tape->flags))
+ (void) idetape_rewind_tape (drive);
+ } else {
+ if (minor & 64) {
+ tape->tape_block_size = tape->stage_size = 32768 + 512;
+ tape->raw = 1;
+ } else {
+ tape->tape_block_size = tape->stage_size = 32768;
+ tape->raw = 0;
+ }
+ }
+ if (idetape_wait_ready(drive, 60 * HZ)) {
+ clear_bit(IDETAPE_BUSY, &tape->flags);
+ MOD_DEC_USE_COUNT;
+#if ONSTREAM_DEBUG
+ if (tape->debug_level >= 6)
+ printk(KERN_INFO "ide-tape: MOD_DEC_USE_COUNT in idetape_chrdev_open-1\n");
+#endif
+ printk(KERN_ERR "ide-tape: %s: drive not ready\n", tape->name);
+ return -EBUSY;
+ }
+ idetape_read_position(drive);
+ MOD_DEC_USE_COUNT;
+#if ONSTREAM_DEBUG
+ if (tape->debug_level >= 6)
+ printk(KERN_INFO "ide-tape: MOD_DEC_USE_COUNT in idetape_chrdev_open-2\n");
+#endif
+ clear_bit (IDETAPE_PIPELINE_ERROR, &tape->flags);
+
+ if (tape->chrdev_direction == idetape_direction_none) {
+ MOD_INC_USE_COUNT;
+#if ONSTREAM_DEBUG
+ if (tape->debug_level >= 6)
+ printk(KERN_INFO "ide-tape: MOD_INC_USE_COUNT in idetape_chrdev_open-2\n");
+#endif
+ idetape_create_prevent_cmd(drive, &pc, 1);
+ if (!idetape_queue_pc_tail (drive,&pc)) {
+ if (tape->door_locked != DOOR_EXPLICITLY_LOCKED)
+ tape->door_locked = DOOR_LOCKED;
+ }
+ idetape_analyze_headers(drive);
+ }
+ tape->max_frames = tape->cur_frames = tape->req_buffer_fill = 0;
+ idetape_restart_speed_control(drive);
+ tape->restart_speed_control_req = 0;
+ return 0;
+}
+
+/*
+ * Our character device release function.
+ */
+static int idetape_chrdev_release (struct inode *inode, struct file *filp)
+{
+ ide_drive_t *drive = get_drive_ptr (inode->i_rdev);
+ idetape_tape_t *tape = drive->driver_data;
+ idetape_pc_t pc;
+ unsigned int minor=MINOR (inode->i_rdev);
+
+#if IDETAPE_DEBUG_LOG
+ if (tape->debug_level >= 3)
+ printk (KERN_INFO "ide-tape: Reached idetape_chrdev_release\n");
+#endif /* IDETAPE_DEBUG_LOG */
+
+ if (tape->chrdev_direction == idetape_direction_write) {
+ idetape_empty_write_pipeline (drive);
+ tape->merge_stage = __idetape_kmalloc_stage (tape, 1, 0);
+ if (tape->merge_stage != NULL) {
+ idetape_pad_zeros (drive, tape->tape_block_size * (tape->user_bs_factor - 1));
+ __idetape_kfree_stage (tape->merge_stage);
+ tape->merge_stage = NULL;
+ }
+ idetape_write_filemark(drive);
+ idetape_write_eod(drive);
+ idetape_flush_tape_buffers (drive);
+ idetape_write_header(drive, minor >= 128);
+ idetape_flush_tape_buffers (drive);
+ }
+ if (tape->chrdev_direction == idetape_direction_read) {
+ if (minor < 128)
+ idetape_discard_read_pipeline (drive, 1);
+ else
+ idetape_wait_for_pipeline (drive);
+ }
+ if (tape->cache_stage != NULL) {
+ __idetape_kfree_stage (tape->cache_stage);
+ tape->cache_stage = NULL;
+ }
+ if (minor < 128)
+ (void) idetape_rewind_tape (drive);
+ if (tape->chrdev_direction == idetape_direction_none) {
+ if (tape->door_locked != DOOR_EXPLICITLY_LOCKED) {
+ idetape_create_prevent_cmd(drive, &pc, 0);
+ if (!idetape_queue_pc_tail (drive,&pc))
+ tape->door_locked = DOOR_UNLOCKED;
+ }
+ MOD_DEC_USE_COUNT;
+#if ONSTREAM_DEBUG
+ if (tape->debug_level >= 6)
+ printk(KERN_INFO "ide-tape: MOD_DEC_USE_COUNT in idetape_chrdev_release\n");
+#endif
+ }
+ clear_bit (IDETAPE_BUSY, &tape->flags);
+ return 0;
+}
+
+/*
+ * idetape_identify_device is called to check the contents of the
+ * ATAPI IDENTIFY command results. We return:
+ *
+ * 1 If the tape can be supported by us, based on the information
+ * we have so far.
+ *
+ * 0 If this tape driver is not currently supported by us.
+ */
+static int idetape_identify_device (ide_drive_t *drive,struct hd_driveid *id)
+{
+ struct idetape_id_gcw gcw;
+#if IDETAPE_DEBUG_INFO
+ unsigned short mask,i;
+#endif /* IDETAPE_DEBUG_INFO */
+
+ if (!id)
+ return 0;
+
+ *((unsigned short *) &gcw) = id->config;
+
+#if IDETAPE_DEBUG_INFO
+ printk (KERN_INFO "ide-tape: Dumping ATAPI Identify Device tape parameters\n");
+ printk (KERN_INFO "ide-tape: Protocol Type: ");
+ switch (gcw.protocol) {
+ case 0: case 1: printk (KERN_INFO "ATA\n");break;
+ case 2: printk (KERN_INFO "ATAPI\n");break;
+ case 3: printk (KERN_INFO "Reserved (Unknown to ide-tape)\n");break;
+ }
+ printk (KERN_INFO "ide-tape: Device Type: %x - ",gcw.device_type);
+ switch (gcw.device_type) {
+ case 0: printk (KERN_INFO "Direct-access Device\n");break;
+ case 1: printk (KERN_INFO "Streaming Tape Device\n");break;
+ case 2: case 3: case 4: printk (KERN_INFO "Reserved\n");break;
+ case 5: printk (KERN_INFO "CD-ROM Device\n");break;
+ case 6: printk (KERN_INFO "Reserved\n");
+ case 7: printk (KERN_INFO "Optical memory Device\n");break;
+ case 0x1f: printk (KERN_INFO "Unknown or no Device type\n");break;
+ default: printk (KERN_INFO "Reserved\n");
+ }
+ printk (KERN_INFO "ide-tape: Removable: %s",gcw.removable ? "Yes\n":"No\n");
+ printk (KERN_INFO "ide-tape: Command Packet DRQ Type: ");
+ switch (gcw.drq_type) {
+ case 0: printk (KERN_INFO "Microprocessor DRQ\n");break;
+ case 1: printk (KERN_INFO "Interrupt DRQ\n");break;
+ case 2: printk (KERN_INFO "Accelerated DRQ\n");break;
+ case 3: printk (KERN_INFO "Reserved\n");break;
+ }
+ printk (KERN_INFO "ide-tape: Command Packet Size: ");
+ switch (gcw.packet_size) {
+ case 0: printk (KERN_INFO "12 bytes\n");break;
+ case 1: printk (KERN_INFO "16 bytes\n");break;
+ default: printk (KERN_INFO "Reserved\n");break;
+ }
+ printk (KERN_INFO "ide-tape: Model: %.40s\n",id->model);
+ printk (KERN_INFO "ide-tape: Firmware Revision: %.8s\n",id->fw_rev);
+ printk (KERN_INFO "ide-tape: Serial Number: %.20s\n",id->serial_no);
+ printk (KERN_INFO "ide-tape: Write buffer size: %d bytes\n",id->buf_size*512);
+ printk (KERN_INFO "ide-tape: DMA: %s",id->capability & 0x01 ? "Yes\n":"No\n");
+ printk (KERN_INFO "ide-tape: LBA: %s",id->capability & 0x02 ? "Yes\n":"No\n");
+ printk (KERN_INFO "ide-tape: IORDY can be disabled: %s",id->capability & 0x04 ? "Yes\n":"No\n");
+ printk (KERN_INFO "ide-tape: IORDY supported: %s",id->capability & 0x08 ? "Yes\n":"Unknown\n");
+ printk (KERN_INFO "ide-tape: ATAPI overlap supported: %s",id->capability & 0x20 ? "Yes\n":"No\n");
+ printk (KERN_INFO "ide-tape: PIO Cycle Timing Category: %d\n",id->tPIO);
+ printk (KERN_INFO "ide-tape: DMA Cycle Timing Category: %d\n",id->tDMA);
+ printk (KERN_INFO "ide-tape: Single Word DMA supported modes: ");
+ for (i=0,mask=1;i<8;i++,mask=mask << 1) {
+ if (id->dma_1word & mask)
+ printk (KERN_INFO "%d ",i);
+ if (id->dma_1word & (mask << 8))
+ printk (KERN_INFO "(active) ");
+ }
+ printk (KERN_INFO "\n");
+ printk (KERN_INFO "ide-tape: Multi Word DMA supported modes: ");
+ for (i=0,mask=1;i<8;i++,mask=mask << 1) {
+ if (id->dma_mword & mask)
+ printk (KERN_INFO "%d ",i);
+ if (id->dma_mword & (mask << 8))
+ printk (KERN_INFO "(active) ");
+ }
+ printk (KERN_INFO "\n");
+ if (id->field_valid & 0x0002) {
+ printk (KERN_INFO "ide-tape: Enhanced PIO Modes: %s\n",id->eide_pio_modes & 1 ? "Mode 3":"None");
+ printk (KERN_INFO "ide-tape: Minimum Multi-word DMA cycle per word: ");
+ if (id->eide_dma_min == 0)
+ printk (KERN_INFO "Not supported\n");
+ else
+ printk (KERN_INFO "%d ns\n",id->eide_dma_min);
+
+ printk (KERN_INFO "ide-tape: Manufacturer\'s Recommended Multi-word cycle: ");
+ if (id->eide_dma_time == 0)
+ printk (KERN_INFO "Not supported\n");
+ else
+ printk (KERN_INFO "%d ns\n",id->eide_dma_time);
+
+ printk (KERN_INFO "ide-tape: Minimum PIO cycle without IORDY: ");
+ if (id->eide_pio == 0)
+ printk (KERN_INFO "Not supported\n");
+ else
+ printk (KERN_INFO "%d ns\n",id->eide_pio);
+
+ printk (KERN_INFO "ide-tape: Minimum PIO cycle with IORDY: ");
+ if (id->eide_pio_iordy == 0)
+ printk (KERN_INFO "Not supported\n");
+ else
+ printk (KERN_INFO "%d ns\n",id->eide_pio_iordy);
+
+ } else
+ printk (KERN_INFO "ide-tape: According to the device, fields 64-70 are not valid.\n");
+#endif /* IDETAPE_DEBUG_INFO */
+
+ /* Check that we can support this device */
+
+ if (gcw.protocol !=2 )
+ printk (KERN_ERR "ide-tape: Protocol is not ATAPI\n");
+ else if (gcw.device_type != 1)
+ printk (KERN_ERR "ide-tape: Device type is not set to tape\n");
+ else if (!gcw.removable)
+ printk (KERN_ERR "ide-tape: The removable flag is not set\n");
+ else if (gcw.packet_size != 0) {
+ printk (KERN_ERR "ide-tape: Packet size is not 12 bytes long\n");
+ if (gcw.packet_size == 1)
+ printk (KERN_ERR "ide-tape: Sorry, padding to 16 bytes is still not supported\n");
+ } else
+ return 1;
+ return 0;
+}
+
+/*
+ * Notify vendor ID to the OnStream tape drive
+ */
+static void idetape_onstream_set_vendor (ide_drive_t *drive, char *vendor)
+{
+ idetape_pc_t pc;
+ idetape_mode_parameter_header_t *header;
+
+ idetape_create_mode_select_cmd(&pc, sizeof(*header) + 8);
+ pc.buffer[0] = 3 + 8; /* Mode Data Length */
+ pc.buffer[1] = 0; /* Medium Type - ignoring */
+ pc.buffer[2] = 0; /* Reserved */
+ pc.buffer[3] = 0; /* Block Descriptor Length */
+ pc.buffer[4 + 0] = 0x36 | (1 << 7);
+ pc.buffer[4 + 1] = 6;
+ pc.buffer[4 + 2] = vendor[0];
+ pc.buffer[4 + 3] = vendor[1];
+ pc.buffer[4 + 4] = vendor[2];
+ pc.buffer[4 + 5] = vendor[3];
+ pc.buffer[4 + 6] = 0;
+ pc.buffer[4 + 7] = 0;
+ if (idetape_queue_pc_tail (drive,&pc))
+ printk (KERN_ERR "ide-tape: Couldn't set vendor name to %s\n", vendor);
+
+}
+
+/*
+ * Various unused OnStream commands
+ */
+#if ONSTREAM_DEBUG
+static void idetape_onstream_set_retries (ide_drive_t *drive, int retries)
+{
+ idetape_pc_t pc;
+
+ idetape_create_mode_select_cmd(&pc, sizeof(idetape_mode_parameter_header_t) + 4);
+ pc.buffer[0] = 3 + 4;
+ pc.buffer[1] = 0; /* Medium Type - ignoring */
+ pc.buffer[2] = 0; /* Reserved */
+ pc.buffer[3] = 0; /* Block Descriptor Length */
+ pc.buffer[4 + 0] = 0x2f | (1 << 7);
+ pc.buffer[4 + 1] = 2;
+ pc.buffer[4 + 2] = 4;
+ pc.buffer[4 + 3] = retries;
+ if (idetape_queue_pc_tail (drive,&pc))
+ printk (KERN_ERR "ide-tape: Couldn't set retries to %d\n", retries);
+}
+#endif
+
+/*
+ * Configure 32.5KB block size.
+ */
+static void idetape_onstream_configure_block_size (ide_drive_t *drive)
+{
+ idetape_pc_t pc;
+ idetape_mode_parameter_header_t *header;
+ idetape_block_size_page_t *bs;
+
+ /*
+ * Get the current block size from the block size mode page
+ */
+ idetape_create_mode_sense_cmd (&pc,IDETAPE_BLOCK_SIZE_PAGE);
+ if (idetape_queue_pc_tail (drive,&pc))
+ printk (KERN_ERR "ide-tape: can't get tape block size mode page\n");
+ header = (idetape_mode_parameter_header_t *) pc.buffer;
+ bs = (idetape_block_size_page_t *) (pc.buffer + sizeof(idetape_mode_parameter_header_t) + header->bdl);
+
+#if IDETAPE_DEBUG_INFO
+ printk(KERN_INFO "ide-tape: 32KB play back: %s\n", bs->play32 ? "Yes" : "No");
+ printk(KERN_INFO "ide-tape: 32.5KB play back: %s\n", bs->play32_5 ? "Yes" : "No");
+ printk(KERN_INFO "ide-tape: 32KB record: %s\n", bs->record32 ? "Yes" : "No");
+ printk(KERN_INFO "ide-tape: 32.5KB record: %s\n", bs->record32_5 ? "Yes" : "No");
+#endif /* IDETAPE_DEBUG_INFO */
+
+ /*
+ * Configure default auto columns mode, 32.5KB block size
+ */
+ bs->one = 1;
+ bs->play32 = 0;
+ bs->play32_5 = 1;
+ bs->record32 = 0;
+ bs->record32_5 = 1;
+ idetape_create_mode_select_cmd(&pc, sizeof(*header) + sizeof(*bs));
+ if (idetape_queue_pc_tail (drive,&pc))
+ printk (KERN_ERR "ide-tape: Couldn't set tape block size mode page\n");
+
+#if ONSTREAM_DEBUG
+ /*
+ * In debug mode, we want to see as many errors as possible
+ * to test the error recovery mechanism.
+ */
+ idetape_onstream_set_retries(drive, 0);
+#endif
+}
+
+/*
+ * Use INQUIRY to get the firmware revision
+ */
+static void idetape_get_inquiry_results (ide_drive_t *drive)
+{
+ char *r;
+ idetape_tape_t *tape = drive->driver_data;
+ idetape_pc_t pc;
+ idetape_inquiry_result_t *inquiry;
+
+ idetape_create_inquiry_cmd(&pc);
+ if (idetape_queue_pc_tail (drive,&pc)) {
+ printk (KERN_ERR "ide-tape: %s: can't get INQUIRY results\n", tape->name);
+ return;
+ }
+ inquiry = (idetape_inquiry_result_t *) pc.buffer;
+ memcpy(tape->vendor_id, inquiry->vendor_id, 8);
+ memcpy(tape->product_id, inquiry->product_id, 16);
+ memcpy(tape->firmware_revision, inquiry->revision_level, 4);
+ ide_fixstring(tape->vendor_id, 10, 0);
+ ide_fixstring(tape->product_id, 18, 0);
+ ide_fixstring(tape->firmware_revision, 6, 0);
+ r = tape->firmware_revision;
+ if (*(r + 1) == '.')
+ tape->firmware_revision_num = (*r - '0') * 100 + (*(r + 2) - '0') * 10 + *(r + 3) - '0';
+ else if (tape->onstream)
+ tape->firmware_revision_num = (*r - '0') * 100 + (*(r + 1) - '0') * 10 + *(r + 2) - '0';
+ printk(KERN_INFO "ide-tape: %s <-> %s: %s %s rev %s\n", drive->name, tape->name, tape->vendor_id, tape->product_id, tape->firmware_revision);
+}
+
+/*
+ * Configure the OnStream ATAPI tape drive for default operation
+ */
+static void idetape_configure_onstream (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+
+ if (tape->firmware_revision_num < 105) {
+ printk(KERN_INFO "ide-tape: %s: Old OnStream firmware revision detected (%s)\n", tape->name, tape->firmware_revision);
+ printk(KERN_INFO "ide-tape: %s: An upgrade to version 1.05 or above is recommended\n", tape->name);
+ }
+
+ /*
+ * Configure 32.5KB (data+aux) block size.
+ */
+ idetape_onstream_configure_block_size(drive);
+
+ /*
+ * Set vendor name to 'LIN3' for "Linux support version 3".
+ */
+ idetape_onstream_set_vendor(drive, "LIN3");
+}
+
+/*
+ * idetape_get_mode_sense_results asks the tape about its various
+ * parameters. In particular, we will adjust our data transfer buffer
+ * size to the recommended value as returned by the tape.
+ */
+static void idetape_get_mode_sense_results (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ idetape_pc_t pc;
+ idetape_mode_parameter_header_t *header;
+ idetape_capabilities_page_t *capabilities;
+
+ idetape_create_mode_sense_cmd (&pc,IDETAPE_CAPABILITIES_PAGE);
+ if (idetape_queue_pc_tail (drive,&pc)) {
+ printk (KERN_ERR "ide-tape: Can't get tape parameters - assuming some default values\n");
+ tape->tape_block_size = 512; tape->capabilities.ctl = 52;
+ tape->capabilities.speed = 450; tape->capabilities.buffer_size = 6 * 52;
+ return;
+ }
+ header = (idetape_mode_parameter_header_t *) pc.buffer;
+ capabilities = (idetape_capabilities_page_t *) (pc.buffer + sizeof(idetape_mode_parameter_header_t) + header->bdl);
+
+ capabilities->max_speed = ntohs (capabilities->max_speed);
+ capabilities->ctl = ntohs (capabilities->ctl);
+ capabilities->speed = ntohs (capabilities->speed);
+ capabilities->buffer_size = ntohs (capabilities->buffer_size);
+
+ if (!capabilities->speed) {
+ printk(KERN_INFO "ide-tape: %s: overriding capabilities->speed (assuming 650KB/sec)\n", drive->name);
+ capabilities->speed = 650;
+ }
+ if (!capabilities->max_speed) {
+ printk(KERN_INFO "ide-tape: %s: overriding capabilities->max_speed (assuming 650KB/sec)\n", drive->name);
+ capabilities->max_speed = 650;
+ }
+
+ tape->capabilities = *capabilities; /* Save us a copy */
+ if (capabilities->blk512)
+ tape->tape_block_size = 512;
+ else if (capabilities->blk1024)
+ tape->tape_block_size = 1024;
+ else if (tape->onstream && capabilities->blk32768)
+ tape->tape_block_size = 32768;
+
+#if IDETAPE_DEBUG_INFO
+ printk (KERN_INFO "ide-tape: Dumping the results of the MODE SENSE packet command\n");
+ printk (KERN_INFO "ide-tape: Mode Parameter Header:\n");
+ printk (KERN_INFO "ide-tape: Mode Data Length - %d\n",header->mode_data_length);
+ printk (KERN_INFO "ide-tape: Medium Type - %d\n",header->medium_type);
+ printk (KERN_INFO "ide-tape: Device Specific Parameter - %d\n",header->dsp);
+ printk (KERN_INFO "ide-tape: Block Descriptor Length - %d\n",header->bdl);
+
+ printk (KERN_INFO "ide-tape: Capabilities and Mechanical Status Page:\n");
+ printk (KERN_INFO "ide-tape: Page code - %d\n",capabilities->page_code);
+ printk (KERN_INFO "ide-tape: Page length - %d\n",capabilities->page_length);
+ printk (KERN_INFO "ide-tape: Read only - %s\n",capabilities->ro ? "Yes":"No");
+ printk (KERN_INFO "ide-tape: Supports reverse space - %s\n",capabilities->sprev ? "Yes":"No");
+ printk (KERN_INFO "ide-tape: Supports erase initiated formatting - %s\n",capabilities->efmt ? "Yes":"No");
+ printk (KERN_INFO "ide-tape: Supports QFA two Partition format - %s\n",capabilities->qfa ? "Yes":"No");
+ printk (KERN_INFO "ide-tape: Supports locking the medium - %s\n",capabilities->lock ? "Yes":"No");
+ printk (KERN_INFO "ide-tape: The volume is currently locked - %s\n",capabilities->locked ? "Yes":"No");
+ printk (KERN_INFO "ide-tape: The device defaults in the prevent state - %s\n",capabilities->prevent ? "Yes":"No");
+ printk (KERN_INFO "ide-tape: Supports ejecting the medium - %s\n",capabilities->eject ? "Yes":"No");
+ printk (KERN_INFO "ide-tape: Supports error correction - %s\n",capabilities->ecc ? "Yes":"No");
+ printk (KERN_INFO "ide-tape: Supports data compression - %s\n",capabilities->cmprs ? "Yes":"No");
+ printk (KERN_INFO "ide-tape: Supports 512 bytes block size - %s\n",capabilities->blk512 ? "Yes":"No");
+ printk (KERN_INFO "ide-tape: Supports 1024 bytes block size - %s\n",capabilities->blk1024 ? "Yes":"No");
+ printk (KERN_INFO "ide-tape: Supports 32768 bytes block size / Restricted byte count for PIO transfers - %s\n",capabilities->blk32768 ? "Yes":"No");
+ printk (KERN_INFO "ide-tape: Maximum supported speed in KBps - %d\n",capabilities->max_speed);
+ printk (KERN_INFO "ide-tape: Continuous transfer limits in blocks - %d\n",capabilities->ctl);
+ printk (KERN_INFO "ide-tape: Current speed in KBps - %d\n",capabilities->speed);
+ printk (KERN_INFO "ide-tape: Buffer size - %d\n",capabilities->buffer_size*512);
+#endif /* IDETAPE_DEBUG_INFO */
+}
+
+static void idetape_add_settings (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+
+/*
+ * drive setting name read/write ioctl ioctl data type min max mul_factor div_factor data pointer set function
+ */
+ ide_add_setting(drive, "buffer", SETTING_READ, -1, -1, TYPE_SHORT, 0, 0xffff, 1, 2, &tape->capabilities.buffer_size, NULL);
+ ide_add_setting(drive, "pipeline_min", SETTING_RW, -1, -1, TYPE_INT, 2, 0xffff, tape->stage_size / 1024, 1, &tape->min_pipeline, NULL);
+ ide_add_setting(drive, "pipeline", SETTING_RW, -1, -1, TYPE_INT, 2, 0xffff, tape->stage_size / 1024, 1, &tape->max_stages, NULL);
+ ide_add_setting(drive, "pipeline_max", SETTING_RW, -1, -1, TYPE_INT, 2, 0xffff, tape->stage_size / 1024, 1, &tape->max_pipeline, NULL);
+ ide_add_setting(drive, "pipeline_used",SETTING_READ, -1, -1, TYPE_INT, 0, 0xffff, tape->stage_size / 1024, 1, &tape->nr_stages, NULL);
+ ide_add_setting(drive, "pipeline_pending",SETTING_READ,-1, -1, TYPE_INT, 0, 0xffff, tape->stage_size / 1024, 1, &tape->nr_pending_stages, NULL);
+ ide_add_setting(drive, "speed", SETTING_READ, -1, -1, TYPE_SHORT, 0, 0xffff, 1, 1, &tape->capabilities.speed, NULL);
+ ide_add_setting(drive, "stage", SETTING_READ, -1, -1, TYPE_INT, 0, 0xffff, 1, 1024, &tape->stage_size, NULL);
+ ide_add_setting(drive, "tdsc", SETTING_RW, -1, -1, TYPE_INT, IDETAPE_DSC_RW_MIN, IDETAPE_DSC_RW_MAX, 1000, HZ, &tape->best_dsc_rw_frequency, NULL);
+ ide_add_setting(drive, "dsc_overlap", SETTING_RW, -1, -1, TYPE_BYTE, 0, 1, 1, 1, &drive->dsc_overlap, NULL);
+ ide_add_setting(drive, "pipeline_head_speed_c",SETTING_READ, -1, -1, TYPE_INT, 0, 0xffff, 1, 1, &tape->controlled_pipeline_head_speed, NULL);
+ ide_add_setting(drive, "pipeline_head_speed_u",SETTING_READ, -1, -1, TYPE_INT, 0, 0xffff, 1, 1, &tape->uncontrolled_pipeline_head_speed, NULL);
+ ide_add_setting(drive, "avg_speed", SETTING_READ, -1, -1, TYPE_INT, 0, 0xffff, 1, 1, &tape->avg_speed, NULL);
+ if (tape->onstream) {
+ ide_add_setting(drive, "cur_frames", SETTING_READ, -1, -1, TYPE_SHORT, 0, 0xffff, 1, 1, &tape->cur_frames, NULL);
+ ide_add_setting(drive, "max_frames", SETTING_READ, -1, -1, TYPE_SHORT, 0, 0xffff, 1, 1, &tape->max_frames, NULL);
+ ide_add_setting(drive, "insert_speed", SETTING_READ, -1, -1, TYPE_INT, 0, 0xffff, 1, 1, &tape->insert_speed, NULL);
+ ide_add_setting(drive, "speed_control",SETTING_RW, -1, -1, TYPE_INT, 0, 0xffff, 1, 1, &tape->speed_control, NULL);
+ ide_add_setting(drive, "debug_level",SETTING_RW, -1, -1, TYPE_INT, 0, 0xffff, 1, 1, &tape->debug_level, NULL);
+ ide_add_setting(drive, "tape_still_time",SETTING_READ, -1, -1, TYPE_INT, 0, 0xffff, 1, 1, &tape->tape_still_time, NULL);
+ ide_add_setting(drive, "max_insert_speed",SETTING_RW, -1, -1, TYPE_INT, 0, 0xffff, 1, 1, &tape->max_insert_speed, NULL);
+ ide_add_setting(drive, "insert_size", SETTING_READ, -1, -1, TYPE_INT, 0, 0xffff, 1, 1, &tape->insert_size, NULL);
+ }
+}
+
+/*
+ * ide_setup is called to:
+ *
+ * 1. Initialize our various state variables.
+ * 2. Ask the tape for its capabilities.
+ * 3. Allocate a buffer which will be used for data
+ * transfer. The buffer size is chosen based on
+ * the recommendation which we received in step (2).
+ *
+ * Note that at this point ide.c already assigned us an irq, so that
+ * we can queue requests here and wait for their completion.
+ */
+static void idetape_setup (ide_drive_t *drive, idetape_tape_t *tape, int minor)
+{
+ unsigned long t1, tmid, tn, t;
+ int speed;
+ struct idetape_id_gcw gcw;
+ int stage_size;
+
+ memset (tape, 0, sizeof (idetape_tape_t));
+ spin_lock_init(&tape->spinlock);
+ drive->driver_data = tape;
+ drive->ready_stat = 0; /* An ATAPI device ignores DRDY */
+ if (strstr(drive->id->model, "OnStream DI-30"))
+ tape->onstream = 1;
+ drive->dsc_overlap = 1;
+#ifdef CONFIG_BLK_DEV_IDEPCI
+ if (!tape->onstream && HWIF(drive)->pci_dev != NULL) {
+ /*
+ * These two ide-pci host adapters appear to need DSC overlap disabled.
+ * This probably needs further analysis.
+ */
+ if ((HWIF(drive)->pci_dev->device == PCI_DEVICE_ID_ARTOP_ATP850UF) ||
+ (HWIF(drive)->pci_dev->device == PCI_DEVICE_ID_TTI_HPT343)) {
+ printk(KERN_INFO "ide-tape: %s: disabling DSC overlap\n", tape->name);
+ drive->dsc_overlap = 0;
+ }
+ }
+#endif /* CONFIG_BLK_DEV_IDEPCI */
+ tape->drive = drive;
+ tape->minor = minor;
+ tape->name[0] = 'h'; tape->name[1] = 't'; tape->name[2] = '0' + minor;
+ tape->chrdev_direction = idetape_direction_none;
+ tape->pc = tape->pc_stack;
+ tape->max_insert_speed = 10000;
+ tape->speed_control = 1;
+ *((unsigned short *) &gcw) = drive->id->config;
+ if (gcw.drq_type == 1)
+ set_bit(IDETAPE_DRQ_INTERRUPT, &tape->flags);
+
+ tape->min_pipeline = tape->max_pipeline = tape->max_stages = 10;
+
+ idetape_get_inquiry_results(drive);
+ idetape_get_mode_sense_results(drive);
+ if (tape->onstream)
+ idetape_configure_onstream(drive);
+
+ tape->user_bs_factor = 1;
+ tape->stage_size = tape->capabilities.ctl * tape->tape_block_size;
+ while (tape->stage_size > 0xffff) {
+ printk (KERN_NOTICE "ide-tape: decreasing stage size\n");
+ tape->capabilities.ctl /= 2;
+ tape->stage_size = tape->capabilities.ctl * tape->tape_block_size;
+ }
+ stage_size = tape->stage_size;
+ if (tape->onstream)
+ stage_size = 32768 + 512;
+ tape->pages_per_stage = stage_size / PAGE_SIZE;
+ if (stage_size % PAGE_SIZE) {
+ tape->pages_per_stage++;
+ tape->excess_bh_size = PAGE_SIZE - stage_size % PAGE_SIZE;
+ }
+
+ /*
+ * Select the "best" DSC read/write polling frequency
+ * and pipeline size.
+ */
+ speed = IDE_MAX (tape->capabilities.speed, tape->capabilities.max_speed);
+
+ tape->max_stages = speed * 1000 * 10 / tape->stage_size;
+ tape->min_pipeline = tape->max_stages;
+ tape->max_pipeline = tape->max_stages * 2;
+
+ t1 = (tape->stage_size * HZ) / (speed * 1000);
+ tmid = (tape->capabilities.buffer_size * 32 * HZ) / (speed * 125);
+ tn = (IDETAPE_FIFO_THRESHOLD * tape->stage_size * HZ) / (speed * 1000);
+
+ if (tape->max_stages)
+ t = tn;
+ else
+ t = t1;
+
+ /*
+ * Ensure that the number we got makes sense; limit
+ * it within IDETAPE_DSC_RW_MIN and IDETAPE_DSC_RW_MAX.
+ */
+ tape->best_dsc_rw_frequency = IDE_MAX (IDE_MIN (t, IDETAPE_DSC_RW_MAX), IDETAPE_DSC_RW_MIN);
+ printk (KERN_INFO "ide-tape: %s <-> %s: %dKBps, %d*%dkB buffer, %dkB pipeline, %lums tDSC%s\n",
+ drive->name, tape->name, tape->capabilities.speed, (tape->capabilities.buffer_size * 512) / tape->stage_size,
+ tape->stage_size / 1024, tape->max_stages * tape->stage_size / 1024,
+ tape->best_dsc_rw_frequency * 1000 / HZ, drive->using_dma ? ", DMA":"");
+
+ idetape_add_settings(drive);
+}
+
+static int idetape_cleanup (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ int minor = tape->minor;
+ unsigned long flags;
+
+ save_flags (flags); /* all CPUs (overkill?) */
+ cli(); /* all CPUs (overkill?) */
+ if (test_bit (IDETAPE_BUSY, &tape->flags) || tape->first_stage != NULL || tape->merge_stage_size || drive->usage) {
+ restore_flags(flags); /* all CPUs (overkill?) */
+ return 1;
+ }
+ idetape_chrdevs[minor].drive = NULL;
+ restore_flags (flags); /* all CPUs (overkill?) */
+ DRIVER(drive)->busy = 0;
+ (void) ide_unregister_subdriver (drive);
+ drive->driver_data = NULL;
+ devfs_unregister (tape->de_r);
+ devfs_unregister (tape->de_n);
+ kfree (tape);
+ for (minor = 0; minor < MAX_HWIFS * MAX_DRIVES; minor++)
+ if (idetape_chrdevs[minor].drive != NULL)
+ return 0;
+ devfs_unregister_chrdev (IDETAPE_MAJOR, "ht");
+ idetape_chrdev_present = 0;
+ return 0;
+}
+
+#ifdef CONFIG_PROC_FS
+
+static int proc_idetape_read_name
+ (char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+ ide_drive_t *drive = (ide_drive_t *) data;
+ idetape_tape_t *tape = drive->driver_data;
+ char *out = page;
+ int len;
+
+ len = sprintf(out,"%s\n", tape->name);
+ PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
+static ide_proc_entry_t idetape_proc[] = {
+ { "name", S_IFREG|S_IRUGO, proc_idetape_read_name, NULL },
+ { NULL, 0, NULL, NULL }
+};
+
+#else
+
+#define idetape_proc NULL
+
+#endif
+
+/*
+ * IDE subdriver functions, registered with ide.c
+ */
+static ide_driver_t idetape_driver = {
+ "ide-tape", /* name */
+ IDETAPE_VERSION, /* version */
+ ide_tape, /* media */
+ 1, /* busy */
+ 1, /* supports_dma */
+ 1, /* supports_dsc_overlap */
+ idetape_cleanup, /* cleanup */
+ idetape_do_request, /* do_request */
+ idetape_end_request, /* end_request */
+ idetape_blkdev_ioctl, /* ioctl */
+ idetape_blkdev_open, /* open */
+ idetape_blkdev_release, /* release */
+ NULL, /* media_change */
+ idetape_pre_reset, /* pre_reset */
+ NULL, /* capacity */
+ NULL, /* special */
+ idetape_proc /* proc */
+};
+
+int idetape_init (void);
+static ide_module_t idetape_module = {
+ IDE_DRIVER_MODULE,
+ idetape_init,
+ &idetape_driver,
+ NULL
+};
+
+/*
+ * Our character device supporting functions, passed to register_chrdev.
+ */
+static struct file_operations idetape_fops = {
+ read: idetape_chrdev_read,
+ write: idetape_chrdev_write,
+ ioctl: idetape_chrdev_ioctl,
+ open: idetape_chrdev_open,
+ release: idetape_chrdev_release,
+};
+
+/*
+ * idetape_init will register the driver for each tape.
+ */
+int idetape_init (void)
+{
+ ide_drive_t *drive;
+ idetape_tape_t *tape;
+ int minor, failed = 0, supported = 0;
+
+ MOD_INC_USE_COUNT;
+#if ONSTREAM_DEBUG
+ printk(KERN_INFO "ide-tape: MOD_INC_USE_COUNT in idetape_init\n");
+#endif
+ if (!idetape_chrdev_present)
+ for (minor = 0; minor < MAX_HWIFS * MAX_DRIVES; minor++ )
+ idetape_chrdevs[minor].drive = NULL;
+
+ if ((drive = ide_scan_devices (ide_tape, idetape_driver.name, NULL, failed++)) == NULL) {
+ ide_register_module (&idetape_module);
+ MOD_DEC_USE_COUNT;
+#if ONSTREAM_DEBUG
+ printk(KERN_INFO "ide-tape: MOD_DEC_USE_COUNT in idetape_init\n");
+#endif
+ return 0;
+ }
+ if (!idetape_chrdev_present &&
+ devfs_register_chrdev (IDETAPE_MAJOR, "ht", &idetape_fops)) {
+ printk (KERN_ERR "ide-tape: Failed to register character device interface\n");
+ MOD_DEC_USE_COUNT;
+#if ONSTREAM_DEBUG
+ printk(KERN_INFO "ide-tape: MOD_DEC_USE_COUNT in idetape_init\n");
+#endif
+ return -EBUSY;
+ }
+ do {
+ if (!idetape_identify_device (drive, drive->id)) {
+ printk (KERN_ERR "ide-tape: %s: not supported by this version of ide-tape\n", drive->name);
+ continue;
+ }
+ if (drive->scsi) {
+ if (strstr(drive->id->model, "OnStream DI-30")) {
+ printk("ide-tape: ide-scsi emulation is not supported for %s.\n", drive->id->model);
+ } else {
+ printk("ide-tape: passing drive %s to ide-scsi emulation.\n", drive->name);
+ continue;
+ }
+ }
+ tape = (idetape_tape_t *) kmalloc (sizeof (idetape_tape_t), GFP_KERNEL);
+ if (tape == NULL) {
+ printk (KERN_ERR "ide-tape: %s: Can't allocate a tape structure\n", drive->name);
+ continue;
+ }
+ if (ide_register_subdriver (drive, &idetape_driver, IDE_SUBDRIVER_VERSION)) {
+ printk (KERN_ERR "ide-tape: %s: Failed to register the driver with ide.c\n", drive->name);
+ kfree (tape);
+ continue;
+ }
+ for (minor = 0; idetape_chrdevs[minor].drive != NULL; minor++);
+ idetape_setup (drive, tape, minor);
+ idetape_chrdevs[minor].drive = drive;
+ tape->de_r =
+ devfs_register (drive->de, "mt", 2, DEVFS_FL_DEFAULT,
+ HWIF(drive)->major, minor,
+ S_IFCHR | S_IRUGO | S_IWUGO, 0, 0,
+ &idetape_fops, NULL);
+ tape->de_n =
+ devfs_register (drive->de, "mtn", 3, DEVFS_FL_DEFAULT,
+ HWIF(drive)->major, minor + 128,
+ S_IFCHR | S_IRUGO | S_IWUGO, 0, 0,
+ &idetape_fops, NULL);
+ devfs_register_tape (tape->de_r);
+ supported++; failed--;
+ } while ((drive = ide_scan_devices (ide_tape, idetape_driver.name, NULL, failed++)) != NULL);
+ if (!idetape_chrdev_present && !supported) {
+ devfs_unregister_chrdev (IDETAPE_MAJOR, "ht");
+ } else
+ idetape_chrdev_present = 1;
+ ide_register_module (&idetape_module);
+ MOD_DEC_USE_COUNT;
+#if ONSTREAM_DEBUG
+ printk(KERN_INFO "ide-tape: MOD_DEC_USE_COUNT in idetape_init\n");
+#endif
+ return 0;
+}
+
+#ifdef MODULE
+int init_module (void)
+{
+ return idetape_init ();
+}
+
+void cleanup_module (void)
+{
+ ide_drive_t *drive;
+ int minor;
+
+ for (minor = 0; minor < MAX_HWIFS * MAX_DRIVES; minor++) {
+ drive = idetape_chrdevs[minor].drive;
+ if (drive != NULL && idetape_cleanup (drive))
+ printk (KERN_ERR "ide-tape: %s: cleanup_module() called while still busy\n", drive->name);
+ }
+ ide_unregister_module(&idetape_module);
+}
+#endif /* MODULE */
diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c
new file mode 100644
index 000000000..9c409dfb6
--- /dev/null
+++ b/drivers/ide/ide.c
@@ -0,0 +1,3639 @@
+/*
+ * linux/drivers/block/ide.c Version 6.30 Dec 28, 1999
+ *
+ * Copyright (C) 1994-1998 Linus Torvalds & authors (see below)
+ */
+
+/*
+ * Mostly written by Mark Lord <mlord@pobox.com>
+ * and Gadi Oxman <gadio@netvision.net.il>
+ *
+ * See linux/MAINTAINERS for address of current maintainer.
+ *
+ * This is the multiple IDE interface driver, as evolved from hd.c.
+ * It supports up to MAX_HWIFS IDE interfaces, on one or more IRQs (usually 14 & 15).
+ * There can be up to two drives per interface, as per the ATA-2 spec.
+ *
+ * Primary: ide0, port 0x1f0; major=3; hda is minor=0; hdb is minor=64
+ * Secondary: ide1, port 0x170; major=22; hdc is minor=0; hdd is minor=64
+ * Tertiary: ide2, port 0x???; major=33; hde is minor=0; hdf is minor=64
+ * Quaternary: ide3, port 0x???; major=34; hdg is minor=0; hdh is minor=64
+ * ...
+ *
+ * From hd.c:
+ * |
+ * | It traverses the request-list, using interrupts to jump between functions.
+ * | As nearly all functions can be called within interrupts, we may not sleep.
+ * | Special care is recommended. Have Fun!
+ * |
+ * | modified by Drew Eckhardt to check nr of hd's from the CMOS.
+ * |
+ * | Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug
+ * | in the early extended-partition checks and added DM partitions.
+ * |
+ * | Early work on error handling by Mika Liljeberg (liljeber@cs.Helsinki.FI).
+ * |
+ * | IRQ-unmask, drive-id, multiple-mode, support for ">16 heads",
+ * | and general streamlining by Mark Lord (mlord@pobox.com).
+ *
+ * October, 1994 -- Complete line-by-line overhaul for linux 1.1.x, by:
+ *
+ * Mark Lord (mlord@pobox.com) (IDE Perf.Pkg)
+ * Delman Lee (delman@ieee.org) ("Mr. atdisk2")
+ * Scott Snyder (snyder@fnald0.fnal.gov) (ATAPI IDE cd-rom)
+ *
+ * This was a rewrite of just about everything from hd.c, though some original
+ * code is still sprinkled about. Think of it as a major evolution, with
+ * inspiration from lots of linux users, esp. hamish@zot.apana.org.au
+ *
+ * Version 1.0 ALPHA initial code, primary i/f working okay
+ * Version 1.3 BETA dual i/f on shared irq tested & working!
+ * Version 1.4 BETA added auto probing for irq(s)
+ * Version 1.5 BETA added ALPHA (untested) support for IDE cd-roms,
+ * ...
+ * Version 5.50 allow values as small as 20 for idebus=
+ * Version 5.51 force non io_32bit in drive_cmd_intr()
+ * change delay_10ms() to delay_50ms() to fix problems
+ * Version 5.52 fix incorrect invalidation of removable devices
+ * add "hdx=slow" command line option
+ * Version 5.60 start to modularize the driver; the disk and ATAPI
+ * drivers can be compiled as loadable modules.
+ * move IDE probe code to ide-probe.c
+ * move IDE disk code to ide-disk.c
+ * add support for generic IDE device subdrivers
+ * add m68k code from Geert Uytterhoeven
+ * probe all interfaces by default
+ * add ioctl to (re)probe an interface
+ * Version 6.00 use per device request queues
+ * attempt to optimize shared hwgroup performance
+ * add ioctl to manually adjust bandwidth algorithms
+ * add kerneld support for the probe module
+ * fix bug in ide_error()
+ * fix bug in the first ide_get_lock() call for Atari
+ * don't flush leftover data for ATAPI devices
+ * Version 6.01 clear hwgroup->active while the hwgroup sleeps
+ * support HDIO_GETGEO for floppies
+ * Version 6.02 fix ide_ack_intr() call
+ * check partition table on floppies
+ * Version 6.03 handle bad status bit sequencing in ide_wait_stat()
+ * Version 6.10 deleted old entries from this list of updates
+ * replaced triton.c with ide-dma.c generic PCI DMA
+ * added support for BIOS-enabled UltraDMA
+ * rename all "promise" things to "pdc4030"
+ * fix EZ-DRIVE handling on small disks
+ * Version 6.11 fix probe error in ide_scan_devices()
+ * fix ancient "jiffies" polling bugs
+ * mask all hwgroup interrupts on each irq entry
+ * Version 6.12 integrate ioctl and proc interfaces
+ * fix parsing of "idex=" command line parameter
+ * Version 6.13 add support for ide4/ide5 courtesy rjones@orchestream.com
+ * Version 6.14 fixed IRQ sharing among PCI devices
+ * Version 6.15 added SMP awareness to IDE drivers
+ * Version 6.16 fixed various bugs; even more SMP friendly
+ * Version 6.17 fix for newest EZ-Drive problem
+ * Version 6.18 default unpartitioned-disk translation now "BIOS LBA"
+ * Version 6.19 Re-design for a UNIFORM driver for all platforms,
+ * model based on suggestions from Russell King and
+ * Geert Uytterhoeven
+ * Promise DC4030VL now supported.
+ * add support for ide6/ide7
+ * delay_50ms() changed to ide_delay_50ms() and exported.
+ * Version 6.20 Added/Fixed Generic ATA-66 support and hwif detection.
+ * Added hdx=flash to allow for second flash disk
+ * detection w/o the hang loop.
+ * Added support for ide8/ide9
+ * Added idex=ata66 for the quirky chipsets that are
+ * ATA-66 compliant, but have yet to determine a method
+ * of verification of the 80c cable presence.
+ * Specifically Promise's PDC20262 chipset.
+ * Version 6.21 Fixing/Fixed SMP spinlock issue with insight from an old
+ * hat that clarified original low level driver design.
+ * Version 6.30 Added SMP support; fixed multmode issues. -ml
+ *
+ * Some additional driver compile-time options are in ./include/linux/ide.h
+ *
+ * To do, in likely order of completion:
+ * - modify kernel to obtain BIOS geometry for drives on 2nd/3rd/4th i/f
+ *
+ */
+
+#define REVISION "Revision: 6.30"
+#define VERSION "Id: ide.c 6.30 1999/12/28"
+
+#undef REALLY_SLOW_IO /* most systems can safely undef this */
+
+#define _IDE_C /* Tell ide.h it's really us */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/major.h>
+#include <linux/errno.h>
+#include <linux/genhd.h>
+#include <linux/blkpg.h>
+#include <linux/malloc.h>
+#ifndef MODULE
+#include <linux/init.h>
+#endif /* MODULE */
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/ide.h>
+#include <linux/devfs_fs_kernel.h>
+
+#include <asm/byteorder.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/bitops.h>
+
+#include "ide_modes.h"
+
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
+#endif /* CONFIG_KMOD */
+
+#ifdef CONFIG_BLK_DEV_VIA82CXXX
+extern byte fifoconfig; /* defined in via82cxxx.c used by ide_setup() */
+#endif /* CONFIG_BLK_DEV_VIA82CXXX */
+
+static const byte ide_hwif_to_major[] = { IDE0_MAJOR, IDE1_MAJOR, IDE2_MAJOR, IDE3_MAJOR, IDE4_MAJOR, IDE5_MAJOR, IDE6_MAJOR, IDE7_MAJOR, IDE8_MAJOR, IDE9_MAJOR };
+
+static int idebus_parameter = 0; /* holds the "idebus=" parameter */
+static int system_bus_speed = 0; /* holds what we think is VESA/PCI bus speed */
+static int initializing; /* set while initializing built-in drivers */
+
+#ifdef CONFIG_BLK_DEV_IDEPCI
+static int ide_scan_direction = 0; /* THIS was formerly 2.2.x pci=reverse */
+#endif /* CONFIG_BLK_DEV_IDEPCI */
+
+#if defined(__mc68000__) || defined(CONFIG_APUS)
+/*
+ * ide_lock is used by the Atari code to obtain access to the IDE interrupt,
+ * which is shared between several drivers.
+ */
+static int ide_lock = 0;
+#endif /* __mc68000__ || CONFIG_APUS */
+
+/*
+ * ide_modules keeps track of the available IDE chipset/probe/driver modules.
+ */
+ide_module_t *ide_modules = NULL;
+ide_module_t *ide_probe = NULL;
+
+/*
+ * This is declared extern in ide.h, for access by other IDE modules:
+ */
+ide_hwif_t ide_hwifs[MAX_HWIFS]; /* master data repository */
+
+#if (DISK_RECOVERY_TIME > 0)
+/*
+ * For really screwy hardware (hey, at least it *can* be used with Linux)
+ * we can enforce a minimum delay time between successive operations.
+ */
+static unsigned long read_timer (void)
+{
+ unsigned long t, flags;
+ int i;
+
+ __save_flags(flags); /* local CPU only */
+ __cli(); /* local CPU only */
+ t = jiffies * 11932;
+ outb_p(0, 0x43);
+ i = inb_p(0x40);
+ i |= inb(0x40) << 8;
+ __restore_flags(flags); /* local CPU only */
+ return (t - i);
+}
+#endif /* DISK_RECOVERY_TIME */
+
+static inline void set_recovery_timer (ide_hwif_t *hwif)
+{
+#if (DISK_RECOVERY_TIME > 0)
+ hwif->last_time = read_timer();
+#endif /* DISK_RECOVERY_TIME */
+}
+
+/*
+ * Do not even *think* about calling this!
+ */
+static void init_hwif_data (unsigned int index)
+{
+ unsigned int unit;
+ hw_regs_t hw;
+ ide_hwif_t *hwif = &ide_hwifs[index];
+
+ /* bulk initialize hwif & drive info with zeros */
+ memset(hwif, 0, sizeof(ide_hwif_t));
+ memset(&hw, 0, sizeof(hw_regs_t));
+
+ /* fill in any non-zero initial values */
+ hwif->index = index;
+ ide_init_hwif_ports(&hw, ide_default_io_base(index), 0, &hwif->irq);
+ memcpy(&hwif->hw, &hw, sizeof(hw));
+ memcpy(hwif->io_ports, hw.io_ports, sizeof(hw.io_ports));
+ hwif->noprobe = !hwif->io_ports[IDE_DATA_OFFSET];
+#ifdef CONFIG_BLK_DEV_HD
+ if (hwif->io_ports[IDE_DATA_OFFSET] == HD_DATA)
+ hwif->noprobe = 1; /* may be overridden by ide_setup() */
+#endif /* CONFIG_BLK_DEV_HD */
+ hwif->major = ide_hwif_to_major[index];
+ hwif->name[0] = 'i';
+ hwif->name[1] = 'd';
+ hwif->name[2] = 'e';
+ hwif->name[3] = '0' + index;
+ for (unit = 0; unit < MAX_DRIVES; ++unit) {
+ ide_drive_t *drive = &hwif->drives[unit];
+
+ drive->media = ide_disk;
+ drive->select.all = (unit<<4)|0xa0;
+ drive->hwif = hwif;
+ drive->ctl = 0x08;
+ drive->ready_stat = READY_STAT;
+ drive->bad_wstat = BAD_W_STAT;
+ drive->special.b.recalibrate = 1;
+ drive->special.b.set_geometry = 1;
+ drive->name[0] = 'h';
+ drive->name[1] = 'd';
+ drive->name[2] = 'a' + (index * MAX_DRIVES) + unit;
+ init_waitqueue_head(&drive->wqueue);
+ }
+}
+
+/*
+ * init_ide_data() sets reasonable default values into all fields
+ * of all instances of the hwifs and drives, but only on the first call.
+ * Subsequent calls have no effect (they don't wipe out anything).
+ *
+ * This routine is normally called at driver initialization time,
+ * but may also be called MUCH earlier during kernel "command-line"
+ * parameter processing. As such, we cannot depend on any other parts
+ * of the kernel (such as memory allocation) to be functioning yet.
+ *
+ * This is too bad, as otherwise we could dynamically allocate the
+ * ide_drive_t structs as needed, rather than always consuming memory
+ * for the max possible number (MAX_HWIFS * MAX_DRIVES) of them.
+ */
+#define MAGIC_COOKIE 0x12345678
+static void __init init_ide_data (void)
+{
+ unsigned int index;
+ static unsigned long magic_cookie = MAGIC_COOKIE;
+
+ if (magic_cookie != MAGIC_COOKIE)
+ return; /* already initialized */
+ magic_cookie = 0;
+
+ /* Initialise all interface structures */
+ for (index = 0; index < MAX_HWIFS; ++index)
+ init_hwif_data(index);
+
+ /* Add default hw interfaces */
+ ide_init_default_hwifs();
+
+ idebus_parameter = 0;
+ system_bus_speed = 0;
+}
+
+/*
+ * CompactFlash cards and their brethern pretend to be removable hard disks, except:
+ * (1) they never have a slave unit, and
+ * (2) they don't have doorlock mechanisms.
+ * This test catches them, and is invoked elsewhere when setting appropriate config bits.
+ *
+ * FIXME: This treatment is probably applicable for *all* PCMCIA (PC CARD) devices,
+ * so in linux 2.3.x we should change this to just treat all PCMCIA drives this way,
+ * and get rid of the model-name tests below (too big of an interface change for 2.2.x).
+ * At that time, we might also consider parameterizing the timeouts and retries,
+ * since these are MUCH faster than mechanical drives. -M.Lord
+ */
+int drive_is_flashcard (ide_drive_t *drive)
+{
+ struct hd_driveid *id = drive->id;
+
+ if (drive->removable && id != NULL) {
+ if (id->config == 0x848a) return 1; /* CompactFlash */
+ if (!strncmp(id->model, "KODAK ATA_FLASH", 15) /* Kodak */
+ || !strncmp(id->model, "Hitachi CV", 10) /* Hitachi */
+ || !strncmp(id->model, "SunDisk SDCFB", 13) /* SunDisk */
+ || !strncmp(id->model, "HAGIWARA HPC", 12) /* Hagiwara */
+ || !strncmp(id->model, "ATA_FLASH", 9)) /* Simple Tech */
+ {
+ return 1; /* yes, it is a flash memory card */
+ }
+ }
+ return 0; /* no, it is not a flash memory card */
+}
+
+/*
+ * ide_system_bus_speed() returns what we think is the system VESA/PCI
+ * bus speed (in MHz). This is used for calculating interface PIO timings.
+ * The default is 40 for known PCI systems, 50 otherwise.
+ * The "idebus=xx" parameter can be used to override this value.
+ * The actual value to be used is computed/displayed the first time through.
+ */
+int ide_system_bus_speed (void)
+{
+ if (!system_bus_speed) {
+ if (idebus_parameter)
+ system_bus_speed = idebus_parameter; /* user supplied value */
+#ifdef CONFIG_PCI
+ else if (pci_present())
+ system_bus_speed = 40; /* safe default value for PCI */
+#endif /* CONFIG_PCI */
+ else
+ system_bus_speed = 50; /* safe default value for VESA and PCI */
+ printk("ide: Assuming %dMHz system bus speed for PIO modes%s\n", system_bus_speed,
+ idebus_parameter ? "" : "; override with idebus=xx");
+ }
+ return system_bus_speed;
+}
+
+#if SUPPORT_VLB_SYNC
+/*
+ * Some localbus EIDE interfaces require a special access sequence
+ * when using 32-bit I/O instructions to transfer data. We call this
+ * the "vlb_sync" sequence, which consists of three successive reads
+ * of the sector count register location, with interrupts disabled
+ * to ensure that the reads all happen together.
+ */
+static inline void do_vlb_sync (ide_ioreg_t port) {
+ (void) inb (port);
+ (void) inb (port);
+ (void) inb (port);
+}
+#endif /* SUPPORT_VLB_SYNC */
+
+/*
+ * This is used for most PIO data transfers *from* the IDE interface
+ */
+void ide_input_data (ide_drive_t *drive, void *buffer, unsigned int wcount)
+{
+ byte io_32bit = drive->io_32bit;
+
+ if (io_32bit) {
+#if SUPPORT_VLB_SYNC
+ if (io_32bit & 2) {
+ unsigned long flags;
+ __save_flags(flags); /* local CPU only */
+ __cli(); /* local CPU only */
+ do_vlb_sync(IDE_NSECTOR_REG);
+ insl(IDE_DATA_REG, buffer, wcount);
+ __restore_flags(flags); /* local CPU only */
+ } else
+#endif /* SUPPORT_VLB_SYNC */
+ insl(IDE_DATA_REG, buffer, wcount);
+ } else {
+#if SUPPORT_SLOW_DATA_PORTS
+ if (drive->slow) {
+ unsigned short *ptr = (unsigned short *) buffer;
+ while (wcount--) {
+ *ptr++ = inw_p(IDE_DATA_REG);
+ *ptr++ = inw_p(IDE_DATA_REG);
+ }
+ } else
+#endif /* SUPPORT_SLOW_DATA_PORTS */
+ insw(IDE_DATA_REG, buffer, wcount<<1);
+ }
+}
+
+/*
+ * This is used for most PIO data transfers *to* the IDE interface
+ */
+void ide_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount)
+{
+ byte io_32bit = drive->io_32bit;
+
+ if (io_32bit) {
+#if SUPPORT_VLB_SYNC
+ if (io_32bit & 2) {
+ unsigned long flags;
+ __save_flags(flags); /* local CPU only */
+ __cli(); /* local CPU only */
+ do_vlb_sync(IDE_NSECTOR_REG);
+ outsl(IDE_DATA_REG, buffer, wcount);
+ __restore_flags(flags); /* local CPU only */
+ } else
+#endif /* SUPPORT_VLB_SYNC */
+ outsl(IDE_DATA_REG, buffer, wcount);
+ } else {
+#if SUPPORT_SLOW_DATA_PORTS
+ if (drive->slow) {
+ unsigned short *ptr = (unsigned short *) buffer;
+ while (wcount--) {
+ outw_p(*ptr++, IDE_DATA_REG);
+ outw_p(*ptr++, IDE_DATA_REG);
+ }
+ } else
+#endif /* SUPPORT_SLOW_DATA_PORTS */
+ outsw(IDE_DATA_REG, buffer, wcount<<1);
+ }
+}
+
+/*
+ * The following routines are mainly used by the ATAPI drivers.
+ *
+ * These routines will round up any request for an odd number of bytes,
+ * so if an odd bytecount is specified, be sure that there's at least one
+ * extra byte allocated for the buffer.
+ */
+void atapi_input_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount)
+{
+ ++bytecount;
+#if defined(CONFIG_ATARI) || defined(CONFIG_Q40)
+ if (MACH_IS_ATARI || MACH_IS_Q40) {
+ /* Atari has a byte-swapped IDE interface */
+ insw_swapw(IDE_DATA_REG, buffer, bytecount / 2);
+ return;
+ }
+#endif /* CONFIG_ATARI */
+ ide_input_data (drive, buffer, bytecount / 4);
+ if ((bytecount & 0x03) >= 2)
+ insw (IDE_DATA_REG, ((byte *)buffer) + (bytecount & ~0x03), 1);
+}
+
+void atapi_output_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount)
+{
+ ++bytecount;
+#if defined(CONFIG_ATARI) || defined(CONFIG_Q40)
+ if (MACH_IS_ATARI || MACH_IS_Q40) {
+ /* Atari has a byte-swapped IDE interface */
+ outsw_swapw(IDE_DATA_REG, buffer, bytecount / 2);
+ return;
+ }
+#endif /* CONFIG_ATARI */
+ ide_output_data (drive, buffer, bytecount / 4);
+ if ((bytecount & 0x03) >= 2)
+ outsw (IDE_DATA_REG, ((byte *)buffer) + (bytecount & ~0x03), 1);
+}
+
+/*
+ * Needed for PCI irq sharing
+ */
+static inline int drive_is_ready (ide_drive_t *drive)
+{
+ if (drive->waiting_for_dma)
+ return HWIF(drive)->dmaproc(ide_dma_test_irq, drive);
+#if 0
+ udelay(1); /* need to guarantee 400ns since last command was issued */
+#endif
+ if (GET_STAT() & BUSY_STAT) /* Note: this may clear a pending IRQ!! */
+ return 0; /* drive busy: definitely not interrupting */
+ return 1; /* drive ready: *might* be interrupting */
+}
+
+/*
+ * This is our end_request replacement function.
+ */
+void ide_end_request (byte uptodate, ide_hwgroup_t *hwgroup)
+{
+ struct request *rq;
+ unsigned long flags;
+
+ spin_lock_irqsave(&io_request_lock, flags);
+ rq = hwgroup->rq;
+
+ if (!end_that_request_first(rq, uptodate, hwgroup->drive->name)) {
+ add_blkdev_randomness(MAJOR(rq->rq_dev));
+ blkdev_dequeue_request(rq);
+ hwgroup->rq = NULL;
+ end_that_request_last(rq);
+ }
+ spin_unlock_irqrestore(&io_request_lock, flags);
+}
+
+/*
+ * This should get invoked any time we exit the driver to
+ * wait for an interrupt response from a drive. handler() points
+ * at the appropriate code to handle the next interrupt, and a
+ * timer is started to prevent us from waiting forever in case
+ * something goes wrong (see the ide_timer_expiry() handler later on).
+ */
+void ide_set_handler (ide_drive_t *drive, ide_handler_t *handler,
+ unsigned int timeout, ide_expiry_t *expiry)
+{
+ unsigned long flags;
+ ide_hwgroup_t *hwgroup = HWGROUP(drive);
+
+ spin_lock_irqsave(&io_request_lock, flags);
+ if (hwgroup->handler != NULL) {
+ printk("%s: ide_set_handler: handler not null; old=%p, new=%p\n",
+ drive->name, hwgroup->handler, handler);
+ }
+ hwgroup->handler = handler;
+ hwgroup->expiry = expiry;
+ hwgroup->timer.expires = jiffies + timeout;
+ add_timer(&hwgroup->timer);
+ spin_unlock_irqrestore(&io_request_lock, flags);
+}
+
+/*
+ * current_capacity() returns the capacity (in sectors) of a drive
+ * according to its current geometry/LBA settings.
+ */
+unsigned long current_capacity (ide_drive_t *drive)
+{
+ if (!drive->present)
+ return 0;
+ if (drive->driver != NULL)
+ return DRIVER(drive)->capacity(drive);
+ return 0;
+}
+
+extern struct block_device_operations ide_fops[];
+/*
+ * ide_geninit() is called exactly *once* for each interface.
+ */
+void ide_geninit (ide_hwif_t *hwif)
+{
+ unsigned int unit;
+ struct gendisk *gd = hwif->gd;
+
+ for (unit = 0; unit < MAX_DRIVES; ++unit) {
+ ide_drive_t *drive = &hwif->drives[unit];
+
+ if (!drive->present)
+ continue;
+ if (drive->media!=ide_disk && drive->media!=ide_floppy)
+ continue;
+ register_disk(gd,MKDEV(hwif->major,unit<<PARTN_BITS),
+#ifdef CONFIG_BLK_DEV_ISAPNP
+ (drive->forced_geom && drive->noprobe) ? 1 :
+#endif /* CONFIG_BLK_DEV_ISAPNP */
+ 1<<PARTN_BITS, ide_fops,
+ current_capacity(drive));
+ }
+}
+
+static ide_startstop_t do_reset1 (ide_drive_t *, int); /* needed below */
+
+/*
+ * atapi_reset_pollfunc() gets invoked to poll the interface for completion every 50ms
+ * during an atapi drive reset operation. If the drive has not yet responded,
+ * and we have not yet hit our maximum waiting time, then the timer is restarted
+ * for another 50ms.
+ */
+static ide_startstop_t atapi_reset_pollfunc (ide_drive_t *drive)
+{
+ ide_hwgroup_t *hwgroup = HWGROUP(drive);
+ byte stat;
+
+ SELECT_DRIVE(HWIF(drive),drive);
+ udelay (10);
+
+ if (OK_STAT(stat=GET_STAT(), 0, BUSY_STAT)) {
+ printk("%s: ATAPI reset complete\n", drive->name);
+ } else {
+ if (0 < (signed long)(hwgroup->poll_timeout - jiffies)) {
+ ide_set_handler (drive, &atapi_reset_pollfunc, HZ/20, NULL);
+ return ide_started; /* continue polling */
+ }
+ hwgroup->poll_timeout = 0; /* end of polling */
+ printk("%s: ATAPI reset timed-out, status=0x%02x\n", drive->name, stat);
+ return do_reset1 (drive, 1); /* do it the old fashioned way */
+ }
+ hwgroup->poll_timeout = 0; /* done polling */
+ return ide_stopped;
+}
+
+/*
+ * reset_pollfunc() gets invoked to poll the interface for completion every 50ms
+ * during an ide reset operation. If the drives have not yet responded,
+ * and we have not yet hit our maximum waiting time, then the timer is restarted
+ * for another 50ms.
+ */
+static ide_startstop_t reset_pollfunc (ide_drive_t *drive)
+{
+ ide_hwgroup_t *hwgroup = HWGROUP(drive);
+ ide_hwif_t *hwif = HWIF(drive);
+ byte tmp;
+
+ if (!OK_STAT(tmp=GET_STAT(), 0, BUSY_STAT)) {
+ if (0 < (signed long)(hwgroup->poll_timeout - jiffies)) {
+ ide_set_handler (drive, &reset_pollfunc, HZ/20, NULL);
+ return ide_started; /* continue polling */
+ }
+ printk("%s: reset timed-out, status=0x%02x\n", hwif->name, tmp);
+ } else {
+ printk("%s: reset: ", hwif->name);
+ if ((tmp = GET_ERR()) == 1)
+ printk("success\n");
+ else {
+#if FANCY_STATUS_DUMPS
+ printk("master: ");
+ switch (tmp & 0x7f) {
+ case 1: printk("passed");
+ break;
+ case 2: printk("formatter device error");
+ break;
+ case 3: printk("sector buffer error");
+ break;
+ case 4: printk("ECC circuitry error");
+ break;
+ case 5: printk("controlling MPU error");
+ break;
+ default:printk("error (0x%02x?)", tmp);
+ }
+ if (tmp & 0x80)
+ printk("; slave: failed");
+ printk("\n");
+#else
+ printk("failed\n");
+#endif /* FANCY_STATUS_DUMPS */
+ }
+ }
+ hwgroup->poll_timeout = 0; /* done polling */
+ return ide_stopped;
+}
+
+static void pre_reset (ide_drive_t *drive)
+{
+ if (drive->driver != NULL)
+ DRIVER(drive)->pre_reset(drive);
+
+ if (!drive->keep_settings) {
+ if (drive->using_dma) {
+ (void) HWIF(drive)->dmaproc(ide_dma_off, drive);
+ } else {
+ drive->unmask = 0;
+ drive->io_32bit = 0;
+ }
+ }
+}
+
+/*
+ * do_reset1() attempts to recover a confused drive by resetting it.
+ * Unfortunately, resetting a disk drive actually resets all devices on
+ * the same interface, so it can really be thought of as resetting the
+ * interface rather than resetting the drive.
+ *
+ * ATAPI devices have their own reset mechanism which allows them to be
+ * individually reset without clobbering other devices on the same interface.
+ *
+ * Unfortunately, the IDE interface does not generate an interrupt to let
+ * us know when the reset operation has finished, so we must poll for this.
+ * Equally poor, though, is the fact that this may a very long time to complete,
+ * (up to 30 seconds worstcase). So, instead of busy-waiting here for it,
+ * we set a timer to poll at 50ms intervals.
+ */
+static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi)
+{
+ unsigned int unit;
+ unsigned long flags;
+ ide_hwif_t *hwif = HWIF(drive);
+ ide_hwgroup_t *hwgroup = HWGROUP(drive);
+
+ __save_flags(flags); /* local CPU only */
+ __cli(); /* local CPU only */
+
+ /* For an ATAPI device, first try an ATAPI SRST. */
+ if (drive->media != ide_disk && !do_not_try_atapi) {
+ pre_reset(drive);
+ SELECT_DRIVE(hwif,drive);
+ udelay (20);
+ OUT_BYTE (WIN_SRST, IDE_COMMAND_REG);
+ hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE;
+ ide_set_handler (drive, &atapi_reset_pollfunc, HZ/20, NULL);
+ __restore_flags (flags); /* local CPU only */
+ return ide_started;
+ }
+
+ /*
+ * First, reset any device state data we were maintaining
+ * for any of the drives on this interface.
+ */
+ for (unit = 0; unit < MAX_DRIVES; ++unit)
+ pre_reset(&hwif->drives[unit]);
+
+#if OK_TO_RESET_CONTROLLER
+ if (!IDE_CONTROL_REG) {
+ __restore_flags(flags);
+ return ide_stopped;
+ }
+ /*
+ * Note that we also set nIEN while resetting the device,
+ * to mask unwanted interrupts from the interface during the reset.
+ * However, due to the design of PC hardware, this will cause an
+ * immediate interrupt due to the edge transition it produces.
+ * This single interrupt gives us a "fast poll" for drives that
+ * recover from reset very quickly, saving us the first 50ms wait time.
+ */
+ OUT_BYTE(drive->ctl|6,IDE_CONTROL_REG); /* set SRST and nIEN */
+ udelay(10); /* more than enough time */
+ OUT_BYTE(drive->ctl|2,IDE_CONTROL_REG); /* clear SRST, leave nIEN */
+ udelay(10); /* more than enough time */
+ hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE;
+ ide_set_handler (drive, &reset_pollfunc, HZ/20, NULL);
+
+ /*
+ * Some weird controller like resetting themselves to a strange
+ * state when the disks are reset this way. At least, the Winbond
+ * 553 documentation says that
+ */
+ if (hwif->resetproc != NULL)
+ hwif->resetproc(drive);
+
+#endif /* OK_TO_RESET_CONTROLLER */
+
+ __restore_flags (flags); /* local CPU only */
+ return ide_started;
+}
+
+/*
+ * ide_do_reset() is the entry point to the drive/interface reset code.
+ */
+ide_startstop_t ide_do_reset (ide_drive_t *drive)
+{
+ return do_reset1 (drive, 0);
+}
+
+/*
+ * Clean up after success/failure of an explicit drive cmd
+ */
+void ide_end_drive_cmd (ide_drive_t *drive, byte stat, byte err)
+{
+ unsigned long flags;
+ struct request *rq = HWGROUP(drive)->rq;
+
+ if (rq->cmd == IDE_DRIVE_CMD) {
+ byte *args = (byte *) rq->buffer;
+ rq->errors = !OK_STAT(stat,READY_STAT,BAD_STAT);
+ if (args) {
+ args[0] = stat;
+ args[1] = err;
+ args[2] = IN_BYTE(IDE_NSECTOR_REG);
+ }
+ }
+ spin_lock_irqsave(&io_request_lock, flags);
+ blkdev_dequeue_request(rq);
+ HWGROUP(drive)->rq = NULL;
+ rq->rq_status = RQ_INACTIVE;
+ spin_unlock_irqrestore(&io_request_lock, flags);
+ if (rq->sem != NULL)
+ up(rq->sem); /* inform originator that rq has been serviced */
+}
+
+/*
+ * Error reporting, in human readable form (luxurious, but a memory hog).
+ */
+byte ide_dump_status (ide_drive_t *drive, const char *msg, byte stat)
+{
+ unsigned long flags;
+ byte err = 0;
+
+ __save_flags (flags); /* local CPU only */
+ ide__sti(); /* local CPU only */
+ printk("%s: %s: status=0x%02x", drive->name, msg, stat);
+#if FANCY_STATUS_DUMPS
+ printk(" { ");
+ if (stat & BUSY_STAT)
+ printk("Busy ");
+ else {
+ if (stat & READY_STAT) printk("DriveReady ");
+ if (stat & WRERR_STAT) printk("DeviceFault ");
+ if (stat & SEEK_STAT) printk("SeekComplete ");
+ if (stat & DRQ_STAT) printk("DataRequest ");
+ if (stat & ECC_STAT) printk("CorrectedError ");
+ if (stat & INDEX_STAT) printk("Index ");
+ if (stat & ERR_STAT) printk("Error ");
+ }
+ printk("}");
+#endif /* FANCY_STATUS_DUMPS */
+ printk("\n");
+ if ((stat & (BUSY_STAT|ERR_STAT)) == ERR_STAT) {
+ err = GET_ERR();
+ printk("%s: %s: error=0x%02x", drive->name, msg, err);
+#if FANCY_STATUS_DUMPS
+ if (drive->media == ide_disk) {
+ printk(" { ");
+ if (err & ABRT_ERR) printk("DriveStatusError ");
+ if (err & ICRC_ERR) printk((err & ABRT_ERR) ? "BadCRC " : "BadSector ");
+ if (err & ECC_ERR) printk("UncorrectableError ");
+ if (err & ID_ERR) printk("SectorIdNotFound ");
+ if (err & TRK0_ERR) printk("TrackZeroNotFound ");
+ if (err & MARK_ERR) printk("AddrMarkNotFound ");
+ printk("}");
+ if ((err & (BBD_ERR | ABRT_ERR)) == BBD_ERR || (err & (ECC_ERR|ID_ERR|MARK_ERR))) {
+ byte cur = IN_BYTE(IDE_SELECT_REG);
+ if (cur & 0x40) { /* using LBA? */
+ printk(", LBAsect=%ld", (unsigned long)
+ ((cur&0xf)<<24)
+ |(IN_BYTE(IDE_HCYL_REG)<<16)
+ |(IN_BYTE(IDE_LCYL_REG)<<8)
+ | IN_BYTE(IDE_SECTOR_REG));
+ } else {
+ printk(", CHS=%d/%d/%d",
+ (IN_BYTE(IDE_HCYL_REG)<<8) +
+ IN_BYTE(IDE_LCYL_REG),
+ cur & 0xf,
+ IN_BYTE(IDE_SECTOR_REG));
+ }
+ if (HWGROUP(drive)->rq)
+ printk(", sector=%ld", HWGROUP(drive)->rq->sector);
+ }
+ }
+#endif /* FANCY_STATUS_DUMPS */
+ printk("\n");
+ }
+ __restore_flags (flags); /* local CPU only */
+ return err;
+}
+
+/*
+ * try_to_flush_leftover_data() is invoked in response to a drive
+ * unexpectedly having its DRQ_STAT bit set. As an alternative to
+ * resetting the drive, this routine tries to clear the condition
+ * by read a sector's worth of data from the drive. Of course,
+ * this may not help if the drive is *waiting* for data from *us*.
+ */
+static void try_to_flush_leftover_data (ide_drive_t *drive)
+{
+ int i = (drive->mult_count ? drive->mult_count : 1) * SECTOR_WORDS;
+
+ if (drive->media != ide_disk)
+ return;
+ while (i > 0) {
+ u32 buffer[16];
+ unsigned int wcount = (i > 16) ? 16 : i;
+ i -= wcount;
+ ide_input_data (drive, buffer, wcount);
+ }
+}
+
+/*
+ * ide_error() takes action based on the error returned by the drive.
+ */
+ide_startstop_t ide_error (ide_drive_t *drive, const char *msg, byte stat)
+{
+ struct request *rq;
+ byte err;
+
+ err = ide_dump_status(drive, msg, stat);
+ if (drive == NULL || (rq = HWGROUP(drive)->rq) == NULL)
+ return ide_stopped;
+ /* retry only "normal" I/O: */
+ if (rq->cmd == IDE_DRIVE_CMD) {
+ rq->errors = 1;
+ ide_end_drive_cmd(drive, stat, err);
+ return ide_stopped;
+ }
+ if (stat & BUSY_STAT || ((stat & WRERR_STAT) && !drive->nowerr)) { /* other bits are useless when BUSY */
+ rq->errors |= ERROR_RESET;
+ } else {
+ if (drive->media == ide_disk && (stat & ERR_STAT)) {
+ /* err has different meaning on cdrom and tape */
+ if (err == ABRT_ERR) {
+ if (drive->select.b.lba && IN_BYTE(IDE_COMMAND_REG) == WIN_SPECIFY)
+ return ide_stopped; /* some newer drives don't support WIN_SPECIFY */
+ } else if ((err & (ABRT_ERR | ICRC_ERR)) == (ABRT_ERR | ICRC_ERR))
+ ; /* UDMA crc error -- just retry the operation */
+ else if (err & (BBD_ERR | ECC_ERR)) /* retries won't help these */
+ rq->errors = ERROR_MAX;
+ else if (err & TRK0_ERR) /* help it find track zero */
+ rq->errors |= ERROR_RECAL;
+ }
+ if ((stat & DRQ_STAT) && rq->cmd != WRITE)
+ try_to_flush_leftover_data(drive);
+ }
+ if (GET_STAT() & (BUSY_STAT|DRQ_STAT))
+ OUT_BYTE(WIN_IDLEIMMEDIATE,IDE_COMMAND_REG); /* force an abort */
+
+ if (rq->errors >= ERROR_MAX) {
+ if (drive->driver != NULL)
+ DRIVER(drive)->end_request(0, HWGROUP(drive));
+ else
+ ide_end_request(0, HWGROUP(drive));
+ } else {
+ if ((rq->errors & ERROR_RESET) == ERROR_RESET) {
+ ++rq->errors;
+ return ide_do_reset(drive);
+ }
+ if ((rq->errors & ERROR_RECAL) == ERROR_RECAL)
+ drive->special.b.recalibrate = 1;
+ ++rq->errors;
+ }
+ return ide_stopped;
+}
+
+/*
+ * Issue a simple drive command
+ * The drive must be selected beforehand.
+ */
+void ide_cmd (ide_drive_t *drive, byte cmd, byte nsect, ide_handler_t *handler)
+{
+ ide_set_handler (drive, handler, WAIT_CMD, NULL);
+ if (IDE_CONTROL_REG)
+ OUT_BYTE(drive->ctl,IDE_CONTROL_REG); /* clear nIEN */
+ OUT_BYTE(nsect,IDE_NSECTOR_REG);
+ OUT_BYTE(cmd,IDE_COMMAND_REG);
+}
+
+/*
+ * drive_cmd_intr() is invoked on completion of a special DRIVE_CMD.
+ */
+static ide_startstop_t drive_cmd_intr (ide_drive_t *drive)
+{
+ struct request *rq = HWGROUP(drive)->rq;
+ byte *args = (byte *) rq->buffer;
+ byte stat = GET_STAT();
+ int retries = 10;
+
+ ide__sti(); /* local CPU only */
+ if ((stat & DRQ_STAT) && args && args[3]) {
+ byte io_32bit = drive->io_32bit;
+ drive->io_32bit = 0;
+ ide_input_data(drive, &args[4], args[3] * SECTOR_WORDS);
+ drive->io_32bit = io_32bit;
+ while (((stat = GET_STAT()) & BUSY_STAT) && retries--)
+ udelay(100);
+ }
+
+ if (!OK_STAT(stat, READY_STAT, BAD_STAT))
+ return ide_error(drive, "drive_cmd", stat); /* calls ide_end_drive_cmd */
+ ide_end_drive_cmd (drive, stat, GET_ERR());
+ return ide_stopped;
+}
+
+/*
+ * do_special() is used to issue WIN_SPECIFY, WIN_RESTORE, and WIN_SETMULT
+ * commands to a drive. It used to do much more, but has been scaled back.
+ */
+static ide_startstop_t do_special (ide_drive_t *drive)
+{
+ special_t *s = &drive->special;
+
+#ifdef DEBUG
+ printk("%s: do_special: 0x%02x\n", drive->name, s->all);
+#endif
+ if (s->b.set_tune) {
+ ide_tuneproc_t *tuneproc = HWIF(drive)->tuneproc;
+ s->b.set_tune = 0;
+ if (tuneproc != NULL)
+ tuneproc(drive, drive->tune_req);
+ } else if (drive->driver != NULL) {
+ return DRIVER(drive)->special(drive);
+ } else if (s->all) {
+ printk("%s: bad special flag: 0x%02x\n", drive->name, s->all);
+ s->all = 0;
+ }
+ return ide_stopped;
+}
+
+/*
+ * This routine busy-waits for the drive status to be not "busy".
+ * It then checks the status for all of the "good" bits and none
+ * of the "bad" bits, and if all is okay it returns 0. All other
+ * cases return 1 after invoking ide_error() -- caller should just return.
+ *
+ * This routine should get fixed to not hog the cpu during extra long waits..
+ * That could be done by busy-waiting for the first jiffy or two, and then
+ * setting a timer to wake up at half second intervals thereafter,
+ * until timeout is achieved, before timing out.
+ */
+int ide_wait_stat (ide_startstop_t *startstop, ide_drive_t *drive, byte good, byte bad, unsigned long timeout) {
+ byte stat;
+ int i;
+ unsigned long flags;
+
+ udelay(1); /* spec allows drive 400ns to assert "BUSY" */
+ if ((stat = GET_STAT()) & BUSY_STAT) {
+ __save_flags(flags); /* local CPU only */
+ ide__sti(); /* local CPU only */
+ timeout += jiffies;
+ while ((stat = GET_STAT()) & BUSY_STAT) {
+ if (0 < (signed long)(jiffies - timeout)) {
+ __restore_flags(flags); /* local CPU only */
+ *startstop = ide_error(drive, "status timeout", stat);
+ return 1;
+ }
+ }
+ __restore_flags(flags); /* local CPU only */
+ }
+ /*
+ * Allow status to settle, then read it again.
+ * A few rare drives vastly violate the 400ns spec here,
+ * so we'll wait up to 10usec for a "good" status
+ * rather than expensively fail things immediately.
+ * This fix courtesy of Matthew Faupel & Niccolo Rigacci.
+ */
+ for (i = 0; i < 10; i++) {
+ udelay(1);
+ if (OK_STAT((stat = GET_STAT()), good, bad))
+ return 0;
+ }
+ *startstop = ide_error(drive, "status error", stat);
+ return 1;
+}
+
+/*
+ * execute_drive_cmd() issues a special drive command,
+ * usually initiated by ioctl() from the external hdparm program.
+ */
+static ide_startstop_t execute_drive_cmd (ide_drive_t *drive, struct request *rq)
+{
+ byte *args = rq->buffer;
+ if (args) {
+#ifdef DEBUG
+ printk("%s: DRIVE_CMD cmd=0x%02x sc=0x%02x fr=0x%02x xx=0x%02x\n",
+ drive->name, args[0], args[1], args[2], args[3]);
+#endif
+ if (args[0] == WIN_SMART) {
+ OUT_BYTE(0x4f, IDE_LCYL_REG);
+ OUT_BYTE(0xc2, IDE_HCYL_REG);
+ OUT_BYTE(args[2],IDE_FEATURE_REG);
+ OUT_BYTE(args[1],IDE_SECTOR_REG);
+ ide_cmd(drive, args[0], args[3], &drive_cmd_intr);
+ return ide_started;
+ }
+ OUT_BYTE(args[2],IDE_FEATURE_REG);
+ ide_cmd(drive, args[0], args[1], &drive_cmd_intr);
+ return ide_started;
+ } else {
+ /*
+ * NULL is actually a valid way of waiting for
+ * all current requests to be flushed from the queue.
+ */
+#ifdef DEBUG
+ printk("%s: DRIVE_CMD (null)\n", drive->name);
+#endif
+ ide_end_drive_cmd(drive, GET_STAT(), GET_ERR());
+ return ide_stopped;
+ }
+}
+
+/*
+ * start_request() initiates handling of a new I/O request
+ */
+static ide_startstop_t start_request (ide_drive_t *drive)
+{
+ ide_startstop_t startstop;
+ unsigned long block, blockend;
+ struct request *rq = blkdev_entry_next_request(&drive->queue.queue_head);
+ unsigned int minor = MINOR(rq->rq_dev), unit = minor >> PARTN_BITS;
+ ide_hwif_t *hwif = HWIF(drive);
+
+#ifdef DEBUG
+ printk("%s: start_request: current=0x%08lx\n", hwif->name, (unsigned long) rq);
+#endif
+ if (unit >= MAX_DRIVES) {
+ printk("%s: bad device number: %s\n", hwif->name, kdevname(rq->rq_dev));
+ goto kill_rq;
+ }
+#ifdef DEBUG
+ if (rq->bh && !buffer_locked(rq->bh)) {
+ printk("%s: block not locked\n", drive->name);
+ goto kill_rq;
+ }
+#endif
+ block = rq->sector;
+ blockend = block + rq->nr_sectors;
+#if 0
+ if ((rq->cmd == READ || rq->cmd == WRITE) &&
+ (drive->media == ide_disk || drive->media == ide_floppy))
+#endif
+ {
+ if ((blockend < block) || (blockend > drive->part[minor&PARTN_MASK].nr_sects)) {
+ printk("%s%c: bad access: block=%ld, count=%ld\n", drive->name,
+ (minor&PARTN_MASK)?'0'+(minor&PARTN_MASK):' ', block, rq->nr_sectors);
+ goto kill_rq;
+ }
+ block += drive->part[minor&PARTN_MASK].start_sect + drive->sect0;
+ }
+ /* Yecch - this will shift the entire interval,
+ possibly killing some innocent following sector */
+ if (block == 0 && drive->remap_0_to_1 == 1)
+ block = 1; /* redirect MBR access to EZ-Drive partn table */
+
+#if (DISK_RECOVERY_TIME > 0)
+ while ((read_timer() - hwif->last_time) < DISK_RECOVERY_TIME);
+#endif
+
+ SELECT_DRIVE(hwif, drive);
+ if (ide_wait_stat(&startstop, drive, drive->ready_stat, BUSY_STAT|DRQ_STAT, WAIT_READY)) {
+ printk("%s: drive not ready for command\n", drive->name);
+ return startstop;
+ }
+ if (!drive->special.all) {
+ if (rq->cmd == IDE_DRIVE_CMD) {
+ return execute_drive_cmd(drive, rq);
+ }
+ if (drive->driver != NULL) {
+ return (DRIVER(drive)->do_request(drive, rq, block));
+ }
+ printk("%s: media type %d not supported\n", drive->name, drive->media);
+ goto kill_rq;
+ }
+ return do_special(drive);
+kill_rq:
+ if (drive->driver != NULL)
+ DRIVER(drive)->end_request(0, HWGROUP(drive));
+ else
+ ide_end_request(0, HWGROUP(drive));
+ return ide_stopped;
+}
+
+/*
+ * ide_stall_queue() can be used by a drive to give excess bandwidth back
+ * to the hwgroup by sleeping for timeout jiffies.
+ */
+void ide_stall_queue (ide_drive_t *drive, unsigned long timeout)
+{
+ if (timeout > WAIT_WORSTCASE)
+ timeout = WAIT_WORSTCASE;
+ drive->sleep = timeout + jiffies;
+}
+
+#define WAKEUP(drive) ((drive)->service_start + 2 * (drive)->service_time)
+
+/*
+ * choose_drive() selects the next drive which will be serviced.
+ */
+static inline ide_drive_t *choose_drive (ide_hwgroup_t *hwgroup)
+{
+ ide_drive_t *drive, *best;
+
+repeat:
+ best = NULL;
+ drive = hwgroup->drive;
+ do {
+ if (!list_empty(&drive->queue.queue_head) && (!drive->sleep || 0 <= (signed long)(jiffies - drive->sleep))) {
+ if (!best
+ || (drive->sleep && (!best->sleep || 0 < (signed long)(best->sleep - drive->sleep)))
+ || (!best->sleep && 0 < (signed long)(WAKEUP(best) - WAKEUP(drive))))
+ {
+ if( !drive->queue.plugged )
+ best = drive;
+ }
+ }
+ } while ((drive = drive->next) != hwgroup->drive);
+ if (best && best->nice1 && !best->sleep && best != hwgroup->drive && best->service_time > WAIT_MIN_SLEEP) {
+ long t = (signed long)(WAKEUP(best) - jiffies);
+ if (t >= WAIT_MIN_SLEEP) {
+ /*
+ * We *may* have some time to spare, but first let's see if
+ * someone can potentially benefit from our nice mood today..
+ */
+ drive = best->next;
+ do {
+ if (!drive->sleep
+ && 0 < (signed long)(WAKEUP(drive) - (jiffies - best->service_time))
+ && 0 < (signed long)((jiffies + t) - WAKEUP(drive)))
+ {
+ ide_stall_queue(best, IDE_MIN(t, 10 * WAIT_MIN_SLEEP));
+ goto repeat;
+ }
+ } while ((drive = drive->next) != best);
+ }
+ }
+ return best;
+}
+
+/*
+ * Issue a new request to a drive from hwgroup
+ * Caller must have already done spin_lock_irqsave(&io_request_lock, ..);
+ *
+ * A hwgroup is a serialized group of IDE interfaces. Usually there is
+ * exactly one hwif (interface) per hwgroup, but buggy controllers (eg. CMD640)
+ * may have both interfaces in a single hwgroup to "serialize" access.
+ * Or possibly multiple ISA interfaces can share a common IRQ by being grouped
+ * together into one hwgroup for serialized access.
+ *
+ * Note also that several hwgroups can end up sharing a single IRQ,
+ * possibly along with many other devices. This is especially common in
+ * PCI-based systems with off-board IDE controller cards.
+ *
+ * The IDE driver uses the single global io_request_lock spinlock to protect
+ * access to the request queues, and to protect the hwgroup->busy flag.
+ *
+ * The first thread into the driver for a particular hwgroup sets the
+ * hwgroup->busy flag to indicate that this hwgroup is now active,
+ * and then initiates processing of the top request from the request queue.
+ *
+ * Other threads attempting entry notice the busy setting, and will simply
+ * queue their new requests and exit immediately. Note that hwgroup->busy
+ * remains set even when the driver is merely awaiting the next interrupt.
+ * Thus, the meaning is "this hwgroup is busy processing a request".
+ *
+ * When processing of a request completes, the completing thread or IRQ-handler
+ * will start the next request from the queue. If no more work remains,
+ * the driver will clear the hwgroup->busy flag and exit.
+ *
+ * The io_request_lock (spinlock) is used to protect all access to the
+ * hwgroup->busy flag, but is otherwise not needed for most processing in
+ * the driver. This makes the driver much more friendlier to shared IRQs
+ * than previous designs, while remaining 100% (?) SMP safe and capable.
+ */
+static void ide_do_request (ide_hwgroup_t *hwgroup, int masked_irq)
+{
+ ide_drive_t *drive;
+ ide_hwif_t *hwif;
+ ide_startstop_t startstop;
+
+ ide_get_lock(&ide_lock, ide_intr, hwgroup); /* for atari only: POSSIBLY BROKEN HERE(?) */
+
+ __cli(); /* necessary paranoia: ensure IRQs are masked on local CPU */
+
+ while (!hwgroup->busy) {
+ hwgroup->busy = 1;
+ drive = choose_drive(hwgroup);
+ if (drive == NULL) {
+ unsigned long sleep = 0;
+ hwgroup->rq = NULL;
+ drive = hwgroup->drive;
+ do {
+ if (drive->sleep && (!sleep || 0 < (signed long)(sleep - drive->sleep)))
+ sleep = drive->sleep;
+ } while ((drive = drive->next) != hwgroup->drive);
+ if (sleep) {
+ /*
+ * Take a short snooze, and then wake up this hwgroup again.
+ * This gives other hwgroups on the same a chance to
+ * play fairly with us, just in case there are big differences
+ * in relative throughputs.. don't want to hog the cpu too much.
+ */
+ if (0 < (signed long)(jiffies + WAIT_MIN_SLEEP - sleep))
+ sleep = jiffies + WAIT_MIN_SLEEP;
+#if 1
+ if (hwgroup->timer.next || hwgroup->timer.prev)
+ printk("ide_set_handler: timer already active\n");
+#endif
+ hwgroup->sleeping = 1; /* so that ide_timer_expiry knows what to do */
+ mod_timer(&hwgroup->timer, sleep);
+ /* we purposely leave hwgroup->busy==1 while sleeping */
+ } else {
+ /* Ugly, but how can we sleep for the lock otherwise? perhaps from tq_scheduler? */
+ ide_release_lock(&ide_lock); /* for atari only */
+ hwgroup->busy = 0;
+ }
+ return; /* no more work for this hwgroup (for now) */
+ }
+ hwif = HWIF(drive);
+ if (hwgroup->hwif->sharing_irq && hwif != hwgroup->hwif && hwif->io_ports[IDE_CONTROL_OFFSET]) {
+ /* set nIEN for previous hwif */
+ OUT_BYTE(hwgroup->drive->ctl|2, hwgroup->hwif->io_ports[IDE_CONTROL_OFFSET]);
+ }
+ hwgroup->hwif = hwif;
+ hwgroup->drive = drive;
+ drive->sleep = 0;
+ drive->service_start = jiffies;
+
+ if ( drive->queue.plugged ) /* paranoia */
+ printk("%s: Huh? nuking plugged queue\n", drive->name);
+ hwgroup->rq = blkdev_entry_next_request(&drive->queue.queue_head);
+ /*
+ * Some systems have trouble with IDE IRQs arriving while
+ * the driver is still setting things up. So, here we disable
+ * the IRQ used by this interface while the request is being started.
+ * This may look bad at first, but pretty much the same thing
+ * happens anyway when any interrupt comes in, IDE or otherwise
+ * -- the kernel masks the IRQ while it is being handled.
+ */
+ if (hwif->irq != masked_irq)
+ disable_irq_nosync(hwif->irq);
+ spin_unlock(&io_request_lock);
+ ide__sti(); /* allow other IRQs while we start this request */
+ startstop = start_request(drive);
+ spin_lock_irq(&io_request_lock);
+ if (hwif->irq != masked_irq)
+ enable_irq(hwif->irq);
+ if (startstop == ide_stopped)
+ hwgroup->busy = 0;
+ }
+}
+
+/*
+ * ide_get_queue() returns the queue which corresponds to a given device.
+ */
+request_queue_t *ide_get_queue (kdev_t dev)
+{
+ ide_hwif_t *hwif = (ide_hwif_t *)blk_dev[MAJOR(dev)].data;
+
+ return &hwif->drives[DEVICE_NR(dev) & 1].queue;
+}
+
+void do_ide0_request (request_queue_t *q)
+{
+ ide_do_request (ide_hwifs[0].hwgroup, 0);
+}
+
+#if MAX_HWIFS > 1
+void do_ide1_request (request_queue_t *q)
+{
+ ide_do_request (ide_hwifs[1].hwgroup, 0);
+}
+#endif /* MAX_HWIFS > 1 */
+
+#if MAX_HWIFS > 2
+void do_ide2_request (request_queue_t *q)
+{
+ ide_do_request (ide_hwifs[2].hwgroup, 0);
+}
+#endif /* MAX_HWIFS > 2 */
+
+#if MAX_HWIFS > 3
+void do_ide3_request (request_queue_t *q)
+{
+ ide_do_request (ide_hwifs[3].hwgroup, 0);
+}
+#endif /* MAX_HWIFS > 3 */
+
+#if MAX_HWIFS > 4
+void do_ide4_request (request_queue_t *q)
+{
+ ide_do_request (ide_hwifs[4].hwgroup, 0);
+}
+#endif /* MAX_HWIFS > 4 */
+
+#if MAX_HWIFS > 5
+void do_ide5_request (request_queue_t *q)
+{
+ ide_do_request (ide_hwifs[5].hwgroup, 0);
+}
+#endif /* MAX_HWIFS > 5 */
+
+#if MAX_HWIFS > 6
+void do_ide6_request (request_queue_t *q)
+{
+ ide_do_request (ide_hwifs[6].hwgroup, 0);
+}
+#endif /* MAX_HWIFS > 6 */
+
+#if MAX_HWIFS > 7
+void do_ide7_request (request_queue_t *q)
+{
+ ide_do_request (ide_hwifs[7].hwgroup, 0);
+}
+#endif /* MAX_HWIFS > 7 */
+
+#if MAX_HWIFS > 8
+void do_ide8_request (request_queue_t *q)
+{
+ ide_do_request (ide_hwifs[8].hwgroup, 0);
+}
+#endif /* MAX_HWIFS > 8 */
+
+#if MAX_HWIFS > 9
+void do_ide9_request (request_queue_t *q)
+{
+ ide_do_request (ide_hwifs[9].hwgroup, 0);
+}
+#endif /* MAX_HWIFS > 9 */
+
+/*
+ * ide_timer_expiry() is our timeout function for all drive operations.
+ * But note that it can also be invoked as a result of a "sleep" operation
+ * triggered by the mod_timer() call in ide_do_request.
+ */
+void ide_timer_expiry (unsigned long data)
+{
+ ide_hwgroup_t *hwgroup = (ide_hwgroup_t *) data;
+ ide_handler_t *handler;
+ ide_expiry_t *expiry;
+ unsigned long flags;
+ unsigned long wait;
+
+ spin_lock_irqsave(&io_request_lock, flags);
+ del_timer(&hwgroup->timer);
+
+ if ((handler = hwgroup->handler) == NULL) {
+ /*
+ * Either a marginal timeout occured
+ * (got the interrupt just as timer expired),
+ * or we were "sleeping" to give other devices a chance.
+ * Either way, we don't really want to complain about anything.
+ */
+ if (hwgroup->sleeping) {
+ hwgroup->sleeping = 0;
+ hwgroup->busy = 0;
+ }
+ } else {
+ ide_drive_t *drive = hwgroup->drive;
+ if (!drive) {
+ printk("ide_timer_expiry: hwgroup->drive was NULL\n");
+ hwgroup->handler = NULL;
+ } else {
+ ide_hwif_t *hwif;
+ ide_startstop_t startstop;
+ if (!hwgroup->busy) {
+ hwgroup->busy = 1; /* paranoia */
+ printk("%s: ide_timer_expiry: hwgroup->busy was 0 ??\n", drive->name);
+ }
+ if ((expiry = hwgroup->expiry) != NULL) {
+ /* continue */
+ if ((wait = expiry(drive)) != 0) {
+ /* reset timer */
+ hwgroup->timer.expires = jiffies + wait;
+ add_timer(&hwgroup->timer);
+ spin_unlock_irqrestore(&io_request_lock, flags);
+ return;
+ }
+ }
+ hwgroup->handler = NULL;
+ /*
+ * We need to simulate a real interrupt when invoking
+ * the handler() function, which means we need to globally
+ * mask the specific IRQ:
+ */
+ spin_unlock(&io_request_lock);
+ hwif = HWIF(drive);
+ disable_irq(hwif->irq); /* disable_irq_nosync ?? */
+ __cli(); /* local CPU only, as if we were handling an interrupt */
+ if (hwgroup->poll_timeout != 0) {
+ startstop = handler(drive);
+ } else if (drive_is_ready(drive)) {
+ if (drive->waiting_for_dma)
+ (void) hwgroup->hwif->dmaproc(ide_dma_lostirq, drive);
+ (void)ide_ack_intr(hwif);
+ printk("%s: lost interrupt\n", drive->name);
+ startstop = handler(drive);
+ } else {
+ if (drive->waiting_for_dma) {
+ (void) hwgroup->hwif->dmaproc(ide_dma_end, drive);
+ printk("%s: timeout waiting for DMA\n", drive->name);
+ (void) hwgroup->hwif->dmaproc(ide_dma_timeout, drive);
+ }
+ startstop = ide_error(drive, "irq timeout", GET_STAT());
+ }
+ set_recovery_timer(hwif);
+ drive->service_time = jiffies - drive->service_start;
+ enable_irq(hwif->irq);
+ spin_lock_irq(&io_request_lock);
+ if (startstop == ide_stopped)
+ hwgroup->busy = 0;
+ }
+ }
+ ide_do_request(hwgroup, 0);
+ spin_unlock_irqrestore(&io_request_lock, flags);
+}
+
+/*
+ * There's nothing really useful we can do with an unexpected interrupt,
+ * other than reading the status register (to clear it), and logging it.
+ * There should be no way that an irq can happen before we're ready for it,
+ * so we needn't worry much about losing an "important" interrupt here.
+ *
+ * On laptops (and "green" PCs), an unexpected interrupt occurs whenever the
+ * drive enters "idle", "standby", or "sleep" mode, so if the status looks
+ * "good", we just ignore the interrupt completely.
+ *
+ * This routine assumes __cli() is in effect when called.
+ *
+ * If an unexpected interrupt happens on irq15 while we are handling irq14
+ * and if the two interfaces are "serialized" (CMD640), then it looks like
+ * we could screw up by interfering with a new request being set up for irq15.
+ *
+ * In reality, this is a non-issue. The new command is not sent unless the
+ * drive is ready to accept one, in which case we know the drive is not
+ * trying to interrupt us. And ide_set_handler() is always invoked before
+ * completing the issuance of any new drive command, so we will not be
+ * accidently invoked as a result of any valid command completion interrupt.
+ *
+ */
+static void unexpected_intr (int irq, ide_hwgroup_t *hwgroup)
+{
+ byte stat;
+ ide_hwif_t *hwif = hwgroup->hwif;
+
+ /*
+ * handle the unexpected interrupt
+ */
+ do {
+ if (hwif->irq == irq) {
+ stat = IN_BYTE(hwif->io_ports[IDE_STATUS_OFFSET]);
+ if (!OK_STAT(stat, READY_STAT, BAD_STAT)) {
+ /* Try to not flood the console with msgs */
+ static unsigned long last_msgtime = 0, count = 0;
+ ++count;
+ if (0 < (signed long)(jiffies - (last_msgtime + HZ))) {
+ last_msgtime = jiffies;
+ printk("%s%s: unexpected interrupt, status=0x%02x, count=%ld\n",
+ hwif->name, (hwif->next == hwgroup->hwif) ? "" : "(?)", stat, count);
+ }
+ }
+ }
+ } while ((hwif = hwif->next) != hwgroup->hwif);
+}
+
+/*
+ * entry point for all interrupts, caller does __cli() for us
+ */
+void ide_intr (int irq, void *dev_id, struct pt_regs *regs)
+{
+ unsigned long flags;
+ ide_hwgroup_t *hwgroup = (ide_hwgroup_t *)dev_id;
+ ide_hwif_t *hwif;
+ ide_drive_t *drive;
+ ide_handler_t *handler;
+ ide_startstop_t startstop;
+
+ spin_lock_irqsave(&io_request_lock, flags);
+ hwif = hwgroup->hwif;
+
+ if (!ide_ack_intr(hwif)) {
+ spin_unlock_irqrestore(&io_request_lock, flags);
+ return;
+ }
+
+ if ((handler = hwgroup->handler) == NULL || hwgroup->poll_timeout != 0) {
+ /*
+ * Not expecting an interrupt from this drive.
+ * That means this could be:
+ * (1) an interrupt from another PCI device
+ * sharing the same PCI INT# as us.
+ * or (2) a drive just entered sleep or standby mode,
+ * and is interrupting to let us know.
+ * or (3) a spurious interrupt of unknown origin.
+ *
+ * For PCI, we cannot tell the difference,
+ * so in that case we just ignore it and hope it goes away.
+ */
+#ifdef CONFIG_BLK_DEV_IDEPCI
+ if (IDE_PCI_DEVID_EQ(hwif->pci_devid, IDE_PCI_DEVID_NULL))
+#endif /* CONFIG_BLK_DEV_IDEPCI */
+ {
+ /*
+ * Probably not a shared PCI interrupt,
+ * so we can safely try to do something about it:
+ */
+ unexpected_intr(irq, hwgroup);
+#ifdef CONFIG_BLK_DEV_IDEPCI
+ } else {
+ /*
+ * Whack the status register, just in case we have a leftover pending IRQ.
+ */
+ (void) IN_BYTE(hwif->io_ports[IDE_STATUS_OFFSET]);
+#endif /* CONFIG_BLK_DEV_IDEPCI */
+ }
+ spin_unlock_irqrestore(&io_request_lock, flags);
+ return;
+ }
+ drive = hwgroup->drive;
+ if (!drive) {
+ /*
+ * This should NEVER happen, and there isn't much we could do about it here.
+ */
+ spin_unlock_irqrestore(&io_request_lock, flags);
+ return;
+ }
+ if (!drive_is_ready(drive)) {
+ /*
+ * This happens regularly when we share a PCI IRQ with another device.
+ * Unfortunately, it can also happen with some buggy drives that trigger
+ * the IRQ before their status register is up to date. Hopefully we have
+ * enough advance overhead that the latter isn't a problem.
+ */
+ spin_unlock_irqrestore(&io_request_lock, flags);
+ return;
+ }
+ if (!hwgroup->busy) {
+ hwgroup->busy = 1; /* paranoia */
+ printk("%s: ide_intr: hwgroup->busy was 0 ??\n", drive->name);
+ }
+ hwgroup->handler = NULL;
+ del_timer(&hwgroup->timer);
+ spin_unlock(&io_request_lock);
+
+ if (drive->unmask)
+ ide__sti(); /* local CPU only */
+ startstop = handler(drive); /* service this interrupt, may set handler for next interrupt */
+ spin_lock_irq(&io_request_lock);
+
+ /*
+ * Note that handler() may have set things up for another
+ * interrupt to occur soon, but it cannot happen until
+ * we exit from this routine, because it will be the
+ * same irq as is currently being serviced here, and Linux
+ * won't allow another of the same (on any CPU) until we return.
+ */
+ set_recovery_timer(HWIF(drive));
+ drive->service_time = jiffies - drive->service_start;
+ if (startstop == ide_stopped) {
+ if (hwgroup->handler == NULL) { /* paranoia */
+ hwgroup->busy = 0;
+ ide_do_request(hwgroup, hwif->irq);
+ } else {
+ printk("%s: ide_intr: huh? expected NULL handler on exit\n", drive->name);
+ }
+ }
+ spin_unlock_irqrestore(&io_request_lock, flags);
+}
+
+/*
+ * get_info_ptr() returns the (ide_drive_t *) for a given device number.
+ * It returns NULL if the given device number does not match any present drives.
+ */
+ide_drive_t *get_info_ptr (kdev_t i_rdev)
+{
+ int major = MAJOR(i_rdev);
+#if 0
+ int minor = MINOR(i_rdev) & PARTN_MASK;
+#endif
+ unsigned int h;
+
+ for (h = 0; h < MAX_HWIFS; ++h) {
+ ide_hwif_t *hwif = &ide_hwifs[h];
+ if (hwif->present && major == hwif->major) {
+ unsigned unit = DEVICE_NR(i_rdev);
+ if (unit < MAX_DRIVES) {
+ ide_drive_t *drive = &hwif->drives[unit];
+#if 0
+ if ((drive->present) && (drive->part[minor].nr_sects))
+#else
+ if (drive->present)
+#endif
+ return drive;
+ }
+ break;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * This function is intended to be used prior to invoking ide_do_drive_cmd().
+ */
+void ide_init_drive_cmd (struct request *rq)
+{
+ rq->buffer = NULL;
+ rq->cmd = IDE_DRIVE_CMD;
+ rq->sector = 0;
+ rq->nr_sectors = 0;
+ rq->current_nr_sectors = 0;
+ rq->sem = NULL;
+ rq->bh = NULL;
+ rq->bhtail = NULL;
+ rq->q = NULL;
+}
+
+/*
+ * This function issues a special IDE device request
+ * onto the request queue.
+ *
+ * If action is ide_wait, then the rq is queued at the end of the
+ * request queue, and the function sleeps until it has been processed.
+ * This is for use when invoked from an ioctl handler.
+ *
+ * If action is ide_preempt, then the rq is queued at the head of
+ * the request queue, displacing the currently-being-processed
+ * request and this function returns immediately without waiting
+ * for the new rq to be completed. This is VERY DANGEROUS, and is
+ * intended for careful use by the ATAPI tape/cdrom driver code.
+ *
+ * If action is ide_next, then the rq is queued immediately after
+ * the currently-being-processed-request (if any), and the function
+ * returns without waiting for the new rq to be completed. As above,
+ * This is VERY DANGEROUS, and is intended for careful use by the
+ * ATAPI tape/cdrom driver code.
+ *
+ * If action is ide_end, then the rq is queued at the end of the
+ * request queue, and the function returns immediately without waiting
+ * for the new rq to be completed. This is again intended for careful
+ * use by the ATAPI tape/cdrom driver code.
+ */
+int ide_do_drive_cmd (ide_drive_t *drive, struct request *rq, ide_action_t action)
+{
+ unsigned long flags;
+ ide_hwgroup_t *hwgroup = HWGROUP(drive);
+ unsigned int major = HWIF(drive)->major;
+ struct list_head * queue_head;
+ DECLARE_MUTEX_LOCKED(sem);
+
+#ifdef CONFIG_BLK_DEV_PDC4030
+ if (HWIF(drive)->chipset == ide_pdc4030 && rq->buffer != NULL)
+ return -ENOSYS; /* special drive cmds not supported */
+#endif
+ rq->errors = 0;
+ rq->rq_status = RQ_ACTIVE;
+ rq->rq_dev = MKDEV(major,(drive->select.b.unit)<<PARTN_BITS);
+ if (action == ide_wait)
+ rq->sem = &sem;
+ spin_lock_irqsave(&io_request_lock, flags);
+ queue_head = &drive->queue.queue_head;
+ if (list_empty(queue_head) || action == ide_preempt) {
+ if (action == ide_preempt)
+ hwgroup->rq = NULL;
+ } else {
+ if (action == ide_wait || action == ide_end) {
+ queue_head = queue_head->prev;
+ } else
+ queue_head = queue_head->next;
+ }
+ list_add(&rq->queue, queue_head);
+ ide_do_request(hwgroup, 0);
+ spin_unlock_irqrestore(&io_request_lock, flags);
+ if (action == ide_wait) {
+ down(&sem); /* wait for it to be serviced */
+ return rq->errors ? -EIO : 0; /* return -EIO if errors */
+ }
+ return 0;
+
+}
+
+/*
+ * This routine is called to flush all partitions and partition tables
+ * for a changed disk, and then re-read the new partition table.
+ * If we are revalidating a disk because of a media change, then we
+ * enter with usage == 0. If we are using an ioctl, we automatically have
+ * usage == 1 (we need an open channel to use an ioctl :-), so this
+ * is our limit.
+ */
+int ide_revalidate_disk (kdev_t i_rdev)
+{
+ ide_drive_t *drive;
+ ide_hwgroup_t *hwgroup;
+ unsigned int p, major, minor;
+ long flags;
+
+ if ((drive = get_info_ptr(i_rdev)) == NULL)
+ return -ENODEV;
+ major = MAJOR(i_rdev);
+ minor = drive->select.b.unit << PARTN_BITS;
+ hwgroup = HWGROUP(drive);
+ spin_lock_irqsave(&io_request_lock, flags);
+ if (drive->busy || (drive->usage > 1)) {
+ spin_unlock_irqrestore(&io_request_lock, flags);
+ return -EBUSY;
+ };
+ drive->busy = 1;
+ MOD_INC_USE_COUNT;
+ spin_unlock_irqrestore(&io_request_lock, flags);
+
+ for (p = 0; p < (1<<PARTN_BITS); ++p) {
+ if (drive->part[p].nr_sects > 0) {
+ kdev_t devp = MKDEV(major, minor+p);
+ struct super_block * sb = get_super(devp);
+ fsync_dev (devp);
+ if (sb)
+ invalidate_inodes(sb);
+ invalidate_buffers (devp);
+ set_blocksize(devp, 1024);
+ }
+ drive->part[p].start_sect = 0;
+ drive->part[p].nr_sects = 0;
+ };
+
+ grok_partitions(HWIF(drive)->gd, drive->select.b.unit,
+ (drive->media != ide_disk &&
+ drive->media != ide_floppy) ? 1 : 1<<PARTN_BITS,
+ current_capacity(drive));
+
+ drive->busy = 0;
+ wake_up(&drive->wqueue);
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static void revalidate_drives (void)
+{
+ ide_hwif_t *hwif;
+ ide_drive_t *drive;
+ int index, unit;
+
+ for (index = 0; index < MAX_HWIFS; ++index) {
+ hwif = &ide_hwifs[index];
+ for (unit = 0; unit < MAX_DRIVES; ++unit) {
+ drive = &ide_hwifs[index].drives[unit];
+ if (drive->revalidate) {
+ drive->revalidate = 0;
+ if (!initializing)
+ (void) ide_revalidate_disk(MKDEV(hwif->major, unit<<PARTN_BITS));
+ }
+ }
+ }
+}
+
+static void ide_probe_module (void)
+{
+ if (!ide_probe) {
+#ifdef CONFIG_KMOD
+ (void) request_module("ide-probe-mod");
+#endif /* CONFIG_KMOD */
+ } else {
+ (void) ide_probe->init();
+ }
+ revalidate_drives();
+}
+
+static void ide_driver_module (void)
+{
+ int index;
+ ide_module_t *module = ide_modules;
+
+ for (index = 0; index < MAX_HWIFS; ++index)
+ if (ide_hwifs[index].present)
+ goto search;
+ ide_probe_module();
+search:
+ while (module) {
+ (void) module->init();
+ module = module->next;
+ }
+ revalidate_drives();
+}
+
+static int ide_open (struct inode * inode, struct file * filp)
+{
+ ide_drive_t *drive;
+ int rc;
+
+ if ((drive = get_info_ptr(inode->i_rdev)) == NULL)
+ return -ENXIO;
+ MOD_INC_USE_COUNT;
+ if (drive->driver == NULL)
+ ide_driver_module();
+#ifdef CONFIG_KMOD
+ if (drive->driver == NULL) {
+ if (drive->media == ide_disk)
+ (void) request_module("ide-disk");
+ if (drive->media == ide_cdrom)
+ (void) request_module("ide-cd");
+ if (drive->media == ide_tape)
+ (void) request_module("ide-tape");
+ if (drive->media == ide_floppy)
+ (void) request_module("ide-floppy");
+ }
+#endif /* CONFIG_KMOD */
+ while (drive->busy)
+ sleep_on(&drive->wqueue);
+ drive->usage++;
+ if (drive->driver != NULL) {
+ if ((rc = DRIVER(drive)->open(inode, filp, drive)))
+ MOD_DEC_USE_COUNT;
+ return rc;
+ }
+ printk ("%s: driver not present\n", drive->name);
+ drive->usage--;
+ MOD_DEC_USE_COUNT;
+ return -ENXIO;
+}
+
+/*
+ * Releasing a block device means we sync() it, so that it can safely
+ * be forgotten about...
+ */
+static int ide_release (struct inode * inode, struct file * file)
+{
+ ide_drive_t *drive;
+
+ if ((drive = get_info_ptr(inode->i_rdev)) != NULL) {
+ drive->usage--;
+ if (drive->driver != NULL)
+ DRIVER(drive)->release(inode, file, drive);
+ MOD_DEC_USE_COUNT;
+ }
+ return 0;
+}
+
+int ide_replace_subdriver (ide_drive_t *drive, const char *driver)
+{
+ if (!drive->present || drive->busy || drive->usage)
+ goto abort;
+ if (drive->driver != NULL && DRIVER(drive)->cleanup(drive))
+ goto abort;
+ strncpy(drive->driver_req, driver, 9);
+ ide_driver_module();
+ drive->driver_req[0] = 0;
+ ide_driver_module();
+ if (DRIVER(drive) && !strcmp(DRIVER(drive)->name, driver))
+ return 0;
+abort:
+ return 1;
+}
+
+#ifdef CONFIG_PROC_FS
+ide_proc_entry_t generic_subdriver_entries[] = {
+ { "capacity", S_IFREG|S_IRUGO, proc_ide_read_capacity, NULL },
+ { NULL, 0, NULL, NULL }
+};
+#endif
+
+/*
+ * Note that we only release the standard ports,
+ * and do not even try to handle any extra ports
+ * allocated for weird IDE interface chipsets.
+ */
+void hwif_unregister (ide_hwif_t *hwif)
+{
+ if (hwif->straight8) {
+ ide_release_region(hwif->io_ports[IDE_DATA_OFFSET], 8);
+ goto jump_eight;
+ }
+ if (hwif->io_ports[IDE_DATA_OFFSET])
+ ide_release_region(hwif->io_ports[IDE_DATA_OFFSET], 1);
+ if (hwif->io_ports[IDE_ERROR_OFFSET])
+ ide_release_region(hwif->io_ports[IDE_ERROR_OFFSET], 1);
+ if (hwif->io_ports[IDE_NSECTOR_OFFSET])
+ ide_release_region(hwif->io_ports[IDE_NSECTOR_OFFSET], 1);
+ if (hwif->io_ports[IDE_SECTOR_OFFSET])
+ ide_release_region(hwif->io_ports[IDE_SECTOR_OFFSET], 1);
+ if (hwif->io_ports[IDE_LCYL_OFFSET])
+ ide_release_region(hwif->io_ports[IDE_LCYL_OFFSET], 1);
+ if (hwif->io_ports[IDE_HCYL_OFFSET])
+ ide_release_region(hwif->io_ports[IDE_HCYL_OFFSET], 1);
+ if (hwif->io_ports[IDE_SELECT_OFFSET])
+ ide_release_region(hwif->io_ports[IDE_SELECT_OFFSET], 1);
+ if (hwif->io_ports[IDE_STATUS_OFFSET])
+ ide_release_region(hwif->io_ports[IDE_STATUS_OFFSET], 1);
+jump_eight:
+ if (hwif->io_ports[IDE_CONTROL_OFFSET])
+ ide_release_region(hwif->io_ports[IDE_CONTROL_OFFSET], 1);
+ if (hwif->io_ports[IDE_IRQ_OFFSET])
+ ide_release_region(hwif->io_ports[IDE_IRQ_OFFSET], 1);
+}
+
+void ide_unregister (unsigned int index)
+{
+ struct gendisk *gd, **gdp;
+ ide_drive_t *drive, *d;
+ ide_hwif_t *hwif, *g;
+ ide_hwgroup_t *hwgroup;
+ int irq_count = 0, unit, i;
+ unsigned long flags;
+ unsigned int p, minor;
+ ide_hwif_t old_hwif;
+
+ if (index >= MAX_HWIFS)
+ return;
+ save_flags(flags); /* all CPUs */
+ cli(); /* all CPUs */
+ hwif = &ide_hwifs[index];
+ if (!hwif->present)
+ goto abort;
+ for (unit = 0; unit < MAX_DRIVES; ++unit) {
+ drive = &hwif->drives[unit];
+ if (!drive->present)
+ continue;
+ if (drive->busy || drive->usage)
+ goto abort;
+ if (drive->driver != NULL && DRIVER(drive)->cleanup(drive))
+ goto abort;
+ }
+ hwif->present = 0;
+
+ /*
+ * All clear? Then blow away the buffer cache
+ */
+ sti();
+ for (unit = 0; unit < MAX_DRIVES; ++unit) {
+ drive = &hwif->drives[unit];
+ if (!drive->present)
+ continue;
+ minor = drive->select.b.unit << PARTN_BITS;
+ for (p = 0; p < (1<<PARTN_BITS); ++p) {
+ if (drive->part[p].nr_sects > 0) {
+ kdev_t devp = MKDEV(hwif->major, minor+p);
+ struct super_block * sb = get_super(devp);
+ if (sb) invalidate_inodes(sb);
+ invalidate_buffers (devp);
+ }
+ }
+#ifdef CONFIG_PROC_FS
+ destroy_proc_ide_drives(hwif);
+#endif
+ }
+ cli();
+ hwgroup = hwif->hwgroup;
+
+ /*
+ * free the irq if we were the only hwif using it
+ */
+ g = hwgroup->hwif;
+ do {
+ if (g->irq == hwif->irq)
+ ++irq_count;
+ g = g->next;
+ } while (g != hwgroup->hwif);
+ if (irq_count == 1)
+ free_irq(hwif->irq, hwgroup);
+
+ /*
+ * Note that we only release the standard ports,
+ * and do not even try to handle any extra ports
+ * allocated for weird IDE interface chipsets.
+ */
+ hwif_unregister(hwif);
+
+ /*
+ * Remove us from the hwgroup, and free
+ * the hwgroup if we were the only member
+ */
+ d = hwgroup->drive;
+ for (i = 0; i < MAX_DRIVES; ++i) {
+ drive = &hwif->drives[i];
+ if (drive->de) {
+ devfs_unregister (drive->de);
+ drive->de = NULL;
+ }
+ if (!drive->present)
+ continue;
+ while (hwgroup->drive->next != drive)
+ hwgroup->drive = hwgroup->drive->next;
+ hwgroup->drive->next = drive->next;
+ if (hwgroup->drive == drive)
+ hwgroup->drive = NULL;
+ if (drive->id != NULL) {
+ kfree(drive->id);
+ drive->id = NULL;
+ }
+ drive->present = 0;
+ }
+ if (d->present)
+ hwgroup->drive = d;
+ while (hwgroup->hwif->next != hwif)
+ hwgroup->hwif = hwgroup->hwif->next;
+ hwgroup->hwif->next = hwif->next;
+ if (hwgroup->hwif == hwif)
+ kfree(hwgroup);
+ else
+ hwgroup->hwif = HWIF(hwgroup->drive);
+
+#ifdef CONFIG_BLK_DEV_IDEDMA
+ if (hwif->dma_base)
+ (void) ide_release_dma(hwif);
+#endif /* CONFIG_BLK_DEV_IDEDMA */
+
+ /*
+ * Remove us from the kernel's knowledge
+ */
+ unregister_blkdev(hwif->major, hwif->name);
+ kfree(blksize_size[hwif->major]);
+ kfree(max_sectors[hwif->major]);
+ kfree(max_readahead[hwif->major]);
+ blk_cleanup_queue(BLK_DEFAULT_QUEUE(hwif->major));
+ blk_dev[hwif->major].data = NULL;
+ blk_dev[hwif->major].queue = NULL;
+ blksize_size[hwif->major] = NULL;
+ for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next))
+ if (*gdp == hwif->gd)
+ break;
+ if (*gdp == NULL)
+ printk("gd not in disk chain!\n");
+ else {
+ gd = *gdp; *gdp = gd->next;
+ kfree(gd->sizes);
+ kfree(gd->part);
+ if (gd->de_arr)
+ kfree (gd->de_arr);
+ if (gd->flags)
+ kfree (gd->flags);
+ kfree(gd);
+ }
+ old_hwif = *hwif;
+ init_hwif_data (index); /* restore hwif data to pristine status */
+ hwif->hwgroup = old_hwif.hwgroup;
+ hwif->tuneproc = old_hwif.tuneproc;
+ hwif->selectproc = old_hwif.selectproc;
+ hwif->resetproc = old_hwif.resetproc;
+ hwif->dmaproc = old_hwif.dmaproc;
+ hwif->dma_base = old_hwif.dma_base;
+ hwif->dma_extra = old_hwif.dma_extra;
+ hwif->config_data = old_hwif.config_data;
+ hwif->select_data = old_hwif.select_data;
+ hwif->proc = old_hwif.proc;
+ hwif->irq = old_hwif.irq;
+ hwif->major = old_hwif.major;
+ hwif->chipset = old_hwif.chipset;
+ hwif->autodma = old_hwif.autodma;
+ hwif->udma_four = old_hwif.udma_four;
+#ifdef CONFIG_BLK_DEV_IDEPCI
+ hwif->pci_dev = old_hwif.pci_dev;
+ hwif->pci_devid = old_hwif.pci_devid;
+#endif /* CONFIG_BLK_DEV_IDEPCI */
+ hwif->straight8 = old_hwif.straight8;
+
+abort:
+ restore_flags(flags); /* all CPUs */
+}
+
+/*
+ * Setup hw_regs_t structure described by parameters. You
+ * may set up the hw structure yourself OR use this routine to
+ * do it for you.
+ */
+void ide_setup_ports ( hw_regs_t *hw,
+ ide_ioreg_t base, int *offsets,
+ ide_ioreg_t ctrl, ide_ioreg_t intr,
+ ide_ack_intr_t *ack_intr, int irq)
+{
+ int i;
+
+ for (i = 0; i < IDE_NR_PORTS; i++) {
+ if (offsets[i] == -1) {
+ switch(i) {
+ case IDE_CONTROL_OFFSET:
+ hw->io_ports[i] = ctrl;
+ break;
+ case IDE_IRQ_OFFSET:
+ hw->io_ports[i] = intr;
+ break;
+ default:
+ hw->io_ports[i] = 0;
+ break;
+ }
+ } else {
+ hw->io_ports[i] = base + offsets[i];
+ }
+ }
+ hw->irq = irq;
+ hw->dma = NO_DMA;
+ hw->ack_intr = ack_intr;
+}
+
+/*
+ * Register an IDE interface, specifing exactly the registers etc
+ * Set init=1 iff calling before probes have taken place.
+ */
+int ide_register_hw (hw_regs_t *hw, ide_hwif_t **hwifp)
+{
+ int index, retry = 1;
+ ide_hwif_t *hwif;
+
+ do {
+ for (index = 0; index < MAX_HWIFS; ++index) {
+ hwif = &ide_hwifs[index];
+ if (hwif->hw.io_ports[IDE_DATA_OFFSET] == hw->io_ports[IDE_DATA_OFFSET])
+ goto found;
+ }
+ for (index = 0; index < MAX_HWIFS; ++index) {
+ hwif = &ide_hwifs[index];
+ if ((!hwif->present && !hwif->mate && !initializing) ||
+ (!hwif->hw.io_ports[IDE_DATA_OFFSET] && initializing))
+ goto found;
+ }
+ for (index = 0; index < MAX_HWIFS; index++)
+ ide_unregister(index);
+ } while (retry--);
+ return -1;
+found:
+ if (hwif->present)
+ ide_unregister(index);
+ if (hwif->present)
+ return -1;
+ memcpy(&hwif->hw, hw, sizeof(*hw));
+ memcpy(hwif->io_ports, hwif->hw.io_ports, sizeof(hwif->hw.io_ports));
+ hwif->irq = hw->irq;
+ hwif->noprobe = 0;
+
+ if (!initializing) {
+ ide_probe_module();
+#ifdef CONFIG_PROC_FS
+ create_proc_ide_interfaces();
+#endif
+ ide_driver_module();
+ }
+
+ if (hwifp)
+ *hwifp = hwif;
+
+ return (initializing || hwif->present) ? index : -1;
+}
+
+/*
+ * Compatability function with existing drivers. If you want
+ * something different, use the function above.
+ */
+int ide_register (int arg1, int arg2, int irq)
+{
+ hw_regs_t hw;
+ ide_init_hwif_ports(&hw, (ide_ioreg_t) arg1, (ide_ioreg_t) arg2, NULL);
+ hw.irq = irq;
+ return ide_register_hw(&hw, NULL);
+}
+
+void ide_add_setting (ide_drive_t *drive, const char *name, int rw, int read_ioctl, int write_ioctl, int data_type, int min, int max, int mul_factor, int div_factor, void *data, ide_procset_t *set)
+{
+ ide_settings_t **p = (ide_settings_t **) &drive->settings, *setting = NULL;
+
+ while ((*p) && strcmp((*p)->name, name) < 0)
+ p = &((*p)->next);
+ if ((setting = kmalloc(sizeof(*setting), GFP_KERNEL)) == NULL)
+ goto abort;
+ memset(setting, 0, sizeof(*setting));
+ if ((setting->name = kmalloc(strlen(name) + 1, GFP_KERNEL)) == NULL)
+ goto abort;
+ strcpy(setting->name, name); setting->rw = rw;
+ setting->read_ioctl = read_ioctl; setting->write_ioctl = write_ioctl;
+ setting->data_type = data_type; setting->min = min;
+ setting->max = max; setting->mul_factor = mul_factor;
+ setting->div_factor = div_factor; setting->data = data;
+ setting->set = set; setting->next = *p;
+ if (drive->driver)
+ setting->auto_remove = 1;
+ *p = setting;
+ return;
+abort:
+ if (setting)
+ kfree(setting);
+}
+
+void ide_remove_setting (ide_drive_t *drive, char *name)
+{
+ ide_settings_t **p = (ide_settings_t **) &drive->settings, *setting;
+
+ while ((*p) && strcmp((*p)->name, name))
+ p = &((*p)->next);
+ if ((setting = (*p)) == NULL)
+ return;
+ (*p) = setting->next;
+ kfree(setting->name);
+ kfree(setting);
+}
+
+static ide_settings_t *ide_find_setting_by_ioctl (ide_drive_t *drive, int cmd)
+{
+ ide_settings_t *setting = drive->settings;
+
+ while (setting) {
+ if (setting->read_ioctl == cmd || setting->write_ioctl == cmd)
+ break;
+ setting = setting->next;
+ }
+ return setting;
+}
+
+ide_settings_t *ide_find_setting_by_name (ide_drive_t *drive, char *name)
+{
+ ide_settings_t *setting = drive->settings;
+
+ while (setting) {
+ if (strcmp(setting->name, name) == 0)
+ break;
+ setting = setting->next;
+ }
+ return setting;
+}
+
+static void auto_remove_settings (ide_drive_t *drive)
+{
+ ide_settings_t *setting;
+repeat:
+ setting = drive->settings;
+ while (setting) {
+ if (setting->auto_remove) {
+ ide_remove_setting(drive, setting->name);
+ goto repeat;
+ }
+ setting = setting->next;
+ }
+}
+
+int ide_read_setting (ide_drive_t *drive, ide_settings_t *setting)
+{
+ int val = -EINVAL;
+ unsigned long flags;
+
+ if ((setting->rw & SETTING_READ)) {
+ spin_lock_irqsave(&io_request_lock, flags);
+ switch(setting->data_type) {
+ case TYPE_BYTE:
+ val = *((u8 *) setting->data);
+ break;
+ case TYPE_SHORT:
+ val = *((u16 *) setting->data);
+ break;
+ case TYPE_INT:
+ case TYPE_INTA:
+ val = *((u32 *) setting->data);
+ break;
+ }
+ spin_unlock_irqrestore(&io_request_lock, flags);
+ }
+ return val;
+}
+
+int ide_spin_wait_hwgroup (ide_drive_t *drive, unsigned long *flags)
+{
+ ide_hwgroup_t *hwgroup = HWGROUP(drive);
+ unsigned long timeout = jiffies + (3 * HZ);
+
+ spin_lock_irqsave(&io_request_lock, *flags);
+ while (hwgroup->busy) {
+ unsigned long lflags;
+ spin_unlock_irqrestore(&io_request_lock, *flags);
+ __save_flags(lflags); /* local CPU only */
+ __sti(); /* local CPU only; needed for jiffies */
+ if (0 < (signed long)(jiffies - timeout)) {
+ __restore_flags(lflags); /* local CPU only */
+ printk("%s: channel busy\n", drive->name);
+ return -EBUSY;
+ }
+ __restore_flags(lflags); /* local CPU only */
+ spin_lock_irqsave(&io_request_lock, *flags);
+ }
+ return 0;
+}
+
+/*
+ * FIXME: This should be changed to enqueue a special request
+ * to the driver to change settings, and then wait on a sema for completion.
+ * The current scheme of polling is kludgey, though safe enough.
+ */
+int ide_write_setting (ide_drive_t *drive, ide_settings_t *setting, int val)
+{
+ unsigned long flags;
+ int i;
+ u32 *p;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+ if (!(setting->rw & SETTING_WRITE))
+ return -EPERM;
+ if (val < setting->min || val > setting->max)
+ return -EINVAL;
+ if (setting->set)
+ return setting->set(drive, val);
+ if (ide_spin_wait_hwgroup(drive, &flags))
+ return -EBUSY;
+ switch (setting->data_type) {
+ case TYPE_BYTE:
+ *((u8 *) setting->data) = val;
+ break;
+ case TYPE_SHORT:
+ *((u16 *) setting->data) = val;
+ break;
+ case TYPE_INT:
+ *((u32 *) setting->data) = val;
+ break;
+ case TYPE_INTA:
+ p = (u32 *) setting->data;
+ for (i = 0; i < 1 << PARTN_BITS; i++, p++)
+ *p = val;
+ break;
+ }
+ spin_unlock_irqrestore(&io_request_lock, flags);
+ return 0;
+}
+
+static int set_io_32bit(ide_drive_t *drive, int arg)
+{
+ drive->io_32bit = arg;
+#ifdef CONFIG_BLK_DEV_DTC2278
+ if (HWIF(drive)->chipset == ide_dtc2278)
+ HWIF(drive)->drives[!drive->select.b.unit].io_32bit = arg;
+#endif /* CONFIG_BLK_DEV_DTC2278 */
+ return 0;
+}
+
+static int set_using_dma (ide_drive_t *drive, int arg)
+{
+ if (!drive->driver || !DRIVER(drive)->supports_dma)
+ return -EPERM;
+ if (!drive->id || !(drive->id->capability & 1) || !HWIF(drive)->dmaproc)
+ return -EPERM;
+ if (HWIF(drive)->dmaproc(arg ? ide_dma_on : ide_dma_off, drive))
+ return -EIO;
+ return 0;
+}
+
+static int set_pio_mode (ide_drive_t *drive, int arg)
+{
+ struct request rq;
+
+ if (!HWIF(drive)->tuneproc)
+ return -ENOSYS;
+ if (drive->special.b.set_tune)
+ return -EBUSY;
+ ide_init_drive_cmd(&rq);
+ drive->tune_req = (byte) arg;
+ drive->special.b.set_tune = 1;
+ (void) ide_do_drive_cmd (drive, &rq, ide_wait);
+ return 0;
+}
+
+void ide_add_generic_settings (ide_drive_t *drive)
+{
+/*
+ * drive setting name read/write access read ioctl write ioctl data type min max mul_factor div_factor data pointer set function
+ */
+ ide_add_setting(drive, "io_32bit", drive->no_io_32bit ? SETTING_READ : SETTING_RW, HDIO_GET_32BIT, HDIO_SET_32BIT, TYPE_BYTE, 0, 1 + (SUPPORT_VLB_SYNC << 1), 1, 1, &drive->io_32bit, set_io_32bit);
+ ide_add_setting(drive, "keepsettings", SETTING_RW, HDIO_GET_KEEPSETTINGS, HDIO_SET_KEEPSETTINGS, TYPE_BYTE, 0, 1, 1, 1, &drive->keep_settings, NULL);
+ ide_add_setting(drive, "nice1", SETTING_RW, -1, -1, TYPE_BYTE, 0, 1, 1, 1, &drive->nice1, NULL);
+ ide_add_setting(drive, "pio_mode", SETTING_WRITE, -1, HDIO_SET_PIO_MODE, TYPE_BYTE, 0, 255, 1, 1, NULL, set_pio_mode);
+ ide_add_setting(drive, "slow", SETTING_RW, -1, -1, TYPE_BYTE, 0, 1, 1, 1, &drive->slow, NULL);
+ ide_add_setting(drive, "unmaskirq", drive->no_unmask ? SETTING_READ : SETTING_RW, HDIO_GET_UNMASKINTR, HDIO_SET_UNMASKINTR, TYPE_BYTE, 0, 1, 1, 1, &drive->unmask, NULL);
+ ide_add_setting(drive, "using_dma", SETTING_RW, HDIO_GET_DMA, HDIO_SET_DMA, TYPE_BYTE, 0, 1, 1, 1, &drive->using_dma, set_using_dma);
+ ide_add_setting(drive, "ide_scsi", SETTING_RW, -1, -1, TYPE_BYTE, 0, 1, 1, 1, &drive->scsi, NULL);
+}
+
+int ide_wait_cmd (ide_drive_t *drive, int cmd, int nsect, int feature, int sectors, byte *buf)
+{
+ struct request rq;
+ byte buffer[4];
+
+ if (!buf)
+ buf = buffer;
+ memset(buf, 0, 4 + SECTOR_WORDS * 4 * sectors);
+ ide_init_drive_cmd(&rq);
+ rq.buffer = buf;
+ *buf++ = cmd;
+ *buf++ = nsect;
+ *buf++ = feature;
+ *buf++ = sectors;
+ return ide_do_drive_cmd(drive, &rq, ide_wait);
+}
+
+/*
+ * Delay for *at least* 50ms. As we don't know how much time is left
+ * until the next tick occurs, we wait an extra tick to be safe.
+ * This is used only during the probing/polling for drives at boot time.
+ *
+ * However, its usefullness may be needed in other places, thus we export it now.
+ * The future may change this to a millisecond setable delay.
+ */
+void ide_delay_50ms (void)
+{
+ unsigned long timeout = jiffies + ((HZ + 19)/20) + 1;
+ while (0 < (signed long)(timeout - jiffies));
+}
+
+static int ide_ioctl (struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int err = 0, major, minor;
+ ide_drive_t *drive;
+ struct request rq;
+ kdev_t dev;
+ ide_settings_t *setting;
+
+ if (!inode || !(dev = inode->i_rdev))
+ return -EINVAL;
+ major = MAJOR(dev); minor = MINOR(dev);
+ if ((drive = get_info_ptr(inode->i_rdev)) == NULL)
+ return -ENODEV;
+
+ if ((setting = ide_find_setting_by_ioctl(drive, cmd)) != NULL) {
+ if (cmd == setting->read_ioctl) {
+ err = ide_read_setting(drive, setting);
+ return err >= 0 ? put_user(err, (long *) arg) : err;
+ } else {
+ if ((MINOR(inode->i_rdev) & PARTN_MASK))
+ return -EINVAL;
+ return ide_write_setting(drive, setting, arg);
+ }
+ }
+
+ ide_init_drive_cmd (&rq);
+ switch (cmd) {
+ case HDIO_GETGEO:
+ {
+ struct hd_geometry *loc = (struct hd_geometry *) arg;
+ unsigned short bios_cyl = drive->bios_cyl; /* truncate */
+ if (!loc || (drive->media != ide_disk && drive->media != ide_floppy)) return -EINVAL;
+ if (put_user(drive->bios_head, (byte *) &loc->heads)) return -EFAULT;
+ if (put_user(drive->bios_sect, (byte *) &loc->sectors)) return -EFAULT;
+ if (put_user(bios_cyl, (unsigned short *) &loc->cylinders)) return -EFAULT;
+ if (put_user((unsigned)drive->part[MINOR(inode->i_rdev)&PARTN_MASK].start_sect,
+ (unsigned long *) &loc->start)) return -EFAULT;
+ return 0;
+ }
+
+ case BLKGETSIZE: /* Return device size */
+ return put_user(drive->part[MINOR(inode->i_rdev)&PARTN_MASK].nr_sects, (long *) arg);
+
+ case BLKRRPART: /* Re-read partition tables */
+ if (!capable(CAP_SYS_ADMIN)) return -EACCES;
+ return ide_revalidate_disk(inode->i_rdev);
+
+ case HDIO_OBSOLETE_IDENTITY:
+ case HDIO_GET_IDENTITY:
+ if (MINOR(inode->i_rdev) & PARTN_MASK)
+ return -EINVAL;
+ if (drive->id == NULL)
+ return -ENOMSG;
+ if (copy_to_user((char *)arg, (char *)drive->id, (cmd == HDIO_GET_IDENTITY) ? sizeof(*drive->id) : 142))
+ return -EFAULT;
+ return 0;
+
+ case HDIO_GET_NICE:
+ return put_user(drive->dsc_overlap << IDE_NICE_DSC_OVERLAP |
+ drive->atapi_overlap << IDE_NICE_ATAPI_OVERLAP |
+ drive->nice0 << IDE_NICE_0 |
+ drive->nice1 << IDE_NICE_1 |
+ drive->nice2 << IDE_NICE_2,
+ (long *) arg);
+ case HDIO_DRIVE_CMD:
+ {
+ byte args[4], *argbuf = args;
+ int argsize = 4;
+ if (!capable(CAP_SYS_ADMIN)) return -EACCES;
+ if (NULL == (void *) arg)
+ return ide_do_drive_cmd(drive, &rq, ide_wait);
+ if (copy_from_user(args, (void *)arg, 4))
+ return -EFAULT;
+ if (args[3]) {
+ argsize = 4 + (SECTOR_WORDS * 4 * args[3]);
+ argbuf = kmalloc(argsize, GFP_KERNEL);
+ if (argbuf == NULL)
+ return -ENOMEM;
+ memcpy(argbuf, args, 4);
+ }
+ if (ide_ata66_check(drive, args[0], args[1], args[2]))
+ goto abort;
+
+ err = ide_wait_cmd(drive, args[0], args[1], args[2], args[3], argbuf);
+
+ if (!err && set_transfer(drive, args[0], args[1], args[2])) {
+#if 0
+ /* active-retuning-calls future */
+ if (HWIF(drive)->tune2proc)
+ HWIF(drive)->tune2proc(drive, args[1]);
+#endif
+ ide_driveid_update(drive);
+ }
+ abort:
+ if (copy_to_user((void *)arg, argbuf, argsize))
+ err = -EFAULT;
+ if (argsize > 4)
+ kfree(argbuf);
+ return err;
+ }
+
+ case HDIO_SCAN_HWIF:
+ {
+ int args[3];
+ if (!capable(CAP_SYS_ADMIN)) return -EACCES;
+ if (copy_from_user(args, (void *)arg, 3 * sizeof(int)))
+ return -EFAULT;
+ if (ide_register(args[0], args[1], args[2]) == -1)
+ return -EIO;
+ return 0;
+ }
+ case HDIO_UNREGISTER_HWIF:
+ if (!capable(CAP_SYS_ADMIN)) return -EACCES;
+ /* should I check here for arg > MAX_HWIFS, or
+ just let ide_unregister fail silently? -- shaver */
+ ide_unregister(arg);
+ return 0;
+ case HDIO_SET_NICE:
+ if (!capable(CAP_SYS_ADMIN)) return -EACCES;
+ if (drive->driver == NULL)
+ return -EPERM;
+ if (arg != (arg & ((1 << IDE_NICE_DSC_OVERLAP) | (1 << IDE_NICE_1))))
+ return -EPERM;
+ drive->dsc_overlap = (arg >> IDE_NICE_DSC_OVERLAP) & 1;
+ if (drive->dsc_overlap && !DRIVER(drive)->supports_dsc_overlap) {
+ drive->dsc_overlap = 0;
+ return -EPERM;
+ }
+ drive->nice1 = (arg >> IDE_NICE_1) & 1;
+ return 0;
+
+ case BLKROSET:
+ case BLKROGET:
+ case BLKFLSBUF:
+ case BLKSSZGET:
+ case BLKPG:
+ case BLKELVGET:
+ case BLKELVSET:
+ return blk_ioctl(inode->i_rdev, cmd, arg);
+
+ default:
+ if (drive->driver != NULL)
+ return DRIVER(drive)->ioctl(drive, inode, file, cmd, arg);
+ return -EPERM;
+ }
+}
+
+static int ide_check_media_change (kdev_t i_rdev)
+{
+ ide_drive_t *drive;
+
+ if ((drive = get_info_ptr(i_rdev)) == NULL)
+ return -ENODEV;
+ if (drive->driver != NULL)
+ return DRIVER(drive)->media_change(drive);
+ return 0;
+}
+
+void ide_fixstring (byte *s, const int bytecount, const int byteswap)
+{
+ byte *p = s, *end = &s[bytecount & ~1]; /* bytecount must be even */
+
+ if (byteswap) {
+ /* convert from big-endian to host byte order */
+ for (p = end ; p != s;) {
+ unsigned short *pp = (unsigned short *) (p -= 2);
+ *pp = ntohs(*pp);
+ }
+ }
+
+ /* strip leading blanks */
+ while (s != end && *s == ' ')
+ ++s;
+
+ /* compress internal blanks and strip trailing blanks */
+ while (s != end && *s) {
+ if (*s++ != ' ' || (s != end && *s && *s != ' '))
+ *p++ = *(s-1);
+ }
+
+ /* wipe out trailing garbage */
+ while (p != end)
+ *p++ = '\0';
+}
+
+/*
+ * stridx() returns the offset of c within s,
+ * or -1 if c is '\0' or not found within s.
+ */
+static int __init stridx (const char *s, char c)
+{
+ char *i = strchr(s, c);
+ return (i && c) ? i - s : -1;
+}
+
+/*
+ * match_parm() does parsing for ide_setup():
+ *
+ * 1. the first char of s must be '='.
+ * 2. if the remainder matches one of the supplied keywords,
+ * the index (1 based) of the keyword is negated and returned.
+ * 3. if the remainder is a series of no more than max_vals numbers
+ * separated by commas, the numbers are saved in vals[] and a
+ * count of how many were saved is returned. Base10 is assumed,
+ * and base16 is allowed when prefixed with "0x".
+ * 4. otherwise, zero is returned.
+ */
+static int __init match_parm (char *s, const char *keywords[], int vals[], int max_vals)
+{
+ static const char *decimal = "0123456789";
+ static const char *hex = "0123456789abcdef";
+ int i, n;
+
+ if (*s++ == '=') {
+ /*
+ * Try matching against the supplied keywords,
+ * and return -(index+1) if we match one
+ */
+ if (keywords != NULL) {
+ for (i = 0; *keywords != NULL; ++i) {
+ if (!strcmp(s, *keywords++))
+ return -(i+1);
+ }
+ }
+ /*
+ * Look for a series of no more than "max_vals"
+ * numeric values separated by commas, in base10,
+ * or base16 when prefixed with "0x".
+ * Return a count of how many were found.
+ */
+ for (n = 0; (i = stridx(decimal, *s)) >= 0;) {
+ vals[n] = i;
+ while ((i = stridx(decimal, *++s)) >= 0)
+ vals[n] = (vals[n] * 10) + i;
+ if (*s == 'x' && !vals[n]) {
+ while ((i = stridx(hex, *++s)) >= 0)
+ vals[n] = (vals[n] * 0x10) + i;
+ }
+ if (++n == max_vals)
+ break;
+ if (*s == ',' || *s == ';')
+ ++s;
+ }
+ if (!*s)
+ return n;
+ }
+ return 0; /* zero = nothing matched */
+}
+
+/*
+ * ide_setup() gets called VERY EARLY during initialization,
+ * to handle kernel "command line" strings beginning with "hdx="
+ * or "ide". Here is the complete set currently supported:
+ *
+ * "hdx=" is recognized for all "x" from "a" to "h", such as "hdc".
+ * "idex=" is recognized for all "x" from "0" to "3", such as "ide1".
+ *
+ * "hdx=noprobe" : drive may be present, but do not probe for it
+ * "hdx=none" : drive is NOT present, ignore cmos and do not probe
+ * "hdx=nowerr" : ignore the WRERR_STAT bit on this drive
+ * "hdx=cdrom" : drive is present, and is a cdrom drive
+ * "hdx=cyl,head,sect" : disk drive is present, with specified geometry
+ * "hdx=noremap" : do not remap 0->1 even though EZD was detected
+ * "hdx=autotune" : driver will attempt to tune interface speed
+ * to the fastest PIO mode supported,
+ * if possible for this drive only.
+ * Not fully supported by all chipset types,
+ * and quite likely to cause trouble with
+ * older/odd IDE drives.
+ *
+ * "hdx=slow" : insert a huge pause after each access to the data
+ * port. Should be used only as a last resort.
+ *
+ * "hdx=swapdata" : when the drive is a disk, byte swap all data
+ * "hdx=bswap" : same as above..........
+ * "hdxlun=xx" : set the drive last logical unit.
+ * "hdx=flash" : allows for more than one ata_flash disk to be
+ * registered. In most cases, only one device
+ * will be present.
+ * "hdx=scsi" : the return of the ide-scsi flag, this is useful for
+ * allowwing ide-floppy, ide-tape, and ide-cdrom|writers
+ * to use ide-scsi emulation on a device specific option.
+ * "idebus=xx" : inform IDE driver of VESA/PCI bus speed in MHz,
+ * where "xx" is between 20 and 66 inclusive,
+ * used when tuning chipset PIO modes.
+ * For PCI bus, 25 is correct for a P75 system,
+ * 30 is correct for P90,P120,P180 systems,
+ * and 33 is used for P100,P133,P166 systems.
+ * If in doubt, use idebus=33 for PCI.
+ * As for VLB, it is safest to not specify it.
+ *
+ * "idex=noprobe" : do not attempt to access/use this interface
+ * "idex=base" : probe for an interface at the addr specified,
+ * where "base" is usually 0x1f0 or 0x170
+ * and "ctl" is assumed to be "base"+0x206
+ * "idex=base,ctl" : specify both base and ctl
+ * "idex=base,ctl,irq" : specify base, ctl, and irq number
+ * "idex=autotune" : driver will attempt to tune interface speed
+ * to the fastest PIO mode supported,
+ * for all drives on this interface.
+ * Not fully supported by all chipset types,
+ * and quite likely to cause trouble with
+ * older/odd IDE drives.
+ * "idex=noautotune" : driver will NOT attempt to tune interface speed
+ * This is the default for most chipsets,
+ * except the cmd640.
+ * "idex=serialize" : do not overlap operations on idex and ide(x^1)
+ * "idex=four" : four drives on idex and ide(x^1) share same ports
+ * "idex=reset" : reset interface before first use
+ * "idex=dma" : enable DMA by default on both drives if possible
+ * "idex=ata66" : informs the interface that it has an 80c cable
+ * for chipsets that are ATA-66 capable, but
+ * the ablity to bit test for detection is
+ * currently unknown.
+ * "ide=reverse" : Formerly called to pci sub-system, but now local.
+ *
+ * "splitfifo=betweenChan"
+ * : FIFO Configuration of VIA 82c586(<nothing>,"A"or"B").
+ * --see what follows...
+ * "splitfifo=betweenChan,thresholdprim,thresholdsec"
+ * : FIFO Configuration of VIA 82c586(<nothing>,"A" or "B").
+ * betweenChan = 1(all FIFO's to primary channel)
+ * , 2(all FIFO's to secondary channel)
+ * , 3 or 4(evenly shared between them).
+ * note: without FIFO, a channel is (u)dma disabled!
+ * thresholdprim = 4, 3, 2 or 1
+ * (standing for 1, 3/4, 1/2, 1/4).
+ * Sets the threshold of FIFO to begin dma
+ * transfer on the primary channel.
+ * thresholdsec = cf upper, but for secondary channel.
+ *
+ * The following are valid ONLY on ide0, (except dc4030)
+ * and the defaults for the base,ctl ports must not be altered.
+ *
+ * "ide0=dtc2278" : probe/support DTC2278 interface
+ * "ide0=ht6560b" : probe/support HT6560B interface
+ * "ide0=cmd640_vlb" : *REQUIRED* for VLB cards with the CMD640 chip
+ * (not for PCI -- automatically detected)
+ * "ide0=qd6580" : probe/support qd6580 interface
+ * "ide0=ali14xx" : probe/support ali14xx chipsets (ALI M1439, M1443, M1445)
+ * "ide0=umc8672" : probe/support umc8672 chipsets
+ * "idex=dc4030" : probe/support Promise DC4030VL interface
+ * "ide=doubler" : probe/support IDE doublers on Amiga
+ */
+int __init ide_setup (char *s)
+{
+ int i, vals[3];
+ ide_hwif_t *hwif;
+ ide_drive_t *drive;
+ unsigned int hw, unit;
+ const char max_drive = 'a' + ((MAX_HWIFS * MAX_DRIVES) - 1);
+ const char max_hwif = '0' + (MAX_HWIFS - 1);
+
+ printk("ide_setup: %s", s);
+ init_ide_data ();
+
+#ifdef CONFIG_BLK_DEV_IDEDOUBLER
+ if (!strcmp(s, "ide=doubler")) {
+ extern int ide_doubler;
+
+ printk(" : Enabled support for IDE doublers\n");
+ ide_doubler = 1;
+ return 0;
+ }
+#endif /* CONFIG_BLK_DEV_IDEDOUBLER */
+
+#ifdef CONFIG_BLK_DEV_IDEPCI
+ if (!strcmp(s, "ide=reverse")) {
+ ide_scan_direction = 1;
+ printk(" : Enabled support for IDE inverse scan order.\n");
+ return 0;
+ }
+#endif /* CONFIG_BLK_DEV_IDEPCI */
+
+ /*
+ * Look for drive options: "hdx="
+ */
+ if (s[0] == 'h' && s[1] == 'd' && s[2] >= 'a' && s[2] <= max_drive) {
+ const char *hd_words[] = {"none", "noprobe", "nowerr", "cdrom",
+ "serialize", "autotune", "noautotune",
+ "slow", "swapdata", "bswap", "flash",
+ "remap", "noremap", "scsi", NULL};
+ unit = s[2] - 'a';
+ hw = unit / MAX_DRIVES;
+ unit = unit % MAX_DRIVES;
+ hwif = &ide_hwifs[hw];
+ drive = &hwif->drives[unit];
+ if (strncmp(s + 4, "ide-", 4) == 0) {
+ strncpy(drive->driver_req, s + 4, 9);
+ goto done;
+ }
+ /*
+ * Look for last lun option: "hdxlun="
+ */
+ if (s[3] == 'l' && s[4] == 'u' && s[5] == 'n') {
+ if (match_parm(&s[6], NULL, vals, 1) != 1)
+ goto bad_option;
+ if (vals[0] >= 0 && vals[0] <= 7) {
+ drive->last_lun = vals[0];
+ drive->forced_lun = 1;
+ } else
+ printk(" -- BAD LAST LUN! Expected value from 0 to 7");
+ goto done;
+ }
+ switch (match_parm(&s[3], hd_words, vals, 3)) {
+ case -1: /* "none" */
+ drive->nobios = 1; /* drop into "noprobe" */
+ case -2: /* "noprobe" */
+ drive->noprobe = 1;
+ goto done;
+ case -3: /* "nowerr" */
+ drive->bad_wstat = BAD_R_STAT;
+ hwif->noprobe = 0;
+ goto done;
+ case -4: /* "cdrom" */
+ drive->present = 1;
+ drive->media = ide_cdrom;
+ hwif->noprobe = 0;
+ goto done;
+ case -5: /* "serialize" */
+ printk(" -- USE \"ide%d=serialize\" INSTEAD", hw);
+ goto do_serialize;
+ case -6: /* "autotune" */
+ drive->autotune = 1;
+ goto done;
+ case -7: /* "noautotune" */
+ drive->autotune = 2;
+ goto done;
+ case -8: /* "slow" */
+ drive->slow = 1;
+ goto done;
+ case -9: /* "swapdata" or "bswap" */
+ case -10:
+ drive->bswap = 1;
+ goto done;
+ case -11: /* "flash" */
+ drive->ata_flash = 1;
+ goto done;
+ case -12: /* "remap" */
+ drive->remap_0_to_1 = 1;
+ goto done;
+ case -13: /* "noremap" */
+ drive->remap_0_to_1 = 2;
+ goto done;
+ case -14: /* "scsi" */
+#if defined(CONFIG_BLK_DEV_IDESCSI) && defined(CONFIG_SCSI)
+ drive->scsi = 1;
+ goto done;
+#else
+ drive->scsi = 0;
+ goto bad_option;
+#endif /* defined(CONFIG_BLK_DEV_IDESCSI) && defined(CONFIG_SCSI) */
+ case 3: /* cyl,head,sect */
+ drive->media = ide_disk;
+ drive->cyl = drive->bios_cyl = vals[0];
+ drive->head = drive->bios_head = vals[1];
+ drive->sect = drive->bios_sect = vals[2];
+ drive->present = 1;
+ drive->forced_geom = 1;
+ hwif->noprobe = 0;
+ goto done;
+ default:
+ goto bad_option;
+ }
+ }
+
+#if defined(CONFIG_BLK_DEV_VIA82CXXX)
+ /*
+ * Look for drive option "splitfifo=..."
+ */
+
+ if (s[0] == 's' && s[1] == 'p' && s[2] == 'l' &&
+ s[3] == 'i' && s[4] == 't' && s[5] == 'f' &&
+ s[6] == 'i' && s[7] == 'f' && s[8] == 'o') {
+ byte tmp = 0x3a; /* default config byte */
+
+ i = match_parm(&s[9], NULL, vals, 3);
+ switch(i) {
+ case 3:
+ tmp &= 0xf0;
+ if ((vals[1] > 0) && (vals[1] < 5)) {
+ /* sets threshold for primary Channel: */
+ byte x = 4 - vals[1];
+ tmp |= (x << 2);
+ }
+ else
+ goto bad_option;
+ if ((vals[2] > 0) && (vals[2] < 5)) {
+ /* sets threshold for secondary Channel: */
+ byte x = 4 - vals[2];
+ tmp |= x;
+ }
+ else
+ goto bad_option;
+ case 1:
+ /* set the FIFO config between channels to 0: */
+ tmp &= 0x9f;
+ /* set the needed FIFO config between channels: */
+ if (vals[0] == 1) /* primary fifo only */
+ tmp |= 0x10;
+ else if (vals[0] == 2) /* secondary fifo only */
+ tmp |= 0x70;
+ else if (vals[0] == 4) /* other shared fifo config */
+ tmp |= 0x50;
+ else if (vals[0] == 3) /* default config */
+ tmp |= 0x30;
+ else
+ goto bad_option;
+ break;
+ default:
+ goto bad_option;
+ }
+ /* set the found option in fifoconfig */
+ fifoconfig = tmp;
+ goto done;
+ }
+#endif /* defined(CONFIG_BLK_DEV_VIA82CXXX) */
+
+ if (s[0] != 'i' || s[1] != 'd' || s[2] != 'e')
+ goto bad_option;
+ /*
+ * Look for bus speed option: "idebus="
+ */
+ if (s[3] == 'b' && s[4] == 'u' && s[5] == 's') {
+ if (match_parm(&s[6], NULL, vals, 1) != 1)
+ goto bad_option;
+ if (vals[0] >= 20 && vals[0] <= 66) {
+ idebus_parameter = vals[0];
+ } else
+ printk(" -- BAD BUS SPEED! Expected value from 20 to 66");
+ goto done;
+ }
+ /*
+ * Look for interface options: "idex="
+ */
+ if (s[3] >= '0' && s[3] <= max_hwif) {
+ /*
+ * Be VERY CAREFUL changing this: note hardcoded indexes below
+ * -8,-9,-10 : are reserved for future idex calls to ease the hardcoding.
+ */
+ const char *ide_words[] = {
+ "noprobe", "serialize", "autotune", "noautotune", "reset", "dma", "ata66",
+ "minus8", "minus9", "minus10",
+ "four", "qd6580", "ht6560b", "cmd640_vlb", "dtc2278", "umc8672", "ali14xx", "dc4030", NULL };
+ hw = s[3] - '0';
+ hwif = &ide_hwifs[hw];
+ i = match_parm(&s[4], ide_words, vals, 3);
+
+ /*
+ * Cryptic check to ensure chipset not already set for hwif:
+ */
+ if (i > 0 || i <= -11) { /* is parameter a chipset name? */
+ if (hwif->chipset != ide_unknown)
+ goto bad_option; /* chipset already specified */
+ if (i <= -11 && i != -18 && hw != 0)
+ goto bad_hwif; /* chipset drivers are for "ide0=" only */
+ if (i <= -11 && i != -18 && ide_hwifs[hw+1].chipset != ide_unknown)
+ goto bad_option; /* chipset for 2nd port already specified */
+ printk("\n");
+ }
+
+ switch (i) {
+#ifdef CONFIG_BLK_DEV_PDC4030
+ case -18: /* "dc4030" */
+ {
+ extern void init_pdc4030(void);
+ init_pdc4030();
+ goto done;
+ }
+#endif /* CONFIG_BLK_DEV_PDC4030 */
+#ifdef CONFIG_BLK_DEV_ALI14XX
+ case -17: /* "ali14xx" */
+ {
+ extern void init_ali14xx (void);
+ init_ali14xx();
+ goto done;
+ }
+#endif /* CONFIG_BLK_DEV_ALI14XX */
+#ifdef CONFIG_BLK_DEV_UMC8672
+ case -16: /* "umc8672" */
+ {
+ extern void init_umc8672 (void);
+ init_umc8672();
+ goto done;
+ }
+#endif /* CONFIG_BLK_DEV_UMC8672 */
+#ifdef CONFIG_BLK_DEV_DTC2278
+ case -15: /* "dtc2278" */
+ {
+ extern void init_dtc2278 (void);
+ init_dtc2278();
+ goto done;
+ }
+#endif /* CONFIG_BLK_DEV_DTC2278 */
+#ifdef CONFIG_BLK_DEV_CMD640
+ case -14: /* "cmd640_vlb" */
+ {
+ extern int cmd640_vlb; /* flag for cmd640.c */
+ cmd640_vlb = 1;
+ goto done;
+ }
+#endif /* CONFIG_BLK_DEV_CMD640 */
+#ifdef CONFIG_BLK_DEV_HT6560B
+ case -13: /* "ht6560b" */
+ {
+ extern void init_ht6560b (void);
+ init_ht6560b();
+ goto done;
+ }
+#endif /* CONFIG_BLK_DEV_HT6560B */
+#if CONFIG_BLK_DEV_QD6580
+ case -12: /* "qd6580" */
+ {
+ extern void init_qd6580 (void);
+ init_qd6580();
+ goto done;
+ }
+#endif /* CONFIG_BLK_DEV_QD6580 */
+#ifdef CONFIG_BLK_DEV_4DRIVES
+ case -11: /* "four" drives on one set of ports */
+ {
+ ide_hwif_t *mate = &ide_hwifs[hw^1];
+ mate->drives[0].select.all ^= 0x20;
+ mate->drives[1].select.all ^= 0x20;
+ hwif->chipset = mate->chipset = ide_4drives;
+ mate->irq = hwif->irq;
+ memcpy(mate->io_ports, hwif->io_ports, sizeof(hwif->io_ports));
+ goto do_serialize;
+ }
+#endif /* CONFIG_BLK_DEV_4DRIVES */
+ case -10: /* minus10 */
+ case -9: /* minus9 */
+ case -8: /* minus8 */
+ goto bad_option;
+ case -7: /* ata66 */
+#ifdef CONFIG_BLK_DEV_IDEPCI
+ hwif->udma_four = 1;
+ goto done;
+#else /* !CONFIG_BLK_DEV_IDEPCI */
+ hwif->udma_four = 0;
+ goto bad_hwif;
+#endif /* CONFIG_BLK_DEV_IDEPCI */
+ case -6: /* dma */
+ hwif->autodma = 1;
+ goto done;
+ case -5: /* "reset" */
+ hwif->reset = 1;
+ goto done;
+ case -4: /* "noautotune" */
+ hwif->drives[0].autotune = 2;
+ hwif->drives[1].autotune = 2;
+ goto done;
+ case -3: /* "autotune" */
+ hwif->drives[0].autotune = 1;
+ hwif->drives[1].autotune = 1;
+ goto done;
+ case -2: /* "serialize" */
+ do_serialize:
+ hwif->mate = &ide_hwifs[hw^1];
+ hwif->mate->mate = hwif;
+ hwif->serialized = hwif->mate->serialized = 1;
+ goto done;
+
+ case -1: /* "noprobe" */
+ hwif->noprobe = 1;
+ goto done;
+
+ case 1: /* base */
+ vals[1] = vals[0] + 0x206; /* default ctl */
+ case 2: /* base,ctl */
+ vals[2] = 0; /* default irq = probe for it */
+ case 3: /* base,ctl,irq */
+ hwif->hw.irq = vals[2];
+ ide_init_hwif_ports(&hwif->hw, (ide_ioreg_t) vals[0], (ide_ioreg_t) vals[1], &hwif->irq);
+ memcpy(hwif->io_ports, hwif->hw.io_ports, sizeof(hwif->io_ports));
+ hwif->irq = vals[2];
+ hwif->noprobe = 0;
+ hwif->chipset = ide_generic;
+ goto done;
+
+ case 0: goto bad_option;
+ default:
+ printk(" -- SUPPORT NOT CONFIGURED IN THIS KERNEL\n");
+ return 0;
+ }
+ }
+bad_option:
+ printk(" -- BAD OPTION\n");
+ return 0;
+bad_hwif:
+ printk("-- NOT SUPPORTED ON ide%d", hw);
+done:
+ printk("\n");
+ return 0;
+}
+
+/*
+ * probe_for_hwifs() finds/initializes "known" IDE interfaces
+ */
+static void __init probe_for_hwifs (void)
+{
+#ifdef CONFIG_PCI
+ if (pci_present())
+ {
+#ifdef CONFIG_BLK_DEV_IDEPCI
+ ide_scan_pcibus(ide_scan_direction);
+#else
+#ifdef CONFIG_BLK_DEV_RZ1000
+ {
+ extern void ide_probe_for_rz100x(void);
+ ide_probe_for_rz100x();
+ }
+#endif /* CONFIG_BLK_DEV_RZ1000 */
+#endif /* CONFIG_BLK_DEV_IDEPCI */
+ }
+#endif /* CONFIG_PCI */
+
+#ifdef CONFIG_BLK_DEV_CMD640
+ {
+ extern void ide_probe_for_cmd640x(void);
+ ide_probe_for_cmd640x();
+ }
+#endif /* CONFIG_BLK_DEV_CMD640 */
+#ifdef CONFIG_BLK_DEV_PDC4030
+ {
+ extern int ide_probe_for_pdc4030(void);
+ (void) ide_probe_for_pdc4030();
+ }
+#endif /* CONFIG_BLK_DEV_PDC4030 */
+#ifdef CONFIG_BLK_DEV_IDE_PMAC
+ {
+ extern void pmac_ide_probe(void);
+ pmac_ide_probe();
+ }
+#endif /* CONFIG_BLK_DEV_IDE_PMAC */
+#ifdef CONFIG_BLK_DEV_IDE_ICSIDE
+ {
+ extern void icside_init(void);
+ icside_init();
+ }
+#endif /* CONFIG_BLK_DEV_IDE_ICSIDE */
+#ifdef CONFIG_BLK_DEV_IDE_RAPIDE
+ {
+ extern void rapide_init(void);
+ rapide_init();
+ }
+#endif /* CONFIG_BLK_DEV_IDE_RAPIDE */
+#ifdef CONFIG_BLK_DEV_GAYLE
+ {
+ extern void gayle_init(void);
+ gayle_init();
+ }
+#endif /* CONFIG_BLK_DEV_GAYLE */
+#ifdef CONFIG_BLK_DEV_FALCON_IDE
+ {
+ extern void falconide_init(void);
+ falconide_init();
+ }
+#endif /* CONFIG_BLK_DEV_FALCON_IDE */
+#ifdef CONFIG_BLK_DEV_MAC_IDE
+ {
+ extern void macide_init(void);
+ macide_init();
+ }
+#endif /* CONFIG_BLK_DEV_MAC_IDE */
+#ifdef CONFIG_BLK_DEV_BUDDHA
+ {
+ extern void buddha_init(void);
+ buddha_init();
+ }
+#endif /* CONFIG_BLK_DEV_BUDDHA */
+#if defined(CONFIG_BLK_DEV_ISAPNP) && defined(CONFIG_ISAPNP)
+ {
+ extern void pnpide_init(int enable);
+ pnpide_init(1);
+ }
+#endif /* CONFIG_BLK_DEV_ISAPNP */
+}
+
+void __init ide_init_builtin_drivers (void)
+{
+ /*
+ * Probe for special PCI and other "known" interface chipsets
+ */
+ probe_for_hwifs ();
+
+#ifdef CONFIG_BLK_DEV_IDE
+#if defined(__mc68000__) || defined(CONFIG_APUS)
+ if (ide_hwifs[0].io_ports[IDE_DATA_OFFSET]) {
+ ide_get_lock(&ide_lock, NULL, NULL); /* for atari only */
+ disable_irq(ide_hwifs[0].irq); /* disable_irq_nosync ?? */
+ }
+#endif /* __mc68000__ || CONFIG_APUS */
+
+ (void) ideprobe_init();
+
+#if defined(__mc68000__) || defined(CONFIG_APUS)
+ if (ide_hwifs[0].io_ports[IDE_DATA_OFFSET]) {
+ enable_irq(ide_hwifs[0].irq);
+ ide_release_lock(&ide_lock); /* for atari only */
+ }
+#endif /* __mc68000__ || CONFIG_APUS */
+#endif /* CONFIG_BLK_DEV_IDE */
+
+#ifdef CONFIG_PROC_FS
+ proc_ide_create();
+#endif
+
+ /*
+ * Attempt to match drivers for the available drives
+ */
+#ifdef CONFIG_BLK_DEV_IDEDISK
+ (void) idedisk_init();
+#endif /* CONFIG_BLK_DEV_IDEDISK */
+#ifdef CONFIG_BLK_DEV_IDECD
+ (void) ide_cdrom_init();
+#endif /* CONFIG_BLK_DEV_IDECD */
+#ifdef CONFIG_BLK_DEV_IDETAPE
+ (void) idetape_init();
+#endif /* CONFIG_BLK_DEV_IDETAPE */
+#ifdef CONFIG_BLK_DEV_IDEFLOPPY
+ (void) idefloppy_init();
+#endif /* CONFIG_BLK_DEV_IDEFLOPPY */
+#ifdef CONFIG_BLK_DEV_IDESCSI
+ #ifdef CONFIG_SCSI
+ (void) idescsi_init();
+ #else
+ #warning ide scsi-emulation selected but no SCSI-subsystem in kernel
+ #endif
+#endif /* CONFIG_BLK_DEV_IDESCSI */
+}
+
+static int default_cleanup (ide_drive_t *drive)
+{
+ return ide_unregister_subdriver(drive);
+}
+
+static ide_startstop_t default_do_request(ide_drive_t *drive, struct request *rq, unsigned long block)
+{
+ ide_end_request(0, HWGROUP(drive));
+ return ide_stopped;
+}
+
+static void default_end_request (byte uptodate, ide_hwgroup_t *hwgroup)
+{
+ ide_end_request(uptodate, hwgroup);
+}
+
+static int default_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return -EIO;
+}
+
+static int default_open (struct inode *inode, struct file *filp, ide_drive_t *drive)
+{
+ drive->usage--;
+ return -EIO;
+}
+
+static void default_release (struct inode *inode, struct file *filp, ide_drive_t *drive)
+{
+}
+
+static int default_check_media_change (ide_drive_t *drive)
+{
+ return 1;
+}
+
+static void default_pre_reset (ide_drive_t *drive)
+{
+}
+
+static unsigned long default_capacity (ide_drive_t *drive)
+{
+ return 0x7fffffff; /* cdrom or tape */
+}
+
+static ide_startstop_t default_special (ide_drive_t *drive)
+{
+ special_t *s = &drive->special;
+
+ s->all = 0;
+ drive->mult_req = 0;
+ return ide_stopped;
+}
+
+static void setup_driver_defaults (ide_drive_t *drive)
+{
+ ide_driver_t *d = drive->driver;
+
+ if (d->cleanup == NULL) d->cleanup = default_cleanup;
+ if (d->do_request == NULL) d->do_request = default_do_request;
+ if (d->end_request == NULL) d->end_request = default_end_request;
+ if (d->ioctl == NULL) d->ioctl = default_ioctl;
+ if (d->open == NULL) d->open = default_open;
+ if (d->release == NULL) d->release = default_release;
+ if (d->media_change == NULL) d->media_change = default_check_media_change;
+ if (d->pre_reset == NULL) d->pre_reset = default_pre_reset;
+ if (d->capacity == NULL) d->capacity = default_capacity;
+ if (d->special == NULL) d->special = default_special;
+}
+
+ide_drive_t *ide_scan_devices (byte media, const char *name, ide_driver_t *driver, int n)
+{
+ unsigned int unit, index, i;
+
+ for (index = 0, i = 0; index < MAX_HWIFS; ++index) {
+ ide_hwif_t *hwif = &ide_hwifs[index];
+ if (!hwif->present)
+ continue;
+ for (unit = 0; unit < MAX_DRIVES; ++unit) {
+ ide_drive_t *drive = &hwif->drives[unit];
+ char *req = drive->driver_req;
+ if (*req && !strstr(name, req))
+ continue;
+ if (drive->present && drive->media == media && drive->driver == driver && ++i > n)
+ return drive;
+ }
+ }
+ return NULL;
+}
+
+int ide_register_subdriver (ide_drive_t *drive, ide_driver_t *driver, int version)
+{
+ unsigned long flags;
+
+ save_flags(flags); /* all CPUs */
+ cli(); /* all CPUs */
+ if (version != IDE_SUBDRIVER_VERSION || !drive->present || drive->driver != NULL || drive->busy || drive->usage) {
+ restore_flags(flags); /* all CPUs */
+ return 1;
+ }
+ drive->driver = driver;
+ setup_driver_defaults(drive);
+ restore_flags(flags); /* all CPUs */
+ if (drive->autotune != 2) {
+ if (driver->supports_dma && HWIF(drive)->dmaproc != NULL) {
+ /*
+ * Force DMAing for the beginning of the check.
+ * Some chipsets appear to do interesting things,
+ * if not checked and cleared.
+ * PARANOIA!!!
+ */
+ (void) (HWIF(drive)->dmaproc(ide_dma_off_quietly, drive));
+ (void) (HWIF(drive)->dmaproc(ide_dma_check, drive));
+ }
+ drive->dsc_overlap = (drive->next != drive && driver->supports_dsc_overlap);
+ drive->nice1 = 1;
+ }
+ drive->revalidate = 1;
+#ifdef CONFIG_PROC_FS
+ ide_add_proc_entries(drive->proc, generic_subdriver_entries, drive);
+ ide_add_proc_entries(drive->proc, driver->proc, drive);
+#endif
+ return 0;
+}
+
+int ide_unregister_subdriver (ide_drive_t *drive)
+{
+ unsigned long flags;
+
+ save_flags(flags); /* all CPUs */
+ cli(); /* all CPUs */
+ if (drive->usage || drive->busy || drive->driver == NULL || DRIVER(drive)->busy) {
+ restore_flags(flags); /* all CPUs */
+ return 1;
+ }
+#if defined(CONFIG_BLK_DEV_ISAPNP) && defined(CONFIG_ISAPNP) && defined(MODULE)
+ pnpide_init(0);
+#endif /* CONFIG_BLK_DEV_ISAPNP */
+#ifdef CONFIG_PROC_FS
+ ide_remove_proc_entries(drive->proc, DRIVER(drive)->proc);
+ ide_remove_proc_entries(drive->proc, generic_subdriver_entries);
+#endif
+ auto_remove_settings(drive);
+ drive->driver = NULL;
+ restore_flags(flags); /* all CPUs */
+ return 0;
+}
+
+int ide_register_module (ide_module_t *module)
+{
+ ide_module_t *p = ide_modules;
+
+ while (p) {
+ if (p == module)
+ return 1;
+ p = p->next;
+ }
+ module->next = ide_modules;
+ ide_modules = module;
+ revalidate_drives();
+ return 0;
+}
+
+void ide_unregister_module (ide_module_t *module)
+{
+ ide_module_t **p;
+
+ for (p = &ide_modules; (*p) && (*p) != module; p = &((*p)->next));
+ if (*p)
+ *p = (*p)->next;
+}
+
+struct block_device_operations ide_fops[] = {{
+ open: ide_open,
+ release: ide_release,
+ ioctl: ide_ioctl,
+ check_media_change: ide_check_media_change,
+ revalidate: ide_revalidate_disk
+}};
+
+EXPORT_SYMBOL(ide_hwifs);
+EXPORT_SYMBOL(ide_register_module);
+EXPORT_SYMBOL(ide_unregister_module);
+EXPORT_SYMBOL(ide_spin_wait_hwgroup);
+
+/*
+ * Probe module
+ */
+devfs_handle_t ide_devfs_handle = NULL;
+
+EXPORT_SYMBOL(ide_probe);
+EXPORT_SYMBOL(drive_is_flashcard);
+EXPORT_SYMBOL(ide_timer_expiry);
+EXPORT_SYMBOL(ide_intr);
+EXPORT_SYMBOL(ide_fops);
+EXPORT_SYMBOL(ide_get_queue);
+EXPORT_SYMBOL(do_ide0_request);
+EXPORT_SYMBOL(ide_add_generic_settings);
+EXPORT_SYMBOL(ide_devfs_handle);
+#if MAX_HWIFS > 1
+EXPORT_SYMBOL(do_ide1_request);
+#endif /* MAX_HWIFS > 1 */
+#if MAX_HWIFS > 2
+EXPORT_SYMBOL(do_ide2_request);
+#endif /* MAX_HWIFS > 2 */
+#if MAX_HWIFS > 3
+EXPORT_SYMBOL(do_ide3_request);
+#endif /* MAX_HWIFS > 3 */
+#if MAX_HWIFS > 4
+EXPORT_SYMBOL(do_ide4_request);
+#endif /* MAX_HWIFS > 4 */
+#if MAX_HWIFS > 5
+EXPORT_SYMBOL(do_ide5_request);
+#endif /* MAX_HWIFS > 5 */
+#if MAX_HWIFS > 6
+EXPORT_SYMBOL(do_ide6_request);
+#endif /* MAX_HWIFS > 6 */
+#if MAX_HWIFS > 7
+EXPORT_SYMBOL(do_ide7_request);
+#endif /* MAX_HWIFS > 7 */
+#if MAX_HWIFS > 8
+EXPORT_SYMBOL(do_ide8_request);
+#endif /* MAX_HWIFS > 8 */
+#if MAX_HWIFS > 9
+EXPORT_SYMBOL(do_ide9_request);
+#endif /* MAX_HWIFS > 9 */
+
+/*
+ * Driver module
+ */
+EXPORT_SYMBOL(ide_scan_devices);
+EXPORT_SYMBOL(ide_register_subdriver);
+EXPORT_SYMBOL(ide_unregister_subdriver);
+EXPORT_SYMBOL(ide_replace_subdriver);
+EXPORT_SYMBOL(ide_input_data);
+EXPORT_SYMBOL(ide_output_data);
+EXPORT_SYMBOL(atapi_input_bytes);
+EXPORT_SYMBOL(atapi_output_bytes);
+EXPORT_SYMBOL(ide_set_handler);
+EXPORT_SYMBOL(ide_dump_status);
+EXPORT_SYMBOL(ide_error);
+EXPORT_SYMBOL(ide_fixstring);
+EXPORT_SYMBOL(ide_wait_stat);
+EXPORT_SYMBOL(ide_do_reset);
+EXPORT_SYMBOL(ide_init_drive_cmd);
+EXPORT_SYMBOL(ide_do_drive_cmd);
+EXPORT_SYMBOL(ide_end_drive_cmd);
+EXPORT_SYMBOL(ide_end_request);
+EXPORT_SYMBOL(ide_revalidate_disk);
+EXPORT_SYMBOL(ide_cmd);
+EXPORT_SYMBOL(ide_wait_cmd);
+EXPORT_SYMBOL(ide_delay_50ms);
+EXPORT_SYMBOL(ide_stall_queue);
+#ifdef CONFIG_PROC_FS
+EXPORT_SYMBOL(ide_add_proc_entries);
+EXPORT_SYMBOL(ide_remove_proc_entries);
+EXPORT_SYMBOL(proc_ide_read_geometry);
+EXPORT_SYMBOL(create_proc_ide_interfaces);
+#endif
+EXPORT_SYMBOL(ide_add_setting);
+EXPORT_SYMBOL(ide_remove_setting);
+
+EXPORT_SYMBOL(ide_register_hw);
+EXPORT_SYMBOL(ide_register);
+EXPORT_SYMBOL(ide_unregister);
+EXPORT_SYMBOL(ide_setup_ports);
+EXPORT_SYMBOL(hwif_unregister);
+EXPORT_SYMBOL(get_info_ptr);
+EXPORT_SYMBOL(current_capacity);
+
+/*
+ * This is gets invoked once during initialization, to set *everything* up
+ */
+int __init ide_init (void)
+{
+ static char banner_printed = 0;
+ int i;
+
+ if (!banner_printed) {
+ printk(KERN_INFO "Uniform Multi-Platform E-IDE driver " REVISION "\n");
+ ide_devfs_handle = devfs_mk_dir (NULL, "ide", 3, NULL);
+ (void) ide_system_bus_speed();
+ banner_printed = 1;
+ }
+
+ init_ide_data ();
+
+ initializing = 1;
+ ide_init_builtin_drivers();
+ initializing = 0;
+
+ for (i = 0; i < MAX_HWIFS; ++i) {
+ ide_hwif_t *hwif = &ide_hwifs[i];
+ if (hwif->present)
+ ide_geninit(hwif);
+ }
+
+ return 0;
+}
+
+static void __init parse_options (char *line)
+{
+ char *next = line;
+
+ if (line == NULL || !*line)
+ return;
+ while ((line = next) != NULL) {
+ if ((next = strchr(line,' ')) != NULL)
+ *next++ = 0;
+ if (!strncmp(line,"ide",3) ||
+ !strncmp(line,"idebus",6) ||
+#ifdef CONFIG_BLK_DEV_VIA82CXXX
+ !strncmp(line,"splitfifo",9) ||
+#endif /* CONFIG_BLK_DEV_VIA82CXXX */
+ !strncmp(line,"hdxlun",6) ||
+ (!strncmp(line,"hd",2) && line[2] != '='))
+ (void) ide_setup(line);
+ }
+}
+
+#ifdef MODULE
+char *options = NULL;
+MODULE_PARM(options,"s");
+
+int init_module (void)
+{
+ parse_options(options);
+ return ide_init();
+}
+
+void cleanup_module (void)
+{
+ int index;
+
+ for (index = 0; index < MAX_HWIFS; ++index)
+ ide_unregister(index);
+
+#ifdef CONFIG_PROC_FS
+ proc_ide_destroy();
+#endif
+ devfs_unregister (ide_devfs_handle);
+}
+
+#else /* !MODULE */
+
+static int parse_ide_setup (char *line)
+{
+ parse_options(line);
+ return 0;
+}
+__setup("", parse_ide_setup);
+
+#endif /* MODULE */
diff --git a/drivers/ide/ide_modes.h b/drivers/ide/ide_modes.h
new file mode 100644
index 000000000..f94d91313
--- /dev/null
+++ b/drivers/ide/ide_modes.h
@@ -0,0 +1,233 @@
+/*
+ * linux/drivers/block/ide_modes.h
+ *
+ * Copyright (C) 1996 Linus Torvalds, Igor Abramov, and Mark Lord
+ */
+
+#ifndef _IDE_MODES_H
+#define _IDE_MODES_H
+
+#include <linux/config.h>
+
+/*
+ * Shared data/functions for determining best PIO mode for an IDE drive.
+ * Most of this stuff originally lived in cmd640.c, and changes to the
+ * ide_pio_blacklist[] table should be made with EXTREME CAUTION to avoid
+ * breaking the fragile cmd640.c support.
+ */
+
+#ifdef CONFIG_BLK_DEV_IDE_MODES
+
+/*
+ * Standard (generic) timings for PIO modes, from ATA2 specification.
+ * These timings are for access to the IDE data port register *only*.
+ * Some drives may specify a mode, while also specifying a different
+ * value for cycle_time (from drive identification data).
+ */
+typedef struct ide_pio_timings_s {
+ int setup_time; /* Address setup (ns) minimum */
+ int active_time; /* Active pulse (ns) minimum */
+ int cycle_time; /* Cycle time (ns) minimum = (setup + active + recovery) */
+} ide_pio_timings_t;
+
+typedef struct ide_pio_data_s {
+ byte pio_mode;
+ byte use_iordy;
+ byte overridden;
+ byte blacklisted;
+ unsigned int cycle_time;
+} ide_pio_data_t;
+
+#ifndef _IDE_C
+
+int ide_scan_pio_blacklist (char *model);
+byte ide_get_best_pio_mode (ide_drive_t *drive, byte mode_wanted, byte max_mode, ide_pio_data_t *d);
+extern const ide_pio_timings_t ide_pio_timings[6];
+
+#else /* _IDE_C */
+
+const ide_pio_timings_t ide_pio_timings[6] = {
+ { 70, 165, 600 }, /* PIO Mode 0 */
+ { 50, 125, 383 }, /* PIO Mode 1 */
+ { 30, 100, 240 }, /* PIO Mode 2 */
+ { 30, 80, 180 }, /* PIO Mode 3 with IORDY */
+ { 25, 70, 120 }, /* PIO Mode 4 with IORDY */
+ { 20, 50, 100 } /* PIO Mode 5 with IORDY (nonstandard) */
+};
+
+/*
+ * Black list. Some drives incorrectly report their maximal PIO mode,
+ * at least in respect to CMD640. Here we keep info on some known drives.
+ */
+static struct ide_pio_info {
+ const char *name;
+ int pio;
+} ide_pio_blacklist [] = {
+/* { "Conner Peripherals 1275MB - CFS1275A", 4 }, */
+ { "Conner Peripherals 540MB - CFS540A", 3 },
+
+ { "WDC AC2700", 3 },
+ { "WDC AC2540", 3 },
+ { "WDC AC2420", 3 },
+ { "WDC AC2340", 3 },
+ { "WDC AC2250", 0 },
+ { "WDC AC2200", 0 },
+ { "WDC AC21200", 4 },
+ { "WDC AC2120", 0 },
+ { "WDC AC2850", 3 },
+ { "WDC AC1270", 3 },
+ { "WDC AC1170", 1 },
+ { "WDC AC1210", 1 },
+ { "WDC AC280", 0 },
+/* { "WDC AC21000", 4 }, */
+ { "WDC AC31000", 3 },
+ { "WDC AC31200", 3 },
+/* { "WDC AC31600", 4 }, */
+
+ { "Maxtor 7131 AT", 1 },
+ { "Maxtor 7171 AT", 1 },
+ { "Maxtor 7213 AT", 1 },
+ { "Maxtor 7245 AT", 1 },
+ { "Maxtor 7345 AT", 1 },
+ { "Maxtor 7546 AT", 3 },
+ { "Maxtor 7540 AV", 3 },
+
+ { "SAMSUNG SHD-3121A", 1 },
+ { "SAMSUNG SHD-3122A", 1 },
+ { "SAMSUNG SHD-3172A", 1 },
+
+/* { "ST51080A", 4 },
+ * { "ST51270A", 4 },
+ * { "ST31220A", 4 },
+ * { "ST31640A", 4 },
+ * { "ST32140A", 4 },
+ * { "ST3780A", 4 },
+ */
+ { "ST5660A", 3 },
+ { "ST3660A", 3 },
+ { "ST3630A", 3 },
+ { "ST3655A", 3 },
+ { "ST3391A", 3 },
+ { "ST3390A", 1 },
+ { "ST3600A", 1 },
+ { "ST3290A", 0 },
+ { "ST3144A", 0 },
+ { "ST3491A", 1 }, /* reports 3, should be 1 or 2 (depending on */
+ /* drive) according to Seagates FIND-ATA program */
+
+ { "QUANTUM ELS127A", 0 },
+ { "QUANTUM ELS170A", 0 },
+ { "QUANTUM LPS240A", 0 },
+ { "QUANTUM LPS210A", 3 },
+ { "QUANTUM LPS270A", 3 },
+ { "QUANTUM LPS365A", 3 },
+ { "QUANTUM LPS540A", 3 },
+ { "QUANTUM LIGHTNING 540A", 3 },
+ { "QUANTUM LIGHTNING 730A", 3 },
+ { "QUANTUM FIREBALL", 3 }, /* For models 540/640/1080/1280 */
+ /* 1080A works fine in mode4 with triton */
+ { NULL, 0 }
+};
+
+/*
+ * This routine searches the ide_pio_blacklist for an entry
+ * matching the start/whole of the supplied model name.
+ *
+ * Returns -1 if no match found.
+ * Otherwise returns the recommended PIO mode from ide_pio_blacklist[].
+ */
+int ide_scan_pio_blacklist (char *model)
+{
+ struct ide_pio_info *p;
+
+ for (p = ide_pio_blacklist; p->name != NULL; p++) {
+ if (strncmp(p->name, model, strlen(p->name)) == 0)
+ return p->pio;
+ }
+ return -1;
+}
+
+/*
+ * This routine returns the recommended PIO settings for a given drive,
+ * based on the drive->id information and the ide_pio_blacklist[].
+ * This is used by most chipset support modules when "auto-tuning".
+ */
+
+/*
+ * Drive PIO mode auto selection
+ */
+byte ide_get_best_pio_mode (ide_drive_t *drive, byte mode_wanted, byte max_mode, ide_pio_data_t *d)
+{
+ int pio_mode;
+ int cycle_time = 0;
+ int use_iordy = 0;
+ struct hd_driveid* id = drive->id;
+ int overridden = 0;
+ int blacklisted = 0;
+
+ if (mode_wanted != 255) {
+ pio_mode = mode_wanted;
+ } else if (!drive->id) {
+ pio_mode = 0;
+ } else if ((pio_mode = ide_scan_pio_blacklist(id->model)) != -1) {
+ overridden = 1;
+ blacklisted = 1;
+ use_iordy = (pio_mode > 2);
+ } else {
+ pio_mode = id->tPIO;
+ if (pio_mode > 2) { /* 2 is maximum allowed tPIO value */
+ pio_mode = 2;
+ overridden = 1;
+ }
+ if (id->field_valid & 2) { /* drive implements ATA2? */
+ if (id->capability & 8) { /* drive supports use_iordy? */
+ use_iordy = 1;
+ cycle_time = id->eide_pio_iordy;
+ if (id->eide_pio_modes & 7) {
+ overridden = 0;
+ if (id->eide_pio_modes & 4)
+ pio_mode = 5;
+ else if (id->eide_pio_modes & 2)
+ pio_mode = 4;
+ else
+ pio_mode = 3;
+ }
+ } else {
+ cycle_time = id->eide_pio;
+ }
+ }
+
+#if 0
+ if (drive->id->major_rev_num & 0x0004) printf("ATA-2 ");
+#endif
+
+ /*
+ * Conservative "downgrade" for all pre-ATA2 drives
+ */
+ if (pio_mode && pio_mode < 4) {
+ pio_mode--;
+ overridden = 1;
+#if 0
+ use_iordy = (pio_mode > 2);
+#endif
+ if (cycle_time && cycle_time < ide_pio_timings[pio_mode].cycle_time)
+ cycle_time = 0; /* use standard timing */
+ }
+ }
+ if (pio_mode > max_mode) {
+ pio_mode = max_mode;
+ cycle_time = 0;
+ }
+ if (d) {
+ d->pio_mode = pio_mode;
+ d->cycle_time = cycle_time ? cycle_time : ide_pio_timings[pio_mode].cycle_time;
+ d->use_iordy = use_iordy;
+ d->overridden = overridden;
+ d->blacklisted = blacklisted;
+ }
+ return pio_mode;
+}
+
+#endif /* _IDE_C */
+#endif /* CONFIG_BLK_DEV_IDE_MODES */
+#endif /* _IDE_MODES_H */
diff --git a/drivers/ide/macide.c b/drivers/ide/macide.c
new file mode 100644
index 000000000..46a14ba19
--- /dev/null
+++ b/drivers/ide/macide.c
@@ -0,0 +1,116 @@
+/*
+ * linux/drivers/ide/macide.c -- Macintosh IDE Driver
+ *
+ * Copyright (C) 1998 by Michael Schmitz
+ *
+ * This driver was written based on information obtained from the MacOS IDE
+ * driver binary by Mikael Forselius
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/delay.h>
+#include <linux/ide.h>
+
+#include <asm/machw.h>
+#include <asm/macintosh.h>
+#include <asm/macints.h>
+
+ /*
+ * Base of the IDE interface (see ATAManager ROM code)
+ */
+
+#define MAC_HD_BASE 0x50f1a000
+
+ /*
+ * Offsets from the above base (scaling 4)
+ */
+
+#define MAC_HD_DATA 0x00
+#define MAC_HD_ERROR 0x04 /* see err-bits */
+#define MAC_HD_NSECTOR 0x08 /* nr of sectors to read/write */
+#define MAC_HD_SECTOR 0x0c /* starting sector */
+#define MAC_HD_LCYL 0x10 /* starting cylinder */
+#define MAC_HD_HCYL 0x14 /* high byte of starting cyl */
+#define MAC_HD_SELECT 0x18 /* 101dhhhh , d=drive, hhhh=head */
+#define MAC_HD_STATUS 0x1c /* see status-bits */
+#define MAC_HD_CONTROL 0x38 /* control/altstatus */
+
+static int __init macide_offsets[IDE_NR_PORTS] = {
+ MAC_HD_DATA, MAC_HD_ERROR, MAC_HD_NSECTOR, MAC_HD_SECTOR, MAC_HD_LCYL,
+ MAC_HD_HCYL, MAC_HD_SELECT, MAC_HD_STATUS, MAC_HD_CONTROL
+};
+
+ /*
+ * Other registers
+ */
+
+ /*
+ * IDE interrupt status register for both (?) hwifs on Quadra
+ * Initial setting: 0xc
+ * Guessing again:
+ * Bit 0+1: some interrupt flags
+ * Bit 2+3: some interrupt enable
+ * Bit 4: ??
+ * Bit 5: IDE interrupt flag (any hwif)
+ * Bit 6: maybe IDE interrupt enable (any hwif) ??
+ * Bit 7: Any interrupt condition
+ *
+ * Only relevant item: bit 5, to be checked by mac_ack_intr
+ */
+
+#define MAC_HD_ISR 0x101
+
+static int mac_ack_intr(ide_hwif_t* hwif)
+{
+ unsigned char isr;
+ isr = readb(MAC_HD_BASE + MAC_HD_ISR);
+ if (isr & (1<<5)) {
+ writeb(isr & ~(1<<5), MAC_HD_BASE + MAC_HD_ISR);
+ return 1;
+ }
+
+ return 0;
+}
+
+ /*
+ * Probe for a Macintosh IDE interface
+ */
+
+void __init macide_init(void)
+{
+ hw_regs_t hw;
+ int index = -1;
+
+ if (!MACH_IS_MAC || macintosh_config->ide_type == 0)
+ return;
+
+ switch (macintosh_config->ide_type) {
+ case MAC_IDE_QUADRA:
+ ide_setup_ports(&hw, (ide_ioreg_t)MAC_HD_BASE, macide_offsets,
+ 0, (ide_ioreg_t)(MAC_HD_BASE+MAC_HD_ISR),
+ mac_ack_intr, IRQ_NUBUS_F);
+ index = ide_register_hw(&hw, NULL);
+ break;
+
+ default:
+ ide_setup_ports(&hw, (ide_ioreg_t)MAC_HD_BASE, macide_offsets,
+ 0, 0, NULL, IRQ_NUBUS_C);
+ index = ide_register_hw(&hw, NULL);
+ break;
+ }
+
+ if (index != -1) {
+ if (macintosh_config->ide_type == MAC_IDE_QUADRA)
+ printk("ide%d: Macintosh Quadra IDE interface\n", index);
+ else
+ printk("ide%d: Macintosh Powerbook IDE interface\n", index);
+ }
+}
diff --git a/drivers/ide/ns87415.c b/drivers/ide/ns87415.c
new file mode 100644
index 000000000..0e5675fde
--- /dev/null
+++ b/drivers/ide/ns87415.c
@@ -0,0 +1,185 @@
+/*
+ * linux/drivers/block/ns87415.c Version 1.00 December 7, 1997
+ *
+ * Copyright (C) 1997-1998 Mark Lord
+ * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be)
+ *
+ * Inspired by an earlier effort from David S. Miller (davem@caipfs.rutgers.edu)
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+
+static unsigned int ns87415_count = 0, ns87415_control[MAX_HWIFS] = { 0 };
+
+/*
+ * This routine either enables/disables (according to drive->present)
+ * the IRQ associated with the port (HWIF(drive)),
+ * and selects either PIO or DMA handshaking for the next I/O operation.
+ */
+static void ns87415_prepare_drive (ide_drive_t *drive, unsigned int use_dma)
+{
+ ide_hwif_t *hwif = HWIF(drive);
+ unsigned int bit, other, new, *old = (unsigned int *) hwif->select_data;
+ struct pci_dev *dev = hwif->pci_dev;
+ unsigned long flags;
+
+ __save_flags(flags); /* local CPU only */
+ __cli(); /* local CPU only */
+ new = *old;
+
+ /* Adjust IRQ enable bit */
+ bit = 1 << (8 + hwif->channel);
+ new = drive->present ? (new & ~bit) : (new | bit);
+
+ /* Select PIO or DMA, DMA may only be selected for one drive/channel. */
+ bit = 1 << (20 + drive->select.b.unit + (hwif->channel << 1));
+ other = 1 << (20 + (1 - drive->select.b.unit) + (hwif->channel << 1));
+ new = use_dma ? ((new & ~other) | bit) : (new & ~bit);
+
+ if (new != *old) {
+ unsigned char stat;
+
+ /*
+ * Don't change DMA engine settings while Write Buffers
+ * are busy.
+ */
+ (void) pci_read_config_byte(dev, 0x43, &stat);
+ while (stat & 0x03) {
+ udelay(1);
+ (void) pci_read_config_byte(dev, 0x43, &stat);
+ }
+
+ *old = new;
+ (void) pci_write_config_dword(dev, 0x40, new);
+
+ /*
+ * And let things settle...
+ */
+ udelay(10);
+ }
+
+ __restore_flags(flags); /* local CPU only */
+}
+
+static void ns87415_selectproc (ide_drive_t *drive)
+{
+ ns87415_prepare_drive (drive, drive->using_dma);
+}
+
+static int ns87415_dmaproc(ide_dma_action_t func, ide_drive_t *drive)
+{
+ ide_hwif_t *hwif = HWIF(drive);
+ byte dma_stat;
+
+ switch (func) {
+ case ide_dma_end: /* returns 1 on error, 0 otherwise */
+ drive->waiting_for_dma = 0;
+ dma_stat = inb(hwif->dma_base+2);
+ outb(inb(hwif->dma_base)&~1, hwif->dma_base); /* stop DMA */
+ outb(inb(hwif->dma_base)|6, hwif->dma_base); /* from ERRATA: clear the INTR & ERROR bits */
+ ide_destroy_dmatable(drive); /* and free any DMA resources */
+ return (dma_stat & 7) != 4; /* verify good DMA status */
+ case ide_dma_write:
+ case ide_dma_read:
+ ns87415_prepare_drive(drive, 1); /* select DMA xfer */
+ if (!ide_dmaproc(func, drive)) /* use standard DMA stuff */
+ return 0;
+ ns87415_prepare_drive(drive, 0); /* DMA failed: select PIO xfer */
+ return 1;
+ case ide_dma_check:
+ if (drive->media != ide_disk)
+ return ide_dmaproc(ide_dma_off_quietly, drive);
+ /* Fallthrough... */
+ default:
+ return ide_dmaproc(func, drive); /* use standard DMA stuff */
+ }
+}
+
+void __init ide_init_ns87415 (ide_hwif_t *hwif)
+{
+ struct pci_dev *dev = hwif->pci_dev;
+ unsigned int ctrl, using_inta;
+ byte progif;
+#ifdef __sparc_v9__
+ int timeout;
+ byte stat;
+#endif
+
+ /* Set a good latency timer and cache line size value. */
+ (void) pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64);
+#ifdef __sparc_v9__
+ (void) pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 0x10);
+#endif
+
+ /*
+ * We cannot probe for IRQ: both ports share common IRQ on INTA.
+ * Also, leave IRQ masked during drive probing, to prevent infinite
+ * interrupts from a potentially floating INTA..
+ *
+ * IRQs get unmasked in selectproc when drive is first used.
+ */
+ (void) pci_read_config_dword(dev, 0x40, &ctrl);
+ (void) pci_read_config_byte(dev, 0x09, &progif);
+ /* is irq in "native" mode? */
+ using_inta = progif & (1 << (hwif->channel << 1));
+ if (!using_inta)
+ using_inta = ctrl & (1 << (4 + hwif->channel));
+ if (hwif->mate) {
+ hwif->select_data = hwif->mate->select_data;
+ } else {
+ hwif->select_data = (unsigned long)
+ &ns87415_control[ns87415_count++];
+ ctrl |= (1 << 8) | (1 << 9); /* mask both IRQs */
+ if (using_inta)
+ ctrl &= ~(1 << 6); /* unmask INTA */
+ *((unsigned int *)hwif->select_data) = ctrl;
+ (void) pci_write_config_dword(dev, 0x40, ctrl);
+
+ /*
+ * Set prefetch size to 512 bytes for both ports,
+ * but don't turn on/off prefetching here.
+ */
+ pci_write_config_byte(dev, 0x55, 0xee);
+
+#ifdef __sparc_v9__
+ /*
+ * XXX: Reset the device, if we don't it will not respond
+ * to SELECT_DRIVE() properly during first probe_hwif().
+ */
+ timeout = 10000;
+ outb(12, hwif->io_ports[IDE_CONTROL_OFFSET]);
+ udelay(10);
+ outb(8, hwif->io_ports[IDE_CONTROL_OFFSET]);
+ do {
+ udelay(50);
+ stat = inb(hwif->io_ports[IDE_STATUS_OFFSET]);
+ if (stat == 0xff)
+ break;
+ } while ((stat & BUSY_STAT) && --timeout);
+#endif
+ }
+
+ if (hwif->dma_base)
+ outb(0x60, hwif->dma_base + 2);
+
+ if (!using_inta)
+ hwif->irq = hwif->channel ? 15 : 14; /* legacy mode */
+ else if (!hwif->irq && hwif->mate && hwif->mate->irq)
+ hwif->irq = hwif->mate->irq; /* share IRQ with mate */
+
+ if (hwif->dma_base)
+ hwif->dmaproc = &ns87415_dmaproc;
+ hwif->selectproc = &ns87415_selectproc;
+}
diff --git a/drivers/ide/opti621.c b/drivers/ide/opti621.c
new file mode 100644
index 000000000..cc2aa567c
--- /dev/null
+++ b/drivers/ide/opti621.c
@@ -0,0 +1,315 @@
+/*
+ * linux/drivers/block/opti621.c Version 0.6 Jan 02, 1999
+ *
+ * Copyright (C) 1996-1998 Linus Torvalds & authors (see below)
+ */
+
+/*
+ * Authors:
+ * Jaromir Koutek <miri@punknet.cz>,
+ * Jan Harkes <jaharkes@cwi.nl>,
+ * Mark Lord <mlord@pobox.com>
+ * Some parts of code are from ali14xx.c and from rz1000.c.
+ *
+ * OPTi is trademark of OPTi, Octek is trademark of Octek.
+ *
+ * I used docs from OPTi databook, from ftp.opti.com, file 9123-0002.ps
+ * and disassembled/traced setupvic.exe (DOS program).
+ * It increases kernel code about 2 kB.
+ * I don't have this card no more, but I hope I can get some in case
+ * of needed development.
+ * My card is Octek PIDE 1.01 (on card) or OPTiViC (program).
+ * It has a place for a secondary connector in circuit, but nothing
+ * is there. Also BIOS says no address for
+ * secondary controller (see bellow in ide_init_opti621).
+ * I've only tested this on my system, which only has one disk.
+ * It's Western Digital WDAC2850, with PIO mode 3. The PCI bus
+ * is at 20 MHz (I have DX2/80, I tried PCI at 40, but I got random
+ * lockups). I tried the OCTEK double speed CD-ROM and
+ * it does not work! But I can't boot DOS also, so it's probably
+ * hardware fault. I have connected Conner 80MB, the Seagate 850MB (no
+ * problems) and Seagate 1GB (as slave, WD as master). My experiences
+ * with the third, 1GB drive: I got 3MB/s (hdparm), but sometimes
+ * it slows to about 100kB/s! I don't know why and I have
+ * not this drive now, so I can't try it again.
+ * I write this driver because I lost the paper ("manual") with
+ * settings of jumpers on the card and I have to boot Linux with
+ * Loadlin except LILO, cause I have to run the setupvic.exe program
+ * already or I get disk errors (my test: rpm -Vf
+ * /usr/X11R6/bin/XF86_SVGA - or any big file).
+ * Some numbers from hdparm -t /dev/hda:
+ * Timing buffer-cache reads: 32 MB in 3.02 seconds =10.60 MB/sec
+ * Timing buffered disk reads: 16 MB in 5.52 seconds = 2.90 MB/sec
+ * I have 4 Megs/s before, but I don't know why (maybe changes
+ * in hdparm test).
+ * After release of 0.1, I got some successful reports, so it might work.
+ *
+ * The main problem with OPTi is that some timings for master
+ * and slave must be the same. For example, if you have master
+ * PIO 3 and slave PIO 0, driver have to set some timings of
+ * master for PIO 0. Second problem is that opti621_tune_drive
+ * got only one drive to set, but have to set both drives.
+ * This is solved in compute_pios. If you don't set
+ * the second drive, compute_pios use ide_get_best_pio_mode
+ * for autoselect mode (you can change it to PIO 0, if you want).
+ * If you then set the second drive to another PIO, the old value
+ * (automatically selected) will be overrided by yours.
+ * There is a 25/33MHz switch in configuration
+ * register, but driver is written for use at any frequency which get
+ * (use idebus=xx to select PCI bus speed).
+ * Use ide0=autotune for automatical tune of the PIO modes.
+ * If you get strange results, do not use this and set PIO manually
+ * by hdparm.
+ *
+ * Version 0.1, Nov 8, 1996
+ * by Jaromir Koutek, for 2.1.8.
+ * Initial version of driver.
+ *
+ * Version 0.2
+ * Number 0.2 skipped.
+ *
+ * Version 0.3, Nov 29, 1997
+ * by Mark Lord (probably), for 2.1.68
+ * Updates for use with new IDE block driver.
+ *
+ * Version 0.4, Dec 14, 1997
+ * by Jan Harkes
+ * Fixed some errors and cleaned the code.
+ *
+ * Version 0.5, Jan 2, 1998
+ * by Jaromir Koutek
+ * Updates for use with (again) new IDE block driver.
+ * Update of documentation.
+ *
+ * Version 0.6, Jan 2, 1999
+ * by Jaromir Koutek
+ * Reversed to version 0.3 of the driver, because
+ * 0.5 doesn't work.
+ */
+
+#undef REALLY_SLOW_IO /* most systems can safely undef this */
+#define OPTI621_DEBUG /* define for debug messages */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+
+#include "ide_modes.h"
+
+#define OPTI621_MAX_PIO 3
+/* In fact, I do not have any PIO 4 drive
+ * (address: 25 ns, data: 70 ns, recovery: 35 ns),
+ * but OPTi 82C621 is programmable and it can do (minimal values):
+ * on 40MHz PCI bus (pulse 25 ns):
+ * address: 25 ns, data: 25 ns, recovery: 50 ns;
+ * on 20MHz PCI bus (pulse 50 ns):
+ * address: 50 ns, data: 50 ns, recovery: 100 ns.
+ */
+
+/* #define READ_PREFETCH 0 */
+/* Uncommnent for disable read prefetch.
+ * There is some readprefetch capatibility in hdparm,
+ * but when I type hdparm -P 1 /dev/hda, I got errors
+ * and till reset drive is inacessible.
+ * This (hw) read prefetch is safe on my drive.
+ */
+
+#ifndef READ_PREFETCH
+#define READ_PREFETCH 0x40 /* read prefetch is enabled */
+#endif /* else read prefetch is disabled */
+
+#define READ_REG 0 /* index of Read cycle timing register */
+#define WRITE_REG 1 /* index of Write cycle timing register */
+#define CNTRL_REG 3 /* index of Control register */
+#define STRAP_REG 5 /* index of Strap register */
+#define MISC_REG 6 /* index of Miscellaneous register */
+
+int reg_base;
+
+#define PIO_NOT_EXIST 254
+#define PIO_DONT_KNOW 255
+
+/* there are stored pio numbers from other calls of opti621_tune_drive */
+
+static void compute_pios(ide_drive_t *drive, byte pio)
+/* Store values into drive->drive_data
+ * second_contr - 0 for primary controller, 1 for secondary
+ * slave_drive - 0 -> pio is for master, 1 -> pio is for slave
+ * pio - PIO mode for selected drive (for other we don't know)
+ */
+{
+ int d;
+ ide_hwif_t *hwif = HWIF(drive);
+
+ drive->drive_data = ide_get_best_pio_mode(drive, pio, OPTI621_MAX_PIO, NULL);
+ for (d = 0; d < 2; ++d) {
+ drive = &hwif->drives[d];
+ if (drive->present) {
+ if (drive->drive_data == PIO_DONT_KNOW)
+ drive->drive_data = ide_get_best_pio_mode(drive, 255, OPTI621_MAX_PIO, NULL);
+#ifdef OPTI621_DEBUG
+ printk("%s: Selected PIO mode %d\n", drive->name, drive->drive_data);
+#endif
+ } else {
+ drive->drive_data = PIO_NOT_EXIST;
+ }
+ }
+}
+
+int cmpt_clk(int time, int bus_speed)
+/* Returns (rounded up) time in clocks for time in ns,
+ * with bus_speed in MHz.
+ * Example: bus_speed = 40 MHz, time = 80 ns
+ * 1000/40 = 25 ns (clk value),
+ * 80/25 = 3.2, rounded up to 4 (I hope ;-)).
+ * Use idebus=xx to select right frequency.
+ */
+{
+ return ((time*bus_speed+999)/1000);
+}
+
+static void write_reg(byte value, int reg)
+/* Write value to register reg, base of register
+ * is at reg_base (0x1f0 primary, 0x170 secondary,
+ * if not changed by PCI configuration).
+ * This is from setupvic.exe program.
+ */
+{
+ inw(reg_base+1);
+ inw(reg_base+1);
+ outb(3, reg_base+2);
+ outb(value, reg_base+reg);
+ outb(0x83, reg_base+2);
+}
+
+static byte read_reg(int reg)
+/* Read value from register reg, base of register
+ * is at reg_base (0x1f0 primary, 0x170 secondary,
+ * if not changed by PCI configuration).
+ * This is from setupvic.exe program.
+ */
+{
+ byte ret;
+ inw(reg_base+1);
+ inw(reg_base+1);
+ outb(3, reg_base+2);
+ ret=inb(reg_base+reg);
+ outb(0x83, reg_base+2);
+ return ret;
+}
+
+typedef struct pio_clocks_s {
+ int address_time; /* Address setup (clocks) */
+ int data_time; /* Active/data pulse (clocks) */
+ int recovery_time; /* Recovery time (clocks) */
+} pio_clocks_t;
+
+static void compute_clocks(int pio, pio_clocks_t *clks)
+{
+ if (pio != PIO_NOT_EXIST) {
+ int adr_setup, data_pls, bus_speed;
+ bus_speed = ide_system_bus_speed();
+ adr_setup = ide_pio_timings[pio].setup_time;
+ data_pls = ide_pio_timings[pio].active_time;
+ clks->address_time = cmpt_clk(adr_setup, bus_speed);
+ clks->data_time = cmpt_clk(data_pls, bus_speed);
+ clks->recovery_time = cmpt_clk(ide_pio_timings[pio].cycle_time
+ - adr_setup-data_pls, bus_speed);
+ if (clks->address_time<1) clks->address_time = 1;
+ if (clks->address_time>4) clks->address_time = 4;
+ if (clks->data_time<1) clks->data_time = 1;
+ if (clks->data_time>16) clks->data_time = 16;
+ if (clks->recovery_time<2) clks->recovery_time = 2;
+ if (clks->recovery_time>17) clks->recovery_time = 17;
+ } else {
+ clks->address_time = 1;
+ clks->data_time = 1;
+ clks->recovery_time = 2;
+ /* minimal values */
+ }
+
+}
+
+/* Main tune procedure, called from tuneproc. */
+static void opti621_tune_drive (ide_drive_t *drive, byte pio)
+{
+ /* primary and secondary drives share some registers,
+ * so we have to program both drives
+ */
+ unsigned long flags;
+ byte pio1, pio2;
+ pio_clocks_t first, second;
+ int ax, drdy;
+ byte cycle1, cycle2, misc;
+ ide_hwif_t *hwif = HWIF(drive);
+
+ /* sets drive->drive_data for both drives */
+ compute_pios(drive, pio);
+ pio1 = hwif->drives[0].drive_data;
+ pio2 = hwif->drives[1].drive_data;
+
+ compute_clocks(pio1, &first);
+ compute_clocks(pio2, &second);
+
+ /* ax = max(a1,a2) */
+ ax = (first.address_time < second.address_time) ? second.address_time : first.address_time;
+
+ drdy = 2; /* DRDY is default 2 (by OPTi Databook) */
+
+ cycle1 = ((first.data_time-1)<<4) | (first.recovery_time-2);
+ cycle2 = ((second.data_time-1)<<4) | (second.recovery_time-2);
+ misc = READ_PREFETCH | ((ax-1)<<4) | ((drdy-2)<<1);
+
+#ifdef OPTI621_DEBUG
+ printk("%s: master: address: %d, data: %d, recovery: %d, drdy: %d [clk]\n",
+ hwif->name, ax, first.data_time, first.recovery_time, drdy);
+ printk("%s: slave: address: %d, data: %d, recovery: %d, drdy: %d [clk]\n",
+ hwif->name, ax, second.data_time, second.recovery_time, drdy);
+#endif
+
+ save_flags(flags); /* all CPUs */
+ cli(); /* all CPUs */
+
+ reg_base = hwif->io_ports[IDE_DATA_OFFSET];
+ outb(0xc0, reg_base+CNTRL_REG); /* allow Register-B */
+ outb(0xff, reg_base+5); /* hmm, setupvic.exe does this ;-) */
+ inb(reg_base+CNTRL_REG); /* if reads 0xff, adapter not exist? */
+ read_reg(CNTRL_REG); /* if reads 0xc0, no interface exist? */
+ read_reg(STRAP_REG); /* read version, probably 0 */
+
+ /* program primary drive */
+ write_reg(0, MISC_REG); /* select Index-0 for Register-A */
+ write_reg(cycle1, READ_REG); /* set read cycle timings */
+ write_reg(cycle1, WRITE_REG); /* set write cycle timings */
+
+ /* program secondary drive */
+ write_reg(1, MISC_REG); /* select Index-1 for Register-B */
+ write_reg(cycle2, READ_REG); /* set read cycle timings */
+ write_reg(cycle2, WRITE_REG); /* set write cycle timings */
+
+ write_reg(0x85, CNTRL_REG); /* use Register-A for drive 0 */
+ /* use Register-B for drive 1 */
+
+ write_reg(misc, MISC_REG); /* set address setup, DRDY timings, */
+ /* and read prefetch for both drives */
+
+ restore_flags(flags); /* all CPUs */
+}
+
+/*
+ * ide_init_opti621() is called once for each hwif found at boot.
+ */
+void ide_init_opti621 (ide_hwif_t *hwif)
+{
+ hwif->drives[0].drive_data = PIO_DONT_KNOW;
+ hwif->drives[1].drive_data = PIO_DONT_KNOW;
+ hwif->tuneproc = &opti621_tune_drive;
+}
diff --git a/drivers/ide/pdc202xx.c b/drivers/ide/pdc202xx.c
new file mode 100644
index 000000000..9ec7c8997
--- /dev/null
+++ b/drivers/ide/pdc202xx.c
@@ -0,0 +1,731 @@
+/*
+ * linux/drivers/block/pdc202xx.c Version 0.29 Feb. 10, 2000
+ *
+ * Copyright (C) 1998-2000 Andre Hedrick (andre@suse.com)
+ * May be copied or modified under the terms of the GNU General Public License
+ *
+ * Promise Ultra33 cards with BIOS v1.20 through 1.28 will need this
+ * compiled into the kernel if you have more than one card installed.
+ * Note that BIOS v1.29 is reported to fix the problem. Since this is
+ * safe chipset tuning, including this support is harmless
+ *
+ * The latest chipset code will support the following ::
+ * Three Ultra33 controllers and 12 drives.
+ * 8 are UDMA supported and 4 are limited to DMA mode 2 multi-word.
+ * The 8/4 ratio is a BIOS code limit by promise.
+ *
+ * UNLESS you enable "CONFIG_PDC202XX_BURST"
+ *
+ * There is only one BIOS in the three contollers.
+ *
+ * May 8 20:56:17 Orion kernel:
+ * Uniform Multi-Platform E-IDE driver Revision: 6.19
+ * PDC20246: IDE controller on PCI bus 00 dev a0
+ * PDC20246: not 100% native mode: will probe irqs later
+ * PDC20246: ROM enabled at 0xfebd0000
+ * PDC20246: (U)DMA Burst Bit ENABLED Primary PCI Mode Secondary PCI Mode.
+ * ide0: BM-DMA at 0xef80-0xef87, BIOS settings: hda:DMA, hdb:DMA
+ * ide1: BM-DMA at 0xef88-0xef8f, BIOS settings: hdc:pio, hdd:pio
+ * PDC20246: IDE controller on PCI bus 00 dev 98
+ * PDC20246: not 100% native mode: will probe irqs later
+ * PDC20246: ROM enabled at 0xfebc0000
+ * PDC20246: (U)DMA Burst Bit ENABLED Primary PCI Mode Secondary PCI Mode.
+ * ide2: BM-DMA at 0xef40-0xef47, BIOS settings: hde:DMA, hdf:DMA
+ * ide3: BM-DMA at 0xef48-0xef4f, BIOS settings: hdg:DMA, hdh:DMA
+ * PDC20246: IDE controller on PCI bus 00 dev 90
+ * PDC20246: not 100% native mode: will probe irqs later
+ * PDC20246: ROM enabled at 0xfebb0000
+ * PDC20246: (U)DMA Burst Bit DISABLED Primary PCI Mode Secondary PCI Mode.
+ * PDC20246: FORCING BURST BIT 0x00 -> 0x01 ACTIVE
+ * ide4: BM-DMA at 0xef00-0xef07, BIOS settings: hdi:DMA, hdj:pio
+ * ide5: BM-DMA at 0xef08-0xef0f, BIOS settings: hdk:pio, hdl:pio
+ * PIIX3: IDE controller on PCI bus 00 dev 39
+ * PIIX3: device not capable of full native PCI mode
+ *
+ * ide0 at 0xeff0-0xeff7,0xefe6 on irq 19
+ * ide1 at 0xefa8-0xefaf,0xebe6 on irq 19
+ * ide2 at 0xefa0-0xefa7,0xef7e on irq 18
+ * ide3 at 0xef68-0xef6f,0xef66 on irq 18
+ * ide4 at 0xef38-0xef3f,0xef62 on irq 17
+ * hda: QUANTUM FIREBALL ST6.4A, 6149MB w/81kB Cache, CHS=13328/15/63, UDMA(33)
+ * hdb: QUANTUM FIREBALL ST3.2A, 3079MB w/81kB Cache, CHS=6256/16/63, UDMA(33)
+ * hde: Maxtor 72004 AP, 1916MB w/128kB Cache, CHS=3893/16/63, DMA
+ * hdf: Maxtor 71626 A, 1554MB w/64kB Cache, CHS=3158/16/63, DMA
+ * hdi: Maxtor 90680D4, 6485MB w/256kB Cache, CHS=13176/16/63, UDMA(33)
+ * hdj: Maxtor 90680D4, 6485MB w/256kB Cache, CHS=13176/16/63, UDMA(33)
+ *
+ * Promise Ultra66 cards with BIOS v1.11 this
+ * compiled into the kernel if you have more than one card installed.
+ *
+ * PDC20262: IDE controller on PCI bus 00 dev a0
+ * PDC20262: not 100% native mode: will probe irqs later
+ * PDC20262: ROM enabled at 0xfebb0000
+ * PDC20262: (U)DMA Burst Bit ENABLED Primary PCI Mode Secondary PCI Mode.
+ * ide0: BM-DMA at 0xef00-0xef07, BIOS settings: hda:pio, hdb:pio
+ * ide1: BM-DMA at 0xef08-0xef0f, BIOS settings: hdc:pio, hdd:pio
+ *
+ * UDMA 4/2 and UDMA 3/1 only differ by the testing bit 13 in word93.
+ * Chipset timing speeds must be identical
+ *
+ * drive_number
+ * = ((HWIF(drive)->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
+ * = ((hwif->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
+ */
+
+/*
+ * Portions Copyright (C) 1999 Promise Technology, Inc.
+ * Author: Frank Tiernan (frankt@promise.com)
+ * Released under terms of General Public License
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include "ide_modes.h"
+
+#define PDC202XX_DEBUG_DRIVE_INFO 0
+#define PDC202XX_DECODE_REGISTER_INFO 0
+
+#undef DISPLAY_PDC202XX_TIMINGS
+
+#if defined(DISPLAY_PDC202XX_TIMINGS) && defined(CONFIG_PROC_FS)
+#include <linux/stat.h>
+#include <linux/proc_fs.h>
+
+static int pdc202xx_get_info(char *, char **, off_t, int);
+extern int (*pdc202xx_display_info)(char *, char **, off_t, int); /* ide-proc.c */
+extern char *ide_media_verbose(ide_drive_t *);
+static struct pci_dev *bmide_dev;
+
+static int pdc202xx_get_info (char *buffer, char **addr, off_t offset, int count)
+{
+ char *p = buffer;
+
+ u32 bibma = bmide_dev->resource[4].start;
+ u8 c0 = 0, c1 = 0;
+
+ /*
+ * at that point bibma+0x2 et bibma+0xa are byte registers
+ * to investigate:
+ */
+ c0 = inb_p((unsigned short)bibma + 0x02);
+ c1 = inb_p((unsigned short)bibma + 0x0a);
+
+ switch(bmide_dev->device) {
+ case PCI_DEVICE_ID_PROMISE_20262:
+ p += sprintf(p, "\n PDC20262 Chipset.\n");
+ break;
+ case PCI_DEVICE_ID_PROMISE_20246:
+ p += sprintf(p, "\n PDC20246 Chipset.\n");
+ break;
+ default:
+ p += sprintf(p, "\n PDC202XX Chipset.\n");
+ break;
+ }
+
+ p += sprintf(p, "--------------- Primary Channel ---------------- Secondary Channel -------------\n");
+ p += sprintf(p, " %sabled %sabled\n",
+ (c0&0x80) ? "dis" : " en",
+ (c1&0x80) ? "dis" : " en");
+ p += sprintf(p, "--------------- drive0 --------- drive1 -------- drive0 ---------- drive1 ------\n");
+ p += sprintf(p, "DMA enabled: %s %s %s %s\n",
+ (c0&0x20) ? "yes" : "no ", (c0&0x40) ? "yes" : "no ",
+ (c1&0x20) ? "yes" : "no ", (c1&0x40) ? "yes" : "no " );
+
+ p += sprintf(p, "UDMA\n");
+ p += sprintf(p, "DMA\n");
+ p += sprintf(p, "PIO\n");
+
+ return p-buffer; /* => must be less than 4k! */
+}
+#endif /* defined(DISPLAY_PDC202XX_TIMINGS) && defined(CONFIG_PROC_FS) */
+
+byte pdc202xx_proc = 0;
+
+extern char *ide_xfer_verbose (byte xfer_rate);
+
+/* A Register */
+#define SYNC_ERRDY_EN 0xC0
+
+#define SYNC_IN 0x80 /* control bit, different for master vs. slave drives */
+#define ERRDY_EN 0x40 /* control bit, different for master vs. slave drives */
+#define IORDY_EN 0x20 /* PIO: IOREADY */
+#define PREFETCH_EN 0x10 /* PIO: PREFETCH */
+
+#define PA3 0x08 /* PIO"A" timing */
+#define PA2 0x04 /* PIO"A" timing */
+#define PA1 0x02 /* PIO"A" timing */
+#define PA0 0x01 /* PIO"A" timing */
+
+/* B Register */
+
+#define MB2 0x80 /* DMA"B" timing */
+#define MB1 0x40 /* DMA"B" timing */
+#define MB0 0x20 /* DMA"B" timing */
+
+#define PB4 0x10 /* PIO_FORCE 1:0 */
+
+#define PB3 0x08 /* PIO"B" timing */ /* PIO flow Control mode */
+#define PB2 0x04 /* PIO"B" timing */ /* PIO 4 */
+#define PB1 0x02 /* PIO"B" timing */ /* PIO 3 half */
+#define PB0 0x01 /* PIO"B" timing */ /* PIO 3 other half */
+
+/* C Register */
+#define IORDYp_NO_SPEED 0x4F
+#define SPEED_DIS 0x0F
+
+#define DMARQp 0x80
+#define IORDYp 0x40
+#define DMAR_EN 0x20
+#define DMAW_EN 0x10
+
+#define MC3 0x08 /* DMA"C" timing */
+#define MC2 0x04 /* DMA"C" timing */
+#define MC1 0x02 /* DMA"C" timing */
+#define MC0 0x01 /* DMA"C" timing */
+
+#if PDC202XX_DECODE_REGISTER_INFO
+
+#define REG_A 0x01
+#define REG_B 0x02
+#define REG_C 0x04
+#define REG_D 0x08
+
+static void decode_registers (byte registers, byte value)
+{
+ byte bit = 0, bit1 = 0, bit2 = 0;
+
+ switch(registers) {
+ case REG_A:
+ bit2 = 0;
+ printk("A Register ");
+ if (value & 0x80) printk("SYNC_IN ");
+ if (value & 0x40) printk("ERRDY_EN ");
+ if (value & 0x20) printk("IORDY_EN ");
+ if (value & 0x10) printk("PREFETCH_EN ");
+ if (value & 0x08) { printk("PA3 ");bit2 |= 0x08; }
+ if (value & 0x04) { printk("PA2 ");bit2 |= 0x04; }
+ if (value & 0x02) { printk("PA1 ");bit2 |= 0x02; }
+ if (value & 0x01) { printk("PA0 ");bit2 |= 0x01; }
+ printk("PIO(A) = %d ", bit2);
+ break;
+ case REG_B:
+ bit1 = 0;bit2 = 0;
+ printk("B Register ");
+ if (value & 0x80) { printk("MB2 ");bit1 |= 0x80; }
+ if (value & 0x40) { printk("MB1 ");bit1 |= 0x40; }
+ if (value & 0x20) { printk("MB0 ");bit1 |= 0x20; }
+ printk("DMA(B) = %d ", bit1 >> 5);
+ if (value & 0x10) printk("PIO_FORCED/PB4 ");
+ if (value & 0x08) { printk("PB3 ");bit2 |= 0x08; }
+ if (value & 0x04) { printk("PB2 ");bit2 |= 0x04; }
+ if (value & 0x02) { printk("PB1 ");bit2 |= 0x02; }
+ if (value & 0x01) { printk("PB0 ");bit2 |= 0x01; }
+ printk("PIO(B) = %d ", bit2);
+ break;
+ case REG_C:
+ bit2 = 0;
+ printk("C Register ");
+ if (value & 0x80) printk("DMARQp ");
+ if (value & 0x40) printk("IORDYp ");
+ if (value & 0x20) printk("DMAR_EN ");
+ if (value & 0x10) printk("DMAW_EN ");
+
+ if (value & 0x08) { printk("MC3 ");bit2 |= 0x08; }
+ if (value & 0x04) { printk("MC2 ");bit2 |= 0x04; }
+ if (value & 0x02) { printk("MC1 ");bit2 |= 0x02; }
+ if (value & 0x01) { printk("MC0 ");bit2 |= 0x01; }
+ printk("DMA(C) = %d ", bit2);
+ break;
+ case REG_D:
+ printk("D Register ");
+ break;
+ default:
+ return;
+ }
+ printk("\n %s ", (registers & REG_D) ? "DP" :
+ (registers & REG_C) ? "CP" :
+ (registers & REG_B) ? "BP" :
+ (registers & REG_A) ? "AP" : "ERROR");
+ for (bit=128;bit>0;bit/=2)
+ printk("%s", (value & bit) ? "1" : "0");
+ printk("\n");
+}
+
+#endif /* PDC202XX_DECODE_REGISTER_INFO */
+
+static int config_chipset_for_dma (ide_drive_t *drive, byte ultra)
+{
+ struct hd_driveid *id = drive->id;
+ ide_hwif_t *hwif = HWIF(drive);
+ struct pci_dev *dev = hwif->pci_dev;
+ unsigned long high_16 = dev->resource[4].start & PCI_BASE_ADDRESS_IO_MASK;
+
+ int err;
+ unsigned int drive_conf;
+ byte drive_pci;
+ byte test1, test2, speed = -1;
+ byte AP, BP, CP, DP, TB, TC;
+ unsigned short EP;
+ byte CLKSPD = IN_BYTE(high_16 + 0x11);
+ int drive_number = ((hwif->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
+ byte udma_66 = ((id->hw_config & 0x2000) && (hwif->udma_four)) ? 1 : 0;
+ byte udma_33 = ultra ? (inb(high_16 + 0x001f) & 1) : 0;
+
+ /*
+ * Set the control register to use the 66Mhz system
+ * clock for UDMA 3/4 mode operation. If one drive on
+ * a channel is U66 capable but the other isn't we
+ * fall back to U33 mode. The BIOS INT 13 hooks turn
+ * the clock on then off for each read/write issued. I don't
+ * do that here because it would require modifying the
+ * kernel, seperating the fop routines from the kernel or
+ * somehow hooking the fops calls. It may also be possible to
+ * leave the 66Mhz clock on and readjust the timing
+ * parameters.
+ */
+
+ byte mask = hwif->channel ? 0x08 : 0x02;
+ unsigned short c_mask = hwif->channel ? (1<<11) : (1<<10);
+ byte ultra_66 = ((id->dma_ultra & 0x0010) || (id->dma_ultra & 0x0008)) ? 1 : 0;
+
+ pci_read_config_word(dev, 0x50, &EP);
+
+ if ((ultra_66) && (EP & c_mask)) {
+#ifdef DEBUG
+ printk("ULTRA66: %s channel of Ultra 66 requires an 80-pin cable for Ultra66 operation.\n", hwif->channel ? "Secondary", "Primary");
+ printk(" Switching to Ultra33 mode.\n");
+#endif /* DEBUG */
+ /* Primary : zero out second bit */
+ /* Secondary : zero out fourth bit */
+ OUT_BYTE(CLKSPD & ~mask, (high_16 + 0x11));
+ } else {
+ if (ultra_66) {
+ /*
+ * check to make sure drive on same channel
+ * is u66 capable
+ */
+ if (hwif->drives[!(drive_number%2)].id) {
+ if ((hwif->drives[!(drive_number%2)].id->dma_ultra & 0x0010) ||
+ (hwif->drives[!(drive_number%2)].id->dma_ultra & 0x0008)) {
+ OUT_BYTE(CLKSPD | mask, (high_16 + 0x11));
+ } else {
+ OUT_BYTE(CLKSPD & ~mask, (high_16 + 0x11));
+ }
+ } else { /* udma4 drive by itself */
+ OUT_BYTE(CLKSPD | mask, (high_16 + 0x11));
+ }
+ }
+ }
+
+ switch(drive_number) {
+ case 0: drive_pci = 0x60;
+ pci_read_config_dword(dev, drive_pci, &drive_conf);
+ if ((drive_conf != 0x004ff304) && (drive_conf != 0x004ff3c4))
+ goto chipset_is_set;
+ pci_read_config_byte(dev, (drive_pci), &test1);
+ if (!(test1 & SYNC_ERRDY_EN))
+ pci_write_config_byte(dev, (drive_pci), test1|SYNC_ERRDY_EN);
+ break;
+ case 1: drive_pci = 0x64;
+ pci_read_config_dword(dev, drive_pci, &drive_conf);
+ if ((drive_conf != 0x004ff304) && (drive_conf != 0x004ff3c4))
+ goto chipset_is_set;
+ pci_read_config_byte(dev, 0x60, &test1);
+ pci_read_config_byte(dev, (drive_pci), &test2);
+ if ((test1 & SYNC_ERRDY_EN) && !(test2 & SYNC_ERRDY_EN))
+ pci_write_config_byte(dev, (drive_pci), test2|SYNC_ERRDY_EN);
+ break;
+ case 2: drive_pci = 0x68;
+ pci_read_config_dword(dev, drive_pci, &drive_conf);
+ if ((drive_conf != 0x004ff304) && (drive_conf != 0x004ff3c4))
+ goto chipset_is_set;
+ pci_read_config_byte(dev, (drive_pci), &test1);
+ if (!(test1 & SYNC_ERRDY_EN))
+ pci_write_config_byte(dev, (drive_pci), test1|SYNC_ERRDY_EN);
+ break;
+ case 3: drive_pci = 0x6c;
+ pci_read_config_dword(dev, drive_pci, &drive_conf);
+ if ((drive_conf != 0x004ff304) && (drive_conf != 0x004ff3c4))
+ goto chipset_is_set;
+ pci_read_config_byte(dev, 0x68, &test1);
+ pci_read_config_byte(dev, (drive_pci), &test2);
+ if ((test1 & SYNC_ERRDY_EN) && !(test2 & SYNC_ERRDY_EN))
+ pci_write_config_byte(dev, (drive_pci), test2|SYNC_ERRDY_EN);
+ break;
+ default:
+ return ide_dma_off;
+ }
+
+chipset_is_set:
+
+ if (drive->media != ide_disk)
+ return ide_dma_off_quietly;
+
+ pci_read_config_byte(dev, (drive_pci), &AP);
+ pci_read_config_byte(dev, (drive_pci)|0x01, &BP);
+ pci_read_config_byte(dev, (drive_pci)|0x02, &CP);
+ pci_read_config_byte(dev, (drive_pci)|0x03, &DP);
+
+ if (id->capability & 4) { /* IORDY_EN */
+ pci_write_config_byte(dev, (drive_pci), AP|IORDY_EN);
+ pci_read_config_byte(dev, (drive_pci), &AP);
+ }
+
+ if (drive->media == ide_disk) { /* PREFETCH_EN */
+ pci_write_config_byte(dev, (drive_pci), AP|PREFETCH_EN);
+ pci_read_config_byte(dev, (drive_pci), &AP);
+ }
+
+ if ((BP & 0xF0) && (CP & 0x0F)) {
+ /* clear DMA modes of upper 842 bits of B Register */
+ /* clear PIO forced mode upper 1 bit of B Register */
+ pci_write_config_byte(dev, (drive_pci)|0x01, BP & ~0xF0);
+ pci_read_config_byte(dev, (drive_pci)|0x01, &BP);
+
+ /* clear DMA modes of lower 8421 bits of C Register */
+ pci_write_config_byte(dev, (drive_pci)|0x02, CP & ~0x0F);
+ pci_read_config_byte(dev, (drive_pci)|0x02, &CP);
+ }
+
+ pci_read_config_byte(dev, (drive_pci), &AP);
+ pci_read_config_byte(dev, (drive_pci)|0x01, &BP);
+ pci_read_config_byte(dev, (drive_pci)|0x02, &CP);
+
+ if ((id->dma_ultra & 0x0010) && (udma_66) && (udma_33)) {
+ /* speed 8 == UDMA mode 4 == speed 6 plus cable */
+ speed = XFER_UDMA_4; TB = 0x20; TC = 0x01;
+ } else if ((id->dma_ultra & 0x0008) && (udma_66) && (udma_33)) {
+ /* speed 7 == UDMA mode 3 == speed 5 plus cable */
+ speed = XFER_UDMA_3; TB = 0x40; TC = 0x02;
+ } else if ((id->dma_ultra & 0x0004) && (udma_33)) {
+ /* speed 6 == UDMA mode 2 */
+ speed = XFER_UDMA_2; TB = 0x20; TC = 0x01;
+ } else if ((id->dma_ultra & 0x0002) && (udma_33)) {
+ /* speed 5 == UDMA mode 1 */
+ speed = XFER_UDMA_1; TB = 0x40; TC = 0x02;
+ } else if ((id->dma_ultra & 0x0001) && (udma_33)) {
+ /* speed 4 == UDMA mode 0 */
+ speed = XFER_UDMA_0; TB = 0x60; TC = 0x03;
+ } else if (id->dma_mword & 0x0004) {
+ /* speed 4 == DMA mode 2 multi-word */
+ speed = XFER_MW_DMA_2; TB = 0x60; TC = 0x03;
+ } else if (id->dma_mword & 0x0002) {
+ /* speed 3 == DMA mode 1 multi-word */
+ speed = XFER_MW_DMA_1; TB = 0x60; TC = 0x04;
+ } else if (id->dma_mword & 0x0001) {
+ /* speed 2 == DMA mode 0 multi-word */
+ speed = XFER_MW_DMA_0; TB = 0x60; TC = 0x05;
+ } else if (id->dma_1word & 0x0004) {
+ /* speed 2 == DMA mode 2 single-word */
+ speed = XFER_SW_DMA_2; TB = 0x60; TC = 0x05;
+ } else if (id->dma_1word & 0x0002) {
+ /* speed 1 == DMA mode 1 single-word */
+ speed = XFER_SW_DMA_1; TB = 0x80; TC = 0x06;
+ } else if (id->dma_1word & 0x0001) {
+ /* speed 0 == DMA mode 0 single-word */
+ speed = XFER_SW_DMA_0; TB = 0xC0; TC = 0x0B;
+ } else {
+ /* restore original pci-config space */
+ pci_write_config_dword(dev, drive_pci, drive_conf);
+ return ide_dma_off_quietly;
+ }
+
+ pci_write_config_byte(dev, (drive_pci)|0x01, BP|TB);
+ pci_write_config_byte(dev, (drive_pci)|0x02, CP|TC);
+
+#if PDC202XX_DECODE_REGISTER_INFO
+ pci_read_config_byte(dev, (drive_pci), &AP);
+ pci_read_config_byte(dev, (drive_pci)|0x01, &BP);
+ pci_read_config_byte(dev, (drive_pci)|0x02, &CP);
+
+ decode_registers(REG_A, AP);
+ decode_registers(REG_B, BP);
+ decode_registers(REG_C, CP);
+ decode_registers(REG_D, DP);
+#endif /* PDC202XX_DECODE_REGISTER_INFO */
+
+ err = ide_config_drive_speed(drive, speed);
+
+#if PDC202XX_DEBUG_DRIVE_INFO
+ printk("%s: %s drive%d 0x%08x ",
+ drive->name, ide_xfer_verbose(speed),
+ drive_number, drive_conf);
+ pci_read_config_dword(dev, drive_pci, &drive_conf);
+ printk("0x%08x\n", drive_conf);
+#endif /* PDC202XX_DEBUG_DRIVE_INFO */
+
+ return ((int) ((id->dma_ultra >> 11) & 3) ? ide_dma_on :
+ ((id->dma_ultra >> 8) & 7) ? ide_dma_on :
+ ((id->dma_mword >> 8) & 7) ? ide_dma_on :
+ ((id->dma_1word >> 8) & 7) ? ide_dma_on :
+ ide_dma_off_quietly);
+}
+
+/* 0 1 2 3 4 5 6 7 8
+ * 960, 480, 390, 300, 240, 180, 120, 90, 60
+ * 180, 150, 120, 90, 60
+ * DMA_Speed
+ * 180, 120, 90, 90, 90, 60, 30
+ * 11, 5, 4, 3, 2, 1, 0
+ */
+
+static int config_chipset_for_pio (ide_drive_t *drive, byte pio)
+{
+ ide_hwif_t *hwif = HWIF(drive);
+ struct pci_dev *dev = hwif->pci_dev;
+ byte drive_pci, speed;
+ byte AP, BP, TA, TB;
+
+ int drive_number = ((hwif->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
+ int err;
+
+ switch (drive_number) {
+ case 0: drive_pci = 0x60; break;
+ case 1: drive_pci = 0x64; break;
+ case 2: drive_pci = 0x68; break;
+ case 3: drive_pci = 0x6c; break;
+ default: return 1;
+ }
+
+ pci_read_config_byte(dev, (drive_pci), &AP);
+ pci_read_config_byte(dev, (drive_pci)|0x01, &BP);
+
+
+ if ((AP & 0x0F) || (BP & 0x07)) {
+ /* clear PIO modes of lower 8421 bits of A Register */
+ pci_write_config_byte(dev, (drive_pci), AP & ~0x0F);
+ pci_read_config_byte(dev, (drive_pci), &AP);
+
+ /* clear PIO modes of lower 421 bits of B Register */
+ pci_write_config_byte(dev, (drive_pci)|0x01, BP & ~0x07);
+ pci_read_config_byte(dev, (drive_pci)|0x01, &BP);
+
+ pci_read_config_byte(dev, (drive_pci), &AP);
+ pci_read_config_byte(dev, (drive_pci)|0x01, &BP);
+ }
+
+ pio = (pio == 5) ? 4 : pio;
+ switch (ide_get_best_pio_mode(drive, 255, pio, NULL)) {
+ case 4: speed = XFER_PIO_4; TA=0x01; TB=0x04; break;
+ case 3: speed = XFER_PIO_3; TA=0x02; TB=0x06; break;
+ case 2: speed = XFER_PIO_2; TA=0x03; TB=0x08; break;
+ case 1: speed = XFER_PIO_1; TA=0x05; TB=0x0C; break;
+ case 0:
+ default: speed = XFER_PIO_0; TA=0x09; TB=0x13; break;
+ }
+ pci_write_config_byte(dev, (drive_pci), AP|TA);
+ pci_write_config_byte(dev, (drive_pci)|0x01, BP|TB);
+
+#if PDC202XX_DECODE_REGISTER_INFO
+ pci_read_config_byte(dev, (drive_pci), &AP);
+ pci_read_config_byte(dev, (drive_pci)|0x01, &BP);
+ pci_read_config_byte(dev, (drive_pci)|0x02, &CP);
+ pci_read_config_byte(dev, (drive_pci)|0x03, &DP);
+
+ decode_registers(REG_A, AP);
+ decode_registers(REG_B, BP);
+ decode_registers(REG_C, CP);
+ decode_registers(REG_D, DP);
+#endif /* PDC202XX_DECODE_REGISTER_INFO */
+
+ err = ide_config_drive_speed(drive, speed);
+
+#if PDC202XX_DEBUG_DRIVE_INFO
+ printk("%s: %s drive%d 0x%08x ",
+ drive->name, ide_xfer_verbose(speed),
+ drive_number, drive_conf);
+ pci_read_config_dword(dev, drive_pci, &drive_conf);
+ printk("0x%08x\n", drive_conf);
+#endif /* PDC202XX_DEBUG_DRIVE_INFO */
+
+ return err;
+}
+
+static void pdc202xx_tune_drive (ide_drive_t *drive, byte pio)
+{
+ (void) config_chipset_for_pio(drive, pio);
+}
+
+static int config_drive_xfer_rate (ide_drive_t *drive)
+{
+ struct hd_driveid *id = drive->id;
+ ide_hwif_t *hwif = HWIF(drive);
+ ide_dma_action_t dma_func = ide_dma_off_quietly;
+
+ if (id && (id->capability & 1) && hwif->autodma) {
+ /* Consult the list of known "bad" drives */
+ if (ide_dmaproc(ide_dma_bad_drive, drive)) {
+ dma_func = ide_dma_off;
+ goto fast_ata_pio;
+ }
+ dma_func = ide_dma_off_quietly;
+ if (id->field_valid & 4) {
+ if (id->dma_ultra & 0x001F) {
+ /* Force if Capable UltraDMA */
+ dma_func = config_chipset_for_dma(drive, 1);
+ if ((id->field_valid & 2) &&
+ (dma_func != ide_dma_on))
+ goto try_dma_modes;
+ }
+ } else if (id->field_valid & 2) {
+try_dma_modes:
+ if ((id->dma_mword & 0x0007) ||
+ (id->dma_1word & 0x0007)) {
+ /* Force if Capable regular DMA modes */
+ dma_func = config_chipset_for_dma(drive, 0);
+ if (dma_func != ide_dma_on)
+ goto no_dma_set;
+ }
+ } else if (ide_dmaproc(ide_dma_good_drive, drive)) {
+ if (id->eide_dma_time > 150) {
+ goto no_dma_set;
+ }
+ /* Consult the list of known "good" drives */
+ dma_func = config_chipset_for_dma(drive, 0);
+ if (dma_func != ide_dma_on)
+ goto no_dma_set;
+ } else {
+ goto fast_ata_pio;
+ }
+ } else if ((id->capability & 8) || (id->field_valid & 2)) {
+fast_ata_pio:
+ dma_func = ide_dma_off_quietly;
+no_dma_set:
+ (void) config_chipset_for_pio(drive, 5);
+ }
+
+ return HWIF(drive)->dmaproc(dma_func, drive);
+}
+
+/*
+ * pdc202xx_dmaproc() initiates/aborts (U)DMA read/write operations on a drive.
+ */
+int pdc202xx_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
+{
+ switch (func) {
+ case ide_dma_check:
+ return config_drive_xfer_rate(drive);
+ default:
+ break;
+ }
+ return ide_dmaproc(func, drive); /* use standard DMA stuff */
+}
+
+unsigned int __init pci_init_pdc202xx (struct pci_dev *dev, const char *name)
+{
+ unsigned long high_16 = dev->resource[4].start & PCI_BASE_ADDRESS_IO_MASK;
+ byte udma_speed_flag = inb(high_16 + 0x001f);
+ byte primary_mode = inb(high_16 + 0x001a);
+ byte secondary_mode = inb(high_16 + 0x001b);
+
+ if (dev->device == PCI_DEVICE_ID_PROMISE_20262) {
+ int i = 0;
+ /*
+ * software reset - this is required because the bios
+ * will set UDMA timing on if the hdd supports it. The
+ * user may want to turn udma off. A bug in the pdc20262
+ * is that it cannot handle a downgrade in timing from UDMA
+ * to DMA. Disk accesses after issuing a set feature command
+ * will result in errors. A software reset leaves the timing
+ * registers intact, but resets the drives.
+ */
+
+ OUT_BYTE(udma_speed_flag | 0x10, high_16 + 0x001f);
+ ide_delay_50ms();
+ ide_delay_50ms();
+ OUT_BYTE(udma_speed_flag & ~0x10, high_16 + 0x001f);
+ for (i=0; i<40; i++)
+ ide_delay_50ms();
+ }
+
+ if (dev->resource[PCI_ROM_RESOURCE].start) {
+ pci_write_config_dword(dev, PCI_ROM_ADDRESS, dev->resource[PCI_ROM_RESOURCE].start | PCI_ROM_ADDRESS_ENABLE);
+ printk("%s: ROM enabled at 0x%08lx\n", name, dev->resource[PCI_ROM_RESOURCE].start);
+ }
+
+ if ((dev->class >> 8) != PCI_CLASS_STORAGE_IDE) {
+ byte irq = 0, irq2 = 0;
+ pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq);
+ pci_read_config_byte(dev, (PCI_INTERRUPT_LINE)|0x80, &irq2); /* 0xbc */
+ if (irq != irq2) {
+ pci_write_config_byte(dev, (PCI_INTERRUPT_LINE)|0x80, irq); /* 0xbc */
+ printk("%s: pci-config space interrupt mirror fixed.\n", name);
+ }
+ }
+
+ printk("%s: (U)DMA Burst Bit %sABLED " \
+ "Primary %s Mode " \
+ "Secondary %s Mode.\n",
+ name,
+ (udma_speed_flag & 1) ? "EN" : "DIS",
+ (primary_mode & 1) ? "MASTER" : "PCI",
+ (secondary_mode & 1) ? "MASTER" : "PCI" );
+
+#ifdef CONFIG_PDC202XX_BURST
+ if (!(udma_speed_flag & 1)) {
+ printk("%s: FORCING BURST BIT 0x%02x -> 0x%02x ", name, udma_speed_flag, (udma_speed_flag|1));
+ outb(udma_speed_flag|1, high_16 + 0x001f);
+ printk("%sCTIVE\n", (inb(high_16 + 0x001f) & 1) ? "A" : "INA");
+ }
+#endif /* CONFIG_PDC202XX_BURST */
+
+#ifdef CONFIG_PDC202XX_MASTER
+ if (!(primary_mode & 1)) {
+ printk("%s: FORCING PRIMARY MODE BIT 0x%02x -> 0x%02x ",
+ name, primary_mode, (primary_mode|1));
+ outb(primary_mode|1, high_16 + 0x001a);
+ printk("%s\n", (inb(high_16 + 0x001a) & 1) ? "MASTER" : "PCI");
+ }
+
+ if (!(secondary_mode & 1)) {
+ printk("%s: FORCING SECONDARY MODE BIT 0x%02x -> 0x%02x ",
+ name, secondary_mode, (secondary_mode|1));
+ outb(secondary_mode|1, high_16 + 0x001b);
+ printk("%s\n", (inb(high_16 + 0x001b) & 1) ? "MASTER" : "PCI");
+ }
+#endif /* CONFIG_PDC202XX_MASTER */
+
+#if defined(DISPLAY_PDC202XX_TIMINGS) && defined(CONFIG_PROC_FS)
+ pdc202xx_proc = 1;
+ bmide_dev = dev;
+ pdc202xx_display_info = &pdc202xx_get_info;
+#endif /* DISPLAY_PDC202XX_TIMINGS && CONFIG_PROC_FS */
+
+ return dev->irq;
+}
+
+unsigned int __init ata66_pdc202xx (ide_hwif_t *hwif)
+{
+ unsigned short mask = (hwif->channel) ? (1<<11) : (1<<10);
+ unsigned short CIS;
+
+ pci_read_config_word(hwif->pci_dev, 0x50, &CIS);
+ return ((CIS & mask) ? 0 : 1);
+}
+
+void __init ide_init_pdc202xx (ide_hwif_t *hwif)
+{
+ hwif->tuneproc = &pdc202xx_tune_drive;
+
+ if (hwif->dma_base) {
+ hwif->dmaproc = &pdc202xx_dmaproc;
+ } else {
+ hwif->drives[0].autotune = 1;
+ hwif->drives[1].autotune = 1;
+ }
+}
diff --git a/drivers/ide/pdc4030.c b/drivers/ide/pdc4030.c
new file mode 100644
index 000000000..f42c4946f
--- /dev/null
+++ b/drivers/ide/pdc4030.c
@@ -0,0 +1,552 @@
+/* -*- linux-c -*-
+ * linux/drivers/ide/pdc4030.c Version 0.90 May 27, 1999
+ *
+ * Copyright (C) 1995-1999 Linus Torvalds & authors (see below)
+ */
+
+/*
+ * Principal Author/Maintainer: peterd@pnd-pc.demon.co.uk
+ *
+ * This file provides support for the second port and cache of Promise
+ * IDE interfaces, e.g. DC4030VL, DC4030VL-1 and DC4030VL-2.
+ *
+ * Thanks are due to Mark Lord for advice and patiently answering stupid
+ * questions, and all those mugs^H^H^H^Hbrave souls who've tested this,
+ * especially Andre Hedrick.
+ *
+ * Version 0.01 Initial version, #include'd in ide.c rather than
+ * compiled separately.
+ * Reads use Promise commands, writes as before. Drives
+ * on second channel are read-only.
+ * Version 0.02 Writes working on second channel, reads on both
+ * channels. Writes fail under high load. Suspect
+ * transfers of >127 sectors don't work.
+ * Version 0.03 Brought into line with ide.c version 5.27.
+ * Other minor changes.
+ * Version 0.04 Updated for ide.c version 5.30
+ * Changed initialization strategy
+ * Version 0.05 Kernel integration. -ml
+ * Version 0.06 Ooops. Add hwgroup to direct call of ide_intr() -ml
+ * Version 0.07 Added support for DC4030 variants
+ * Secondary interface autodetection
+ * Version 0.08 Renamed to pdc4030.c
+ * Version 0.09 Obsolete - never released - did manual write request
+ * splitting before max_sectors[major][minor] available.
+ * Version 0.10 Updated for 2.1 series of kernels
+ * Version 0.11 Updated for 2.3 series of kernels
+ * Autodetection code added.
+ *
+ * Version 0.90 Transition to BETA code. No lost/unexpected interrupts
+ */
+
+/*
+ * Once you've compiled it in, you'll have to also enable the interface
+ * setup routine from the kernel command line, as in
+ *
+ * 'linux ide0=dc4030' or 'linux ide1=dc4030'
+ *
+ * It should now work as a second controller also ('ide1=dc4030') but only
+ * if you DON'T have BIOS V4.44, which has a bug. If you have this version
+ * and EPROM programming facilities, you need to fix 4 bytes:
+ * 2496: 81 81
+ * 2497: 3E 3E
+ * 2498: 22 98 *
+ * 2499: 06 05 *
+ * 249A: F0 F0
+ * 249B: 01 01
+ * ...
+ * 24A7: 81 81
+ * 24A8: 3E 3E
+ * 24A9: 22 98 *
+ * 24AA: 06 05 *
+ * 24AB: 70 70
+ * 24AC: 01 01
+ *
+ * As of January 1999, Promise Technology Inc. have finally supplied me with
+ * some technical information which has shed a glimmer of light on some of the
+ * problems I was having, especially with writes.
+ *
+ * There are still problems with the robustness and efficiency of this driver
+ * because I still don't understand what the card is doing with interrupts.
+ */
+
+#define DEBUG_READ
+#define DEBUG_WRITE
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include "pdc4030.h"
+
+/*
+ * promise_selectproc() is invoked by ide.c
+ * in preparation for access to the specified drive.
+ */
+static void promise_selectproc (ide_drive_t *drive)
+{
+ unsigned int number;
+
+ number = (HWIF(drive)->channel << 1) + drive->select.b.unit;
+ OUT_BYTE(number,IDE_FEATURE_REG);
+}
+
+/*
+ * pdc4030_cmd handles the set of vendor specific commands that are initiated
+ * by command F0. They all have the same success/failure notification -
+ * 'P' (=0x50) on success, 'p' (=0x70) on failure.
+ */
+int pdc4030_cmd(ide_drive_t *drive, byte cmd)
+{
+ unsigned long timeout, timer;
+ byte status_val;
+
+ promise_selectproc(drive); /* redundant? */
+ OUT_BYTE(0xF3,IDE_SECTOR_REG);
+ OUT_BYTE(cmd,IDE_SELECT_REG);
+ OUT_BYTE(PROMISE_EXTENDED_COMMAND,IDE_COMMAND_REG);
+ timeout = HZ * 10;
+ timeout += jiffies;
+ do {
+ if(time_after(jiffies, timeout)) {
+ return 2; /* device timed out */
+ }
+ /* This is out of delay_10ms() */
+ /* Delays at least 10ms to give interface a chance */
+ timer = jiffies + (HZ + 99)/100 + 1;
+ while (time_after(timer, jiffies));
+ status_val = IN_BYTE(IDE_SECTOR_REG);
+ } while (status_val != 0x50 && status_val != 0x70);
+
+ if(status_val == 0x50)
+ return 0; /* device returned success */
+ else
+ return 1; /* device returned failure */
+}
+
+/*
+ * pdc4030_identify sends a vendor-specific IDENTIFY command to the drive
+ */
+int pdc4030_identify(ide_drive_t *drive)
+{
+ return pdc4030_cmd(drive, PROMISE_IDENTIFY);
+}
+
+int enable_promise_support = 0;
+
+void __init init_pdc4030 (void)
+{
+ enable_promise_support = 1;
+}
+
+/*
+ * setup_pdc4030()
+ * Completes the setup of a Promise DC4030 controller card, once found.
+ */
+int __init setup_pdc4030 (ide_hwif_t *hwif)
+{
+ ide_drive_t *drive;
+ ide_hwif_t *hwif2;
+ struct dc_ident ident;
+ int i;
+ ide_startstop_t startstop;
+
+ if (!hwif) return 0;
+
+ drive = &hwif->drives[0];
+ hwif2 = &ide_hwifs[hwif->index+1];
+ if (hwif->chipset == ide_pdc4030) /* we've already been found ! */
+ return 1;
+
+ if (IN_BYTE(IDE_NSECTOR_REG) == 0xFF || IN_BYTE(IDE_SECTOR_REG) == 0xFF) {
+ return 0;
+ }
+ if (IDE_CONTROL_REG)
+ OUT_BYTE(0x08,IDE_CONTROL_REG);
+ if (pdc4030_cmd(drive,PROMISE_GET_CONFIG)) {
+ return 0;
+ }
+ if (ide_wait_stat(&startstop, drive,DATA_READY,BAD_W_STAT,WAIT_DRQ)) {
+ printk(KERN_INFO
+ "%s: Failed Promise read config!\n",hwif->name);
+ return 0;
+ }
+ ide_input_data(drive,&ident,SECTOR_WORDS);
+ if (ident.id[1] != 'P' || ident.id[0] != 'T') {
+ return 0;
+ }
+ printk(KERN_INFO "%s: Promise caching controller, ",hwif->name);
+ switch(ident.type) {
+ case 0x43: printk("DC4030VL-2, "); break;
+ case 0x41: printk("DC4030VL-1, "); break;
+ case 0x40: printk("DC4030VL, "); break;
+ default:
+ printk("unknown - type 0x%02x - please report!\n"
+ ,ident.type);
+ printk("Please e-mail the following data to "
+ "promise@pnd-pc.demon.co.uk along with\n"
+ "a description of your card and drives:\n");
+ for (i=0; i < 0x90; i++) {
+ printk("%02x ", ((unsigned char *)&ident)[i]);
+ if ((i & 0x0f) == 0x0f) printk("\n");
+ }
+ return 0;
+ }
+ printk("%dKB cache, ",(int)ident.cache_mem);
+ switch(ident.irq) {
+ case 0x00: hwif->irq = 14; break;
+ case 0x01: hwif->irq = 12; break;
+ default: hwif->irq = 15; break;
+ }
+ printk("on IRQ %d\n",hwif->irq);
+
+ /*
+ * Once found and identified, we set up the next hwif in the array
+ * (hwif2 = ide_hwifs[hwif->index+1]) with the same io ports, irq
+ * and other settings as the main hwif. This gives us two "mated"
+ * hwifs pointing to the Promise card.
+ *
+ * We also have to shift the default values for the remaining
+ * interfaces "up by one" to make room for the second interface on the
+ * same set of values.
+ */
+
+ hwif->chipset = hwif2->chipset = ide_pdc4030;
+ hwif->mate = hwif2;
+ hwif2->mate = hwif;
+ hwif2->channel = 1;
+ hwif->selectproc = hwif2->selectproc = &promise_selectproc;
+ hwif->serialized = hwif2->serialized = 1;
+
+/* Shift the remaining interfaces down by one */
+ for (i=MAX_HWIFS-1 ; i > hwif->index+1 ; i--) {
+ ide_hwif_t *h = &ide_hwifs[i];
+
+#ifdef DEBUG
+ printk(KERN_DEBUG "Shifting i/f %d values to i/f %d\n",i-1,i);
+#endif
+ ide_init_hwif_ports(&h->hw, (h-1)->io_ports[IDE_DATA_OFFSET], 0, NULL);
+ memcpy(h->io_ports, h->hw.io_ports, sizeof(h->io_ports));
+ h->noprobe = (h-1)->noprobe;
+ }
+ ide_init_hwif_ports(&hwif2->hw, hwif->io_ports[IDE_DATA_OFFSET], 0, NULL);
+ memcpy(hwif2->io_ports, hwif->hw.io_ports, sizeof(hwif2->io_ports));
+ hwif2->irq = hwif->irq;
+ hwif2->hw.irq = hwif->hw.irq = hwif->irq;
+ for (i=0; i<2 ; i++) {
+ hwif->drives[i].io_32bit = 3;
+ hwif2->drives[i].io_32bit = 3;
+ hwif->drives[i].keep_settings = 1;
+ hwif2->drives[i].keep_settings = 1;
+ if (!ident.current_tm[i].cyl)
+ hwif->drives[i].noprobe = 1;
+ if (!ident.current_tm[i+2].cyl)
+ hwif2->drives[i].noprobe = 1;
+ }
+ return 1;
+}
+
+/*
+ * detect_pdc4030()
+ * Tests for the presence of a DC4030 Promise card on this interface
+ * Returns: 1 if found, 0 if not found
+ */
+int __init detect_pdc4030(ide_hwif_t *hwif)
+{
+ ide_drive_t *drive = &hwif->drives[0];
+
+ if (IDE_DATA_REG == 0) { /* Skip test for non-existent interface */
+ return 0;
+ }
+ OUT_BYTE(0xF3, IDE_SECTOR_REG);
+ OUT_BYTE(0x14, IDE_SELECT_REG);
+ OUT_BYTE(PROMISE_EXTENDED_COMMAND, IDE_COMMAND_REG);
+
+ ide_delay_50ms();
+
+ if (IN_BYTE(IDE_ERROR_REG) == 'P' &&
+ IN_BYTE(IDE_NSECTOR_REG) == 'T' &&
+ IN_BYTE(IDE_SECTOR_REG) == 'I') {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+void __init ide_probe_for_pdc4030(void)
+{
+ unsigned int index;
+ ide_hwif_t *hwif;
+
+ if (enable_promise_support == 0)
+ return;
+ for (index = 0; index < MAX_HWIFS; index++) {
+ hwif = &ide_hwifs[index];
+ if (hwif->chipset == ide_unknown && detect_pdc4030(hwif)) {
+ setup_pdc4030(hwif);
+ }
+ }
+}
+
+
+
+/*
+ * promise_read_intr() is the handler for disk read/multread interrupts
+ */
+static ide_startstop_t promise_read_intr (ide_drive_t *drive)
+{
+ byte stat;
+ int total_remaining;
+ unsigned int sectors_left, sectors_avail, nsect;
+ struct request *rq;
+
+ if (!OK_STAT(stat=GET_STAT(),DATA_READY,BAD_R_STAT)) {
+ return ide_error(drive, "promise_read_intr", stat);
+ }
+
+read_again:
+ do {
+ sectors_left = IN_BYTE(IDE_NSECTOR_REG);
+ IN_BYTE(IDE_SECTOR_REG);
+ } while (IN_BYTE(IDE_NSECTOR_REG) != sectors_left);
+ rq = HWGROUP(drive)->rq;
+ sectors_avail = rq->nr_sectors - sectors_left;
+ if (!sectors_avail)
+ goto read_again;
+
+read_next:
+ rq = HWGROUP(drive)->rq;
+ nsect = rq->current_nr_sectors;
+ if (nsect > sectors_avail)
+ nsect = sectors_avail;
+ sectors_avail -= nsect;
+ ide_input_data(drive, rq->buffer, nsect * SECTOR_WORDS);
+#ifdef DEBUG_READ
+ printk(KERN_DEBUG "%s: promise_read: sectors(%ld-%ld), "
+ "buf=0x%08lx, rem=%ld\n", drive->name, rq->sector,
+ rq->sector+nsect-1, (unsigned long) rq->buffer,
+ rq->nr_sectors-nsect);
+#endif
+ rq->sector += nsect;
+ rq->buffer += nsect<<9;
+ rq->errors = 0;
+ rq->nr_sectors -= nsect;
+ total_remaining = rq->nr_sectors;
+ if ((rq->current_nr_sectors -= nsect) <= 0) {
+ ide_end_request(1, HWGROUP(drive));
+ }
+/*
+ * Now the data has been read in, do the following:
+ *
+ * if there are still sectors left in the request,
+ * if we know there are still sectors available from the interface,
+ * go back and read the next bit of the request.
+ * else if DRQ is asserted, there are more sectors available, so
+ * go back and find out how many, then read them in.
+ * else if BUSY is asserted, we are going to get an interrupt, so
+ * set the handler for the interrupt and just return
+ */
+ if (total_remaining > 0) {
+ if (sectors_avail)
+ goto read_next;
+ stat = GET_STAT();
+ if (stat & DRQ_STAT)
+ goto read_again;
+ if (stat & BUSY_STAT) {
+ ide_set_handler (drive, &promise_read_intr, WAIT_CMD, NULL);
+#ifdef DEBUG_READ
+ printk(KERN_DEBUG "%s: promise_read: waiting for"
+ "interrupt\n", drive->name);
+#endif
+ return ide_started;
+ }
+ printk(KERN_ERR "%s: Eeek! promise_read_intr: sectors left "
+ "!DRQ !BUSY\n", drive->name);
+ return ide_error(drive, "promise read intr", stat);
+ }
+ return ide_stopped;
+}
+
+/*
+ * promise_complete_pollfunc()
+ * This is the polling function for waiting (nicely!) until drive stops
+ * being busy. It is invoked at the end of a write, after the previous poll
+ * has finished.
+ *
+ * Once not busy, the end request is called.
+ */
+static ide_startstop_t promise_complete_pollfunc(ide_drive_t *drive)
+{
+ ide_hwgroup_t *hwgroup = HWGROUP(drive);
+ struct request *rq = hwgroup->rq;
+ int i;
+
+ if (GET_STAT() & BUSY_STAT) {
+ if (time_before(jiffies, hwgroup->poll_timeout)) {
+ ide_set_handler(drive, &promise_complete_pollfunc, HZ/100, NULL);
+ return ide_started; /* continue polling... */
+ }
+ hwgroup->poll_timeout = 0;
+ printk(KERN_ERR "%s: completion timeout - still busy!\n",
+ drive->name);
+ return ide_error(drive, "busy timeout", GET_STAT());
+ }
+
+ hwgroup->poll_timeout = 0;
+#ifdef DEBUG_WRITE
+ printk(KERN_DEBUG "%s: Write complete - end_request\n", drive->name);
+#endif
+ for (i = rq->nr_sectors; i > 0; ) {
+ i -= rq->current_nr_sectors;
+ ide_end_request(1, hwgroup);
+ }
+ return ide_stopped;
+}
+
+/*
+ * promise_write_pollfunc() is the handler for disk write completion polling.
+ */
+static ide_startstop_t promise_write_pollfunc (ide_drive_t *drive)
+{
+ ide_hwgroup_t *hwgroup = HWGROUP(drive);
+
+ if (IN_BYTE(IDE_NSECTOR_REG) != 0) {
+ if (time_before(jiffies, hwgroup->poll_timeout)) {
+ ide_set_handler (drive, &promise_write_pollfunc, HZ/100, NULL);
+ return ide_started; /* continue polling... */
+ }
+ hwgroup->poll_timeout = 0;
+ printk(KERN_ERR "%s: write timed-out!\n",drive->name);
+ return ide_error (drive, "write timeout", GET_STAT());
+ }
+
+ /*
+ * Now write out last 4 sectors and poll for not BUSY
+ */
+ ide_multwrite(drive, 4);
+ hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE;
+ ide_set_handler(drive, &promise_complete_pollfunc, HZ/100, NULL);
+#ifdef DEBUG_WRITE
+ printk(KERN_DEBUG "%s: Done last 4 sectors - status = %02x\n",
+ drive->name, GET_STAT());
+#endif
+ return ide_started;
+}
+
+/*
+ * promise_write() transfers a block of one or more sectors of data to a
+ * drive as part of a disk write operation. All but 4 sectors are transfered
+ * in the first attempt, then the interface is polled (nicely!) for completion
+ * before the final 4 sectors are transfered. There is no interrupt generated
+ * on writes (at least on the DC4030VL-2), we just have to poll for NOT BUSY.
+ */
+static ide_startstop_t promise_write (ide_drive_t *drive)
+{
+ ide_hwgroup_t *hwgroup = HWGROUP(drive);
+ struct request *rq = &hwgroup->wrq;
+
+#ifdef DEBUG_WRITE
+ printk(KERN_DEBUG "%s: promise_write: sectors(%ld-%ld), "
+ "buffer=0x%08x\n", drive->name, rq->sector,
+ rq->sector + rq->nr_sectors - 1, (unsigned int)rq->buffer);
+#endif
+
+ /*
+ * If there are more than 4 sectors to transfer, do n-4 then go into
+ * the polling strategy as defined above.
+ */
+ if (rq->nr_sectors > 4) {
+ if (ide_multwrite(drive, rq->nr_sectors - 4))
+ return ide_stopped;
+ hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE;
+ ide_set_handler (drive, &promise_write_pollfunc, HZ/100, NULL);
+ return ide_started;
+ } else {
+ /*
+ * There are 4 or fewer sectors to transfer, do them all in one go
+ * and wait for NOT BUSY.
+ */
+ if (ide_multwrite(drive, rq->nr_sectors))
+ return ide_stopped;
+ hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE;
+ ide_set_handler(drive, &promise_complete_pollfunc, HZ/100, NULL);
+#ifdef DEBUG_WRITE
+ printk(KERN_DEBUG "%s: promise_write: <= 4 sectors, "
+ "status = %02x\n", drive->name, GET_STAT());
+#endif
+ return ide_started;
+ }
+}
+
+/*
+ * do_pdc4030_io() is called from do_rw_disk, having had the block number
+ * already set up. It issues a READ or WRITE command to the Promise
+ * controller, assuming LBA has been used to set up the block number.
+ */
+ide_startstop_t do_pdc4030_io (ide_drive_t *drive, struct request *rq)
+{
+ unsigned long timeout;
+ byte stat;
+
+ if (rq->cmd == READ) {
+ OUT_BYTE(PROMISE_READ, IDE_COMMAND_REG);
+/*
+ * The card's behaviour is odd at this point. If the data is
+ * available, DRQ will be true, and no interrupt will be
+ * generated by the card. If this is the case, we need to call the
+ * "interrupt" handler (promise_read_intr) directly. Otherwise, if
+ * an interrupt is going to occur, bit0 of the SELECT register will
+ * be high, so we can set the handler the just return and be interrupted.
+ * If neither of these is the case, we wait for up to 50ms (badly I'm
+ * afraid!) until one of them is.
+ */
+ timeout = jiffies + HZ/20; /* 50ms wait */
+ do {
+ stat=GET_STAT();
+ if (stat & DRQ_STAT) {
+ udelay(1);
+ return promise_read_intr(drive);
+ }
+ if (IN_BYTE(IDE_SELECT_REG) & 0x01) {
+#ifdef DEBUG_READ
+ printk(KERN_DEBUG "%s: read: waiting for "
+ "interrupt\n", drive->name);
+#endif
+ ide_set_handler(drive, &promise_read_intr, WAIT_CMD, NULL);
+ return ide_started;
+ }
+ udelay(1);
+ } while (time_before(jiffies, timeout));
+
+ printk(KERN_ERR "%s: reading: No DRQ and not waiting - Odd!\n",
+ drive->name);
+ return ide_stopped;
+ } else if (rq->cmd == WRITE) {
+ ide_startstop_t startstop;
+ OUT_BYTE(PROMISE_WRITE, IDE_COMMAND_REG);
+ if (ide_wait_stat(&startstop, drive, DATA_READY, drive->bad_wstat, WAIT_DRQ)) {
+ printk(KERN_ERR "%s: no DRQ after issuing "
+ "PROMISE_WRITE\n", drive->name);
+ return startstop;
+ }
+ if (!drive->unmask)
+ __cli(); /* local CPU only */
+ HWGROUP(drive)->wrq = *rq; /* scratchpad */
+ return promise_write(drive);
+
+ } else {
+ printk("KERN_WARNING %s: bad command: %d\n",
+ drive->name, rq->cmd);
+ ide_end_request(0, HWGROUP(drive));
+ return ide_stopped;
+ }
+}
diff --git a/drivers/ide/pdc4030.h b/drivers/ide/pdc4030.h
new file mode 100644
index 000000000..551785c36
--- /dev/null
+++ b/drivers/ide/pdc4030.h
@@ -0,0 +1,44 @@
+/*
+ * linux/drivers/ide/pdc4030.h
+ *
+ * Copyright (C) 1995-1998 Linus Torvalds & authors
+ */
+
+/*
+ * Principal author: Peter Denison <peterd@pnd-pc.demon.co.uk>
+ */
+
+#ifndef IDE_PROMISE_H
+#define IDE_PROMISE_H
+
+#define PROMISE_EXTENDED_COMMAND 0xF0
+#define PROMISE_READ 0xF2
+#define PROMISE_WRITE 0xF3
+/* Extended commands - main command code = 0xf0 */
+#define PROMISE_GET_CONFIG 0x10
+#define PROMISE_IDENTIFY 0x20
+
+struct translation_mode {
+ u16 cyl;
+ u8 head;
+ u8 sect;
+};
+
+struct dc_ident {
+ u8 type;
+ u8 unknown1;
+ u8 hw_revision;
+ u8 firmware_major;
+ u8 firmware_minor;
+ u8 bios_address;
+ u8 irq;
+ u8 unknown2;
+ u16 cache_mem;
+ u16 unknown3;
+ u8 id[2];
+ u16 info;
+ struct translation_mode current_tm[4];
+ u8 pad[SECTOR_WORDS*4 - 32];
+};
+
+#endif IDE_PROMISE_H
diff --git a/drivers/ide/piix.c b/drivers/ide/piix.c
new file mode 100644
index 000000000..97e57fa55
--- /dev/null
+++ b/drivers/ide/piix.c
@@ -0,0 +1,434 @@
+/*
+ * linux/drivers/block/piix.c Version 0.30 Feb. 26, 2000
+ *
+ * Copyright (C) 1998-1999 Andrzej Krzysztofowicz, Author and Maintainer
+ * Copyright (C) 1998-2000 Andre Hedrick (andre@suse.com)
+ * May be copied or modified under the terms of the GNU General Public License
+ *
+ * PIO mode setting function for Intel chipsets.
+ * For use instead of BIOS settings.
+ *
+ * 40-41
+ * 42-43
+ *
+ * 41
+ * 43
+ *
+ * | PIO 0 | c0 | 80 | 0 | piix_tune_drive(drive, 0);
+ * | PIO 2 | SW2 | d0 | 90 | 4 | piix_tune_drive(drive, 2);
+ * | PIO 3 | MW1 | e1 | a1 | 9 | piix_tune_drive(drive, 3);
+ * | PIO 4 | MW2 | e3 | a3 | b | piix_tune_drive(drive, 4);
+ *
+ * sitre = word40 & 0x4000; primary
+ * sitre = word42 & 0x4000; secondary
+ *
+ * 44 8421|8421 hdd|hdb
+ *
+ * 48 8421 hdd|hdc|hdb|hda udma enabled
+ *
+ * 0001 hda
+ * 0010 hdb
+ * 0100 hdc
+ * 1000 hdd
+ *
+ * 4a 84|21 hdb|hda
+ * 4b 84|21 hdd|hdc
+ *
+ * ata-33/82371AB
+ * ata-33/82371EB
+ * ata-33/82801AB ata-66/82801AA
+ * 00|00 udma 0 00|00 reserved
+ * 01|01 udma 1 01|01 udma 3
+ * 10|10 udma 2 10|10 udma 4
+ * 11|11 reserved 11|11 reserved
+ *
+ * 54 8421|8421 ata66 drive|ata66 enable
+ *
+ * pci_read_config_word(HWIF(drive)->pci_dev, 0x40, &reg40);
+ * pci_read_config_word(HWIF(drive)->pci_dev, 0x42, &reg42);
+ * pci_read_config_word(HWIF(drive)->pci_dev, 0x44, &reg44);
+ * pci_read_config_word(HWIF(drive)->pci_dev, 0x48, &reg48);
+ * pci_read_config_word(HWIF(drive)->pci_dev, 0x4a, &reg4a);
+ * pci_read_config_word(HWIF(drive)->pci_dev, 0x54, &reg54);
+ *
+ * 00:1f.1 IDE interface: Intel Corporation:
+ * Unknown device 2411 (rev 01) (prog-if 80 [Master])
+ * Control: I/O+ Mem- BusMaster+ SpecCycle- MemWINV- VGASnoop-
+ * ParErr- Stepping- SERR- FastB2B-
+ * Status: Cap- 66Mhz- UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort-
+ * <TAbort- <MAbort- >SERR- <PERR-
+ * Latency: 0 set
+ * Region 4: I/O ports at ffa0
+ * 00: 86 80 11 24 05 00 80 02 01 80 01 01 00 00 00 00
+ * 10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 20: a1 ff 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 40: 07 a3 03 a3 00 00 00 00 05 00 02 02 00 00 00 00
+ * 50: 00 00 00 00 11 04 00 00 00 00 00 00 00 00 00 00
+ * 60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * f0: 00 00 00 00 00 00 00 00 3a 0f 00 00 00 00 00 00
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/hdreg.h>
+#include <linux/ide.h>
+#include <linux/delay.h>
+
+#include <asm/io.h>
+
+#include "ide_modes.h"
+
+#define PIIX_DEBUG_DRIVE_INFO 0
+
+#define DISPLAY_PIIX_TIMINGS
+
+#if defined(DISPLAY_PIIX_TIMINGS) && defined(CONFIG_PROC_FS)
+#include <linux/stat.h>
+#include <linux/proc_fs.h>
+
+static int piix_get_info(char *, char **, off_t, int);
+extern int (*piix_display_info)(char *, char **, off_t, int); /* ide-proc.c */
+extern char *ide_media_verbose(ide_drive_t *);
+static struct pci_dev *bmide_dev;
+
+static int piix_get_info (char *buffer, char **addr, off_t offset, int count)
+{
+ char *p = buffer;
+ u32 bibma = bmide_dev->resource[4].start;
+ u16 reg40 = 0, psitre = 0, reg42 = 0, ssitre = 0;
+ u8 c0 = 0, c1 = 0;
+ u8 reg44 = 0, reg48 = 0, reg4a = 0, reg4b = 0, reg54 = 0;
+
+ pci_read_config_word(bmide_dev, 0x40, &reg40);
+ pci_read_config_word(bmide_dev, 0x42, &reg42);
+ pci_read_config_byte(bmide_dev, 0x44, &reg44);
+ pci_read_config_byte(bmide_dev, 0x48, &reg48);
+ pci_read_config_byte(bmide_dev, 0x4a, &reg4a);
+ pci_read_config_byte(bmide_dev, 0x4b, &reg4b);
+ pci_read_config_byte(bmide_dev, 0x54, &reg54);
+
+ psitre = (reg40 & 0x4000) ? 1 : 0;
+ ssitre = (reg42 & 0x4000) ? 1 : 0;
+
+ /*
+ * at that point bibma+0x2 et bibma+0xa are byte registers
+ * to investigate:
+ */
+ c0 = inb_p((unsigned short)bibma + 0x02);
+ c1 = inb_p((unsigned short)bibma + 0x0a);
+
+ switch(bmide_dev->device) {
+ case PCI_DEVICE_ID_INTEL_82372FB_1:
+ case PCI_DEVICE_ID_INTEL_82801AA_1:
+ p += sprintf(p, "\n Intel PIIX4 Ultra 66 Chipset.\n");
+ break;
+ case PCI_DEVICE_ID_INTEL_82801AB_1:
+ case PCI_DEVICE_ID_INTEL_82371AB:
+ p += sprintf(p, "\n Intel PIIX4 Ultra 33 Chipset.\n");
+ break;
+ case PCI_DEVICE_ID_INTEL_82371SB_1:
+ p += sprintf(p, "\n Intel PIIX3 Chipset.\n");
+ break;
+ case PCI_DEVICE_ID_INTEL_82371FB_1:
+ case PCI_DEVICE_ID_INTEL_82371FB_0:
+ default:
+ p += sprintf(p, "\n Intel PIIX Chipset.\n");
+ break;
+ }
+ p += sprintf(p, "--------------- Primary Channel ---------------- Secondary Channel -------------\n");
+ p += sprintf(p, " %sabled %sabled\n",
+ (c0&0x80) ? "dis" : " en",
+ (c1&0x80) ? "dis" : " en");
+ p += sprintf(p, "--------------- drive0 --------- drive1 -------- drive0 ---------- drive1 ------\n");
+ p += sprintf(p, "DMA enabled: %s %s %s %s\n",
+ (c0&0x20) ? "yes" : "no ",
+ (c0&0x40) ? "yes" : "no ",
+ (c1&0x20) ? "yes" : "no ",
+ (c1&0x40) ? "yes" : "no " );
+ p += sprintf(p, "UDMA enabled: %s %s %s %s\n",
+ (reg48&0x01) ? "yes" : "no ",
+ (reg48&0x02) ? "yes" : "no ",
+ (reg48&0x04) ? "yes" : "no ",
+ (reg48&0x08) ? "yes" : "no " );
+ p += sprintf(p, "UDMA enabled: %s %s %s %s\n",
+ ((reg54&0x11) && (reg4a&0x02)) ? "4" :
+ ((reg54&0x11) && (reg4a&0x01)) ? "3" :
+ (reg4a&0x02) ? "2" :
+ (reg4a&0x01) ? "1" :
+ (reg4a&0x00) ? "0" : "X",
+ ((reg54&0x22) && (reg4a&0x20)) ? "4" :
+ ((reg54&0x22) && (reg4a&0x10)) ? "3" :
+ (reg4a&0x20) ? "2" :
+ (reg4a&0x10) ? "1" :
+ (reg4a&0x00) ? "0" : "X",
+ ((reg54&0x44) && (reg4b&0x02)) ? "4" :
+ ((reg54&0x44) && (reg4b&0x01)) ? "3" :
+ (reg4b&0x02) ? "2" :
+ (reg4b&0x01) ? "1" :
+ (reg4b&0x00) ? "0" : "X",
+ ((reg54&0x88) && (reg4b&0x20)) ? "4" :
+ ((reg54&0x88) && (reg4b&0x10)) ? "3" :
+ (reg4b&0x20) ? "2" :
+ (reg4b&0x10) ? "1" :
+ (reg4b&0x00) ? "0" : "X");
+
+ p += sprintf(p, "UDMA\n");
+ p += sprintf(p, "DMA\n");
+ p += sprintf(p, "PIO\n");
+
+/*
+ * FIXME.... Add configuration junk data....blah blah......
+ */
+
+ return p-buffer; /* => must be less than 4k! */
+}
+#endif /* defined(DISPLAY_PIIX_TIMINGS) && defined(CONFIG_PROC_FS) */
+
+/*
+ * Used to set Fifo configuration via kernel command line:
+ */
+
+byte piix_proc = 0;
+
+extern char *ide_xfer_verbose (byte xfer_rate);
+
+/*
+ *
+ */
+static byte piix_dma_2_pio (byte xfer_rate) {
+ switch(xfer_rate) {
+ case XFER_UDMA_4:
+ case XFER_UDMA_3:
+ case XFER_UDMA_2:
+ case XFER_UDMA_1:
+ case XFER_UDMA_0:
+ case XFER_MW_DMA_2:
+ case XFER_PIO_4:
+ return 4;
+ case XFER_MW_DMA_1:
+ case XFER_PIO_3:
+ return 3;
+ case XFER_SW_DMA_2:
+ case XFER_PIO_2:
+ return 2;
+ case XFER_MW_DMA_0:
+ case XFER_SW_DMA_1:
+ case XFER_SW_DMA_0:
+ case XFER_PIO_1:
+ case XFER_PIO_0:
+ case XFER_PIO_SLOW:
+ default:
+ return 0;
+ }
+}
+
+/*
+ * Based on settings done by AMI BIOS
+ * (might be usefull if drive is not registered in CMOS for any reason).
+ */
+static void piix_tune_drive (ide_drive_t *drive, byte pio)
+{
+ unsigned long flags;
+ u16 master_data;
+ byte slave_data;
+ int is_slave = (&HWIF(drive)->drives[1] == drive);
+ int master_port = HWIF(drive)->index ? 0x42 : 0x40;
+ int slave_port = 0x44;
+ /* ISP RTC */
+ byte timings[][2] = { { 0, 0 },
+ { 0, 0 },
+ { 1, 0 },
+ { 2, 1 },
+ { 2, 3 }, };
+
+ pio = ide_get_best_pio_mode(drive, pio, 5, NULL);
+ pci_read_config_word(HWIF(drive)->pci_dev, master_port, &master_data);
+ if (is_slave) {
+ master_data = master_data | 0x4000;
+ if (pio > 1)
+ /* enable PPE, IE and TIME */
+ master_data = master_data | 0x0070;
+ pci_read_config_byte(HWIF(drive)->pci_dev, slave_port, &slave_data);
+ slave_data = slave_data & (HWIF(drive)->index ? 0x0f : 0xf0);
+ slave_data = slave_data | ((timings[pio][0] << 2) | (timings[pio][1]
+ << (HWIF(drive)->index ? 4 : 0)));
+ } else {
+ master_data = master_data & 0xccf8;
+ if (pio > 1)
+ /* enable PPE, IE and TIME */
+ master_data = master_data | 0x0007;
+ master_data = master_data | (timings[pio][0] << 12) |
+ (timings[pio][1] << 8);
+ }
+ save_flags(flags);
+ cli();
+ pci_write_config_word(HWIF(drive)->pci_dev, master_port, master_data);
+ if (is_slave)
+ pci_write_config_byte(HWIF(drive)->pci_dev, slave_port, slave_data);
+ restore_flags(flags);
+}
+
+static int piix_config_drive_for_dma (ide_drive_t *drive)
+{
+ struct hd_driveid *id = drive->id;
+ ide_hwif_t *hwif = HWIF(drive);
+ struct pci_dev *dev = hwif->pci_dev;
+
+ int sitre;
+ short reg4042, reg44, reg48, reg4a, reg54;
+ byte speed;
+
+ byte maslave = hwif->channel ? 0x42 : 0x40;
+ byte udma_66 = ((id->hw_config & 0x2000) && (hwif->udma_four)) ? 1 : 0;
+ int ultra66 = ((dev->device == PCI_DEVICE_ID_INTEL_82801AA_1) ||
+ (dev->device == PCI_DEVICE_ID_INTEL_82372FB_1)) ? 1 : 0;
+ int ultra = ((ultra66) ||
+ (dev->device == PCI_DEVICE_ID_INTEL_82371AB) ||
+ (dev->device == PCI_DEVICE_ID_INTEL_82801AB_1)) ? 1 : 0;
+ int drive_number = ((hwif->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
+ int a_speed = 2 << (drive_number * 4);
+ int u_flag = 1 << drive_number;
+ int v_flag = 0x10 << drive_number;
+ int u_speed = 0;
+
+ pci_read_config_word(dev, maslave, &reg4042);
+ sitre = (reg4042 & 0x4000) ? 1 : 0;
+ pci_read_config_word(dev, 0x44, &reg44);
+ pci_read_config_word(dev, 0x48, &reg48);
+ pci_read_config_word(dev, 0x4a, &reg4a);
+ pci_read_config_word(dev, 0x54, &reg54);
+
+ if ((id->dma_ultra & 0x0010) && (ultra)) {
+ u_speed = 2 << (drive_number * 4);
+ speed = ((udma_66) && (ultra66)) ? XFER_UDMA_4 : XFER_UDMA_2;
+ } else if ((id->dma_ultra & 0x0008) && (ultra)) {
+ u_speed = 1 << (drive_number * 4);
+ speed = ((udma_66) && (ultra66)) ? XFER_UDMA_3 : XFER_UDMA_1;
+ } else if ((id->dma_ultra & 0x0004) && (ultra)) {
+ u_speed = 2 << (drive_number * 4);
+ speed = XFER_UDMA_2;
+ } else if ((id->dma_ultra & 0x0002) && (ultra)) {
+ u_speed = 1 << (drive_number * 4);
+ speed = XFER_UDMA_1;
+ } else if ((id->dma_ultra & 0x0001) && (ultra)) {
+ u_speed = 0 << (drive_number * 4);
+ speed = XFER_UDMA_0;
+ } else if (id->dma_mword & 0x0004) {
+ speed = XFER_MW_DMA_2;
+ } else if (id->dma_mword & 0x0002) {
+ speed = XFER_MW_DMA_1;
+ } else if (id->dma_1word & 0x0004) {
+ speed = XFER_SW_DMA_2;
+ } else {
+ speed = XFER_PIO_0 + ide_get_best_pio_mode(drive, 255, 5, NULL);
+ }
+
+ if (speed >= XFER_UDMA_0) {
+ if (!(reg48 & u_flag))
+ pci_write_config_word(dev, 0x48, reg48|u_flag);
+ if (!(reg4a & u_speed)) {
+ pci_write_config_word(dev, 0x4a, reg4a & ~a_speed);
+ pci_write_config_word(dev, 0x4a, reg4a|u_speed);
+ }
+ if (speed > XFER_UDMA_2) {
+ if (!(reg54 & v_flag)) {
+ pci_write_config_word(dev, 0x54, reg54|v_flag);
+ }
+ } else {
+ pci_write_config_word(dev, 0x54, reg54 & ~v_flag);
+ }
+ }
+
+ if (speed < XFER_UDMA_0) {
+ if (reg48 & u_flag)
+ pci_write_config_word(dev, 0x48, reg48 & ~u_flag);
+ if (reg4a & a_speed)
+ pci_write_config_word(dev, 0x4a, reg4a & ~a_speed);
+ if (reg54 & v_flag)
+ pci_write_config_word(dev, 0x54, reg54 & ~v_flag);
+ }
+
+ piix_tune_drive(drive, piix_dma_2_pio(speed));
+
+ (void) ide_config_drive_speed(drive, speed);
+
+#if PIIX_DEBUG_DRIVE_INFO
+ printk("%s: %s drive%d\n", drive->name, ide_xfer_verbose(speed), drive_number);
+#endif /* PIIX_DEBUG_DRIVE_INFO */
+
+ return ((int) ((id->dma_ultra >> 11) & 3) ? ide_dma_on :
+ ((id->dma_ultra >> 8) & 7) ? ide_dma_on :
+ ((id->dma_mword >> 8) & 7) ? ide_dma_on :
+ ((id->dma_1word >> 8) & 7) ? ide_dma_on :
+ ide_dma_off_quietly);
+}
+
+static int piix_dmaproc(ide_dma_action_t func, ide_drive_t *drive)
+{
+ switch (func) {
+ case ide_dma_check:
+ return ide_dmaproc((ide_dma_action_t) piix_config_drive_for_dma(drive), drive);
+ default :
+ break;
+ }
+ /* Other cases are done by generic IDE-DMA code. */
+ return ide_dmaproc(func, drive);
+}
+
+unsigned int __init pci_init_piix (struct pci_dev *dev, const char *name)
+{
+#if defined(DISPLAY_PIIX_TIMINGS) && defined(CONFIG_PROC_FS)
+ piix_proc = 1;
+ bmide_dev = dev;
+ piix_display_info = &piix_get_info;
+#endif /* DISPLAY_PIIX_TIMINGS && CONFIG_PROC_FS */
+ return 0;
+}
+
+/*
+ * Sheesh, someone at Intel needs to go read the ATA-4/5 T13 standards.
+ * It does not specify device detection, but channel!!!
+ * You determine later if bit 13 of word93 is set...
+ */
+unsigned int __init ata66_piix (ide_hwif_t *hwif)
+{
+ byte reg54h = 0, reg55h = 0, ata66 = 0;
+ byte mask = hwif->channel ? 0x0c : 0x03;
+
+ pci_read_config_byte(hwif->pci_dev, 0x54, &reg54h);
+ pci_read_config_byte(hwif->pci_dev, 0x55, &reg55h);
+ ata66 = (reg54h & mask) ? 1 : 0;
+
+ return ata66;
+}
+
+void __init ide_init_piix (ide_hwif_t *hwif)
+{
+ hwif->tuneproc = &piix_tune_drive;
+
+ if (hwif->dma_base) {
+#ifdef CONFIG_PIIX_TUNING
+ hwif->dmaproc = &piix_dmaproc;
+#endif /* CONFIG_PIIX_TUNING */
+ hwif->drives[0].autotune = 0;
+ hwif->drives[1].autotune = 0;
+ } else {
+ hwif->drives[0].autotune = 1;
+ hwif->drives[1].autotune = 1;
+ }
+ if (!hwif->irq)
+ hwif->irq = hwif->channel ? 15 : 14;
+}
diff --git a/drivers/ide/q40ide.c b/drivers/ide/q40ide.c
new file mode 100644
index 000000000..0f2330370
--- /dev/null
+++ b/drivers/ide/q40ide.c
@@ -0,0 +1,109 @@
+/*
+ * linux/drivers/block/q40ide.c -- Q40 I/O port IDE Driver
+ *
+ * original file created 12 Jul 1997 by Geert Uytterhoeven
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ *
+ * RZ:
+ * almost identical with pcide.c, maybe we can merge it later.
+ * Differences:
+ * max 2 HWIFS for now
+ * translate portaddresses to q40 native addresses (not yet...) instead rely on in/out[bw]
+ * address translation
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+
+#include <linux/ide.h>
+
+ /*
+ * Bases of the IDE interfaces
+ */
+
+#define PCIDE_NUM_HWIFS 2
+
+#define PCIDE_BASE1 0x1f0
+#define PCIDE_BASE2 0x170
+#define PCIDE_BASE3 0x1e8
+#define PCIDE_BASE4 0x168
+#define PCIDE_BASE5 0x1e0
+#define PCIDE_BASE6 0x160
+
+static const q40ide_ioreg_t pcide_bases[PCIDE_NUM_HWIFS] = {
+ PCIDE_BASE1, PCIDE_BASE2, /* PCIDE_BASE3, PCIDE_BASE4 , PCIDE_BASE5,
+ PCIDE_BASE6 */
+};
+
+
+ /*
+ * Offsets from one of the above bases
+ */
+
+#undef HD_DATA
+#define HD_DATA 0x1f0
+
+#define PCIDE_REG(x) ((q40ide_ioreg_t)(HD_##x-PCIDE_BASE1))
+
+static const int pcide_offsets[IDE_NR_PORTS] = {
+ PCIDE_REG(DATA), PCIDE_REG(ERROR), PCIDE_REG(NSECTOR), PCIDE_REG(SECTOR),
+ PCIDE_REG(LCYL), PCIDE_REG(HCYL), PCIDE_REG(CURRENT), PCIDE_REG(STATUS),
+ PCIDE_REG(CMD)
+};
+
+int q40ide_default_irq(q40ide_ioreg_t base)
+{
+ switch (base) {
+ case 0x1f0: return 14;
+ case 0x170: return 15;
+ case 0x1e8: return 11;
+ default:
+ return 0;
+ }
+}
+
+void q40_ide_init_hwif_ports (q40ide_ioreg_t *p, q40ide_ioreg_t base, int *irq)
+{
+ q40ide_ioreg_t port = base;
+ int i = 8;
+
+ while (i--)
+ *p++ = port++;
+ *p++ = base + 0x206;
+ if (irq != NULL)
+ *irq = 0;
+}
+
+
+ /*
+ * Probe for PC IDE interfaces
+ */
+
+int q40ide_probe_hwif(int index, ide_hwif_t *hwif)
+{
+ static int pcide_index[PCIDE_NUM_HWIFS] = { 0, };
+ int i;
+
+ if (!MACH_IS_Q40)
+ return 0;
+
+ for (i = 0; i < PCIDE_NUM_HWIFS; i++) {
+ if (!pcide_index[i]) {
+ /*printk("ide%d: Q40 IDE interface\n", index);*/
+ pcide_index[i] = index+1;
+ }
+ if (pcide_index[i] == index+1) {
+ ide_setup_ports(hwif,(ide_ioreg_t) pcide_bases[i], pcide_offsets, 0, /*q40_ack_intr???*/ NULL);
+ hwif->irq = ide_default_irq((ide_ioreg_t)pcide_bases[i]); /*q40_ide_irq[i]; */ /* 14 */
+ return 1;
+ }
+ }
+ return 0;
+}
diff --git a/drivers/ide/qd6580.c b/drivers/ide/qd6580.c
new file mode 100644
index 000000000..31781a9f0
--- /dev/null
+++ b/drivers/ide/qd6580.c
@@ -0,0 +1,71 @@
+/*
+ * linux/drivers/block/qd6580.c Version 0.02 Feb 09, 1996
+ *
+ * Copyright (C) 1996 Linus Torvalds & author (see below)
+ */
+
+/*
+ * QDI QD6580 EIDE controller fast support by Colten Edwards.
+ * No net access, but (maybe) can be reached at pje120@cs.usask.ca
+ */
+
+#undef REALLY_SLOW_IO /* most systems can safely undef this */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+
+#include "ide_modes.h"
+
+/*
+ * Register 0xb3 looks like:
+ * 0x4f is fast mode3 ?
+ * 0x3f is medium mode2 ?
+ * 0x2f is slower mode1 ?
+ * 0x1f is slower yet mode0 ?
+ * 0x0f ??? ???
+ *
+ * Don't know whether this sets BOTH drives, or just the first drive.
+ * Don't know if there is a separate setting for the second drive.
+ *
+ * Feel free to patch this if you have one of these beasts
+ * and can work out the answers!
+ *
+ * I/O ports are 0xb0 0xb2 and 0xb3
+ *
+ * More research on qd6580 being done by willmore@cig.mot.com (David)
+ * -- this is apparently a *dual* IDE interface
+ */
+
+static void tune_qd6580 (ide_drive_t *drive, byte pio)
+{
+ unsigned long flags;
+
+ pio = ide_get_best_pio_mode(drive, pio, 3, NULL);
+
+ save_flags(flags); /* all CPUs */
+ cli(); /* all CPUs */
+ outb_p(0x8d,0xb0);
+ outb_p(0x0 ,0xb2);
+ outb_p(((pio+1)<<4)|0x0f,0xb3);
+ inb(0x3f6);
+ restore_flags(flags); /* all CPUs */
+}
+
+void __init init_qd6580 (void)
+{
+ ide_hwifs[0].chipset = ide_qd6580;
+ ide_hwifs[1].chipset = ide_qd6580;
+ ide_hwifs[0].tuneproc = &tune_qd6580;
+ ide_hwifs[0].mate = &ide_hwifs[1];
+ ide_hwifs[1].mate = &ide_hwifs[0];
+ ide_hwifs[1].channel = 1;
+}
diff --git a/drivers/ide/rapide.c b/drivers/ide/rapide.c
new file mode 100644
index 000000000..5905aca41
--- /dev/null
+++ b/drivers/ide/rapide.c
@@ -0,0 +1,92 @@
+/*
+ * linux/drivers/block/rapide.c
+ *
+ * Copyright (c) 1996-1998 Russell King.
+ *
+ * Changelog:
+ * 08-06-1996 RMK Created
+ * 13-04-1998 RMK Added manufacturer and product IDs
+ */
+
+#include <linux/module.h>
+#include <linux/malloc.h>
+#include <linux/blkdev.h>
+#include <linux/errno.h>
+#include <linux/ide.h>
+
+#include <asm/ecard.h>
+
+static const card_ids __init rapide_cids[] = {
+ { MANU_YELLOWSTONE, PROD_YELLOWSTONE_RAPIDE32 },
+ { 0xffff, 0xffff }
+};
+
+static struct expansion_card *ec[MAX_ECARDS];
+static int result[MAX_ECARDS];
+
+static inline int rapide_register(struct expansion_card *ec)
+{
+ unsigned long port = ecard_address (ec, ECARD_MEMC, 0);
+ hw_regs_t hw;
+
+ int i;
+
+ memset(&hw, 0, sizeof(hw));
+
+ for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) {
+ hw.io_ports[i] = (ide_ioreg_t)port;
+ port += 1 << 4;
+ }
+ hw.io_ports[IDE_CONTROL_OFFSET] = port + 0x206;
+ hw.irq = ec->irq;
+
+ return ide_register_hw(&hw, NULL);
+}
+
+int __init rapide_init(void)
+{
+ int i;
+
+ for (i = 0; i < MAX_ECARDS; i++)
+ ec[i] = NULL;
+
+ ecard_startfind();
+
+ for (i = 0; ; i++) {
+ if ((ec[i] = ecard_find(0, rapide_cids)) == NULL)
+ break;
+
+ ecard_claim(ec[i]);
+ result[i] = rapide_register(ec[i]);
+ }
+ for (i = 0; i < MAX_ECARDS; i++)
+ if (ec[i] && result[i] < 0) {
+ ecard_release(ec[i]);
+ ec[i] = NULL;
+ }
+ return 0;
+}
+
+#ifdef MODULE
+
+int init_module (void)
+{
+ return rapide_init();
+}
+
+void cleanup_module (void)
+{
+ int i;
+
+ for (i = 0; i < MAX_ECARDS; i++)
+ if (ec[i]) {
+ unsigned long port;
+ port = ecard_address(ec[i], ECARD_MEMC, 0);
+
+ ide_unregister_port(port, ec[i]->irq, 16);
+ ecard_release(ec[i]);
+ ec[i] = NULL;
+ }
+}
+#endif
+
diff --git a/drivers/ide/rz1000.c b/drivers/ide/rz1000.c
new file mode 100644
index 000000000..455641c1d
--- /dev/null
+++ b/drivers/ide/rz1000.c
@@ -0,0 +1,96 @@
+/*
+ * linux/drivers/block/rz1000.c Version 0.05 December 8, 1997
+ *
+ * Copyright (C) 1995-1998 Linus Torvalds & author (see below)
+ */
+
+/*
+ * Principal Author: mlord@pobox.com (Mark Lord)
+ *
+ * See linux/MAINTAINERS for address of current maintainer.
+ *
+ * This file provides support for disabling the buggy read-ahead
+ * mode of the RZ1000 IDE chipset, commonly used on Intel motherboards.
+ *
+ * Dunno if this fixes both ports, or only the primary port (?).
+ */
+
+#undef REALLY_SLOW_IO /* most systems can safely undef this */
+
+#include <linux/config.h> /* for CONFIG_BLK_DEV_IDEPCI */
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/pci.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+
+#ifdef CONFIG_BLK_DEV_IDEPCI
+
+void __init ide_init_rz1000 (ide_hwif_t *hwif) /* called from ide-pci.c */
+{
+ unsigned short reg;
+ struct pci_dev *dev = hwif->pci_dev;
+
+ hwif->chipset = ide_rz1000;
+ if (!pci_read_config_word (dev, 0x40, &reg)
+ && !pci_write_config_word(dev, 0x40, reg & 0xdfff))
+ {
+ printk("%s: disabled chipset read-ahead (buggy RZ1000/RZ1001)\n", hwif->name);
+ } else {
+ hwif->serialized = 1;
+ hwif->drives[0].no_unmask = 1;
+ hwif->drives[1].no_unmask = 1;
+ printk("%s: serialized, disabled unmasking (buggy RZ1000/RZ1001)\n", hwif->name);
+ }
+}
+
+#else
+
+static void __init init_rz1000 (struct pci_dev *dev, const char *name)
+{
+ unsigned short reg, h;
+
+ if (!pci_read_config_word (dev, PCI_COMMAND, &reg) && !(reg & PCI_COMMAND_IO)) {
+ printk("%s: buggy IDE controller disabled (BIOS)\n", name);
+ return;
+ }
+ if (!pci_read_config_word (dev, 0x40, &reg)
+ && !pci_write_config_word(dev, 0x40, reg & 0xdfff))
+ {
+ printk("IDE: disabled chipset read-ahead (buggy %s)\n", name);
+ } else {
+ for (h = 0; h < MAX_HWIFS; ++h) {
+ ide_hwif_t *hwif = &ide_hwifs[h];
+ if ((hwif->io_ports[IDE_DATA_OFFSET] == 0x1f0 || hwif->io_ports[IDE_DATA_OFFSET] == 0x170)
+ && (hwif->chipset == ide_unknown || hwif->chipset == ide_generic))
+ {
+ hwif->chipset = ide_rz1000;
+ hwif->serialized = 1;
+ hwif->drives[0].no_unmask = 1;
+ hwif->drives[1].no_unmask = 1;
+ if (hwif->io_ports[IDE_DATA_OFFSET] == 0x170)
+ hwif->channel = 1;
+ printk("%s: serialized, disabled unmasking (buggy %s)\n", hwif->name, name);
+ }
+ }
+ }
+}
+
+void __init ide_probe_for_rz100x (void) /* called from ide.c */
+{
+ struct pci_dev *dev = NULL;
+
+ while ((dev = pci_find_device(PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1000, dev))!=NULL)
+ init_rz1000 (dev, "RZ1000");
+ while ((dev = pci_find_device(PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1001, dev))!=NULL)
+ init_rz1000 (dev, "RZ1001");
+}
+
+#endif CONFIG_BLK_DEV_IDEPCI
diff --git a/drivers/ide/sis5513.c b/drivers/ide/sis5513.c
new file mode 100644
index 000000000..942187900
--- /dev/null
+++ b/drivers/ide/sis5513.c
@@ -0,0 +1,549 @@
+/*
+ * linux/drivers/block/sis5513.c Version 0.09 Feb. 10, 2000
+ *
+ * Copyright (C) 1999-2000 Andre Hedrick (andre@suse.com)
+ * May be copied or modified under the terms of the GNU General Public License
+ *
+ * Thanks to SIS Taiwan for direct support and hardware.
+ * Tested and designed on the SiS620/5513 chipset.
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include "ide_modes.h"
+
+#define DISPLAY_SIS_TIMINGS
+#define SIS5513_DEBUG_DRIVE_INFO 0
+
+static struct pci_dev *host_dev = NULL;
+
+#define arraysize(x) (sizeof(x)/sizeof(*(x)))
+
+#define SIS5513_FLAG_ATA_00 0x00000000
+#define SIS5513_FLAG_ATA_16 0x00000001
+#define SIS5513_FLAG_ATA_33 0x00000002
+#define SIS5513_FLAG_ATA_66 0x00000004
+#define SIS5513_FLAG_LATENCY 0x00000010
+
+static const struct {
+ const char *name;
+ unsigned short host_id;
+ unsigned int flags;
+} SiSHostChipInfo[] = {
+ { "SiS530", PCI_DEVICE_ID_SI_530, SIS5513_FLAG_ATA_66, },
+ { "SiS540", PCI_DEVICE_ID_SI_540, SIS5513_FLAG_ATA_66, },
+ { "SiS620", PCI_DEVICE_ID_SI_620, SIS5513_FLAG_ATA_66|SIS5513_FLAG_LATENCY, },
+ { "SiS630", PCI_DEVICE_ID_SI_630, SIS5513_FLAG_ATA_66|SIS5513_FLAG_LATENCY, },
+ { "SiS5591", PCI_DEVICE_ID_SI_5591, SIS5513_FLAG_ATA_33, },
+ { "SiS5597", PCI_DEVICE_ID_SI_5597, SIS5513_FLAG_ATA_33, },
+ { "SiS5600", PCI_DEVICE_ID_SI_5600, SIS5513_FLAG_ATA_33, },
+ { "SiS5511", PCI_DEVICE_ID_SI_5511, SIS5513_FLAG_ATA_16, },
+};
+
+#if 0
+
+static struct _pio_mode_mapping {
+ byte data_active;
+ byte recovery;
+ byte pio_mode;
+} pio_mode_mapping[] = {
+ { 8, 12, 0 },
+ { 6, 7, 1 },
+ { 4, 4, 2 },
+ { 3, 3, 3 },
+ { 3, 1, 4 }
+};
+
+static struct _dma_mode_mapping {
+ byte data_active;
+ byte recovery;
+ byte dma_mode;
+} dma_mode_mapping[] = {
+ { 8, 8, 0 },
+ { 3, 2, 1 },
+ { 3, 1, 2 }
+};
+
+static struct _udma_mode_mapping {
+ byte cycle_time;
+ char * udma_mode;
+} udma_mode_mapping[] = {
+ { 8, "Mode 0" },
+ { 6, "Mode 1" },
+ { 4, "Mode 2" },
+ { 3, "Mode 3" },
+ { 2, "Mode 4" },
+ { 0, "Undefined" }
+};
+
+static __inline__ char * find_udma_mode (byte cycle_time)
+{
+ int n;
+
+ for (n = 0; n <= 4; n++)
+ if (udma_mode_mapping[n].cycle_time <= cycle_time)
+ return udma_mode_mapping[n].udma_mode;
+ return udma_mode_mapping[4].udma_mode;
+}
+#endif
+
+#if defined(DISPLAY_SIS_TIMINGS) && defined(CONFIG_PROC_FS)
+#include <linux/stat.h>
+#include <linux/proc_fs.h>
+
+static int sis_get_info(char *, char **, off_t, int);
+extern int (*sis_display_info)(char *, char **, off_t, int); /* ide-proc.c */
+struct pci_dev *bmide_dev;
+
+static char *cable_type[] = {
+ "80 pins",
+ "40 pins"
+};
+
+static char *recovery_time [] ={
+ "12 PCICLK", "1 PCICLK",
+ "2 PCICLK", "3 PCICLK",
+ "4 PCICLK", "5 PCICLCK",
+ "6 PCICLK", "7 PCICLCK",
+ "8 PCICLK", "9 PCICLCK",
+ "10 PCICLK", "11 PCICLK",
+ "13 PCICLK", "14 PCICLK",
+ "15 PCICLK", "15 PCICLK"
+};
+
+static char *cycle_time [] = {
+ "Undefined", "2 CLCK",
+ "3 CLK", "4 CLK",
+ "5 CLK", "6 CLK",
+ "7 CLK", "8 CLK"
+};
+
+static char *active_time [] = {
+ "8 PCICLK", "1 PCICLCK",
+ "2 PCICLK", "2 PCICLK",
+ "4 PCICLK", "5 PCICLK",
+ "6 PCICLK", "12 PCICLK"
+};
+
+static int sis_get_info (char *buffer, char **addr, off_t offset, int count)
+{
+ int rc;
+ char *p = buffer;
+ byte reg,reg1;
+ u16 reg2, reg3;
+
+ p += sprintf(p, "--------------- Primary Channel ---------------- Secondary Channel -------------\n");
+ rc = pci_read_config_byte(bmide_dev, 0x4a, &reg);
+ p += sprintf(p, "Channel Status: %s \t \t \t \t %s \n",
+ (reg & 0x02) ? "On" : "Off",
+ (reg & 0x04) ? "On" : "Off");
+
+ rc = pci_read_config_byte(bmide_dev, 0x09, &reg);
+ p += sprintf(p, "Operation Mode: %s \t \t \t %s \n",
+ (reg & 0x01) ? "Native" : "Compatible",
+ (reg & 0x04) ? "Native" : "Compatible");
+
+ rc = pci_read_config_byte(bmide_dev, 0x48, &reg);
+ p += sprintf(p, "Cable Type: %s \t \t \t %s\n",
+ (reg & 0x10) ? cable_type[1] : cable_type[0],
+ (reg & 0x20) ? cable_type[1] : cable_type[0]);
+
+ rc = pci_read_config_word(bmide_dev, 0x4c, &reg2);
+ rc = pci_read_config_word(bmide_dev, 0x4e, &reg3);
+ p += sprintf(p, "Prefetch Count: %d \t \t \t \t %d\n",
+ reg2, reg3);
+
+ rc = pci_read_config_byte(bmide_dev, 0x4b, &reg);
+ p += sprintf(p, "Drvie 0: Postwrite %s \t \t Postwrite %s\n",
+ (reg & 0x10) ? "Enabled" : "Disabled",
+ (reg & 0x40) ? "Enabled" : "Disabled");
+ p += sprintf(p, " Prefetch %s \t \t Prefetch %s\n",
+ (reg & 0x01) ? "Enabled" : "Disabled",
+ (reg & 0x04) ? "Enabled" : "Disabled");
+
+ rc = pci_read_config_byte(bmide_dev, 0x41, &reg);
+ rc = pci_read_config_byte(bmide_dev, 0x45, &reg1);
+ p += sprintf(p, " UDMA %s \t \t \t UDMA %s\n",
+ (reg & 0x80) ? "Enabled" : "Disabled",
+ (reg1 & 0x80) ? "Enabled" : "Disabled");
+ p += sprintf(p, " UDMA Cycle Time %s \t UDMA Cycle Time %s\n",
+ cycle_time[(reg & 0x70) >> 4], cycle_time[(reg1 & 0x70) >> 4]);
+ p += sprintf(p, " Data Active Time %s \t Data Active Time %s\n",
+ active_time[(reg & 0x07)], active_time[(reg &0x07)] );
+
+ rc = pci_read_config_byte(bmide_dev, 0x40, &reg);
+ rc = pci_read_config_byte(bmide_dev, 0x44, &reg1);
+ p += sprintf(p, " Data Recovery Time %s \t Data Recovery Time %s\n",
+ recovery_time[(reg & 0x0f)], recovery_time[(reg1 & 0x0f)]);
+
+
+ rc = pci_read_config_byte(bmide_dev, 0x4b, &reg);
+ p += sprintf(p, "Drvie 1: Postwrite %s \t \t Postwrite %s\n",
+ (reg & 0x20) ? "Enabled" : "Disabled",
+ (reg & 0x80) ? "Enabled" : "Disabled");
+ p += sprintf(p, " Prefetch %s \t \t Prefetch %s\n",
+ (reg & 0x02) ? "Enabled" : "Disabled",
+ (reg & 0x08) ? "Enabled" : "Disabled");
+
+ rc = pci_read_config_byte(bmide_dev, 0x43, &reg);
+ rc = pci_read_config_byte(bmide_dev, 0x47, &reg1);
+ p += sprintf(p, " UDMA %s \t \t \t UDMA %s\n",
+ (reg & 0x80) ? "Enabled" : "Disabled",
+ (reg1 & 0x80) ? "Enabled" : "Disabled");
+ p += sprintf(p, " UDMA Cycle Time %s \t UDMA Cycle Time %s\n",
+ cycle_time[(reg & 0x70) >> 4], cycle_time[(reg1 & 0x70) >> 4]);
+ p += sprintf(p, " Data Active Time %s \t Data Active Time %s\n",
+ active_time[(reg & 0x07)], active_time[(reg &0x07)] );
+
+ rc = pci_read_config_byte(bmide_dev, 0x42, &reg);
+ rc = pci_read_config_byte(bmide_dev, 0x46, &reg1);
+ p += sprintf(p, " Data Recovery Time %s \t Data Recovery Time %s\n",
+ recovery_time[(reg & 0x0f)], recovery_time[(reg1 & 0x0f)]);
+ return p-buffer;
+}
+#endif /* defined(DISPLAY_SIS_TIMINGS) && defined(CONFIG_PROC_FS) */
+
+byte sis_proc = 0;
+extern char *ide_xfer_verbose (byte xfer_rate);
+
+/*
+ * ((id->hw_config & 0x2000) && (HWIF(drive)->udma_four))
+ */
+static int config_chipset_for_dma (ide_drive_t *drive, byte ultra)
+{
+ struct hd_driveid *id = drive->id;
+ ide_hwif_t *hwif = HWIF(drive);
+ struct pci_dev *dev = hwif->pci_dev;
+
+ byte drive_pci, test1, test2, mask;
+ int err;
+
+ unsigned long dma_base = hwif->dma_base;
+ byte unit = (drive->select.b.unit & 0x01);
+ byte speed = 0x00, unmask = 0xE0, four_two = 0x00;
+ int drive_number = ((hwif->channel ? 2 : 0) + unit);
+ byte udma_66 = ((id->hw_config & 0x2000) && (hwif->udma_four)) ? 1 : 0;
+
+ if (host_dev) {
+ switch(host_dev->device) {
+ case PCI_DEVICE_ID_SI_530:
+ case PCI_DEVICE_ID_SI_540:
+ case PCI_DEVICE_ID_SI_620:
+ case PCI_DEVICE_ID_SI_630:
+ unmask = 0xF0;
+ four_two = 0x01;
+ default:
+ break;
+ }
+ }
+
+ switch(drive_number) {
+ case 0: drive_pci = 0x40;break;
+ case 1: drive_pci = 0x42;break;
+ case 2: drive_pci = 0x44;break;
+ case 3: drive_pci = 0x46;break;
+ default: return ide_dma_off;
+ }
+
+ pci_read_config_byte(dev, drive_pci, &test1);
+ pci_read_config_byte(dev, drive_pci|0x01, &test2);
+
+ if ((!ultra) && (test2 & 0x80)) {
+ pci_write_config_byte(dev, drive_pci|0x01, test2 & ~0x80);
+ pci_read_config_byte(dev, drive_pci|0x01, &test2);
+ }
+
+ if ((id->dma_ultra & 0x0010) && (ultra) && (udma_66) && (four_two)) {
+ if (!(test2 & 0x90)) {
+ pci_write_config_byte(dev, drive_pci|0x01, test2 & ~unmask);
+ pci_write_config_byte(dev, drive_pci|0x01, test2|0x90);
+ }
+ speed = XFER_UDMA_4;
+ } else if ((id->dma_ultra & 0x0008) && (ultra) && (udma_66) && (four_two)) {
+ if (!(test2 & 0xA0)) {
+ pci_write_config_byte(dev, drive_pci|0x01, test2 & ~unmask);
+ pci_write_config_byte(dev, drive_pci|0x01, test2|0xA0);
+ }
+ speed = XFER_UDMA_3;
+ } else if ((id->dma_ultra & 0x0004) && (ultra)) {
+ mask = (four_two) ? 0xB0 : 0xA0;
+ if (!(test2 & mask)) {
+ pci_write_config_byte(dev, drive_pci|0x01, test2 & ~unmask);
+ pci_write_config_byte(dev, drive_pci|0x01, test2|mask);
+ }
+ speed = XFER_UDMA_2;
+ } else if ((id->dma_ultra & 0x0002) && (ultra)) {
+ mask = (four_two) ? 0xD0 : 0xC0;
+ if (!(test2 & mask)) {
+ pci_write_config_byte(dev, drive_pci|0x01, test2 & ~unmask);
+ pci_write_config_byte(dev, drive_pci|0x01, test2|mask);
+ }
+ speed = XFER_UDMA_1;
+ } else if ((id->dma_ultra & 0x0001) && (ultra)) {
+ if (!(test2 & unmask)) {
+ pci_write_config_byte(dev, drive_pci|0x01, test2 & ~unmask);
+ pci_write_config_byte(dev, drive_pci|0x01, test2|unmask);
+ }
+ speed = XFER_UDMA_0;
+ } else if (id->dma_mword & 0x0004) {
+ speed = XFER_MW_DMA_2;
+ } else if (id->dma_mword & 0x0002) {
+ speed = XFER_MW_DMA_1;
+ } else if (id->dma_mword & 0x0001) {
+ speed = XFER_MW_DMA_0;
+ } else if (id->dma_1word & 0x0004) {
+ speed = XFER_SW_DMA_2;
+ } else if (id->dma_1word & 0x0002) {
+ speed = XFER_SW_DMA_1;
+ } else if (id->dma_1word & 0x0001) {
+ speed = XFER_SW_DMA_0;
+ } else {
+ return ((int) ide_dma_off_quietly);
+ }
+
+ outb(inb(dma_base+2)|(1<<(5+unit)), dma_base+2);
+ err = ide_config_drive_speed(drive, speed);
+
+#if SIS5513_DEBUG_DRIVE_INFO
+ printk("%s: %s drive%d\n", drive->name, ide_xfer_verbose(speed), drive_number);
+#endif /* SIS5513_DEBUG_DRIVE_INFO */
+
+ return ((int) ((id->dma_ultra >> 11) & 3) ? ide_dma_on :
+ ((id->dma_ultra >> 8) & 7) ? ide_dma_on :
+ ((id->dma_mword >> 8) & 7) ? ide_dma_on :
+ ((id->dma_1word >> 8) & 7) ? ide_dma_on :
+ ide_dma_off_quietly);
+}
+
+static void config_drive_art_rwp (ide_drive_t *drive)
+{
+ ide_hwif_t *hwif = HWIF(drive);
+ struct pci_dev *dev = hwif->pci_dev;
+
+ byte timing, pio, drive_pci, test1, test2;
+
+ unsigned short eide_pio_timing[6] = {600, 390, 240, 180, 120, 90};
+ unsigned short xfer_pio = drive->id->eide_pio_modes;
+ int drive_number = ((hwif->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
+
+ if (drive->media == ide_disk) {
+ struct pci_dev *dev = hwif->pci_dev;
+ byte reg4bh = 0;
+ byte rw_prefetch = (0x11 << drive_number);
+
+ pci_read_config_byte(dev, 0x4b, &reg4bh);
+ if ((reg4bh & rw_prefetch) != rw_prefetch)
+ pci_write_config_byte(dev, 0x4b, reg4bh|rw_prefetch);
+ }
+
+ pio = ide_get_best_pio_mode(drive, 255, 5, NULL);
+
+ if (xfer_pio> 4)
+ xfer_pio = 0;
+
+ if (drive->id->eide_pio_iordy > 0) {
+ for (xfer_pio = 5;
+ xfer_pio>0 &&
+ drive->id->eide_pio_iordy>eide_pio_timing[xfer_pio];
+ xfer_pio--);
+ } else {
+ xfer_pio = (drive->id->eide_pio_modes & 4) ? 0x05 :
+ (drive->id->eide_pio_modes & 2) ? 0x04 :
+ (drive->id->eide_pio_modes & 1) ? 0x03 : xfer_pio;
+ }
+
+ timing = (xfer_pio >= pio) ? xfer_pio : pio;
+
+/*
+ * Mode 0 Mode 1 Mode 2 Mode 3 Mode 4
+ * Active time 8T (240ns) 6T (180ns) 4T (120ns) 3T (90ns) 3T (90ns)
+ * 0x41 2:0 bits 000 110 100 011 011
+ * Recovery time 12T (360ns) 7T (210ns) 4T (120ns) 3T (90ns) 1T (30ns)
+ * 0x40 3:0 bits 0000 0111 0100 0011 0001
+ * Cycle time 20T (600ns) 13T (390ns) 8T (240ns) 6T (180ns) 4T (120ns)
+ */
+
+ switch(drive_number) {
+ case 0: drive_pci = 0x40;break;
+ case 1: drive_pci = 0x42;break;
+ case 2: drive_pci = 0x44;break;
+ case 3: drive_pci = 0x46;break;
+ default: return;
+ }
+
+ pci_read_config_byte(dev, drive_pci, &test1);
+ pci_read_config_byte(dev, drive_pci|0x01, &test2);
+
+ /*
+ * Do a blanket clear of active and recovery timings.
+ */
+
+ test1 &= ~0x07;
+ test2 &= ~0x0F;
+
+ switch(timing) {
+ case 4: test1 |= 0x01;test2 |= 0x03;break;
+ case 3: test1 |= 0x03;test2 |= 0x03;break;
+ case 2: test1 |= 0x04;test2 |= 0x04;break;
+ case 1: test1 |= 0x07;test2 |= 0x06;break;
+ default: break;
+ }
+
+ pci_write_config_byte(dev, drive_pci, test1);
+ pci_write_config_byte(dev, drive_pci|0x01, test2);
+}
+
+static int config_drive_xfer_rate (ide_drive_t *drive)
+{
+ struct hd_driveid *id = drive->id;
+ ide_dma_action_t dma_func = ide_dma_off_quietly;
+
+ if (id && (id->capability & 1) && HWIF(drive)->autodma) {
+ /* Consult the list of known "bad" drives */
+ if (ide_dmaproc(ide_dma_bad_drive, drive)) {
+ return HWIF(drive)->dmaproc(ide_dma_off, drive);
+ }
+
+ if (id->field_valid & 4) {
+ if (id->dma_ultra & 0x001F) {
+ /* Force if Capable UltraDMA */
+ dma_func = config_chipset_for_dma(drive, 1);
+ if ((id->field_valid & 2) &&
+ (dma_func != ide_dma_on))
+ goto try_dma_modes;
+ }
+ } else if (id->field_valid & 2) {
+try_dma_modes:
+ if ((id->dma_mword & 0x0007) ||
+ (id->dma_1word & 0x0007)) {
+ /* Force if Capable regular DMA modes */
+ dma_func = config_chipset_for_dma(drive, 0);
+ }
+ } else if ((ide_dmaproc(ide_dma_good_drive, drive)) &&
+ (id->eide_dma_time > 150)) {
+ /* Consult the list of known "good" drives */
+ dma_func = config_chipset_for_dma(drive, 0);
+ }
+ }
+ return HWIF(drive)->dmaproc(dma_func, drive);
+}
+
+/*
+ * sis5513_dmaproc() initiates/aborts (U)DMA read/write operations on a drive.
+ */
+int sis5513_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
+{
+ switch (func) {
+ case ide_dma_check:
+ config_drive_art_rwp(drive);
+ return config_drive_xfer_rate(drive);
+ default:
+ break;
+ }
+ return ide_dmaproc(func, drive); /* use standard DMA stuff */
+}
+
+unsigned int __init pci_init_sis5513 (struct pci_dev *dev, const char *name)
+{
+ struct pci_dev *host;
+ int i = 0;
+ byte latency = 0;
+
+ pci_read_config_byte(dev, PCI_LATENCY_TIMER, &latency);
+
+ for (i = 0; i < arraysize (SiSHostChipInfo) && !host_dev; i++) {
+ host = pci_find_device (PCI_VENDOR_ID_SI,
+ SiSHostChipInfo[i].host_id,
+ NULL);
+ if (!host)
+ continue;
+
+ host_dev = host;
+ printk(SiSHostChipInfo[i].name);
+ printk("\n");
+ if (SiSHostChipInfo[i].flags & SIS5513_FLAG_LATENCY) {
+ if (latency != 0x10)
+ pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x10);
+ }
+ }
+
+ if (host_dev) {
+ byte reg52h = 0;
+
+ pci_read_config_byte(dev, 0x52, &reg52h);
+ if (!(reg52h & 0x04)) {
+ /* set IDE controller to operate in Compabitility mode obly */
+ pci_write_config_byte(dev, 0x52, reg52h|0x04);
+ }
+#if defined(DISPLAY_SIS_TIMINGS) && defined(CONFIG_PROC_FS)
+ sis_proc = 1;
+ bmide_dev = dev;
+ sis_display_info = &sis_get_info;
+#endif /* defined(DISPLAY_SIS_TIMINGS) && defined(CONFIG_PROC_FS) */
+ }
+ return 0;
+}
+
+unsigned int __init ata66_sis5513 (ide_hwif_t *hwif)
+{
+ byte reg48h = 0, ata66 = 0;
+ byte mask = hwif->channel ? 0x20 : 0x10;
+ pci_read_config_byte(hwif->pci_dev, 0x48, &reg48h);
+
+ if (host_dev) {
+ switch(host_dev->device) {
+ case PCI_DEVICE_ID_SI_530:
+ case PCI_DEVICE_ID_SI_540:
+ case PCI_DEVICE_ID_SI_620:
+ case PCI_DEVICE_ID_SI_630:
+ ata66 = (reg48h & mask) ? 0 : 1;
+ default:
+ break;
+ }
+ }
+ return (ata66);
+}
+
+void __init ide_init_sis5513 (ide_hwif_t *hwif)
+{
+
+ hwif->irq = hwif->channel ? 15 : 14;
+
+ if (!(hwif->dma_base))
+ return;
+
+ if (host_dev) {
+ switch(host_dev->device) {
+ case PCI_DEVICE_ID_SI_530:
+ case PCI_DEVICE_ID_SI_540:
+ case PCI_DEVICE_ID_SI_620:
+ case PCI_DEVICE_ID_SI_630:
+ case PCI_DEVICE_ID_SI_5600:
+ case PCI_DEVICE_ID_SI_5597:
+ case PCI_DEVICE_ID_SI_5591:
+ hwif->autodma = 1;
+ hwif->dmaproc = &sis5513_dmaproc;
+ break;
+ default:
+ hwif->autodma = 0;
+ break;
+ }
+ }
+ return;
+}
diff --git a/drivers/ide/sl82c105.c b/drivers/ide/sl82c105.c
new file mode 100644
index 000000000..483a98fde
--- /dev/null
+++ b/drivers/ide/sl82c105.c
@@ -0,0 +1,138 @@
+/*
+ * linux/drivers/block/sl82c105.c
+ *
+ * SL82C105/Winbond 553 IDE driver
+ *
+ * Maintainer unknown.
+ *
+ * Drive tuning added from Rebel.com's kernel sources
+ * -- Russell King (15/11/98) linux@arm.linux.org.uk
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/pci.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include "ide_modes.h"
+
+extern char *ide_xfer_verbose (byte xfer_rate);
+
+#ifdef CONFIG_ARCH_NETWINDER
+/*
+ * Convert a PIO mode and cycle time to the required on/off
+ * times for the interface. This has protection against run-away
+ * timings.
+ */
+static unsigned int get_timing_sl82c105(ide_pio_data_t *p)
+{
+ unsigned int cmd_on;
+ unsigned int cmd_off;
+
+ cmd_on = (ide_pio_timings[p->pio_mode].active_time + 29) / 30;
+ cmd_off = (p->cycle_time - 30 * cmd_on + 29) / 30;
+
+ if (cmd_on > 32)
+ cmd_on = 32;
+ if (cmd_on == 0)
+ cmd_on = 1;
+
+ if (cmd_off > 32)
+ cmd_off = 32;
+ if (cmd_off == 0)
+ cmd_off = 1;
+
+ return (cmd_on - 1) << 8 | (cmd_off - 1) | (p->use_iordy ? 0x40 : 0x00);
+}
+
+/*
+ * We only deal with PIO mode here - DMA mode 'using_dma' is not
+ * initialised at the point that this function is called.
+ */
+static void tune_sl82c105(ide_drive_t *drive, byte pio)
+{
+ ide_hwif_t *hwif = HWIF(drive);
+ struct pci_dev *dev = hwif->pci_dev;
+ ide_pio_data_t p;
+ unsigned short drv_ctrl = 0x909;
+ unsigned int xfer_mode, reg;
+
+ reg = (hwif->channel ? 0x4c : 0x44) + (drive->select.b.unit ? 4 : 0);
+
+ pio = ide_get_best_pio_mode(drive, pio, 5, &p);
+
+ switch (pio) {
+ default:
+ case 0: xfer_mode = XFER_PIO_0; break;
+ case 1: xfer_mode = XFER_PIO_1; break;
+ case 2: xfer_mode = XFER_PIO_2; break;
+ case 3: xfer_mode = XFER_PIO_3; break;
+ case 4: xfer_mode = XFER_PIO_4; break;
+ }
+
+ if (ide_config_drive_speed(drive, xfer_mode) == 0)
+ drv_ctrl = get_timing_sl82c105(&p);
+
+ pci_write_config_word(dev, reg, drv_ctrl);
+ pci_read_config_word(dev, reg, &drv_ctrl);
+
+ printk("%s: selected %s (%dns) (%04X)\n", drive->name,
+ ide_xfer_verbose(xfer_mode), p.cycle_time, drv_ctrl);
+}
+#endif
+
+void ide_dmacapable_sl82c105(ide_hwif_t *hwif, unsigned long dmabase)
+{
+ unsigned char rev;
+
+ pci_read_config_byte(hwif->pci_dev, PCI_REVISION_ID, &rev);
+
+ if (rev <= 5) {
+ hwif->autodma = 0;
+ hwif->drives[0].autotune = 1;
+ hwif->drives[1].autotune = 1;
+ printk(" %s: revision %d, Bus-Master DMA disabled\n",
+ hwif->name, rev);
+ }
+ ide_setup_dma(hwif, dmabase, 8);
+}
+
+void ide_init_sl82c105(ide_hwif_t *hwif)
+{
+ struct pci_dev *dev = hwif->pci_dev;
+
+#ifdef CONFIG_ARCH_NETWINDER
+ unsigned char ctrl_stat;
+
+ pci_read_config_byte(dev, 0x40, &ctrl_stat);
+ pci_write_config_byte(dev, 0x40, ctrl_stat | 0x33);
+
+ hwif->tuneproc = tune_sl82c105;
+#else
+ unsigned short t16;
+ unsigned int t32;
+ pci_read_config_word(dev, PCI_COMMAND, &t16);
+ printk("SL82C105 command word: %x\n",t16);
+ t16 |= PCI_COMMAND_IO;
+ pci_write_config_word(dev, PCI_COMMAND, t16);
+ /* IDE timing */
+ pci_read_config_dword(dev, 0x44, &t32);
+ printk("IDE timing: %08x, resetting to PIO0 timing\n",t32);
+ pci_write_config_dword(dev, 0x44, 0x03e4);
+#ifndef CONFIG_MBX
+ pci_read_config_dword(dev, 0x40, &t32);
+ printk("IDE control/status register: %08x\n",t32);
+ pci_write_config_dword(dev, 0x40, 0x10ff08a1);
+#endif /* CONFIG_MBX */
+#endif
+}
diff --git a/drivers/ide/trm290.c b/drivers/ide/trm290.c
new file mode 100644
index 000000000..fb5e8d1af
--- /dev/null
+++ b/drivers/ide/trm290.c
@@ -0,0 +1,282 @@
+/*
+ * linux/drivers/block/trm290.c Version 1.01 December 5, 1997
+ *
+ * Copyright (c) 1997-1998 Mark Lord
+ * May be copied or modified under the terms of the GNU General Public License
+ */
+
+/*
+ * This module provides support for the bus-master IDE DMA function
+ * of the Tekram TRM290 chip, used on a variety of PCI IDE add-on boards,
+ * including a "Precision Instruments" board. The TRM290 pre-dates
+ * the sff-8038 standard (ide-dma.c) by a few months, and differs
+ * significantly enough to warrant separate routines for some functions,
+ * while re-using others from ide-dma.c.
+ *
+ * EXPERIMENTAL! It works for me (a sample of one).
+ *
+ * Works reliably for me in DMA mode (READs only),
+ * DMA WRITEs are disabled by default (see #define below);
+ *
+ * DMA is not enabled automatically for this chipset,
+ * but can be turned on manually (with "hdparm -d1") at run time.
+ *
+ * I need volunteers with "spare" drives for further testing
+ * and development, and maybe to help figure out the peculiarities.
+ * Even knowing the registers (below), some things behave strangely.
+ */
+
+#define TRM290_NO_DMA_WRITES /* DMA writes seem unreliable sometimes */
+
+/*
+ * TRM-290 PCI-IDE2 Bus Master Chip
+ * ================================
+ * The configuration registers are addressed in normal I/O port space
+ * and are used as follows:
+ *
+ * trm290_base depends on jumper settings, and is probed for by ide-dma.c
+ *
+ * trm290_base+2 when WRITTEN: chiptest register (byte, write-only)
+ * bit7 must always be written as "1"
+ * bits6-2 undefined
+ * bit1 1=legacy_compatible_mode, 0=native_pci_mode
+ * bit0 1=test_mode, 0=normal(default)
+ *
+ * trm290_base+2 when READ: status register (byte, read-only)
+ * bits7-2 undefined
+ * bit1 channel0 busmaster interrupt status 0=none, 1=asserted
+ * bit0 channel0 interrupt status 0=none, 1=asserted
+ *
+ * trm290_base+3 Interrupt mask register
+ * bits7-5 undefined
+ * bit4 legacy_header: 1=present, 0=absent
+ * bit3 channel1 busmaster interrupt status 0=none, 1=asserted (read only)
+ * bit2 channel1 interrupt status 0=none, 1=asserted (read only)
+ * bit1 channel1 interrupt mask: 1=masked, 0=unmasked(default)
+ * bit0 channel0 interrupt mask: 1=masked, 0=unmasked(default)
+ *
+ * trm290_base+1 "CPR" Config Pointer Register (byte)
+ * bit7 1=autoincrement CPR bits 2-0 after each access of CDR
+ * bit6 1=min. 1 wait-state posted write cycle (default), 0=0 wait-state
+ * bit5 0=enabled master burst access (default), 1=disable (write only)
+ * bit4 PCI DEVSEL# timing select: 1=medium(default), 0=fast
+ * bit3 0=primary IDE channel, 1=secondary IDE channel
+ * bits2-0 register index for accesses through CDR port
+ *
+ * trm290_base+0 "CDR" Config Data Register (word)
+ * two sets of seven config registers,
+ * selected by CPR bit 3 (channel) and CPR bits 2-0 (index 0 to 6),
+ * each index defined below:
+ *
+ * Index-0 Base address register for command block (word)
+ * defaults: 0x1f0 for primary, 0x170 for secondary
+ *
+ * Index-1 general config register (byte)
+ * bit7 1=DMA enable, 0=DMA disable
+ * bit6 1=activate IDE_RESET, 0=no action (default)
+ * bit5 1=enable IORDY, 0=disable IORDY (default)
+ * bit4 0=16-bit data port(default), 1=8-bit (XT) data port
+ * bit3 interrupt polarity: 1=active_low, 0=active_high(default)
+ * bit2 power-saving-mode(?): 1=enable, 0=disable(default) (write only)
+ * bit1 bus_master_mode(?): 1=enable, 0=disable(default)
+ * bit0 enable_io_ports: 1=enable(default), 0=disable
+ *
+ * Index-2 read-ahead counter preload bits 0-7 (byte, write only)
+ * bits7-0 bits7-0 of readahead count
+ *
+ * Index-3 read-ahead config register (byte, write only)
+ * bit7 1=enable_readahead, 0=disable_readahead(default)
+ * bit6 1=clear_FIFO, 0=no_action
+ * bit5 undefined
+ * bit4 mode4 timing control: 1=enable, 0=disable(default)
+ * bit3 undefined
+ * bit2 undefined
+ * bits1-0 bits9-8 of read-ahead count
+ *
+ * Index-4 base address register for control block (word)
+ * defaults: 0x3f6 for primary, 0x376 for secondary
+ *
+ * Index-5 data port timings (shared by both drives) (byte)
+ * standard PCI "clk" (clock) counts, default value = 0xf5
+ *
+ * bits7-6 setup time: 00=1clk, 01=2clk, 10=3clk, 11=4clk
+ * bits5-3 hold time: 000=1clk, 001=2clk, 010=3clk,
+ * 011=4clk, 100=5clk, 101=6clk,
+ * 110=8clk, 111=12clk
+ * bits2-0 active time: 000=2clk, 001=3clk, 010=4clk,
+ * 011=5clk, 100=6clk, 101=8clk,
+ * 110=12clk, 111=16clk
+ *
+ * Index-6 command/control port timings (shared by both drives) (byte)
+ * same layout as Index-5, default value = 0xde
+ *
+ * Suggested CDR programming for PIO mode0 (600ns):
+ * 0x01f0,0x21,0xff,0x80,0x03f6,0xf5,0xde ; primary
+ * 0x0170,0x21,0xff,0x80,0x0376,0xf5,0xde ; secondary
+ *
+ * Suggested CDR programming for PIO mode3 (180ns):
+ * 0x01f0,0x21,0xff,0x80,0x03f6,0x09,0xde ; primary
+ * 0x0170,0x21,0xff,0x80,0x0376,0x09,0xde ; secondary
+ *
+ * Suggested CDR programming for PIO mode4 (120ns):
+ * 0x01f0,0x21,0xff,0x80,0x03f6,0x00,0xde ; primary
+ * 0x0170,0x21,0xff,0x80,0x0376,0x00,0xde ; secondary
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/init.h>
+#include <linux/hdreg.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+
+static void trm290_prepare_drive (ide_drive_t *drive, unsigned int use_dma)
+{
+ ide_hwif_t *hwif = HWIF(drive);
+ unsigned int reg;
+ unsigned long flags;
+
+ /* select PIO or DMA */
+ reg = use_dma ? (0x21 | 0x82) : (0x21 & ~0x82);
+
+ __save_flags(flags); /* local CPU only */
+ __cli(); /* local CPU only */
+
+ if (reg != hwif->select_data) {
+ hwif->select_data = reg;
+ outb(0x51|(hwif->channel<<3), hwif->config_data+1); /* set PIO/DMA */
+ outw(reg & 0xff, hwif->config_data);
+ }
+
+ /* enable IRQ if not probing */
+ if (drive->present) {
+ reg = inw(hwif->config_data+3) & 0x13;
+ reg &= ~(1 << hwif->channel);
+ outw(reg, hwif->config_data+3);
+ }
+
+ __restore_flags(flags); /* local CPU only */
+}
+
+static void trm290_selectproc (ide_drive_t *drive)
+{
+ trm290_prepare_drive(drive, drive->using_dma);
+}
+
+static int trm290_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
+{
+ ide_hwif_t *hwif = HWIF(drive);
+ unsigned int count, reading = 2, writing = 0;
+
+ switch (func) {
+ case ide_dma_write:
+ reading = 0;
+ writing = 1;
+#ifdef TRM290_NO_DMA_WRITES
+ break; /* always use PIO for writes */
+#endif
+ case ide_dma_read:
+ if (!(count = ide_build_dmatable(drive, func)))
+ break; /* try PIO instead of DMA */
+ trm290_prepare_drive(drive, 1); /* select DMA xfer */
+ outl(hwif->dmatable_dma|reading|writing, hwif->dma_base);
+ drive->waiting_for_dma = 1;
+ outw((count * 2) - 1, hwif->dma_base+2); /* start DMA */
+ if (drive->media != ide_disk)
+ return 0;
+ ide_set_handler(drive, &ide_dma_intr, WAIT_CMD, NULL);
+ OUT_BYTE(reading ? WIN_READDMA : WIN_WRITEDMA, IDE_COMMAND_REG);
+ return 0;
+ case ide_dma_begin:
+ return 0;
+ case ide_dma_end:
+ drive->waiting_for_dma = 0;
+ ide_destroy_dmatable(drive); /* purge DMA mappings */
+ return (inw(hwif->dma_base+2) != 0x00ff);
+ case ide_dma_test_irq:
+ return (inw(hwif->dma_base+2) == 0x00ff);
+ default:
+ return ide_dmaproc(func, drive);
+ }
+ trm290_prepare_drive(drive, 0); /* select PIO xfer */
+ return 1;
+}
+
+/*
+ * Invoked from ide-dma.c at boot time.
+ */
+void __init ide_init_trm290 (ide_hwif_t *hwif)
+{
+ unsigned int cfgbase = 0;
+ unsigned long flags;
+ byte reg;
+ struct pci_dev *dev = hwif->pci_dev;
+
+ hwif->chipset = ide_trm290;
+ cfgbase = dev->resource[4].start;
+ if ((dev->class & 5) && cfgbase)
+ {
+ hwif->config_data = cfgbase & PCI_BASE_ADDRESS_IO_MASK;
+ printk("TRM290: chip config base at 0x%04lx\n", hwif->config_data);
+ } else {
+ hwif->config_data = 0x3df0;
+ printk("TRM290: using default config base at 0x%04lx\n", hwif->config_data);
+ }
+
+ __save_flags(flags); /* local CPU only */
+ __cli(); /* local CPU only */
+ /* put config reg into first byte of hwif->select_data */
+ outb(0x51|(hwif->channel<<3), hwif->config_data+1);
+ hwif->select_data = 0x21; /* select PIO as default */
+ outb(hwif->select_data, hwif->config_data);
+ reg = inb(hwif->config_data+3); /* get IRQ info */
+ reg = (reg & 0x10) | 0x03; /* mask IRQs for both ports */
+ outb(reg, hwif->config_data+3);
+ __restore_flags(flags); /* local CPU only */
+
+ if ((reg & 0x10))
+ hwif->irq = hwif->channel ? 15 : 14; /* legacy mode */
+ else if (!hwif->irq && hwif->mate && hwif->mate->irq)
+ hwif->irq = hwif->mate->irq; /* sharing IRQ with mate */
+ ide_setup_dma(hwif, (hwif->config_data + 4) ^ (hwif->channel ? 0x0080 : 0x0000), 3);
+ hwif->dmaproc = &trm290_dmaproc;
+ hwif->selectproc = &trm290_selectproc;
+ hwif->autodma = 0; /* play it safe for now */
+#if 1
+ {
+ /*
+ * My trm290-based card doesn't seem to work with all possible values
+ * for the control basereg, so this kludge ensures that we use only
+ * values that are known to work. Ugh. -ml
+ */
+ unsigned short old, compat = hwif->channel ? 0x374 : 0x3f4;
+ static unsigned short next_offset = 0;
+
+ outb(0x54|(hwif->channel<<3), hwif->config_data+1);
+ old = inw(hwif->config_data) & ~1;
+ if (old != compat && inb(old+2) == 0xff) {
+ compat += (next_offset += 0x400); /* leave lower 10 bits untouched */
+#if 1
+ if (ide_check_region(compat + 2, 1))
+ printk("Aieee %s: ide_check_region failure at 0x%04x\n", hwif->name, (compat + 2));
+ /*
+ * The region check is not needed; however.........
+ * Since this is the checked in ide-probe.c,
+ * this is only an assignment.
+ */
+#endif
+ hwif->io_ports[IDE_CONTROL_OFFSET] = compat + 2;
+ outw(compat|1, hwif->config_data);
+ printk("%s: control basereg workaround: old=0x%04x, new=0x%04x\n", hwif->name, old, inw(hwif->config_data) & ~1);
+ }
+ }
+#endif
+}
diff --git a/drivers/ide/umc8672.c b/drivers/ide/umc8672.c
new file mode 100644
index 000000000..02b581a28
--- /dev/null
+++ b/drivers/ide/umc8672.c
@@ -0,0 +1,159 @@
+/*
+ * linux/drivers/block/umc8672.c Version 0.05 Jul 31, 1996
+ *
+ * Copyright (C) 1995-1996 Linus Torvalds & author (see below)
+ */
+
+/*
+ * Principal Author/Maintainer: PODIEN@hml2.atlas.de (Wolfram Podien)
+ *
+ * This file provides support for the advanced features
+ * of the UMC 8672 IDE interface.
+ *
+ * Version 0.01 Initial version, hacked out of ide.c,
+ * and #include'd rather than compiled separately.
+ * This will get cleaned up in a subsequent release.
+ *
+ * Version 0.02 now configs/compiles separate from ide.c -ml
+ * Version 0.03 enhanced auto-tune, fix display bug
+ * Version 0.05 replace sti() with restore_flags() -ml
+ * add detection of possible race condition -ml
+ */
+
+/*
+ * VLB Controller Support from
+ * Wolfram Podien
+ * Rohoefe 3
+ * D28832 Achim
+ * Germany
+ *
+ * To enable UMC8672 support there must a lilo line like
+ * append="ide0=umc8672"...
+ * To set the speed according to the abilities of the hardware there must be a
+ * line like
+ * #define UMC_DRIVE0 11
+ * in the beginning of the driver, which sets the speed of drive 0 to 11 (there
+ * are some lines present). 0 - 11 are allowed speed values. These values are
+ * the results from the DOS speed test program supplied from UMC. 11 is the
+ * highest speed (about PIO mode 3)
+ */
+#define REALLY_SLOW_IO /* some systems can safely undef this */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+
+#include "ide_modes.h"
+
+/*
+ * Default speeds. These can be changed with "auto-tune" and/or hdparm.
+ */
+#define UMC_DRIVE0 1 /* DOS measured drive speeds */
+#define UMC_DRIVE1 1 /* 0 to 11 allowed */
+#define UMC_DRIVE2 1 /* 11 = Fastest Speed */
+#define UMC_DRIVE3 1 /* In case of crash reduce speed */
+
+static byte current_speeds[4] = {UMC_DRIVE0, UMC_DRIVE1, UMC_DRIVE2, UMC_DRIVE3};
+static const byte pio_to_umc [5] = {0,3,7,10,11}; /* rough guesses */
+
+/* 0 1 2 3 4 5 6 7 8 9 10 11 */
+static const byte speedtab [3][12] = {
+ {0xf, 0xb, 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1 },
+ {0x3, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1 },
+ {0xff,0xcb,0xc0,0x58,0x36,0x33,0x23,0x22,0x21,0x11,0x10,0x0}};
+
+static void out_umc (char port,char wert)
+{
+ outb_p (port,0x108);
+ outb_p (wert,0x109);
+}
+
+static inline byte in_umc (char port)
+{
+ outb_p (port,0x108);
+ return inb_p (0x109);
+}
+
+static void umc_set_speeds (byte speeds[])
+{
+ int i, tmp;
+
+ outb_p (0x5A,0x108); /* enable umc */
+
+ out_umc (0xd7,(speedtab[0][speeds[2]] | (speedtab[0][speeds[3]]<<4)));
+ out_umc (0xd6,(speedtab[0][speeds[0]] | (speedtab[0][speeds[1]]<<4)));
+ tmp = 0;
+ for (i = 3; i >= 0; i--)
+ {
+ tmp = (tmp << 2) | speedtab[1][speeds[i]];
+ }
+ out_umc (0xdc,tmp);
+ for (i = 0;i < 4; i++)
+ {
+ out_umc (0xd0+i,speedtab[2][speeds[i]]);
+ out_umc (0xd8+i,speedtab[2][speeds[i]]);
+ }
+ outb_p (0xa5,0x108); /* disable umc */
+
+ printk ("umc8672: drive speeds [0 to 11]: %d %d %d %d\n",
+ speeds[0], speeds[1], speeds[2], speeds[3]);
+}
+
+static void tune_umc (ide_drive_t *drive, byte pio)
+{
+ unsigned long flags;
+ ide_hwgroup_t *hwgroup = ide_hwifs[HWIF(drive)->index^1].hwgroup;
+
+ pio = ide_get_best_pio_mode(drive, pio, 4, NULL);
+ printk("%s: setting umc8672 to PIO mode%d (speed %d)\n", drive->name, pio, pio_to_umc[pio]);
+ save_flags(flags); /* all CPUs */
+ cli(); /* all CPUs */
+ if (hwgroup && hwgroup->handler != NULL) {
+ printk("umc8672: other interface is busy: exiting tune_umc()\n");
+ } else {
+ current_speeds[drive->name[2] - 'a'] = pio_to_umc[pio];
+ umc_set_speeds (current_speeds);
+ }
+ restore_flags(flags); /* all CPUs */
+}
+
+void __init init_umc8672 (void) /* called from ide.c */
+{
+ unsigned long flags;
+
+ __save_flags(flags); /* local CPU only */
+ __cli(); /* local CPU only */
+ if (check_region(0x108, 2)) {
+ __restore_flags(flags);
+ printk("\numc8672: PORTS 0x108-0x109 ALREADY IN USE\n");
+ return;
+ }
+ outb_p (0x5A,0x108); /* enable umc */
+ if (in_umc (0xd5) != 0xa0)
+ {
+ __restore_flags(flags); /* local CPU only */
+ printk ("umc8672: not found\n");
+ return;
+ }
+ outb_p (0xa5,0x108); /* disable umc */
+
+ umc_set_speeds (current_speeds);
+ __restore_flags(flags); /* local CPU only */
+
+ request_region(0x108, 2, "umc8672");
+ ide_hwifs[0].chipset = ide_umc8672;
+ ide_hwifs[1].chipset = ide_umc8672;
+ ide_hwifs[0].tuneproc = &tune_umc;
+ ide_hwifs[1].tuneproc = &tune_umc;
+ ide_hwifs[0].mate = &ide_hwifs[1];
+ ide_hwifs[1].mate = &ide_hwifs[0];
+ ide_hwifs[1].channel = 1;
+}
diff --git a/drivers/ide/via82cxxx.c b/drivers/ide/via82cxxx.c
new file mode 100644
index 000000000..c2ec24d39
--- /dev/null
+++ b/drivers/ide/via82cxxx.c
@@ -0,0 +1,834 @@
+/*
+ * linux/drivers/block/via82cxxx.c Version 0.07 Feb. 10, 2000
+ *
+ * Copyright (C) 1998-99 Michel Aubry, Maintainer
+ * Copyright (C) 1999 Jeff Garzik, MVP4 Support
+ * (jgarzik@mandrakesoft.com)
+ * Copyright (C) 1998-2000 Andre Hedrick (andre@suse.com)
+ * May be copied or modified under the terms of the GNU General Public License
+ *
+ * The VIA MVP-4 is reported OK with UDMA.
+ * The VIA MVP-3 is reported OK with UDMA.
+ * The TX Pro III is also reported OK with UDMA.
+ *
+ * VIA chips also have a single FIFO, with the same 64 bytes deep
+ * buffer (16 levels of 4 bytes each).
+ *
+ * However, VIA chips can have the buffer split either 8:8 levels,
+ * 16:0 levels or 0:16 levels between both channels. One could think
+ * of using this feature, as even if no level of FIFO is given to a
+ * given channel, one can for instance always reach ATAPI drives through
+ * it, or, if one channel is unused, configuration defaults to
+ * an even split FIFO levels.
+ *
+ * This feature is available only through a kernel command line :
+ * "splitfifo=Chan,Thr0,Thr1" or "splitfifo=Chan".
+ * where: Chan =1,2,3 or 4 and Thrx = 1,2,3,or 4.
+ *
+ * If Chan == 1:
+ * gives all the fifo to channel 0,
+ * sets its threshold to Thr0/4,
+ * and disables any dma access to channel 1.
+ *
+ * If chan == 2:
+ * gives all the fifo to channel 1,
+ * sets its threshold to Thr1/4,
+ * and disables any dma access to channel 0.
+ *
+ * If chan == 3 or 4:
+ * shares evenly fifo between channels,
+ * gives channel 0 a threshold of Thr0/4,
+ * and channel 1 a threshold of Thr1/4.
+ *
+ * Note that by default (if no command line is provided) and if a channel
+ * has been disabled in Bios, all the fifo is given to the active channel,
+ * and its threshold is set to 3/4.
+ *
+ * VT82c586B
+ *
+ * Offset 4B-48 - Drive Timing Control
+ * | pio0 | pio1 | pio2 | pio3 | pio4
+ * 25.0 MHz | 0xA8 | 0x65 | 0x65 | 0x31 | 0x20
+ * 33.0 MHz | 0xA8 | 0x65 | 0x65 | 0x31 | 0x20
+ * 37.5 MHz | 0xA9 | 0x76 | 0x76 | 0x32 | 0x21
+ *
+ * Offset 53-50 - UltraDMA Extended Timing Control
+ * UDMA | NO | 0 | 1 | 2
+ * | 0x03 | 0x62 | 0x61 | 0x60
+ *
+ * VT82c596B & VT82c686A
+ *
+ * Offset 4B-48 - Drive Timing Control
+ * | pio0 | pio1 | pio2 | pio3 | pio4
+ * 25.0 MHz | 0xA8 | 0x65 | 0x65 | 0x31 | 0x20
+ * 33.0 MHz | 0xA8 | 0x65 | 0x65 | 0x31 | 0x20
+ * 37.5 MHz | 0xDB | 0x87 | 0x87 | 0x42 | 0x31
+ * 41.5 MHz | 0xFE | 0xA8 | 0xA8 | 0x53 | 0x32
+ *
+ * Offset 53-50 - UltraDMA Extended Timing Control
+ * UDMA | NO | 0 | 1 | 2
+ * 33.0 MHz | 0x03 | 0xE2 | 0xE1 | 0xE0
+ * 37.5 MHz | 0x03 | 0xE2 | 0xE2 | 0xE1 (1)
+ *
+ * Offset 53-50 - UltraDMA Extended Timing Control
+ * UDMA | NO | 0 | 1 | 2 | 3 | 4
+ * 33.0 MHz | (2) | 0xE6 | 0xE4 | 0xE2 | 0xE1 | 0xE0
+ * 37.5 MHz | (2) | 0xE6 | 0xE6 | 0xE4 | 0xE2 | 0xE1 (1)
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+
+static struct pci_dev *host_dev = NULL;
+static struct pci_dev *isa_dev = NULL;
+
+static const struct {
+ const char *name;
+ unsigned short host_id;
+} ApolloHostChipInfo[] = {
+ { "VT 82C585 Apollo VP1/VPX", PCI_DEVICE_ID_VIA_82C585, },
+ { "VT 82C595 Apollo VP2", PCI_DEVICE_ID_VIA_82C595, },
+ { "VT 82C597 Apollo VP3", PCI_DEVICE_ID_VIA_82C597_0, },
+ { "VT 82C598 Apollo MVP3", PCI_DEVICE_ID_VIA_82C598_0, },
+ { "VT 82C680 Apollo P6", PCI_DEVICE_ID_VIA_82C680, },
+ { "VT 82C691 Apollo Pro", PCI_DEVICE_ID_VIA_82C691, },
+ { "VT 82C693 Apollo Pro Plus", PCI_DEVICE_ID_VIA_82C693, },
+ { "Apollo MVP4", PCI_DEVICE_ID_VIA_8501_0, },
+ { "VT 8371", PCI_DEVICE_ID_VIA_8371_0, },
+ { "VT 8601", PCI_DEVICE_ID_VIA_8601_0, },
+};
+
+#define NUM_APOLLO_ISA_CHIP_DEVICES 2
+#define VIA_FLAG_CHECK_REV 0x00000001
+#define VIA_FLAG_ATA_66 0x00000002
+
+static const struct {
+ unsigned short host_id;
+ unsigned short isa_id;
+ unsigned int flags;
+} ApolloISAChipInfo[] = {
+ { PCI_DEVICE_ID_VIA_82C585, PCI_DEVICE_ID_VIA_82C586_1, VIA_FLAG_CHECK_REV },
+ { PCI_DEVICE_ID_VIA_82C595, PCI_DEVICE_ID_VIA_82C586_1, VIA_FLAG_CHECK_REV },
+ { PCI_DEVICE_ID_VIA_82C597_0, PCI_DEVICE_ID_VIA_82C586_1, VIA_FLAG_CHECK_REV },
+ { PCI_DEVICE_ID_VIA_82C598_0, PCI_DEVICE_ID_VIA_82C586_1, VIA_FLAG_CHECK_REV },
+ { PCI_DEVICE_ID_VIA_82C598_0, PCI_DEVICE_ID_VIA_82C596, 0 },
+ { PCI_DEVICE_ID_VIA_82C680, PCI_DEVICE_ID_VIA_82C586_1, VIA_FLAG_CHECK_REV },
+ { PCI_DEVICE_ID_VIA_82C691, PCI_DEVICE_ID_VIA_82C596, 0 },
+ { PCI_DEVICE_ID_VIA_82C693, PCI_DEVICE_ID_VIA_82C596, 0 },
+ { PCI_DEVICE_ID_VIA_8501_0, PCI_DEVICE_ID_VIA_82C686, VIA_FLAG_ATA_66 },
+ { PCI_DEVICE_ID_VIA_8371_0, PCI_DEVICE_ID_VIA_82C686, VIA_FLAG_ATA_66 },
+ { PCI_DEVICE_ID_VIA_8601_0, PCI_DEVICE_ID_VIA_8231, VIA_FLAG_ATA_66 },
+};
+
+#define arraysize(x) (sizeof(x)/sizeof(*(x)))
+
+#define DISPLAY_VIA_TIMINGS
+
+#if defined(DISPLAY_VIA_TIMINGS) && defined(CONFIG_PROC_FS)
+#include <linux/stat.h>
+#include <linux/proc_fs.h>
+
+static char *FIFO_str[] = {
+ " 1 ",
+ "3/4",
+ "1/2",
+ "1/4"
+};
+
+static char *control3_str[] = {
+ "No limitation",
+ "64",
+ "128",
+ "192"
+};
+
+static int via_get_info(char *, char **, off_t, int);
+extern int (*via_display_info)(char *, char **, off_t, int); /* ide-proc.c */
+static struct pci_dev *bmide_dev;
+
+static char * print_apollo_drive_config (char *buf, struct pci_dev *dev)
+{
+ int rc;
+ unsigned int time;
+ byte tm;
+ char *p = buf;
+
+ /* Drive Timing Control */
+ rc = pci_read_config_dword(dev, 0x48, &time);
+ p += sprintf(p, "Act Pls Width: %02d %02d %02d %02d\n",
+ ((time & 0xf0000000)>>28) + 1,
+ ((time & 0xf00000)>>20) + 1,
+ ((time & 0xf000)>>12) + 1,
+ ((time & 0xf0)>>4) + 1 );
+ p += sprintf(p, "Recovery Time: %02d %02d %02d %02d\n",
+ ((time & 0x0f000000)>>24) + 1,
+ ((time & 0x0f0000)>>16) + 1,
+ ((time & 0x0f00)>>8) + 1,
+ (time & 0x0f) + 1 );
+
+ /* Address Setup Time */
+ rc = pci_read_config_byte(dev, 0x4C, &tm);
+ p += sprintf(p, "Add. Setup T.: %01dT %01dT %01dT %01dT\n",
+ ((tm & 0xc0)>>6) + 1,
+ ((tm & 0x30)>>4) + 1,
+ ((tm & 0x0c)>>2) + 1,
+ (tm & 0x03) + 1 );
+
+ /* UltraDMA33 Extended Timing Control */
+ rc = pci_read_config_dword(dev, 0x50, &time);
+ p += sprintf(p, "------------------UDMA-Timing-Control------------------------\n");
+ p += sprintf(p, "Enable Meth.: %01d %01d %01d %01d\n",
+ (time & 0x80000000) ? 1 : 0,
+ (time & 0x800000) ? 1 : 0,
+ (time & 0x8000) ? 1 : 0,
+ (time & 0x80) ? 1 : 0 );
+ p += sprintf(p, "Enable: %s %s %s %s\n",
+ (time & 0x40000000) ? "yes" : "no ",
+ (time & 0x400000) ? "yes" : "no ",
+ (time & 0x4000) ? "yes" : "no ",
+ (time & 0x40) ? "yes" : "no " );
+ p += sprintf(p, "Transfer Mode: %s %s %s %s\n",
+ (time & 0x20000000) ? "PIO" : "DMA",
+ (time & 0x200000) ? "PIO" : "DMA",
+ (time & 0x2000) ? "PIO" : "DMA",
+ (time & 0x20) ? "PIO" : "DMA" );
+ p += sprintf(p, "Cycle Time: %01dT %01dT %01dT %01dT\n",
+ ((time & 0x03000000)>>24) + 2,
+ ((time & 0x030000)>>16) + 2,
+ ((time & 0x0300)>>8) + 2,
+ (time & 0x03) + 2 );
+
+ return (char *)p;
+}
+
+static char * print_apollo_ide_config (char *buf, struct pci_dev *dev)
+{
+ byte time, tmp;
+ unsigned short size0, size1;
+ int rc;
+ char *p = buf;
+
+ rc = pci_read_config_byte(dev, 0x41, &time);
+ p += sprintf(p, "Prefetch Buffer : %s %s\n",
+ (time & 128) ? "on " : "off",
+ (time & 32) ? "on " : "off" );
+ p += sprintf(p, "Post Write Buffer: %s %s\n",
+ (time & 64) ? "on " : "off",
+ (time & 16) ? "on " : "off" );
+
+ /* FIFO configuration */
+ rc = pci_read_config_byte(dev, 0x43, &time);
+ tmp = ((time & 0x20)>>2) + ((time & 0x40)>>3);
+ p += sprintf(p, "FIFO Conf/Chan. : %02d %02d\n",
+ 16 - tmp, tmp);
+ tmp = (time & 0x0F)>>2;
+ p += sprintf(p, "Threshold Prim. : %s %s\n",
+ FIFO_str[tmp],
+ FIFO_str[time & 0x03] );
+
+ /* chipset Control3 */
+ rc = pci_read_config_byte(dev, 0x46, &time);
+ p += sprintf(p, "Read DMA FIFO flush: %s %s\n",
+ (time & 0x80) ? "on " : "off",
+ (time & 0x40) ? "on " : "off" );
+ p += sprintf(p, "End Sect. FIFO flush: %s %s\n",
+ (time & 0x20) ? "on " : "off",
+ (time & 0x10) ? "on " : "off" );
+ p += sprintf(p, "Max DRDY Pulse Width: %s %s\n",
+ control3_str[(time & 0x03)],
+ (time & 0x03) ? "PCI clocks" : "" );
+
+ /* Primary and Secondary sector sizes */
+ rc = pci_read_config_word(dev, 0x60, &size0);
+ rc = pci_read_config_word(dev, 0x68, &size1);
+ p += sprintf(p, "Bytes Per Sector: %03d %03d\n",
+ size0 & 0xfff,
+ size1 & 0xfff );
+
+ return (char *)p;
+}
+
+static char * print_apollo_chipset_control1 (char *buf, struct pci_dev *dev)
+{
+ byte t;
+ int rc;
+ char *p = buf;
+ unsigned short c;
+ byte l, l_max;
+
+ rc = pci_read_config_word(dev, 0x04, &c);
+ rc = pci_read_config_byte(dev, 0x44, &t);
+ rc = pci_read_config_byte(dev, 0x0d, &l);
+ rc = pci_read_config_byte(dev, 0x3f, &l_max);
+
+ p += sprintf(p, "Command register = 0x%x\n", c);
+ p += sprintf(p, "Master Read Cycle IRDY %d Wait State\n",
+ (t & 64) >>6 );
+ p += sprintf(p, "Master Write Cycle IRDY %d Wait State\n",
+ (t & 32) >> 5 );
+ p += sprintf(p, "FIFO Output Data 1/2 Clock Advance: %s\n",
+ (t & 16) ? "on " : "off" );
+ p += sprintf(p, "Bus Master IDE Status Register Read Retry: %s\n",
+ (t & 8) ? "on " : "off" );
+ p += sprintf(p, "Latency timer = %d (max. = %d)\n",
+ l, l_max);
+
+ return (char *)p;
+}
+
+static char * print_apollo_chipset_control2 (char *buf, struct pci_dev *dev)
+{
+ byte t;
+ int rc;
+ char *p = buf;
+ rc = pci_read_config_byte(dev, 0x45, &t);
+ p += sprintf(p, "Interrupt Steering Swap: %s\n",
+ (t & 64) ? "on ":"off" );
+
+ return (char *)p;
+}
+
+static char * print_apollo_chipset_control3 (char *buf, struct pci_dev *dev,
+ unsigned short n)
+{
+ /*
+ * at that point we can be sure that register 0x20 of the
+ * chipset contains the right address...
+ */
+ unsigned int bibma;
+ int rc;
+ byte c0, c1;
+ char *p = buf;
+
+ rc = pci_read_config_dword(dev, 0x20, &bibma);
+ bibma = (bibma & 0xfff0) ;
+
+ /*
+ * at that point bibma+0x2 et bibma+0xa are byte registers
+ * to investigate:
+ */
+ c0 = inb((unsigned short)bibma + 0x02);
+ c1 = inb((unsigned short)bibma + 0x0a);
+
+ if (n == 0) {
+ /*p = sprintf(p,"--------------------Primary IDE------------Secondary IDE-----");*/
+ p += sprintf(p, "both channels togth: %s %s\n",
+ (c0&0x80) ? "no" : "yes",
+ (c1&0x80) ? "no" : "yes" );
+ } else {
+ /*p = sprintf(p,"--------------drive0------drive1-------drive0------drive1----");*/
+ p += sprintf(p, "DMA enabled: %s %s %s %s\n",
+ (c0&0x20) ? "yes" : "no ",
+ (c0&0x40) ? "yes" : "no ",
+ (c1&0x20) ? "yes" : "no ",
+ (c1&0x40) ? "yes" : "no " );
+ }
+
+ return (char *)p;
+}
+
+static int via_get_info (char *buffer, char **addr, off_t offset, int count)
+{
+ /*
+ * print what /proc/via displays,
+ * if required from DISPLAY_APOLLO_TIMINGS
+ */
+ char *p = buffer;
+ /* Parameter of chipset : */
+
+ /* Miscellaneous control 1 */
+ p = print_apollo_chipset_control1(buffer, bmide_dev);
+
+ /* Miscellaneous control 2 */
+ p = print_apollo_chipset_control2(p, bmide_dev);
+ /* Parameters of drives: */
+
+ /* Header */
+ p += sprintf(p, "------------------Primary IDE------------Secondary IDE-----\n");
+ p = print_apollo_chipset_control3(p, bmide_dev, 0);
+ p = print_apollo_ide_config(p, bmide_dev);
+ p += sprintf(p, "--------------drive0------drive1-------drive0------drive1----\n");
+ p = print_apollo_chipset_control3(p, bmide_dev, 1);
+ p = print_apollo_drive_config(p, bmide_dev);
+
+ return p-buffer; /* hoping it is less than 4K... */
+}
+
+#endif /* defined(DISPLAY_VIA_TIMINGS) && defined(CONFIG_PROC_FS) */
+
+/*
+ * Used to set Fifo configuration via kernel command line:
+ */
+
+byte via_proc = 0;
+byte fifoconfig = 0;
+static byte newfifo = 0;
+
+/* Used to just intialize once Fifo configuration */
+static short int done = 0;
+
+/*
+ * Set VIA Chipset Timings for (U)DMA modes enabled.
+ *
+ * VIA Apollo chipset has complete support for
+ * setting up the timing parameters.
+ */
+static void set_via_timings (ide_hwif_t *hwif)
+{
+ struct pci_dev *dev = hwif->pci_dev;
+ byte post = hwif->channel ? 0x30 : 0xc0;
+ byte flush = hwif->channel ? 0x50 : 0xa0;
+ int mask = hwif->channel ? ((newfifo & 0x60) ? 0 : 1) :
+ (((newfifo & 0x60) == 0x60) ? 1 : 0);
+ byte via_config = 0;
+ int rc = 0, errors = 0;
+
+ printk("%s: VIA Bus-Master ", hwif->name);
+
+ /*
+ * setting IDE read prefetch buffer and IDE post write buffer.
+ * (This feature allows prefetched reads and post writes).
+ */
+ if ((rc = pci_read_config_byte(dev, 0x41, &via_config)))
+ errors++;
+
+ if (mask) {
+ if ((rc = pci_write_config_byte(dev, 0x41, via_config & ~post)))
+ errors++;
+ } else {
+ if ((rc = pci_write_config_byte(dev, 0x41, via_config | post)))
+ errors++;
+ }
+
+ /*
+ * setting Channel read and End-of-sector FIFO flush.
+ * (This feature ensures that FIFO flush is enabled:
+ * - for read DMA when interrupt asserts the given channel.
+ * - at the end of each sector for the given channel.)
+ */
+ if ((rc = pci_read_config_byte(dev, 0x46, &via_config)))
+ errors++;
+
+ if (mask) {
+ if ((rc = pci_write_config_byte(dev, 0x46, via_config & ~flush)))
+ errors++;
+ } else {
+ if ((rc = pci_write_config_byte(dev, 0x46, via_config | flush)))
+ errors++;
+ }
+
+ if (!hwif->dma_base)
+ printk("Config %s. No DMA Enabled\n",
+ errors ? "ERROR":"Success");
+ else
+ printk("(U)DMA Timing Config %s\n",
+ errors ? "ERROR" : "Success");
+}
+
+/*
+ * Sets VIA 82cxxx FIFO configuration:
+ * This chipsets gets a splitable fifo. This can be driven either by command
+ * line option (eg "splitfifo=2,2,3" which asks this driver to switch all the
+ * 16 fifo levels to the second drive, and give it a threshold of 3 for (u)dma
+ * triggering.
+ */
+
+static int via_set_fifoconfig(ide_hwif_t *hwif)
+{
+ byte fifo;
+ unsigned int timings;
+ struct pci_dev *dev = hwif->pci_dev;
+
+ /* read port configuration */
+ if (pci_read_config_dword(dev, 0x40, &timings))
+ return 1;
+
+ /* first read actual fifo config: */
+ if (pci_read_config_byte(dev, 0x43, &fifo))
+ return 1;
+
+ /* keep 4 and 7 bit as they seem to differ between chipsets flavors... */
+ newfifo = fifo & 0x90;
+
+ if (fifoconfig) {
+ /* we received a config request from kernel command line: */
+ newfifo |= fifoconfig & 0x6f;
+ } else {
+ /* If ever just one channel is unused, allocate all fifo levels to it
+ * and give it a 3/4 threshold for (u)dma transfers.
+ * Otherwise, share it evenly between channels:
+ */
+ if ((timings & 3) == 2) {
+ /* only primary channel is enabled
+ * 16 buf. to prim. chan. thresh=3/4
+ */
+ newfifo |= 0x06;
+ } else if ((timings & 3) == 1) {
+ /* only secondary channel is enabled!
+ * 16 buffers to sec. ch. thresh=3/4
+ */
+ newfifo |= 0x69;
+ } else {
+ /* fifo evenly distributed: */
+ newfifo |= 0x2a;
+ }
+ }
+
+ /* write resulting configuration to chipset: */
+ if (pci_write_config_byte(dev, 0x43, newfifo))
+ return 1;
+
+ /* and then reread it to get the actual one */
+ if (pci_read_config_byte(dev, 0x43, &newfifo))
+ return 1;
+
+ /* print a kernel report: */
+ printk("Split FIFO Configuration: %s Primary buffers, threshold = %s\n",
+ ((newfifo & 0x60) == 0x60) ? " 0" :
+ ((newfifo & 0x60) ? " 8" : "16"),
+ !(newfifo & 0x0c) ? "1" :
+ (!(newfifo & 0x08) ? "3/4" :
+ (newfifo & 0x04) ? "1/4" : "1/2"));
+
+ printk(" %s Second. buffers, threshold = %s\n",
+ ((newfifo & 0x60) == 0x60) ? "16" :
+ ((newfifo & 0x60) ? " 8" : " 0"),
+ !(newfifo & 0x03) ? "1" :
+ (!(newfifo & 0x02) ? "3/4" :
+ (newfifo & 0x01) ? "1/4" : "1/2"));
+ return 0;
+}
+
+#ifdef CONFIG_VIA82CXXX_TUNING
+
+static int via82cxxx_tune_chipset (ide_drive_t *drive, byte speed)
+{
+ struct hd_driveid *id = drive->id;
+ ide_hwif_t *hwif = HWIF(drive);
+ struct pci_dev *dev = hwif->pci_dev;
+ unsigned long dma_base = hwif->dma_base;
+ byte unit = (drive->select.b.unit & 0x01);
+ int drive_number = ((hwif->channel ? 2 : 0) + unit);
+
+ byte ata2_pci = 0x00;
+ byte ata3_pci = 0x00;
+ byte timing = 0x00;
+ byte ultra = 0x00;
+ int err;
+
+ int bus_speed = ide_system_bus_speed();
+
+ switch(drive_number) {
+ case 0: ata2_pci = 0x48; ata3_pci = 0x50; break;
+ case 1: ata2_pci = 0x49; ata3_pci = 0x51; break;
+ case 2: ata2_pci = 0x4a; ata3_pci = 0x52; break;
+ case 3: ata2_pci = 0x4b; ata3_pci = 0x53; break;
+ default:
+ return err;
+ }
+
+ pci_read_config_byte(dev, ata2_pci, &timing);
+ pci_read_config_byte(dev, ata3_pci, &ultra);
+
+ switch(speed) {
+ case XFER_UDMA_4:
+ case XFER_UDMA_3:
+ case XFER_UDMA_2:
+ case XFER_UDMA_1:
+ case XFER_UDMA_0:
+ case XFER_MW_DMA_2:
+ case XFER_MW_DMA_1:
+ case XFER_MW_DMA_0:
+ case XFER_SW_DMA_2:
+ case XFER_SW_DMA_1:
+ case XFER_SW_DMA_0:
+ case XFER_PIO_4:
+ case XFER_PIO_3:
+ case XFER_PIO_2:
+ case XFER_PIO_1:
+ case XFER_PIO_0:
+ case XFER_PIO_SLOW:
+ default:
+ break;
+ }
+
+ pci_write_config_byte(dev, ata2_pci, timing);
+ pci_write_config_byte(dev, ata3_pci, ultra);
+
+ err = ide_config_drive_speed(drive, speed);
+
+ return(err);
+}
+
+static int config_chipset_for_dma (ide_drive_t *drive)
+{
+ struct hd_driveid *id = drive->id;
+ byte speed = 0x00;
+ int rval;
+
+ if ((id->dma_ultra & 0x0010) && (HWIF(drive)->udma_four)) {
+ speed = XFER_UDMA_4;
+ } else if ((id->dma_ultra & 0x0008) && (HWIF(drive)->udma_four)) {
+ speed = XFER_UDMA_3;
+ } else if (id->dma_ultra & 0x0004) {
+ speed = XFER_UDMA_2;
+ } else if (id->dma_ultra & 0x0002) {
+ speed = XFER_UDMA_1;
+ } else if (id->dma_ultra & 0x0001) {
+ speed = XFER_UDMA_0;
+ } else if (id->dma_mword & 0x0004) {
+ speed = XFER_MW_DMA_2;
+ } else if (id->dma_mword & 0x0002) {
+ speed = XFER_MW_DMA_1;
+ } else if (id->dma_mword & 0x0001) {
+ speed = XFER_MW_DMA_0;
+ } else if (id->dma_1word & 0x0004) {
+ speed = XFER_SW_DMA_2;
+ } else if (id->dma_1word & 0x0002) {
+ speed = XFER_SW_DMA_1;
+ } else if (id->dma_1word & 0x0001) {
+ speed = XFER_SW_DMA_0;
+ } else {
+ return ((int) ide_dma_off_quietly);
+ }
+
+ (void) via82cxxx_tune_chipset(drive, speed);
+
+ rval = (int)( ((id->dma_ultra >> 11) & 3) ? ide_dma_on :
+ ((id->dma_ultra >> 8) & 7) ? ide_dma_on :
+ ((id->dma_mword >> 8) & 7) ? ide_dma_on :
+ ((id->dma_1word >> 8) & 7) ? ide_dma_on :
+ ide_dma_off_quietly);
+ return rval;
+}
+
+static void config_chipset_for_pio (ide_drive_t *drive)
+{
+ unsigned short eide_pio_timing[6] = {960, 480, 240, 180, 120, 90};
+ unsigned short xfer_pio = drive->id->eide_pio_modes;
+ byte timing, speed, pio;
+
+ pio = ide_get_best_pio_mode(drive, 255, 5, NULL);
+
+ if (xfer_pio> 4)
+ xfer_pio = 0;
+
+ if (drive->id->eide_pio_iordy > 0) {
+ for (xfer_pio = 5;
+ xfer_pio>0 &&
+ drive->id->eide_pio_iordy>eide_pio_timing[xfer_pio];
+ xfer_pio--);
+ } else {
+ xfer_pio = (drive->id->eide_pio_modes & 4) ? 0x05 :
+ (drive->id->eide_pio_modes & 2) ? 0x04 :
+ (drive->id->eide_pio_modes & 1) ? 0x03 :
+ (drive->id->tPIO & 2) ? 0x02 :
+ (drive->id->tPIO & 1) ? 0x01 : xfer_pio;
+ }
+
+ timing = (xfer_pio >= pio) ? xfer_pio : pio;
+
+ switch(timing) {
+ case 4: speed = XFER_PIO_4;break;
+ case 3: speed = XFER_PIO_3;break;
+ case 2: speed = XFER_PIO_2;break;
+ case 1: speed = XFER_PIO_1;break;
+ default:
+ speed = (!drive->id->tPIO) ? XFER_PIO_0 : XFER_PIO_SLOW; break;
+ }
+ (void) via82cxxx_tune_chipset(drive, speed);
+}
+
+static void via82cxxx_tune_drive (ide_drive_t *drive, byte pio)
+{
+ byte speed;
+ switch(pio) {
+ case 4: speed = XFER_PIO_4;break;
+ case 3: speed = XFER_PIO_3;break;
+ case 2: speed = XFER_PIO_2;break;
+ case 1: speed = XFER_PIO_1;break;
+ default: speed = XFER_PIO_0;break;
+ }
+ (void) via82cxxx_tune_chipset(drive, speed);
+}
+
+static int config_drive_xfer_rate (ide_drive_t *drive)
+{
+ struct hd_driveid *id = drive->id;
+ ide_dma_action_t dma_func = ide_dma_on;
+
+ if (id && (id->capability & 1) && HWIF(drive)->autodma) {
+ /* Consult the list of known "bad" drives */
+ if (ide_dmaproc(ide_dma_bad_drive, drive)) {
+ dma_func = ide_dma_off;
+ goto fast_ata_pio;
+ }
+ dma_func = ide_dma_off_quietly;
+ if (id->field_valid & 4) {
+ if (id->dma_ultra & 0x001F) {
+ /* Force if Capable UltraDMA */
+ dma_func = config_chipset_for_dma(drive);
+ if ((id->field_valid & 2) &&
+ (dma_func != ide_dma_on))
+ goto try_dma_modes;
+ }
+ } else if (id->field_valid & 2) {
+try_dma_modes:
+ if ((id->dma_mword & 0x0007) ||
+ (id->dma_1word & 0x0007)) {
+ /* Force if Capable regular DMA modes */
+ dma_func = config_chipset_for_dma(drive);
+ if (dma_func != ide_dma_on)
+ goto no_dma_set;
+ }
+ } else if (ide_dmaproc(ide_dma_good_drive, drive)) {
+ if (id->eide_dma_time > 150) {
+ goto no_dma_set;
+ }
+ /* Consult the list of known "good" drives */
+ dma_func = config_chipset_for_dma(drive);
+ if (dma_func != ide_dma_on)
+ goto no_dma_set;
+ } else {
+ goto fast_ata_pio;
+ }
+ } else if ((id->capability & 8) || (id->field_valid & 2)) {
+fast_ata_pio:
+ dma_func = ide_dma_off_quietly;
+no_dma_set:
+ config_chipset_for_pio(drive);
+ }
+ return HWIF(drive)->dmaproc(dma_func, drive);
+}
+
+int via82cxxx_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
+{
+ switch (func) {
+ case ide_dma_check:
+ return config_drive_xfer_rate(drive);
+ default:
+ break;
+ }
+ return ide_dmaproc(func, drive); /* use standard DMA stuff */
+}
+#endif /* CONFIG_VIA82CXXX_TUNING */
+
+unsigned int __init pci_init_via82cxxx (struct pci_dev *dev, const char *name)
+{
+ struct pci_dev *host;
+ struct pci_dev *isa;
+ int i, j, ata33, ata66;
+
+ byte revision = 0;
+
+ for (i = 0; i < arraysize (ApolloHostChipInfo) && !host_dev; i++) {
+ host = pci_find_device (PCI_VENDOR_ID_VIA,
+ ApolloHostChipInfo[i].host_id,
+ NULL);
+ if (!host)
+ continue;
+
+ host_dev = host;
+ printk(ApolloHostChipInfo[i].name);
+
+ for (j = 0; j < arraysize (ApolloISAChipInfo) && !isa_dev; j++) {
+ if (ApolloISAChipInfo[j].host_id !=
+ ApolloHostChipInfo[i].host_id)
+ continue;
+
+ isa = pci_find_device (PCI_VENDOR_ID_VIA,
+ ApolloISAChipInfo[j].isa_id,
+ NULL);
+ if (!isa)
+ continue;
+
+ isa_dev = isa;
+
+ ata33 = 1;
+ ata66 = 0;
+
+ if (ApolloISAChipInfo[j].flags & VIA_FLAG_CHECK_REV) {
+ pci_read_config_byte(isa_dev, 0x0d, &revision);
+ ata33 = (revision >= 0x20) ? 1 : 0;
+ } else if (ApolloISAChipInfo[j].flags & VIA_FLAG_ATA_66) {
+ ata33 = 0;
+ ata66 = 1;
+ }
+
+ if (ata33 | ata66)
+ printk(" Chipset Core ATA-%s", ata66 ? "66" : "33");
+ }
+ printk("\n");
+ }
+
+#if defined(DISPLAY_VIA_TIMINGS) && defined(CONFIG_PROC_FS)
+ via_proc = 1;
+ bmide_dev = dev;
+ via_display_info = &via_get_info;
+#endif /* DISPLAY_VIA_TIMINGS && CONFIG_PROC_FS*/
+
+ return 0;
+}
+
+unsigned int __init ata66_via82cxxx (ide_hwif_t *hwif)
+{
+ /* (Jeff Garzik) FIXME!!! for MVP4 */
+ return 0;
+}
+
+void __init ide_init_via82cxxx (ide_hwif_t *hwif)
+{
+ set_via_timings(hwif);
+
+#ifdef CONFIG_VIA82CXXX_TUNING
+ hwif->tuneproc = &via82cxxx_tune_drive;
+ if (hwif->dma_base) {
+ hwif->dmaproc = &via82cxxx_dmaproc;
+ } else {
+ hwif->autodma = 0;
+ hwif->drives[0].autotune = 1;
+ hwif->drives[1].autotune = 1;
+ }
+#endif /* CONFIG_VIA82CXXX_TUNING */
+}
+
+/*
+ * ide_dmacapable_via82cxxx(ide_hwif_t *, unsigned long)
+ * checks if channel "channel" of if hwif is dma
+ * capable or not, according to kernel command line,
+ * and the new fifo settings.
+ * It calls "ide_setup_dma" on capable mainboards, and
+ * bypasses the setup if not capable.
+ */
+
+void ide_dmacapable_via82cxxx (ide_hwif_t *hwif, unsigned long dmabase)
+{
+ if (!done) {
+ via_set_fifoconfig(hwif);
+ done = 1;
+ }
+
+ /*
+ * check if any fifo is available for requested port:
+ */
+ if (((hwif->channel == 0) && ((newfifo & 0x60) == 0x60)) ||
+ ((hwif->channel == 1) && ((newfifo & 0x60) == 0x00))) {
+ printk(" %s: VP_IDE Bus-Master DMA disabled (FIFO setting)\n", hwif->name);
+ } else {
+ ide_setup_dma(hwif, dmabase, 8);
+ }
+}