diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-03-19 01:28:40 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-03-19 01:28:40 +0000 |
commit | 8abb719409c9060a7c0676f76e9182c1e0b8ca46 (patch) | |
tree | b88cc5a6cd513a04a512b7e6215c873c90a1c5dd /drivers/ide | |
parent | f01bd7aeafd95a08aafc9e3636bb26974df69d82 (diff) |
Merge with 2.3.99-pre1.
Diffstat (limited to 'drivers/ide')
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, ®54h); + + 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, ®53h); + p += sprintf(p, "PCI Clock: %d.\n", reg53h); + + pci_read_config_byte(bmide_dev, 0x53, ®53h); + 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, ®53h); + 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, ®53h); + 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, ®53h); + p += sprintf(p, + "Channel state: %s %s\n", + channel_status[reg53h & 0x07], + channel_status[(reg53h & 0x70) >> 4] ); + + pci_read_config_byte(bmide_dev, 0x58, ®5xh); + pci_read_config_byte(bmide_dev, 0x5c, ®5yh); + 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, ®5xh); + pci_read_config_byte(bmide_dev, 0x5d, ®5yh); + 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, ®5xh); + pci_read_config_byte(bmide_dev, 0x55, ®5yh); + 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, ®5xh); + pci_read_config_byte(bmide_dev, 0x5b, ®5xh1); + pci_read_config_byte(bmide_dev, 0x5e, ®5yh); + pci_read_config_byte(bmide_dev, 0x5f, ®5yh1); + + 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, ®5xh); + pci_read_config_byte(bmide_dev, 0x57, ®5yh); + 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, ®53); + (void) pci_read_config_byte(bmide_dev, DRWTIM0, ®54); + (void) pci_read_config_byte(bmide_dev, ARTTIM1, ®55); + (void) pci_read_config_byte(bmide_dev, DRWTIM1, ®56); + (void) pci_read_config_byte(bmide_dev, ARTTIM2, ®57); + (void) pci_read_config_byte(bmide_dev, DRWTIM2, ®58); + (void) pci_read_config_byte(bmide_dev, DRWTIM3, ®5b); + (void) pci_read_config_byte(bmide_dev, BMIDESR0, ®72); + (void) pci_read_config_byte(bmide_dev, UDIDETCR0, ®73); + (void) pci_read_config_byte(bmide_dev, BMIDESR1, ®7a); + (void) pci_read_config_byte(bmide_dev, UDIDETCR1, ®7b); + + 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, ®72); + (void) pci_read_config_byte(dev, UDIDETCR0, ®73); + (void) pci_read_config_byte(dev, BMIDESR1, ®7a); + (void) pci_read_config_byte(dev, UDIDETCR1, ®7b); + + 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, ®1); + pci_read_config_dword(HWIF(drive)->pci_dev, 0x48, ®2); + 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, ®1); + pci_read_config_dword(HWIF(drive)->pci_dev, 0x48, ®2); + 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, ®1); + /* 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, ®51h); + +#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, ®50h); + pci_write_config_byte(HWIF(drive)->pci_dev, 0x50, reg50h|0x03); + pci_read_config_byte(HWIF(drive)->pci_dev, 0x50, ®50h); + /* 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, ®40); + * pci_read_config_word(HWIF(drive)->pci_dev, 0x42, ®42); + * pci_read_config_word(HWIF(drive)->pci_dev, 0x44, ®44); + * pci_read_config_word(HWIF(drive)->pci_dev, 0x48, ®48); + * pci_read_config_word(HWIF(drive)->pci_dev, 0x4a, ®4a); + * pci_read_config_word(HWIF(drive)->pci_dev, 0x54, ®54); + * + * 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, ®40); + pci_read_config_word(bmide_dev, 0x42, ®42); + pci_read_config_byte(bmide_dev, 0x44, ®44); + pci_read_config_byte(bmide_dev, 0x48, ®48); + pci_read_config_byte(bmide_dev, 0x4a, ®4a); + pci_read_config_byte(bmide_dev, 0x4b, ®4b); + pci_read_config_byte(bmide_dev, 0x54, ®54); + + 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, ®4042); + sitre = (reg4042 & 0x4000) ? 1 : 0; + pci_read_config_word(dev, 0x44, ®44); + pci_read_config_word(dev, 0x48, ®48); + pci_read_config_word(dev, 0x4a, ®4a); + pci_read_config_word(dev, 0x54, ®54); + + 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, ®54h); + pci_read_config_byte(hwif->pci_dev, 0x55, ®55h); + 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, ®) + && !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 & PCI_COMMAND_IO)) { + printk("%s: buggy IDE controller disabled (BIOS)\n", name); + return; + } + if (!pci_read_config_word (dev, 0x40, ®) + && !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, ®); + 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, ®); + 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, ®); + 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, ®2); + rc = pci_read_config_word(bmide_dev, 0x4e, ®3); + p += sprintf(p, "Prefetch Count: %d \t \t \t \t %d\n", + reg2, reg3); + + rc = pci_read_config_byte(bmide_dev, 0x4b, ®); + 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, ®); + rc = pci_read_config_byte(bmide_dev, 0x45, ®1); + 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, ®); + rc = pci_read_config_byte(bmide_dev, 0x44, ®1); + 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, ®); + 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, ®); + rc = pci_read_config_byte(bmide_dev, 0x47, ®1); + 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, ®); + rc = pci_read_config_byte(bmide_dev, 0x46, ®1); + 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, ®4bh); + 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, ®52h); + 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, ®48h); + + 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); + } +} |