diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/ide | |
download | kernel_samsung_tuna-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.zip kernel_samsung_tuna-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.tar.gz kernel_samsung_tuna-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.tar.bz2 |
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/ide')
73 files changed, 50372 insertions, 0 deletions
diff --git a/drivers/ide/Kconfig b/drivers/ide/Kconfig new file mode 100644 index 0000000..3ac0a53 --- /dev/null +++ b/drivers/ide/Kconfig @@ -0,0 +1,1056 @@ +# +# IDE ATA ATAPI Block device driver configuration +# +# Andre Hedrick <andre@linux-ide.org> +# + +menu "ATA/ATAPI/MFM/RLL support" + +config IDE + tristate "ATA/ATAPI/MFM/RLL support" + ---help--- + If you say Y here, your kernel will be able to manage low cost mass + storage units such as ATA/(E)IDE and ATAPI units. The most common + cases are IDE hard drives and ATAPI CD-ROM drives. + + If your system is pure SCSI and doesn't use these interfaces, you + can say N here. + + Integrated Disk Electronics (IDE aka ATA-1) is a connecting standard + for mass storage units such as hard disks. It was designed by + Western Digital and Compaq Computer in 1984. It was then named + ST506. Quite a number of disks use the IDE interface. + + AT Attachment (ATA) is the superset of the IDE specifications. + ST506 was also called ATA-1. + + Fast-IDE is ATA-2 (also named Fast ATA), Enhanced IDE (EIDE) is + ATA-3. It provides support for larger disks (up to 8.4GB by means of + the LBA standard), more disks (4 instead of 2) and for other mass + storage units such as tapes and cdrom. UDMA/33 (aka UltraDMA/33) is + ATA-4 and provides faster (and more CPU friendly) transfer modes + than previous PIO (Programmed processor Input/Output) from previous + ATA/IDE standards by means of fast DMA controllers. + + ATA Packet Interface (ATAPI) is a protocol used by EIDE tape and + CD-ROM drives, similar in many respects to the SCSI protocol. + + SMART IDE (Self Monitoring, Analysis and Reporting Technology) was + designed in order to prevent data corruption and disk crash by + detecting pre hardware failure conditions (heat, access time, and + the like...). Disks built since June 1995 may follow this standard. + The kernel itself doesn't manage this; however there are quite a + number of user programs such as smart that can query the status of + SMART parameters from disk drives. + + To compile this driver as a module, choose M here: the + module will be called ide. + + For further information, please read <file:Documentation/ide.txt>. + + If unsure, say Y. + +if IDE + +config IDE_MAX_HWIFS + int "Max IDE interfaces" + depends on ALPHA || SUPERH + default 4 + help + This is the maximum number of IDE hardware interfaces that will + be supported by the driver. Make sure it is at least as high as + the number of IDE interfaces in your system. + +config BLK_DEV_IDE + tristate "Enhanced IDE/MFM/RLL disk/cdrom/tape/floppy support" + ---help--- + If you say Y here, you will use the full-featured IDE driver to + control up to ten ATA/IDE interfaces, each being able to serve a + "master" and a "slave" device, for a total of up to twenty ATA/IDE + disk/cdrom/tape/floppy drives. + + Useful information about large (>540 MB) IDE disks, multiple + interfaces, what to do if ATA/IDE devices are not automatically + detected, sound card ATA/IDE ports, module support, and other + topics, is contained in <file:Documentation/ide.txt>. For detailed + information about hard drives, consult the Disk-HOWTO and the + Multi-Disk-HOWTO, available from + <http://www.tldp.org/docs.html#howto>. + + To fine-tune ATA/IDE drive/interface parameters for improved + performance, look for the hdparm package at + <ftp://ibiblio.org/pub/Linux/system/hardware/>. + + To compile this driver as a module, choose M here and read + <file:Documentation/ide.txt>. The module will be called ide-mod. + Do not compile this driver as a module if your root file system (the + one containing the directory /) is located on an IDE device. + + If you have one or more IDE drives, say Y or M here. If your system + has no IDE drives, or if memory requirements are really tight, you + could say N here, and select the "Old hard disk driver" below + instead to save about 13 KB of memory in the kernel. + +if BLK_DEV_IDE + +comment "Please see Documentation/ide.txt for help/info on IDE drives" + +config BLK_DEV_IDE_SATA + bool "Support for SATA (deprecated; conflicts with libata SATA driver)" + default n + ---help--- + There are two drivers for Serial ATA controllers. + + The main driver, "libata", exists inside the SCSI subsystem + and supports most modern SATA controllers. + + The IDE driver (which you are currently configuring) supports + a few first-generation SATA controllers. + + In order to eliminate conflicts between the two subsystems, + this config option enables the IDE driver's SATA support. + Normally this is disabled, as it is preferred that libata + supports SATA controllers, and this (IDE) driver supports + PATA controllers. + + If unsure, say N. + +config BLK_DEV_HD_IDE + bool "Use old disk-only driver on primary interface" + depends on (X86 || SH_MPC1211) + ---help--- + There are two drivers for MFM/RLL/IDE disks. Most people use just + the new enhanced driver by itself. This option however installs the + old hard disk driver to control the primary IDE/disk interface in + the system, leaving the new enhanced IDE driver to take care of only + the 2nd/3rd/4th IDE interfaces. Doing this will prevent you from + having an IDE/ATAPI CD-ROM or tape drive connected to the primary + IDE interface. Choosing this option may be useful for older systems + which have MFM/RLL/ESDI controller+drives at the primary port + address (0x1f0), along with IDE drives at the secondary/3rd/4th port + addresses. + + Normally, just say N here; you will then use the new driver for all + 4 interfaces. + +config BLK_DEV_IDEDISK + tristate "Include IDE/ATA-2 DISK support" + ---help--- + This will include enhanced support for MFM/RLL/IDE hard disks. If + you have a MFM/RLL/IDE disk, and there is no special reason to use + the old hard disk driver instead, say Y. If you have an SCSI-only + system, you can say N here. + + To compile this driver as a module, choose M here: the + module will be called ide-disk. + Do not compile this driver as a module if your root file system + (the one containing the directory /) is located on the IDE disk. + + If unsure, say Y. + +config IDEDISK_MULTI_MODE + bool "Use multi-mode by default" + help + If you get this error, try to say Y here: + + hda: set_multmode: status=0x51 { DriveReady SeekComplete Error } + hda: set_multmode: error=0x04 { DriveStatusError } + + If in doubt, say N. + +config BLK_DEV_IDECS + tristate "PCMCIA IDE support" + depends on PCMCIA + help + Support for outboard IDE disks, tape drives, and CD-ROM drives + connected through a PCMCIA card. + +config BLK_DEV_IDECD + tristate "Include IDE/ATAPI CDROM support" + ---help--- + If you have a CD-ROM drive using the ATAPI protocol, say Y. ATAPI is + a newer protocol used by IDE CD-ROM and TAPE drives, similar to the + SCSI protocol. Most new CD-ROM drives use ATAPI, including the + NEC-260, Mitsumi FX400, Sony 55E, and just about all non-SCSI + double(2X) or better speed drives. + + If you say Y here, the CD-ROM drive will be identified at boot time + along with other IDE devices, as "hdb" or "hdc", or something + similar (check the boot messages with dmesg). If this is your only + CD-ROM drive, you can say N to all other CD-ROM options, but be sure + to say Y or M to "ISO 9660 CD-ROM file system support". + + Note that older versions of LILO (LInux LOader) cannot properly deal + with IDE/ATAPI CD-ROMs, so install LILO 16 or higher, available from + <http://lilo.go.dyndns.org/>. + + To compile this driver as a module, choose M here: the + module will be called ide-cd. + +config BLK_DEV_IDETAPE + tristate "Include IDE/ATAPI TAPE support (EXPERIMENTAL)" + depends on EXPERIMENTAL + help + If you have an IDE tape drive using the ATAPI protocol, say Y. + ATAPI is a newer protocol used by IDE tape and CD-ROM drives, + similar to the SCSI protocol. If you have an SCSI tape drive + however, you can say N here. + + You should also say Y if you have an OnStream DI-30 tape drive; this + will not work with the SCSI protocol, until there is support for the + SC-30 and SC-50 versions. + + If you say Y here, the tape drive will be identified at boot time + along with other IDE devices, as "hdb" or "hdc", or something + similar, and will be mapped to a character device such as "ht0" + (check the boot messages with dmesg). Be sure to consult the + <file:drivers/ide/ide-tape.c> and <file:Documentation/ide.txt> files + for usage information. + + To compile this driver as a module, choose M here: the + module will be called ide-tape. + +config BLK_DEV_IDEFLOPPY + tristate "Include IDE/ATAPI FLOPPY support" + ---help--- + If you have an IDE floppy drive which uses the ATAPI protocol, + answer Y. ATAPI is a newer protocol used by IDE CD-ROM/tape/floppy + drives, similar to the SCSI protocol. + + The LS-120 and the IDE/ATAPI Iomega ZIP drive are also supported by + this driver. For information about jumper settings and the question + of when a ZIP drive uses a partition table, see + <http://www.win.tue.nl/~aeb/linux/zip/zip-1.html>. + (ATAPI PD-CD/CDR drives are not supported by this driver; support + for PD-CD/CDR drives is available if you answer Y to + "SCSI emulation support", below). + + If you say Y here, the FLOPPY drive will be identified along with + other IDE devices, as "hdb" or "hdc", or something similar (check + the boot messages with dmesg). + + To compile this driver as a module, choose M here: the + module will be called ide-floppy. + +config BLK_DEV_IDESCSI + tristate "SCSI emulation support" + depends on SCSI + ---help--- + WARNING: ide-scsi is no longer needed for cd writing applications! + The 2.6 kernel supports direct writing to ide-cd, which eliminates + the need for ide-scsi + the entire scsi stack just for writing a + cd. The new method is more efficient in every way. + + This will provide SCSI host adapter emulation for IDE ATAPI devices, + and will allow you to use a SCSI device driver instead of a native + ATAPI driver. + + This is useful if you have an ATAPI device for which no native + driver has been written (for example, an ATAPI PD-CD drive); + you can then use this emulation together with an appropriate SCSI + device driver. In order to do this, say Y here and to "SCSI support" + and "SCSI generic support", below. You must then provide the kernel + command line "hdx=ide-scsi" (try "man bootparam" or see the + documentation of your boot loader (lilo or loadlin) about how to + pass options to the kernel at boot time) for devices if you want the + native EIDE sub-drivers to skip over the native support, so that + this SCSI emulation can be used instead. + + Note that this option does NOT allow you to attach SCSI devices to a + box that doesn't have a SCSI host adapter installed. + + If both this SCSI emulation and native ATAPI support are compiled + into the kernel, the native support will be used. + +config IDE_TASK_IOCTL + bool "IDE Taskfile Access" + help + This is a direct raw access to the media. It is a complex but + elegant solution to test and validate the domain of the hardware and + perform below the driver data recover if needed. This is the most + basic form of media-forensics. + + If you are unsure, say N here. + +comment "IDE chipset support/bugfixes" + +config IDE_GENERIC + tristate "generic/default IDE chipset support" + default y + help + If unsure, say Y. + +config BLK_DEV_CMD640 + bool "CMD640 chipset bugfix/support" + depends on X86 + ---help--- + The CMD-Technologies CMD640 IDE chip is used on many common 486 and + Pentium motherboards, usually in combination with a "Neptune" or + "SiS" chipset. Unfortunately, it has a number of rather nasty + design flaws that can cause severe data corruption under many common + conditions. Say Y here to include code which tries to automatically + detect and correct the problems under Linux. This option also + enables access to the secondary IDE ports in some CMD640 based + systems. + + This driver will work automatically in PCI based systems (most new + systems have PCI slots). But if your system uses VESA local bus + (VLB) instead of PCI, you must also supply a kernel boot parameter + to enable the CMD640 bugfix/support: "ide0=cmd640_vlb". (Try "man + bootparam" or see the documentation of your boot loader about how to + pass options to the kernel.) + + The CMD640 chip is also used on add-in cards by Acculogic, and on + the "CSA-6400E PCI to IDE controller" that some people have. For + details, read <file:Documentation/ide.txt>. + +config BLK_DEV_CMD640_ENHANCED + bool "CMD640 enhanced support" + depends on BLK_DEV_CMD640 + help + This option includes support for setting/autotuning PIO modes and + prefetch on CMD640 IDE interfaces. For details, read + <file:Documentation/ide.txt>. If you have a CMD640 IDE interface + and your BIOS does not already do this for you, then say Y here. + Otherwise say N. + +config BLK_DEV_IDEPNP + bool "PNP EIDE support" + depends on PNP + help + If you have a PnP (Plug and Play) compatible EIDE card and + would like the kernel to automatically detect and activate + it, say Y here. + +config BLK_DEV_IDEPCI + bool "PCI IDE chipset support" if PCI + default BLK_DEV_IDEDMA_PMAC if PPC_PMAC && BLK_DEV_IDEDMA_PMAC + help + Say Y here for PCI systems which use IDE drive(s). + This option helps the IDE driver to automatically detect and + configure all PCI-based IDE interfaces in your system. + +config IDEPCI_SHARE_IRQ + bool "Sharing PCI IDE interrupts support" + depends on PCI && BLK_DEV_IDEPCI + help + Some ATA/IDE chipsets have hardware support which allows for + sharing a single IRQ with other cards. To enable support for + this in the ATA/IDE driver, say Y here. + + It is safe to say Y to this question, in most cases. + If unsure, say N. + +config BLK_DEV_OFFBOARD + bool "Boot off-board chipsets first support" + depends on PCI && BLK_DEV_IDEPCI + help + Normally, IDE controllers built into the motherboard (on-board + controllers) are assigned to ide0 and ide1 while those on add-in PCI + cards (off-board controllers) are relegated to ide2 and ide3. + Answering Y here will allow you to reverse the situation, with + off-board controllers on ide0/1 and on-board controllers on ide2/3. + This can improve the usability of some boot managers such as lilo + when booting from a drive on an off-board controller. + + If you say Y here, and you actually want to reverse the device scan + order as explained above, you also need to issue the kernel command + line option "ide=reverse". (Try "man bootparam" or see the + documentation of your boot loader (lilo or loadlin) about how to + pass options to the kernel at boot time.) + + Note that, if you do this, the order of the hd* devices will be + rearranged which may require modification of fstab and other files. + + If in doubt, say N. + +config BLK_DEV_GENERIC + tristate "Generic PCI IDE Chipset Support" + depends on BLK_DEV_IDEPCI + +config BLK_DEV_OPTI621 + tristate "OPTi 82C621 chipset enhanced support (EXPERIMENTAL)" + depends on PCI && BLK_DEV_IDEPCI && EXPERIMENTAL + help + This is a driver for the OPTi 82C621 EIDE controller. + Please read the comments at the top of <file:drivers/ide/pci/opti621.c>. + +config BLK_DEV_RZ1000 + tristate "RZ1000 chipset bugfix/support" + depends on PCI && BLK_DEV_IDEPCI && X86 + help + The PC-Technologies RZ1000 IDE chip is used on many common 486 and + Pentium motherboards, usually along with the "Neptune" chipset. + Unfortunately, it has a rather nasty design flaw that can cause + severe data corruption under many conditions. Say Y here to include + code which automatically detects and corrects the problem under + Linux. This may slow disk throughput by a few percent, but at least + things will operate 100% reliably. + +config BLK_DEV_SL82C105 + tristate "Winbond SL82c105 support" + depends on PCI && (PPC || ARM) && BLK_DEV_IDEPCI + help + If you have a Winbond SL82c105 IDE controller, say Y here to enable + special configuration for this chip. This is common on various CHRP + motherboards, but could be used elsewhere. If in doubt, say Y. + +config BLK_DEV_IDEDMA_PCI + bool "Generic PCI bus-master DMA support" + depends on PCI && BLK_DEV_IDEPCI + ---help--- + If your PCI system uses IDE drive(s) (as opposed to SCSI, say) and + is capable of bus-master DMA operation (most Pentium PCI systems), + you will want to say Y here to reduce CPU overhead. You can then use + the "hdparm" utility to enable DMA for drives for which it was not + enabled automatically. By default, DMA is not enabled automatically + for these drives, but you can change that by saying Y to the + following question "Use DMA by default when available". You can get + the latest version of the hdparm utility from + <ftp://ibiblio.org/pub/Linux/system/hardware/>. + + Read the comments at the beginning of <file:drivers/ide/ide-dma.c> + and the file <file:Documentation/ide.txt> for more information. + + It is safe to say Y to this question. + +if BLK_DEV_IDEDMA_PCI + +config BLK_DEV_IDEDMA_FORCED + bool "Force enable legacy 2.0.X HOSTS to use DMA" + help + This is an old piece of lost code from Linux 2.0 Kernels. + + Generally say N here. + +config IDEDMA_PCI_AUTO + bool "Use PCI DMA by default when available" + ---help--- + Prior to kernel version 2.1.112, Linux used to automatically use + DMA for IDE drives and chipsets which support it. Due to concerns + about a couple of cases where buggy hardware may have caused damage, + the default is now to NOT use DMA automatically. To revert to the + previous behaviour, say Y to this question. + + If you suspect your hardware is at all flakey, say N here. + Do NOT email the IDE kernel people regarding this issue! + + It is normally safe to answer Y to this question unless your + motherboard uses a VIA VP2 chipset, in which case you should say N. + +config IDEDMA_ONLYDISK + bool "Enable DMA only for disks " + depends on IDEDMA_PCI_AUTO + help + This is used if you know your ATAPI Devices are going to fail DMA + Transfers. + + Generally say N here. + +config BLK_DEV_AEC62XX + tristate "AEC62XX chipset support" + help + This driver adds explicit support for Acard AEC62xx (Artop ATP8xx) + IDE controllers. This allows the kernel to change PIO, DMA and UDMA + speeds and to configure the chip to optimum performance. + +config BLK_DEV_ALI15X3 + tristate "ALI M15x3 chipset support" + help + This driver ensures (U)DMA support for ALI 1533, 1543 and 1543C + onboard chipsets. It also tests for Simplex mode and enables + normal dual channel support. + + If you say Y here, you also need to say Y to "Use DMA by default + when available", above. Please read the comments at the top of + <file:drivers/ide/pci/alim15x3.c>. + + If unsure, say N. + +config WDC_ALI15X3 + bool "ALI M15x3 WDC support (DANGEROUS)" + depends on BLK_DEV_ALI15X3 + ---help--- + This allows for UltraDMA support for WDC drives that ignore CRC + checking. You are a fool for enabling this option, but there have + been requests. DO NOT COMPLAIN IF YOUR DRIVE HAS FS CORRUPTION, IF + YOU ENABLE THIS! No one will listen, just laugh for ignoring this + SERIOUS WARNING. + + Using this option can allow WDC drives to run at ATA-4/5 transfer + rates with only an ATA-2 support structure. + + SAY N! + +config BLK_DEV_AMD74XX + tristate "AMD and nVidia IDE support" + help + This driver adds explicit support for AMD-7xx and AMD-8111 chips + and also for the nVidia nForce chip. This allows the kernel to + change PIO, DMA and UDMA speeds and to configure the chip to + optimum performance. + +config BLK_DEV_ATIIXP + tristate "ATI IXP chipset IDE support" + depends on X86 + help + This driver adds explicit support for ATI IXP chipset. + This allows the kernel to change PIO, DMA and UDMA speeds + and to configure the chip to optimum performance. + + Say Y here if you have an ATI IXP chipset IDE controller. + +config BLK_DEV_CMD64X + tristate "CMD64{3|6|8|9} chipset support" + help + Say Y here if you have an IDE controller which uses any of these + chipsets: CMD643, CMD646, or CMD648. + +config BLK_DEV_TRIFLEX + tristate "Compaq Triflex IDE support" + help + Say Y here if you have a Compaq Triflex IDE controller, such + as those commonly found on Compaq Pentium-Pro systems + +config BLK_DEV_CY82C693 + tristate "CY82C693 chipset support" + help + This driver adds detection and support for the CY82C693 chipset + used on Digital's PC-Alpha 164SX boards. + + If you say Y here, you need to say Y to "Use DMA by default + when available" as well. + +config BLK_DEV_CS5520 + tristate "Cyrix CS5510/20 MediaGX chipset support (VERY EXPERIMENTAL)" + depends on EXPERIMENTAL + help + Include support for PIO tuning an virtual DMA on the Cyrix MediaGX + 5510/5520 chipset. This will automatically be detected and + configured if found. + + It is safe to say Y to this question. + +config BLK_DEV_CS5530 + tristate "Cyrix/National Semiconductor CS5530 MediaGX chipset support" + help + Include support for UDMA on the Cyrix MediaGX 5530 chipset. This + will automatically be detected and configured if found. + + It is safe to say Y to this question. + +config BLK_DEV_HPT34X + tristate "HPT34X chipset support" + help + This driver adds up to 4 more EIDE devices sharing a single + interrupt. The HPT343 chipset in its current form is a non-bootable + controller; the HPT345/HPT363 chipset is a bootable (needs BIOS FIX) + PCI UDMA controllers. This driver requires dynamic tuning of the + chipset during the ide-probe at boot time. It is reported to support + DVD II drives, by the manufacturer. + +config HPT34X_AUTODMA + bool "HPT34X AUTODMA support (EXPERIMENTAL)" + depends on BLK_DEV_HPT34X && EXPERIMENTAL + help + This is a dangerous thing to attempt currently! Please read the + comments at the top of <file:drivers/ide/pci/hpt34x.c>. If you say Y + here, then say Y to "Use DMA by default when available" as well. + + If unsure, say N. + +config BLK_DEV_HPT366 + tristate "HPT36X/37X chipset support" + ---help--- + HPT366 is an Ultra DMA chipset for ATA-66. + HPT368 is an Ultra DMA chipset for ATA-66 RAID Based. + HPT370 is an Ultra DMA chipset for ATA-100. + HPT372 is an Ultra DMA chipset for ATA-100. + HPT374 is an Ultra DMA chipset for ATA-100. + + This driver adds up to 4 more EIDE devices sharing a single + interrupt. + + The HPT366 chipset in its current form is bootable. One solution + for this problem are special LILO commands for redirecting the + reference to device 0x80. The other solution is to say Y to "Boot + off-board chipsets first support" (CONFIG_BLK_DEV_OFFBOARD) unless + your mother board has the chipset natively mounted. Regardless one + should use the fore mentioned option and call at LILO or include + "ide=reverse" in LILO's append-line. + + This driver requires dynamic tuning of the chipset during the + ide-probe at boot. It is reported to support DVD II drives, by the + manufacturer. + +config BLK_DEV_SC1200 + tristate "National SCx200 chipset support" + help + This driver adds support for the built in IDE on the National + SCx200 series of embedded x86 "Geode" systems + +config BLK_DEV_PIIX + tristate "Intel PIIXn chipsets support" + help + This driver adds explicit support for Intel PIIX and ICH chips + and also for the Efar Victory66 (slc90e66) chip. This allows + the kernel to change PIO, DMA and UDMA speeds and to configure + the chip to optimum performance. + +config BLK_DEV_IT8172 + bool "IT8172 IDE support" + depends on (MIPS_ITE8172 || MIPS_IVR) + help + Say Y here to support the on-board IDE controller on the Integrated + Technology Express, Inc. ITE8172 SBC. Vendor page at + <http://www.ite.com.tw/ia/brief_it8172bsp.htm>; picture of the + board at <http://www.mvista.com/partners/semiconductor/ite.html>. + +config BLK_DEV_NS87415 + tristate "NS87415 chipset support" + help + This driver adds detection and support for the NS87415 chip + (used in SPARC64, among others). + + Please read the comments at the top of <file:drivers/ide/pci/ns87415.c>. + +config BLK_DEV_PDC202XX_OLD + tristate "PROMISE PDC202{46|62|65|67} support" + help + Promise Ultra33 or PDC20246 + Promise Ultra66 or PDC20262 + Promise Ultra100 or PDC20265/PDC20267/PDC20268 + + This driver adds up to 4 more EIDE devices sharing a single + interrupt. This add-on card is a bootable PCI UDMA controller. Since + multiple cards can be installed and there are BIOS ROM problems that + happen if the BIOS revisions of all installed cards (three-max) do + not match, the driver attempts to do dynamic tuning of the chipset + at boot-time for max-speed. Ultra33 BIOS 1.25 or newer is required + for more than one card. This card may require that you say Y to + "Special UDMA Feature". + + If you say Y here, you need to say Y to "Use DMA by default when + available" as well. + + Please read the comments at the top of + <file:drivers/ide/pci/pdc202xx_old.c>. + + If unsure, say N. + +config PDC202XX_BURST + bool "Special UDMA Feature" + depends on BLK_DEV_PDC202XX_OLD + help + This option causes the pdc202xx driver to enable UDMA modes on the + PDC202xx even when the PDC202xx BIOS has not done so. + + It was originally designed for the PDC20246/Ultra33, whose BIOS will + only setup UDMA on the first two PDC20246 cards. It has also been + used succesfully on a PDC20265/Ultra100, allowing use of UDMA modes + when the PDC20265 BIOS has been disabled (for faster boot up). + + Please read the comments at the top of + <file:drivers/ide/pci/pdc202xx_old.c>. + + If unsure, say N. + +config BLK_DEV_PDC202XX_NEW + tristate "PROMISE PDC202{68|69|70|71|75|76|77} support" + +# FIXME - probably wants to be one for old and for new +config PDC202XX_FORCE + bool "Enable controller even if disabled by BIOS" + depends on BLK_DEV_PDC202XX_NEW + help + Enable the PDC202xx controller even if it has been disabled in the BIOS setup. + +config BLK_DEV_SVWKS + tristate "ServerWorks OSB4/CSB5/CSB6 chipsets support" + help + This driver adds PIO/(U)DMA support for the ServerWorks OSB4/CSB5 + chipsets. + +config BLK_DEV_SGIIOC4 + tristate "Silicon Graphics IOC4 chipset support" + depends on IA64_SGI_SN2 || IA64_GENERIC + help + This driver adds PIO & MultiMode DMA-2 support for the SGI IOC4 + chipset, which has one channel and can support two devices. + Please say Y here if you have an Altix System from SGI. + +config BLK_DEV_SIIMAGE + tristate "Silicon Image chipset support" + help + This driver adds PIO/(U)DMA support for the SI CMD680 and SII + 3112 (Serial ATA) chips. + +config BLK_DEV_SIS5513 + tristate "SiS5513 chipset support" + depends on X86 + ---help--- + This driver ensures (U)DMA support for SIS5513 chipset family based + mainboards. + + The following chipsets are supported: + ATA16: SiS5511, SiS5513 + ATA33: SiS5591, SiS5597, SiS5598, SiS5600 + ATA66: SiS530, SiS540, SiS620, SiS630, SiS640 + ATA100: SiS635, SiS645, SiS650, SiS730, SiS735, SiS740, + SiS745, SiS750 + + If you say Y here, you need to say Y to "Use DMA by default when + available" as well. + + Please read the comments at the top of <file:drivers/ide/pci/sis5513.c>. + +config BLK_DEV_SLC90E66 + tristate "SLC90E66 chipset support" + help + This driver ensures (U)DMA support for Victroy66 SouthBridges for + SMsC with Intel NorthBridges. This is an Ultra66 based chipset. + The nice thing about it is that you can mix Ultra/DMA/PIO devices + and it will handle timing cycles. Since this is an improved + look-a-like to the PIIX4 it should be a nice addition. + + If you say Y here, you need to say Y to "Use DMA by default when + available" as well. + + Please read the comments at the top of + <file:drivers/ide/pci/slc90e66.c>. + +config BLK_DEV_TRM290 + tristate "Tekram TRM290 chipset support" + help + This driver adds support for bus master DMA transfers + using the Tekram TRM290 PCI IDE chip. Volunteers are + needed for further tweaking and development. + Please read the comments at the top of <file:drivers/ide/pci/trm290.c>. + +config BLK_DEV_VIA82CXXX + tristate "VIA82CXXX chipset support" + help + This driver adds explicit support for VIA BusMastering IDE chips. + This allows the kernel to change PIO, DMA and UDMA speeds and to + configure the chip to optimum performance. + +endif + +config BLK_DEV_IDE_PMAC + bool "Builtin PowerMac IDE support" + depends on PPC_PMAC && IDE=y + help + This driver provides support for the built-in IDE controller on + most of the recent Apple Power Macintoshes and PowerBooks. + If unsure, say Y. + +config BLK_DEV_IDE_PMAC_ATA100FIRST + bool "Probe internal ATA/100 (Kauai) first" + depends on BLK_DEV_IDE_PMAC + help + This option will cause the ATA/100 controller found in UniNorth2 + based machines (Windtunnel PowerMac, Aluminium PowerBooks, ...) + to be probed before the ATA/66 and ATA/33 controllers. Without + these, those machine used to have the hard disk on hdc and the + CD-ROM on hda. This option changes this to more natural hda for + hard disk and hdc for CD-ROM. + +config BLK_DEV_IDEDMA_PMAC + bool "PowerMac IDE DMA support" + depends on BLK_DEV_IDE_PMAC + help + This option allows the driver for the built-in IDE controller on + Power Macintoshes and PowerBooks to use DMA (direct memory access) + to transfer data to and from memory. Saying Y is safe and improves + performance. + +config BLK_DEV_IDE_PMAC_BLINK + bool "Blink laptop LED on drive activity" + depends on BLK_DEV_IDE_PMAC && ADB_PMU + help + This option enables the use of the sleep LED as a hard drive + activity LED. + +config IDE_ARM + def_bool ARM && (ARCH_A5K || ARCH_CLPS7500 || ARCH_RPC || ARCH_SHARK) + +config BLK_DEV_IDE_ICSIDE + tristate "ICS IDE interface support" + depends on ARM && ARCH_ACORN + help + On Acorn systems, say Y here if you wish to use the ICS IDE + interface card. This is not required for ICS partition support. + If you are unsure, say N to this. + +config BLK_DEV_IDEDMA_ICS + bool "ICS DMA support" + depends on BLK_DEV_IDE_ICSIDE + help + Say Y here if you want to add DMA (Direct Memory Access) support to + the ICS IDE driver. + +config IDEDMA_ICS_AUTO + bool "Use ICS DMA by default" + depends on BLK_DEV_IDEDMA_ICS + help + Prior to kernel version 2.1.112, Linux used to automatically use + DMA for IDE drives and chipsets which support it. Due to concerns + about a couple of cases where buggy hardware may have caused damage, + the default is now to NOT use DMA automatically. To revert to the + previous behaviour, say Y to this question. + + If you suspect your hardware is at all flakey, say N here. + Do NOT email the IDE kernel people regarding this issue! + +config BLK_DEV_IDE_RAPIDE + tristate "RapIDE interface support" + depends on ARM && ARCH_ACORN + help + Say Y here if you want to support the Yellowstone RapIDE controller + manufactured for use with Acorn computers. + +config BLK_DEV_IDE_BAST + tristate "Simtec BAST / Thorcom VR1000 IDE support" + depends on ARM && (ARCH_BAST || MACH_VR1000) + help + Say Y here if you want to support the onboard IDE channels on the + Simtec BAST or the Thorcom VR1000 + +config BLK_DEV_GAYLE + bool "Amiga Gayle IDE interface support" + depends on AMIGA + help + This is the IDE driver for the Amiga Gayle IDE interface. It supports + both the `A1200 style' and `A4000 style' of the Gayle IDE interface, + This includes builtin IDE interfaces on some Amiga models (A600, + A1200, A4000, and A4000T), and IDE interfaces on the Zorro expansion + bus (M-Tech E-Matrix 530 expansion card). + Say Y if you have an Amiga with a Gayle IDE interface and want to use + IDE devices (hard disks, CD-ROM drives, etc.) that are connected to + it. + Note that you also have to enable Zorro bus support if you want to + use Gayle IDE interfaces on the Zorro expansion bus. + +config BLK_DEV_IDEDOUBLER + bool "Amiga IDE Doubler support (EXPERIMENTAL)" + depends on BLK_DEV_GAYLE && EXPERIMENTAL + ---help--- + This driver provides support for the so-called `IDE doublers' (made + by various manufacturers, e.g. Eyetech) that can be connected to the + builtin IDE interface of some Amiga models. Using such an IDE + doubler, you can connect up to four instead of two IDE devices on + the Amiga's builtin IDE interface. + + Note that the normal Amiga Gayle IDE driver may not work correctly + if you have an IDE doubler and don't enable this driver! + + Say Y if you have an IDE doubler. The driver is enabled at kernel + runtime using the "ide=doubler" kernel boot parameter. + +config BLK_DEV_BUDDHA + bool "Buddha/Catweasel/X-Surf IDE interface support (EXPERIMENTAL)" + depends on ZORRO && EXPERIMENTAL + help + This is the IDE driver for the IDE interfaces on the Buddha, + Catweasel and X-Surf expansion boards. It supports up to two interfaces + on the Buddha, three on the Catweasel and two on the X-Surf. + + Say Y if you have a Buddha or Catweasel expansion board and want to + use IDE devices (hard disks, CD-ROM drives, etc.) that are connected + to one of its IDE interfaces. + +config BLK_DEV_FALCON_IDE + bool "Falcon IDE interface support" + depends on ATARI + help + This is the IDE driver for the builtin IDE interface on the Atari + Falcon. Say Y if you have a Falcon and want to use IDE devices (hard + disks, CD-ROM drives, etc.) that are connected to the builtin IDE + interface. + +config BLK_DEV_MAC_IDE + bool "Macintosh Quadra/Powerbook IDE interface support" + depends on MAC + help + This is the IDE driver for the builtin IDE interface on some m68k + Macintosh models. It supports both the `Quadra style' (used in + Quadra/ Centris 630 and Performa 588 models) and `Powerbook style' + (used in the Powerbook 150 and 190 models) IDE interface. + + Say Y if you have such an Macintosh model and want to use IDE + devices (hard disks, CD-ROM drives, etc.) that are connected to the + builtin IDE interface. + +config BLK_DEV_Q40IDE + bool "Q40/Q60 IDE interface support" + depends on Q40 + help + Enable the on-board IDE controller in the Q40/Q60. This should + normally be on; disable it only if you are running a custom hard + drive subsystem through an expansion card. + +config BLK_DEV_MPC8xx_IDE + bool "MPC8xx IDE support" + depends on 8xx + help + This option provides support for IDE on Motorola MPC8xx Systems. + Please see 'Type of MPC8xx IDE interface' for details. + + If unsure, say N. + +choice + prompt "Type of MPC8xx IDE interface" + depends on BLK_DEV_MPC8xx_IDE + default IDE_8xx_PCCARD + +config IDE_8xx_PCCARD + bool "8xx_PCCARD" + ---help--- + Select how the IDE devices are connected to the MPC8xx system: + + 8xx_PCCARD uses the 8xx internal PCMCIA interface in combination + with a PC Card (e.g. ARGOSY portable Hard Disk Adapter), + ATA PC Card HDDs or ATA PC Flash Cards (example: TQM8xxL + systems) + + 8xx_DIRECT is used for directly connected IDE devices using the 8xx + internal PCMCIA interface (example: IVMS8 systems) + + EXT_DIRECT is used for IDE devices directly connected to the 8xx + bus using some glue logic, but _not_ the 8xx internal + PCMCIA interface (example: IDIF860 systems) + +config IDE_8xx_DIRECT + bool "8xx_DIRECT" + +config IDE_EXT_DIRECT + bool "EXT_DIRECT" + +endchoice + +# no isa -> no vlb +config IDE_CHIPSETS + bool "Other IDE chipset support" + depends on ISA + ---help--- + Say Y here if you want to include enhanced support for various IDE + interface chipsets used on motherboards and add-on cards. You can + then pick your particular IDE chip from among the following options. + This enhanced support may be necessary for Linux to be able to + access the 3rd/4th drives in some systems. It may also enable + setting of higher speed I/O rates to improve system performance with + these chipsets. Most of these also require special kernel boot + parameters to actually turn on the support at runtime; you can find + a list of these in the file <file:Documentation/ide.txt>. + + People with SCSI-only systems can say N here. + +if IDE_CHIPSETS + +comment "Note: most of these also require special kernel boot parameters" + +config BLK_DEV_4DRIVES + bool "Generic 4 drives/port support" + help + Certain older chipsets, including the Tekram 690CD, use a single set + of I/O ports at 0x1f0 to control up to four drives, instead of the + customary two drives per port. Support for this can be enabled at + runtime using the "ide0=four" kernel boot parameter if you say Y + here. + +config BLK_DEV_ALI14XX + tristate "ALI M14xx support" + help + This driver is enabled at runtime using the "ide0=ali14xx" kernel + boot parameter. It enables support for the secondary IDE interface + of the ALI M1439/1443/1445/1487/1489 chipsets, and permits faster + I/O speeds to be set as well. See the files + <file:Documentation/ide.txt> and <file:drivers/ide/legacy/ali14xx.c> for + more info. + +config BLK_DEV_DTC2278 + tristate "DTC-2278 support" + help + This driver is enabled at runtime using the "ide0=dtc2278" kernel + boot parameter. It enables support for the secondary IDE interface + of the DTC-2278 card, and permits faster I/O speeds to be set as + well. See the <file:Documentation/ide.txt> and + <file:drivers/ide/legacy/dtc2278.c> files for more info. + +config BLK_DEV_HT6560B + tristate "Holtek HT6560B support" + help + This driver is enabled at runtime using the "ide0=ht6560b" kernel + boot parameter. It enables support for the secondary IDE interface + of the Holtek card, and permits faster I/O speeds to be set as well. + See the <file:Documentation/ide.txt> and + <file:drivers/ide/legacy/ht6560b.c> files for more info. + +config BLK_DEV_QD65XX + tristate "QDI QD65xx support" + help + This driver is enabled at runtime using the "ide0=qd65xx" kernel + boot parameter. It permits faster I/O speeds to be set. See the + <file:Documentation/ide.txt> and <file:drivers/ide/legacy/qd65xx.c> for + more info. + +config BLK_DEV_UMC8672 + tristate "UMC-8672 support" + help + This driver is enabled at runtime using the "ide0=umc8672" kernel + boot parameter. It enables support for the secondary IDE interface + of the UMC-8672, and permits faster I/O speeds to be set as well. + See the files <file:Documentation/ide.txt> and + <file:drivers/ide/legacy/umc8672.c> for more info. + +endif + +config BLK_DEV_IDEDMA + def_bool BLK_DEV_IDEDMA_PCI || BLK_DEV_IDEDMA_PMAC || BLK_DEV_IDEDMA_ICS + +config IDEDMA_IVB + bool "IGNORE word93 Validation BITS" + depends on BLK_DEV_IDEDMA_PCI || BLK_DEV_IDEDMA_PMAC || BLK_DEV_IDEDMA_ICS + ---help--- + There are unclear terms in ATA-4 and ATA-5 standards how certain + hardware (an 80c ribbon) should be detected. Different interpretations + of the standards have been released in hardware. This causes problems: + for example, a host with Ultra Mode 4 (or higher) will not run + in that mode with an 80c ribbon. + + If you are experiencing compatibility or performance problems, you + MAY try to answering Y here. However, it does not necessarily solve + any of your problems, it could even cause more of them. + + It is normally safe to answer Y; however, the default is N. + +config IDEDMA_AUTO + def_bool IDEDMA_PCI_AUTO || IDEDMA_ICS_AUTO + +endif + +config BLK_DEV_HD_ONLY + bool "Old hard disk (MFM/RLL/IDE) driver" + depends on BLK_DEV_IDE=n + help + There are two drivers for MFM/RLL/IDE hard disks. Most people use + the newer enhanced driver, but this old one is still around for two + reasons. Some older systems have strange timing problems and seem to + work only with the old driver (which itself does not work with some + newer systems). The other reason is that the old driver is smaller, + since it lacks the enhanced functionality of the new one. This makes + it a good choice for systems with very tight memory restrictions, or + for systems with only older MFM/RLL/ESDI drives. Choosing the old + driver can save 13 KB or so of kernel memory. + + If you are unsure, then just choose the Enhanced IDE/MFM/RLL driver + instead of this one. For more detailed information, read the + Disk-HOWTO, available from + <http://www.tldp.org/docs.html#howto>. + +config BLK_DEV_HD + def_bool BLK_DEV_HD_IDE || BLK_DEV_HD_ONLY + +endif + +endmenu diff --git a/drivers/ide/Makefile b/drivers/ide/Makefile new file mode 100644 index 0000000..5be8ad6d --- /dev/null +++ b/drivers/ide/Makefile @@ -0,0 +1,54 @@ +# +# Makefile for the kernel ata, atapi, and ide block device drivers. +# +# 12 September 2000, Bartlomiej Zolnierkiewicz <bkz@linux-ide.org> +# Rewritten to use lists instead of if-statements. +# +# Note : at this point, these files are compiled on all systems. +# In the future, some of these should be built conditionally. +# +# First come modules that register themselves with the core + +EXTRA_CFLAGS += -Idrivers/ide + +obj-$(CONFIG_BLK_DEV_IDE) += pci/ + +ide-core-y += ide.o ide-io.o ide-iops.o ide-lib.o ide-probe.o ide-taskfile.o + +ide-core-$(CONFIG_BLK_DEV_CMD640) += pci/cmd640.o + +# Core IDE code - must come before legacy +ide-core-$(CONFIG_BLK_DEV_IDEPCI) += setup-pci.o +ide-core-$(CONFIG_BLK_DEV_IDEDMA) += ide-dma.o +ide-core-$(CONFIG_BLK_DEV_IDE_TCQ) += ide-tcq.o +ide-core-$(CONFIG_PROC_FS) += ide-proc.o +ide-core-$(CONFIG_BLK_DEV_IDEPNP) += ide-pnp.o + +# built-in only drivers from arm/ +ide-core-$(CONFIG_IDE_ARM) += arm/ide_arm.o + +# built-in only drivers from legacy/ +ide-core-$(CONFIG_BLK_DEV_BUDDHA) += legacy/buddha.o +ide-core-$(CONFIG_BLK_DEV_FALCON_IDE) += legacy/falconide.o +ide-core-$(CONFIG_BLK_DEV_GAYLE) += legacy/gayle.o +ide-core-$(CONFIG_BLK_DEV_MAC_IDE) += legacy/macide.o +ide-core-$(CONFIG_BLK_DEV_Q40IDE) += legacy/q40ide.o + +# built-in only drivers from ppc/ +ide-core-$(CONFIG_BLK_DEV_MPC8xx_IDE) += ppc/mpc8xx.o +ide-core-$(CONFIG_BLK_DEV_IDE_PMAC) += ppc/pmac.o + +# built-in only drivers from h8300/ +ide-core-$(CONFIG_H8300) += h8300/ide-h8300.o + +obj-$(CONFIG_BLK_DEV_IDE) += ide-core.o +obj-$(CONFIG_IDE_GENERIC) += ide-generic.o + +obj-$(CONFIG_BLK_DEV_IDEDISK) += ide-disk.o +obj-$(CONFIG_BLK_DEV_IDECD) += ide-cd.o +obj-$(CONFIG_BLK_DEV_IDETAPE) += ide-tape.o +obj-$(CONFIG_BLK_DEV_IDEFLOPPY) += ide-floppy.o + +obj-$(CONFIG_BLK_DEV_IDE) += legacy/ arm/ +obj-$(CONFIG_BLK_DEV_HD) += legacy/ +obj-$(CONFIG_ETRAX_IDE) += cris/ diff --git a/drivers/ide/arm/Makefile b/drivers/ide/arm/Makefile new file mode 100644 index 0000000..6a78f07 --- /dev/null +++ b/drivers/ide/arm/Makefile @@ -0,0 +1,6 @@ + +obj-$(CONFIG_BLK_DEV_IDE_ICSIDE) += icside.o +obj-$(CONFIG_BLK_DEV_IDE_RAPIDE) += rapide.o +obj-$(CONFIG_BLK_DEV_IDE_BAST) += bast-ide.o + +EXTRA_CFLAGS := -Idrivers/ide diff --git a/drivers/ide/arm/bast-ide.c b/drivers/ide/arm/bast-ide.c new file mode 100644 index 0000000..9d474e5 --- /dev/null +++ b/drivers/ide/arm/bast-ide.c @@ -0,0 +1,71 @@ +/* linux/drivers/ide/arm/bast-ide.c + * + * Copyright (c) 2003-2004 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * +*/ + +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/ide.h> +#include <linux/init.h> + +#include <asm/mach-types.h> + +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/arch/map.h> +#include <asm/arch/bast-map.h> +#include <asm/arch/bast-irq.h> + +/* list of registered interfaces */ +static ide_hwif_t *ifs[2]; + +static int __init +bastide_register(unsigned int base, unsigned int aux, int irq, + ide_hwif_t **hwif) +{ + hw_regs_t hw; + int i; + + memset(&hw, 0, sizeof(hw)); + + base += BAST_IDE_CS; + aux += BAST_IDE_CS; + + for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) { + hw.io_ports[i] = (unsigned long)base; + base += 0x20; + } + + hw.io_ports[IDE_CONTROL_OFFSET] = aux + (6 * 0x20); + hw.irq = irq; + + ide_register_hw(&hw, hwif); + + return 0; +} + +static int __init bastide_init(void) +{ + /* we can treat the VR1000 and the BAST the same */ + + if (!(machine_is_bast() || machine_is_vr1000())) + return 0; + + printk("BAST: IDE driver, (c) 2003-2004 Simtec Electronics\n"); + + bastide_register(BAST_VA_IDEPRI, BAST_VA_IDEPRIAUX, IRQ_IDE0, &ifs[0]); + bastide_register(BAST_VA_IDESEC, BAST_VA_IDESECAUX, IRQ_IDE1, &ifs[1]); + return 0; +} + +module_init(bastide_init); + +MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Simtec BAST / Thorcom VR1000 IDE driver"); diff --git a/drivers/ide/arm/icside.c b/drivers/ide/arm/icside.c new file mode 100644 index 0000000..308897e --- /dev/null +++ b/drivers/ide/arm/icside.c @@ -0,0 +1,870 @@ +/* + * linux/drivers/ide/arm/icside.c + * + * Copyright (c) 1996-2004 Russell King. + * + * Please note that this platform does not support 32-bit IDE IO. + */ + +#include <linux/config.h> +#include <linux/string.h> +#include <linux/module.h> +#include <linux/ioport.h> +#include <linux/slab.h> +#include <linux/blkdev.h> +#include <linux/errno.h> +#include <linux/hdreg.h> +#include <linux/ide.h> +#include <linux/dma-mapping.h> +#include <linux/device.h> +#include <linux/init.h> +#include <linux/scatterlist.h> + +#include <asm/dma.h> +#include <asm/ecard.h> +#include <asm/io.h> + +#define ICS_IDENT_OFFSET 0x2280 + +#define ICS_ARCIN_V5_INTRSTAT 0x0000 +#define ICS_ARCIN_V5_INTROFFSET 0x0004 +#define ICS_ARCIN_V5_IDEOFFSET 0x2800 +#define ICS_ARCIN_V5_IDEALTOFFSET 0x2b80 +#define ICS_ARCIN_V5_IDESTEPPING 6 + +#define ICS_ARCIN_V6_IDEOFFSET_1 0x2000 +#define ICS_ARCIN_V6_INTROFFSET_1 0x2200 +#define ICS_ARCIN_V6_INTRSTAT_1 0x2290 +#define ICS_ARCIN_V6_IDEALTOFFSET_1 0x2380 +#define ICS_ARCIN_V6_IDEOFFSET_2 0x3000 +#define ICS_ARCIN_V6_INTROFFSET_2 0x3200 +#define ICS_ARCIN_V6_INTRSTAT_2 0x3290 +#define ICS_ARCIN_V6_IDEALTOFFSET_2 0x3380 +#define ICS_ARCIN_V6_IDESTEPPING 6 + +struct cardinfo { + unsigned int dataoffset; + unsigned int ctrloffset; + unsigned int stepping; +}; + +static struct cardinfo icside_cardinfo_v5 = { + .dataoffset = ICS_ARCIN_V5_IDEOFFSET, + .ctrloffset = ICS_ARCIN_V5_IDEALTOFFSET, + .stepping = ICS_ARCIN_V5_IDESTEPPING, +}; + +static struct cardinfo icside_cardinfo_v6_1 = { + .dataoffset = ICS_ARCIN_V6_IDEOFFSET_1, + .ctrloffset = ICS_ARCIN_V6_IDEALTOFFSET_1, + .stepping = ICS_ARCIN_V6_IDESTEPPING, +}; + +static struct cardinfo icside_cardinfo_v6_2 = { + .dataoffset = ICS_ARCIN_V6_IDEOFFSET_2, + .ctrloffset = ICS_ARCIN_V6_IDEALTOFFSET_2, + .stepping = ICS_ARCIN_V6_IDESTEPPING, +}; + +struct icside_state { + unsigned int channel; + unsigned int enabled; + void __iomem *irq_port; + void __iomem *ioc_base; + unsigned int type; + /* parent device... until the IDE core gets one of its own */ + struct device *dev; + ide_hwif_t *hwif[2]; +}; + +#define ICS_TYPE_A3IN 0 +#define ICS_TYPE_A3USER 1 +#define ICS_TYPE_V6 3 +#define ICS_TYPE_V5 15 +#define ICS_TYPE_NOTYPE ((unsigned int)-1) + +/* ---------------- 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) +{ + struct icside_state *state = ec->irq_data; + + writeb(0, state->irq_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) +{ + struct icside_state *state = ec->irq_data; + + readb(state->irq_port + ICS_ARCIN_V5_INTROFFSET); +} + +static const expansioncard_ops_t icside_ops_arcin_v5 = { + .irqenable = icside_irqenable_arcin_v5, + .irqdisable = icside_irqdisable_arcin_v5, +}; + + +/* ---------------- 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) +{ + struct icside_state *state = ec->irq_data; + void __iomem *base = state->irq_port; + + state->enabled = 1; + + switch (state->channel) { + case 0: + writeb(0, base + ICS_ARCIN_V6_INTROFFSET_1); + readb(base + ICS_ARCIN_V6_INTROFFSET_2); + break; + case 1: + writeb(0, base + ICS_ARCIN_V6_INTROFFSET_2); + readb(base + ICS_ARCIN_V6_INTROFFSET_1); + break; + } +} + +/* 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) +{ + struct icside_state *state = ec->irq_data; + + state->enabled = 0; + + readb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_1); + readb(state->irq_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) +{ + struct icside_state *state = ec->irq_data; + + return readb(state->irq_port + ICS_ARCIN_V6_INTRSTAT_1) & 1 || + readb(state->irq_port + ICS_ARCIN_V6_INTRSTAT_2) & 1; +} + +static const expansioncard_ops_t icside_ops_arcin_v6 = { + .irqenable = icside_irqenable_arcin_v6, + .irqdisable = icside_irqdisable_arcin_v6, + .irqpending = icside_irqpending_arcin_v6, +}; + +/* + * Handle routing of interrupts. This is called before + * we write the command to the drive. + */ +static void icside_maskproc(ide_drive_t *drive, int mask) +{ + ide_hwif_t *hwif = HWIF(drive); + struct icside_state *state = hwif->hwif_data; + unsigned long flags; + + local_irq_save(flags); + + state->channel = hwif->channel; + + if (state->enabled && !mask) { + switch (hwif->channel) { + case 0: + writeb(0, state->irq_port + ICS_ARCIN_V6_INTROFFSET_1); + readb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_2); + break; + case 1: + writeb(0, state->irq_port + ICS_ARCIN_V6_INTROFFSET_2); + readb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_1); + break; + } + } else { + readb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_2); + readb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_1); + } + + local_irq_restore(flags); +} + +#ifdef CONFIG_BLK_DEV_IDEDMA_ICS + +#ifndef CONFIG_IDEDMA_ICS_AUTO +#warning CONFIG_IDEDMA_ICS_AUTO=n support is obsolete, and will be removed soon. +#endif + +/* + * 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. + */ + +static void icside_build_sglist(ide_drive_t *drive, struct request *rq) +{ + ide_hwif_t *hwif = drive->hwif; + struct icside_state *state = hwif->hwif_data; + struct scatterlist *sg = hwif->sg_table; + + ide_map_sg(drive, rq); + + if (rq_data_dir(rq) == READ) + hwif->sg_dma_direction = DMA_FROM_DEVICE; + else + hwif->sg_dma_direction = DMA_TO_DEVICE; + + hwif->sg_nents = dma_map_sg(state->dev, sg, hwif->sg_nents, + hwif->sg_dma_direction); +} + +/* + * Configure the IOMD to give the appropriate timings for the transfer + * mode being requested. We take the advice of the ATA standards, and + * calculate the cycle time based on the transfer mode, and the EIDE + * MW DMA specs that the drive provides in the IDENTIFY command. + * + * We have the following IOMD DMA modes to choose from: + * + * Type Active Recovery Cycle + * A 250 (250) 312 (550) 562 (800) + * B 187 250 437 + * C 125 (125) 125 (375) 250 (500) + * D 62 125 187 + * + * (figures in brackets are actual measured timings) + * + * However, we also need to take care of the read/write active and + * recovery timings: + * + * Read Write + * Mode Active -- Recovery -- Cycle IOMD type + * MW0 215 50 215 480 A + * MW1 80 50 50 150 C + * MW2 70 25 25 120 C + */ +static int icside_set_speed(ide_drive_t *drive, u8 xfer_mode) +{ + int on = 0, cycle_time = 0, use_dma_info = 0; + + /* + * Limit the transfer speed to MW_DMA_2. + */ + if (xfer_mode > XFER_MW_DMA_2) + xfer_mode = XFER_MW_DMA_2; + + switch (xfer_mode) { + case XFER_MW_DMA_2: + cycle_time = 250; + use_dma_info = 1; + break; + + case XFER_MW_DMA_1: + cycle_time = 250; + use_dma_info = 1; + break; + + case XFER_MW_DMA_0: + cycle_time = 480; + break; + + case XFER_SW_DMA_2: + case XFER_SW_DMA_1: + case XFER_SW_DMA_0: + cycle_time = 480; + break; + } + + /* + * If we're going to be doing MW_DMA_1 or MW_DMA_2, we should + * take care to note the values in the ID... + */ + if (use_dma_info && drive->id->eide_dma_time > cycle_time) + cycle_time = drive->id->eide_dma_time; + + drive->drive_data = cycle_time; + + if (cycle_time && ide_config_drive_speed(drive, xfer_mode) == 0) + on = 1; + else + drive->drive_data = 480; + + printk("%s: %s selected (peak %dMB/s)\n", drive->name, + ide_xfer_verbose(xfer_mode), 2000 / drive->drive_data); + + drive->current_speed = xfer_mode; + + return on; +} + +static int icside_dma_host_off(ide_drive_t *drive) +{ + return 0; +} + +static int icside_dma_off_quietly(ide_drive_t *drive) +{ + drive->using_dma = 0; + return icside_dma_host_off(drive); +} + +static int icside_dma_host_on(ide_drive_t *drive) +{ + return 0; +} + +static int icside_dma_on(ide_drive_t *drive) +{ + drive->using_dma = 1; + return icside_dma_host_on(drive); +} + +static int icside_dma_check(ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + ide_hwif_t *hwif = HWIF(drive); + int xfer_mode = XFER_PIO_2; + int on; + + if (!(id->capability & 1) || !hwif->autodma) + goto out; + + /* + * Consult the list of known "bad" drives + */ + if (__ide_dma_bad_drive(drive)) + goto out; + + /* + * Enable DMA on any drive that has multiword DMA + */ + if (id->field_valid & 2) { + xfer_mode = ide_dma_speed(drive, 0); + goto out; + } + + /* + * Consult the list of known "good" drives + */ + if (__ide_dma_good_drive(drive)) { + if (id->eide_dma_time > 150) + goto out; + xfer_mode = XFER_MW_DMA_1; + } + +out: + on = icside_set_speed(drive, xfer_mode); + + if (on) + return icside_dma_on(drive); + else + return icside_dma_off_quietly(drive); +} + +static int icside_dma_end(ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + struct icside_state *state = hwif->hwif_data; + + drive->waiting_for_dma = 0; + + disable_dma(hwif->hw.dma); + + /* Teardown mappings after DMA has completed. */ + dma_unmap_sg(state->dev, hwif->sg_table, hwif->sg_nents, + hwif->sg_dma_direction); + + return get_dma_residue(hwif->hw.dma) != 0; +} + +static void icside_dma_start(ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + + /* We can not enable DMA on both channels simultaneously. */ + BUG_ON(dma_channel_active(hwif->hw.dma)); + enable_dma(hwif->hw.dma); +} + +static int icside_dma_setup(ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + struct request *rq = hwif->hwgroup->rq; + unsigned int dma_mode; + + if (rq_data_dir(rq)) + dma_mode = DMA_MODE_WRITE; + else + dma_mode = DMA_MODE_READ; + + /* + * We can not enable DMA on both channels. + */ + BUG_ON(dma_channel_active(hwif->hw.dma)); + + icside_build_sglist(drive, rq); + + /* + * Ensure that we have the right interrupt routed. + */ + icside_maskproc(drive, 0); + + /* + * Route the DMA signals to the correct interface. + */ + writeb(hwif->select_data, hwif->config_data); + + /* + * Select the correct timing for this drive. + */ + set_dma_speed(hwif->hw.dma, drive->drive_data); + + /* + * Tell the DMA engine about the SG table and + * data direction. + */ + set_dma_sg(hwif->hw.dma, hwif->sg_table, hwif->sg_nents); + set_dma_mode(hwif->hw.dma, dma_mode); + + drive->waiting_for_dma = 1; + + return 0; +} + +static void icside_dma_exec_cmd(ide_drive_t *drive, u8 cmd) +{ + /* issue cmd to drive */ + ide_execute_command(drive, cmd, ide_dma_intr, 2 * WAIT_CMD, NULL); +} + +static int icside_dma_test_irq(ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + struct icside_state *state = hwif->hwif_data; + + return readb(state->irq_port + + (hwif->channel ? + ICS_ARCIN_V6_INTRSTAT_2 : + ICS_ARCIN_V6_INTRSTAT_1)) & 1; +} + +static int icside_dma_timeout(ide_drive_t *drive) +{ + printk(KERN_ERR "%s: DMA timeout occurred: ", drive->name); + + if (icside_dma_test_irq(drive)) + return 0; + + ide_dump_status(drive, "DMA timeout", + HWIF(drive)->INB(IDE_STATUS_REG)); + + return icside_dma_end(drive); +} + +static int icside_dma_lostirq(ide_drive_t *drive) +{ + printk(KERN_ERR "%s: IRQ lost\n", drive->name); + return 1; +} + +static void icside_dma_init(ide_hwif_t *hwif) +{ + int autodma = 0; + +#ifdef CONFIG_IDEDMA_ICS_AUTO + autodma = 1; +#endif + + printk(" %s: SG-DMA", hwif->name); + + hwif->atapi_dma = 1; + hwif->mwdma_mask = 7; /* MW0..2 */ + hwif->swdma_mask = 7; /* SW0..2 */ + + hwif->dmatable_cpu = NULL; + hwif->dmatable_dma = 0; + hwif->speedproc = icside_set_speed; + hwif->autodma = autodma; + + hwif->ide_dma_check = icside_dma_check; + hwif->ide_dma_host_off = icside_dma_host_off; + hwif->ide_dma_off_quietly = icside_dma_off_quietly; + hwif->ide_dma_host_on = icside_dma_host_on; + hwif->ide_dma_on = icside_dma_on; + hwif->dma_setup = icside_dma_setup; + hwif->dma_exec_cmd = icside_dma_exec_cmd; + hwif->dma_start = icside_dma_start; + hwif->ide_dma_end = icside_dma_end; + hwif->ide_dma_test_irq = icside_dma_test_irq; + hwif->ide_dma_timeout = icside_dma_timeout; + hwif->ide_dma_lostirq = icside_dma_lostirq; + + hwif->drives[0].autodma = hwif->autodma; + hwif->drives[1].autodma = hwif->autodma; + + printk(" capable%s\n", hwif->autodma ? ", auto-enable" : ""); +} +#else +#define icside_dma_init(hwif) (0) +#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->io_ports[IDE_DATA_OFFSET] == dataport) + goto found; + } + + for (index = 0; index < MAX_HWIFS; ++index) { + hwif = &ide_hwifs[index]; + if (!hwif->io_ports[IDE_DATA_OFFSET]) + goto found; + } + + hwif = NULL; +found: + return hwif; +} + +static ide_hwif_t * +icside_setup(void __iomem *base, struct cardinfo *info, struct expansion_card *ec) +{ + unsigned long port = (unsigned long)base + info->dataoffset; + ide_hwif_t *hwif; + + hwif = icside_find_hwif(port); + if (hwif) { + int i; + + memset(&hwif->hw, 0, sizeof(hw_regs_t)); + + /* + * Ensure we're using MMIO + */ + default_hwif_mmiops(hwif); + hwif->mmio = 2; + + for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) { + hwif->hw.io_ports[i] = port; + hwif->io_ports[i] = port; + port += 1 << info->stepping; + } + hwif->hw.io_ports[IDE_CONTROL_OFFSET] = (unsigned long)base + info->ctrloffset; + hwif->io_ports[IDE_CONTROL_OFFSET] = (unsigned long)base + info->ctrloffset; + hwif->hw.irq = ec->irq; + hwif->irq = ec->irq; + hwif->noprobe = 0; + hwif->chipset = ide_acorn; + hwif->gendev.parent = &ec->dev; + } + + return hwif; +} + +static int __init +icside_register_v5(struct icside_state *state, struct expansion_card *ec) +{ + ide_hwif_t *hwif; + void __iomem *base; + + base = ioremap(ecard_resource_start(ec, ECARD_RES_MEMC), + ecard_resource_len(ec, ECARD_RES_MEMC)); + if (!base) + return -ENOMEM; + + state->irq_port = base; + + ec->irqaddr = base + ICS_ARCIN_V5_INTRSTAT; + ec->irqmask = 1; + ec->irq_data = state; + ec->ops = &icside_ops_arcin_v5; + + /* + * Be on the safe side - disable interrupts + */ + icside_irqdisable_arcin_v5(ec, 0); + + hwif = icside_setup(base, &icside_cardinfo_v5, ec); + if (!hwif) { + iounmap(base); + return -ENODEV; + } + + state->hwif[0] = hwif; + + probe_hwif_init(hwif); + create_proc_ide_interfaces(); + + return 0; +} + +static int __init +icside_register_v6(struct icside_state *state, struct expansion_card *ec) +{ + ide_hwif_t *hwif, *mate; + void __iomem *ioc_base, *easi_base; + unsigned int sel = 0; + int ret; + + ioc_base = ioremap(ecard_resource_start(ec, ECARD_RES_IOCFAST), + ecard_resource_len(ec, ECARD_RES_IOCFAST)); + if (!ioc_base) { + ret = -ENOMEM; + goto out; + } + + easi_base = ioc_base; + + if (ecard_resource_flags(ec, ECARD_RES_EASI)) { + easi_base = ioremap(ecard_resource_start(ec, ECARD_RES_EASI), + ecard_resource_len(ec, ECARD_RES_EASI)); + if (!easi_base) { + ret = -ENOMEM; + goto unmap_slot; + } + + /* + * Enable access to the EASI region. + */ + sel = 1 << 5; + } + + writeb(sel, ioc_base); + + ec->irq_data = state; + ec->ops = &icside_ops_arcin_v6; + + state->irq_port = easi_base; + state->ioc_base = ioc_base; + + /* + * Be on the safe side - disable interrupts + */ + icside_irqdisable_arcin_v6(ec, 0); + + /* + * Find and register the interfaces. + */ + hwif = icside_setup(easi_base, &icside_cardinfo_v6_1, ec); + mate = icside_setup(easi_base, &icside_cardinfo_v6_2, ec); + + if (!hwif || !mate) { + ret = -ENODEV; + goto unmap_port; + } + + state->hwif[0] = hwif; + state->hwif[1] = mate; + + hwif->maskproc = icside_maskproc; + hwif->channel = 0; + hwif->hwif_data = state; + hwif->mate = mate; + hwif->serialized = 1; + hwif->config_data = (unsigned long)ioc_base; + hwif->select_data = sel; + hwif->hw.dma = ec->dma; + + mate->maskproc = icside_maskproc; + mate->channel = 1; + mate->hwif_data = state; + mate->mate = hwif; + mate->serialized = 1; + mate->config_data = (unsigned long)ioc_base; + mate->select_data = sel | 1; + mate->hw.dma = ec->dma; + + if (ec->dma != NO_DMA && !request_dma(ec->dma, hwif->name)) { + icside_dma_init(hwif); + icside_dma_init(mate); + } + + probe_hwif_init(hwif); + probe_hwif_init(mate); + create_proc_ide_interfaces(); + + return 0; + + unmap_port: + if (easi_base != ioc_base) + iounmap(easi_base); + unmap_slot: + iounmap(ioc_base); + out: + return ret; +} + +static int __devinit +icside_probe(struct expansion_card *ec, const struct ecard_id *id) +{ + struct icside_state *state; + void __iomem *idmem; + int ret; + + ret = ecard_request_resources(ec); + if (ret) + goto out; + + state = kmalloc(sizeof(struct icside_state), GFP_KERNEL); + if (!state) { + ret = -ENOMEM; + goto release; + } + + memset(state, 0, sizeof(state)); + state->type = ICS_TYPE_NOTYPE; + state->dev = &ec->dev; + + idmem = ioremap(ecard_resource_start(ec, ECARD_RES_IOCFAST), + ecard_resource_len(ec, ECARD_RES_IOCFAST)); + if (idmem) { + unsigned int type; + + type = readb(idmem + ICS_IDENT_OFFSET) & 1; + type |= (readb(idmem + ICS_IDENT_OFFSET + 4) & 1) << 1; + type |= (readb(idmem + ICS_IDENT_OFFSET + 8) & 1) << 2; + type |= (readb(idmem + ICS_IDENT_OFFSET + 12) & 1) << 3; + iounmap(idmem); + + state->type = type; + } + + switch (state->type) { + case ICS_TYPE_A3IN: + dev_warn(&ec->dev, "A3IN unsupported\n"); + ret = -ENODEV; + break; + + case ICS_TYPE_A3USER: + dev_warn(&ec->dev, "A3USER unsupported\n"); + ret = -ENODEV; + break; + + case ICS_TYPE_V5: + ret = icside_register_v5(state, ec); + break; + + case ICS_TYPE_V6: + ret = icside_register_v6(state, ec); + break; + + default: + dev_warn(&ec->dev, "unknown interface type\n"); + ret = -ENODEV; + break; + } + + if (ret == 0) { + ecard_set_drvdata(ec, state); + goto out; + } + + kfree(state); + release: + ecard_release_resources(ec); + out: + return ret; +} + +static void __devexit icside_remove(struct expansion_card *ec) +{ + struct icside_state *state = ecard_get_drvdata(ec); + + switch (state->type) { + case ICS_TYPE_V5: + /* FIXME: tell IDE to stop using the interface */ + + /* Disable interrupts */ + icside_irqdisable_arcin_v5(ec, 0); + break; + + case ICS_TYPE_V6: + /* FIXME: tell IDE to stop using the interface */ + if (ec->dma != NO_DMA) + free_dma(ec->dma); + + /* Disable interrupts */ + icside_irqdisable_arcin_v6(ec, 0); + + /* Reset the ROM pointer/EASI selection */ + writeb(0, state->ioc_base); + break; + } + + ecard_set_drvdata(ec, NULL); + ec->ops = NULL; + ec->irq_data = NULL; + + if (state->ioc_base) + iounmap(state->ioc_base); + if (state->ioc_base != state->irq_port) + iounmap(state->irq_port); + + kfree(state); + ecard_release_resources(ec); +} + +static void icside_shutdown(struct expansion_card *ec) +{ + struct icside_state *state = ecard_get_drvdata(ec); + unsigned long flags; + + /* + * Disable interrupts from this card. We need to do + * this before disabling EASI since we may be accessing + * this register via that region. + */ + local_irq_save(flags); + ec->ops->irqdisable(ec, 0); + local_irq_restore(flags); + + /* + * Reset the ROM pointer so that we can read the ROM + * after a soft reboot. This also disables access to + * the IDE taskfile via the EASI region. + */ + if (state->ioc_base) + writeb(0, state->ioc_base); +} + +static const struct ecard_id icside_ids[] = { + { MANU_ICS, PROD_ICS_IDE }, + { MANU_ICS2, PROD_ICS2_IDE }, + { 0xffff, 0xffff } +}; + +static struct ecard_driver icside_driver = { + .probe = icside_probe, + .remove = __devexit_p(icside_remove), + .shutdown = icside_shutdown, + .id_table = icside_ids, + .drv = { + .name = "icside", + }, +}; + +static int __init icside_init(void) +{ + return ecard_register_driver(&icside_driver); +} + +MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("ICS IDE driver"); + +module_init(icside_init); diff --git a/drivers/ide/arm/ide_arm.c b/drivers/ide/arm/ide_arm.c new file mode 100644 index 0000000..23488c4 --- /dev/null +++ b/drivers/ide/arm/ide_arm.c @@ -0,0 +1,43 @@ +/* + * ARM/ARM26 default IDE host driver + * + * Copyright (C) 2004 Bartlomiej Zolnierkiewicz + * Based on code by: Russell King, Ian Molton and Alexander Schulz. + * + * May be copied or modified under the terms of the GNU General Public License. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/ide.h> + +#include <asm/mach-types.h> +#include <asm/irq.h> + +#ifdef CONFIG_ARM26 +# define IDE_ARM_HOST (machine_is_a5k()) +#else +# define IDE_ARM_HOST (1) +#endif + +#ifdef CONFIG_ARCH_CLPS7500 +# include <asm/arch/hardware.h> +# +# define IDE_ARM_IO (ISASLOT_IO + 0x1f0) +# define IDE_ARM_IRQ IRQ_ISA_14 +#else +# define IDE_ARM_IO 0x1f0 +# define IDE_ARM_IRQ IRQ_HARDDISK +#endif + +void __init ide_arm_init(void) +{ + if (IDE_ARM_HOST) { + hw_regs_t hw; + + memset(&hw, 0, sizeof(hw)); + ide_std_init_ports(&hw, IDE_ARM_IO, IDE_ARM_IO + 0x206); + hw.irq = IDE_ARM_IRQ; + ide_register_hw(&hw, NULL); + } +} diff --git a/drivers/ide/arm/rapide.c b/drivers/ide/arm/rapide.c new file mode 100644 index 0000000..3058217 --- /dev/null +++ b/drivers/ide/arm/rapide.c @@ -0,0 +1,125 @@ +/* + * linux/drivers/ide/arm/rapide.c + * + * Copyright (c) 1996-2002 Russell King. + */ + +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/blkdev.h> +#include <linux/errno.h> +#include <linux/ide.h> +#include <linux/init.h> + +#include <asm/ecard.h> + +/* + * Something like this really should be in generic code, but isn't. + */ +static ide_hwif_t * +rapide_locate_hwif(void __iomem *base, void __iomem *ctrl, unsigned int sz, int irq) +{ + unsigned long port = (unsigned long)base; + ide_hwif_t *hwif; + int index, i; + + for (index = 0; index < MAX_HWIFS; ++index) { + hwif = ide_hwifs + index; + if (hwif->io_ports[IDE_DATA_OFFSET] == port) + goto found; + } + + for (index = 0; index < MAX_HWIFS; ++index) { + hwif = ide_hwifs + index; + if (hwif->io_ports[IDE_DATA_OFFSET] == 0) + goto found; + } + + return NULL; + + found: + for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) { + hwif->hw.io_ports[i] = port; + hwif->io_ports[i] = port; + port += sz; + } + hwif->hw.io_ports[IDE_CONTROL_OFFSET] = (unsigned long)ctrl; + hwif->io_ports[IDE_CONTROL_OFFSET] = (unsigned long)ctrl; + hwif->hw.irq = hwif->irq = irq; + hwif->mmio = 2; + default_hwif_mmiops(hwif); + + return hwif; +} + +static int __devinit +rapide_probe(struct expansion_card *ec, const struct ecard_id *id) +{ + ide_hwif_t *hwif; + void __iomem *base; + int ret; + + ret = ecard_request_resources(ec); + if (ret) + goto out; + + base = ioremap(ecard_resource_start(ec, ECARD_RES_MEMC), + ecard_resource_len(ec, ECARD_RES_MEMC)); + if (!base) { + ret = -ENOMEM; + goto release; + } + + hwif = rapide_locate_hwif(base, base + 0x818, 1 << 6, ec->irq); + if (hwif) { + hwif->hwif_data = base; + hwif->gendev.parent = &ec->dev; + hwif->noprobe = 0; + probe_hwif_init(hwif); + create_proc_ide_interfaces(); + ecard_set_drvdata(ec, hwif); + goto out; + } + + iounmap(base); + release: + ecard_release_resources(ec); + out: + return ret; +} + +static void __devexit rapide_remove(struct expansion_card *ec) +{ + ide_hwif_t *hwif = ecard_get_drvdata(ec); + + ecard_set_drvdata(ec, NULL); + + /* there must be a better way */ + ide_unregister(hwif - ide_hwifs); + iounmap(hwif->hwif_data); + ecard_release_resources(ec); +} + +static struct ecard_id rapide_ids[] = { + { MANU_YELLOWSTONE, PROD_YELLOWSTONE_RAPIDE32 }, + { 0xffff, 0xffff } +}; + +static struct ecard_driver rapide_driver = { + .probe = rapide_probe, + .remove = __devexit_p(rapide_remove), + .id_table = rapide_ids, + .drv = { + .name = "rapide", + }, +}; + +static int __init rapide_init(void) +{ + return ecard_register_driver(&rapide_driver); +} + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Yellowstone RAPIDE driver"); + +module_init(rapide_init); diff --git a/drivers/ide/cris/Makefile b/drivers/ide/cris/Makefile new file mode 100644 index 0000000..fdc2943 --- /dev/null +++ b/drivers/ide/cris/Makefile @@ -0,0 +1,3 @@ +EXTRA_CFLAGS += -Idrivers/ide + +obj-$(CONFIG_ETRAX_ARCH_V10) += ide-v10.o diff --git a/drivers/ide/cris/ide-v10.c b/drivers/ide/cris/ide-v10.c new file mode 100644 index 0000000..5b40220 --- /dev/null +++ b/drivers/ide/cris/ide-v10.c @@ -0,0 +1,842 @@ +/* $Id: ide.c,v 1.4 2004/10/12 07:55:48 starvik Exp $ + * + * Etrax specific IDE functions, like init and PIO-mode setting etc. + * Almost the entire ide.c is used for the rest of the Etrax ATA driver. + * Copyright (c) 2000-2004 Axis Communications AB + * + * Authors: Bjorn Wesen (initial version) + * Mikael Starvik (pio setup stuff, Linux 2.6 port) + */ + +/* Regarding DMA: + * + * There are two forms of DMA - "DMA handshaking" between the interface and the drive, + * and DMA between the memory and the interface. We can ALWAYS use the latter, since it's + * something built-in in the Etrax. However only some drives support the DMA-mode handshaking + * on the ATA-bus. The normal PC driver and Triton interface disables memory-if DMA when the + * device can't do DMA handshaking for some stupid reason. We don't need to do that. + */ + +#undef REALLY_SLOW_IO /* most systems can safely undef this */ + +#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/delay.h> +#include <linux/blkdev.h> +#include <linux/hdreg.h> +#include <linux/ide.h> +#include <linux/init.h> +#include <linux/scatterlist.h> + +#include <asm/io.h> +#include <asm/arch/svinto.h> +#include <asm/dma.h> + +/* number of Etrax DMA descriptors */ +#define MAX_DMA_DESCRS 64 + +/* number of times to retry busy-flags when reading/writing IDE-registers + * this can't be too high because a hung harddisk might cause the watchdog + * to trigger (sometimes INB and OUTB are called with irq's disabled) + */ + +#define IDE_REGISTER_TIMEOUT 300 + +static int e100_read_command = 0; + +#define LOWDB(x) +#define D(x) + +static int e100_ide_build_dmatable (ide_drive_t *drive); +static ide_startstop_t etrax_dma_intr (ide_drive_t *drive); + +void +etrax100_ide_outw(unsigned short data, unsigned long reg) { + int timeleft; + LOWDB(printk("ow: data 0x%x, reg 0x%x\n", data, reg)); + + /* note the lack of handling any timeouts. we stop waiting, but we don't + * really notify anybody. + */ + + timeleft = IDE_REGISTER_TIMEOUT; + /* wait for busy flag */ + while(timeleft && (*R_ATA_STATUS_DATA & IO_MASK(R_ATA_STATUS_DATA, busy))) + timeleft--; + + /* + * Fall through at a timeout, so the ongoing command will be + * aborted by the write below, which is expected to be a dummy + * command to the command register. This happens when a faulty + * drive times out on a command. See comment on timeout in + * INB. + */ + if(!timeleft) + printk("ATA timeout reg 0x%lx := 0x%x\n", reg, data); + + *R_ATA_CTRL_DATA = reg | data; /* write data to the drive's register */ + + timeleft = IDE_REGISTER_TIMEOUT; + /* wait for transmitter ready */ + while(timeleft && !(*R_ATA_STATUS_DATA & + IO_MASK(R_ATA_STATUS_DATA, tr_rdy))) + timeleft--; +} + +void +etrax100_ide_outb(unsigned char data, unsigned long reg) +{ + etrax100_ide_outw(data, reg); +} + +void +etrax100_ide_outbsync(ide_drive_t *drive, u8 addr, unsigned long port) +{ + etrax100_ide_outw(addr, port); +} + +unsigned short +etrax100_ide_inw(unsigned long reg) { + int status; + int timeleft; + + timeleft = IDE_REGISTER_TIMEOUT; + /* wait for busy flag */ + while(timeleft && (*R_ATA_STATUS_DATA & IO_MASK(R_ATA_STATUS_DATA, busy))) + timeleft--; + + if(!timeleft) { + /* + * If we're asked to read the status register, like for + * example when a command does not complete for an + * extended time, but the ATA interface is stuck in a + * busy state at the *ETRAX* ATA interface level (as has + * happened repeatedly with at least one bad disk), then + * the best thing to do is to pretend that we read + * "busy" in the status register, so the IDE driver will + * time-out, abort the ongoing command and perform a + * reset sequence. Note that the subsequent OUT_BYTE + * call will also timeout on busy, but as long as the + * write is still performed, everything will be fine. + */ + if ((reg & IO_MASK (R_ATA_CTRL_DATA, addr)) + == IO_FIELD (R_ATA_CTRL_DATA, addr, IDE_STATUS_OFFSET)) + return BUSY_STAT; + else + /* For other rare cases we assume 0 is good enough. */ + return 0; + } + + *R_ATA_CTRL_DATA = reg | IO_STATE(R_ATA_CTRL_DATA, rw, read); /* read data */ + + timeleft = IDE_REGISTER_TIMEOUT; + /* wait for available */ + while(timeleft && !((status = *R_ATA_STATUS_DATA) & + IO_MASK(R_ATA_STATUS_DATA, dav))) + timeleft--; + + if(!timeleft) + return 0; + + LOWDB(printk("inb: 0x%x from reg 0x%x\n", status & 0xff, reg)); + + return (unsigned short)status; +} + +unsigned char +etrax100_ide_inb(unsigned long reg) +{ + return (unsigned char)etrax100_ide_inw(reg); +} + +/* PIO timing (in R_ATA_CONFIG) + * + * _____________________________ + * ADDRESS : ________/ + * + * _______________ + * DIOR : ____________/ \__________ + * + * _______________ + * DATA : XXXXXXXXXXXXXXXX_______________XXXXXXXX + * + * + * DIOR is unbuffered while address and data is buffered. + * This creates two problems: + * 1. The DIOR pulse is to early (because it is unbuffered) + * 2. The rise time of DIOR is long + * + * There are at least three different plausible solutions + * 1. Use a pad capable of larger currents in Etrax + * 2. Use an external buffer + * 3. Make the strobe pulse longer + * + * Some of the strobe timings below are modified to compensate + * for this. This implies a slight performance decrease. + * + * THIS SHOULD NEVER BE CHANGED! + * + * TODO: Is this true for the latest LX boards still ? + */ + +#define ATA_DMA2_STROBE 4 +#define ATA_DMA2_HOLD 0 +#define ATA_DMA1_STROBE 4 +#define ATA_DMA1_HOLD 1 +#define ATA_DMA0_STROBE 12 +#define ATA_DMA0_HOLD 9 +#define ATA_PIO4_SETUP 1 +#define ATA_PIO4_STROBE 5 +#define ATA_PIO4_HOLD 0 +#define ATA_PIO3_SETUP 1 +#define ATA_PIO3_STROBE 5 +#define ATA_PIO3_HOLD 1 +#define ATA_PIO2_SETUP 1 +#define ATA_PIO2_STROBE 6 +#define ATA_PIO2_HOLD 2 +#define ATA_PIO1_SETUP 2 +#define ATA_PIO1_STROBE 11 +#define ATA_PIO1_HOLD 4 +#define ATA_PIO0_SETUP 4 +#define ATA_PIO0_STROBE 19 +#define ATA_PIO0_HOLD 4 + +static int e100_dma_check (ide_drive_t *drive); +static void e100_dma_start(ide_drive_t *drive); +static int e100_dma_end (ide_drive_t *drive); +static void e100_ide_input_data (ide_drive_t *drive, void *, unsigned int); +static void e100_ide_output_data (ide_drive_t *drive, void *, unsigned int); +static void e100_atapi_input_bytes(ide_drive_t *drive, void *, unsigned int); +static void e100_atapi_output_bytes(ide_drive_t *drive, void *, unsigned int); +static int e100_dma_off (ide_drive_t *drive); + + +/* + * good_dma_drives() lists the model names (from "hdparm -i") + * of drives which do not support mword2 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", + NULL}; + +static void tune_e100_ide(ide_drive_t *drive, byte pio) +{ + pio = 4; + /* pio = ide_get_best_pio_mode(drive, pio, 4, NULL); */ + + /* set pio mode! */ + + switch(pio) { + case 0: + *R_ATA_CONFIG = ( IO_FIELD( R_ATA_CONFIG, enable, 1 ) | + IO_FIELD( R_ATA_CONFIG, dma_strobe, ATA_DMA2_STROBE ) | + IO_FIELD( R_ATA_CONFIG, dma_hold, ATA_DMA2_HOLD ) | + IO_FIELD( R_ATA_CONFIG, pio_setup, ATA_PIO0_SETUP ) | + IO_FIELD( R_ATA_CONFIG, pio_strobe, ATA_PIO0_STROBE ) | + IO_FIELD( R_ATA_CONFIG, pio_hold, ATA_PIO0_HOLD ) ); + break; + case 1: + *R_ATA_CONFIG = ( IO_FIELD( R_ATA_CONFIG, enable, 1 ) | + IO_FIELD( R_ATA_CONFIG, dma_strobe, ATA_DMA2_STROBE ) | + IO_FIELD( R_ATA_CONFIG, dma_hold, ATA_DMA2_HOLD ) | + IO_FIELD( R_ATA_CONFIG, pio_setup, ATA_PIO1_SETUP ) | + IO_FIELD( R_ATA_CONFIG, pio_strobe, ATA_PIO1_STROBE ) | + IO_FIELD( R_ATA_CONFIG, pio_hold, ATA_PIO1_HOLD ) ); + break; + case 2: + *R_ATA_CONFIG = ( IO_FIELD( R_ATA_CONFIG, enable, 1 ) | + IO_FIELD( R_ATA_CONFIG, dma_strobe, ATA_DMA2_STROBE ) | + IO_FIELD( R_ATA_CONFIG, dma_hold, ATA_DMA2_HOLD ) | + IO_FIELD( R_ATA_CONFIG, pio_setup, ATA_PIO2_SETUP ) | + IO_FIELD( R_ATA_CONFIG, pio_strobe, ATA_PIO2_STROBE ) | + IO_FIELD( R_ATA_CONFIG, pio_hold, ATA_PIO2_HOLD ) ); + break; + case 3: + *R_ATA_CONFIG = ( IO_FIELD( R_ATA_CONFIG, enable, 1 ) | + IO_FIELD( R_ATA_CONFIG, dma_strobe, ATA_DMA2_STROBE ) | + IO_FIELD( R_ATA_CONFIG, dma_hold, ATA_DMA2_HOLD ) | + IO_FIELD( R_ATA_CONFIG, pio_setup, ATA_PIO3_SETUP ) | + IO_FIELD( R_ATA_CONFIG, pio_strobe, ATA_PIO3_STROBE ) | + IO_FIELD( R_ATA_CONFIG, pio_hold, ATA_PIO3_HOLD ) ); + break; + case 4: + *R_ATA_CONFIG = ( IO_FIELD( R_ATA_CONFIG, enable, 1 ) | + IO_FIELD( R_ATA_CONFIG, dma_strobe, ATA_DMA2_STROBE ) | + IO_FIELD( R_ATA_CONFIG, dma_hold, ATA_DMA2_HOLD ) | + IO_FIELD( R_ATA_CONFIG, pio_setup, ATA_PIO4_SETUP ) | + IO_FIELD( R_ATA_CONFIG, pio_strobe, ATA_PIO4_STROBE ) | + IO_FIELD( R_ATA_CONFIG, pio_hold, ATA_PIO4_HOLD ) ); + break; + } +} + +static int e100_dma_setup(ide_drive_t *drive) +{ + struct request *rq = drive->hwif->hwgroup->rq; + + if (rq_data_dir(rq)) { + e100_read_command = 0; + + RESET_DMA(ATA_TX_DMA_NBR); /* sometimes the DMA channel get stuck so we need to do this */ + WAIT_DMA(ATA_TX_DMA_NBR); + } else { + e100_read_command = 1; + + RESET_DMA(ATA_RX_DMA_NBR); /* sometimes the DMA channel get stuck so we need to do this */ + WAIT_DMA(ATA_RX_DMA_NBR); + } + + /* set up the Etrax DMA descriptors */ + if (e100_ide_build_dmatable(drive)) { + ide_map_sg(drive, rq); + return 1; + } + + return 0; +} + +static void e100_dma_exec_cmd(ide_drive_t *drive, u8 command) +{ + /* set the irq handler which will finish the request when DMA is done */ + ide_set_handler(drive, &etrax_dma_intr, WAIT_CMD, NULL); + + /* issue cmd to drive */ + etrax100_ide_outb(command, IDE_COMMAND_REG); +} + +void __init +init_e100_ide (void) +{ + volatile unsigned int dummy; + int h; + + printk("ide: ETRAX 100LX built-in ATA DMA controller\n"); + + /* first fill in some stuff in the ide_hwifs fields */ + + for(h = 0; h < MAX_HWIFS; h++) { + ide_hwif_t *hwif = &ide_hwifs[h]; + hwif->mmio = 2; + hwif->chipset = ide_etrax100; + hwif->tuneproc = &tune_e100_ide; + hwif->ata_input_data = &e100_ide_input_data; + hwif->ata_output_data = &e100_ide_output_data; + hwif->atapi_input_bytes = &e100_atapi_input_bytes; + hwif->atapi_output_bytes = &e100_atapi_output_bytes; + hwif->ide_dma_check = &e100_dma_check; + hwif->ide_dma_end = &e100_dma_end; + hwif->dma_setup = &e100_dma_setup; + hwif->dma_exec_cmd = &e100_dma_exec_cmd; + hwif->dma_start = &e100_dma_start; + hwif->OUTB = &etrax100_ide_outb; + hwif->OUTW = &etrax100_ide_outw; + hwif->OUTBSYNC = &etrax100_ide_outbsync; + hwif->INB = &etrax100_ide_inb; + hwif->INW = &etrax100_ide_inw; + hwif->ide_dma_off_quietly = &e100_dma_off; + } + + /* actually reset and configure the etrax100 ide/ata interface */ + + *R_ATA_CTRL_DATA = 0; + *R_ATA_TRANSFER_CNT = 0; + *R_ATA_CONFIG = 0; + + genconfig_shadow = (genconfig_shadow & + ~IO_MASK(R_GEN_CONFIG, dma2) & + ~IO_MASK(R_GEN_CONFIG, dma3) & + ~IO_MASK(R_GEN_CONFIG, ata)) | + ( IO_STATE( R_GEN_CONFIG, dma3, ata ) | + IO_STATE( R_GEN_CONFIG, dma2, ata ) | + IO_STATE( R_GEN_CONFIG, ata, select ) ); + + *R_GEN_CONFIG = genconfig_shadow; + + /* pull the chosen /reset-line low */ + +#ifdef CONFIG_ETRAX_IDE_G27_RESET + REG_SHADOW_SET(R_PORT_G_DATA, port_g_data_shadow, 27, 0); +#endif +#ifdef CONFIG_ETRAX_IDE_CSE1_16_RESET + REG_SHADOW_SET(port_cse1_addr, port_cse1_shadow, 16, 0); +#endif +#ifdef CONFIG_ETRAX_IDE_CSP0_8_RESET + REG_SHADOW_SET(port_csp0_addr, port_csp0_shadow, 8, 0); +#endif +#ifdef CONFIG_ETRAX_IDE_PB7_RESET + port_pb_dir_shadow = port_pb_dir_shadow | + IO_STATE(R_PORT_PB_DIR, dir7, output); + *R_PORT_PB_DIR = port_pb_dir_shadow; + REG_SHADOW_SET(R_PORT_PB_DATA, port_pb_data_shadow, 7, 1); +#endif + + /* wait some */ + + udelay(25); + + /* de-assert bus-reset */ + +#ifdef CONFIG_ETRAX_IDE_CSE1_16_RESET + REG_SHADOW_SET(port_cse1_addr, port_cse1_shadow, 16, 1); +#endif +#ifdef CONFIG_ETRAX_IDE_CSP0_8_RESET + REG_SHADOW_SET(port_csp0_addr, port_csp0_shadow, 8, 1); +#endif +#ifdef CONFIG_ETRAX_IDE_G27_RESET + REG_SHADOW_SET(R_PORT_G_DATA, port_g_data_shadow, 27, 1); +#endif + + /* make a dummy read to set the ata controller in a proper state */ + dummy = *R_ATA_STATUS_DATA; + + *R_ATA_CONFIG = ( IO_FIELD( R_ATA_CONFIG, enable, 1 ) | + IO_FIELD( R_ATA_CONFIG, dma_strobe, ATA_DMA2_STROBE ) | + IO_FIELD( R_ATA_CONFIG, dma_hold, ATA_DMA2_HOLD ) | + IO_FIELD( R_ATA_CONFIG, pio_setup, ATA_PIO4_SETUP ) | + IO_FIELD( R_ATA_CONFIG, pio_strobe, ATA_PIO4_STROBE ) | + IO_FIELD( R_ATA_CONFIG, pio_hold, ATA_PIO4_HOLD ) ); + + *R_ATA_CTRL_DATA = ( IO_STATE( R_ATA_CTRL_DATA, rw, read) | + IO_FIELD( R_ATA_CTRL_DATA, addr, 1 ) ); + + while(*R_ATA_STATUS_DATA & IO_MASK(R_ATA_STATUS_DATA, busy)); /* wait for busy flag*/ + + *R_IRQ_MASK0_SET = ( IO_STATE( R_IRQ_MASK0_SET, ata_irq0, set ) | + IO_STATE( R_IRQ_MASK0_SET, ata_irq1, set ) | + IO_STATE( R_IRQ_MASK0_SET, ata_irq2, set ) | + IO_STATE( R_IRQ_MASK0_SET, ata_irq3, set ) ); + + printk("ide: waiting %d seconds for drives to regain consciousness\n", + CONFIG_ETRAX_IDE_DELAY); + + h = jiffies + (CONFIG_ETRAX_IDE_DELAY * HZ); + while(time_before(jiffies, h)) /* nothing */ ; + + /* reset the dma channels we will use */ + + RESET_DMA(ATA_TX_DMA_NBR); + RESET_DMA(ATA_RX_DMA_NBR); + WAIT_DMA(ATA_TX_DMA_NBR); + WAIT_DMA(ATA_RX_DMA_NBR); + +} + +static int e100_dma_off (ide_drive_t *drive) +{ + return 0; +} + +static etrax_dma_descr mydescr; + +/* + * 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. + */ +static void +e100_atapi_input_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount) +{ + unsigned long data_reg = IDE_DATA_REG; + + D(printk("atapi_input_bytes, dreg 0x%x, buffer 0x%x, count %d\n", + data_reg, buffer, bytecount)); + + if(bytecount & 1) { + printk("warning, odd bytecount in cdrom_in_bytes = %d.\n", bytecount); + bytecount++; /* to round off */ + } + + /* make sure the DMA channel is available */ + RESET_DMA(ATA_RX_DMA_NBR); + WAIT_DMA(ATA_RX_DMA_NBR); + + /* setup DMA descriptor */ + + mydescr.sw_len = bytecount; + mydescr.ctrl = d_eol; + mydescr.buf = virt_to_phys(buffer); + + /* start the dma channel */ + + *R_DMA_CH3_FIRST = virt_to_phys(&mydescr); + *R_DMA_CH3_CMD = IO_STATE(R_DMA_CH3_CMD, cmd, start); + + /* initiate a multi word dma read using PIO handshaking */ + + *R_ATA_TRANSFER_CNT = IO_FIELD(R_ATA_TRANSFER_CNT, count, bytecount >> 1); + + *R_ATA_CTRL_DATA = data_reg | + IO_STATE(R_ATA_CTRL_DATA, rw, read) | + IO_STATE(R_ATA_CTRL_DATA, src_dst, dma) | + IO_STATE(R_ATA_CTRL_DATA, handsh, pio) | + IO_STATE(R_ATA_CTRL_DATA, multi, on) | + IO_STATE(R_ATA_CTRL_DATA, dma_size, word); + + /* wait for completion */ + + LED_DISK_READ(1); + WAIT_DMA(ATA_RX_DMA_NBR); + LED_DISK_READ(0); + +#if 0 + /* old polled transfer code + * this should be moved into a new function that can do polled + * transfers if DMA is not available + */ + + /* initiate a multi word read */ + + *R_ATA_TRANSFER_CNT = wcount << 1; + + *R_ATA_CTRL_DATA = data_reg | + IO_STATE(R_ATA_CTRL_DATA, rw, read) | + IO_STATE(R_ATA_CTRL_DATA, src_dst, register) | + IO_STATE(R_ATA_CTRL_DATA, handsh, pio) | + IO_STATE(R_ATA_CTRL_DATA, multi, on) | + IO_STATE(R_ATA_CTRL_DATA, dma_size, word); + + /* svinto has a latency until the busy bit actually is set */ + + nop(); nop(); + nop(); nop(); + nop(); nop(); + nop(); nop(); + nop(); nop(); + + /* unit should be busy during multi transfer */ + while((status = *R_ATA_STATUS_DATA) & IO_MASK(R_ATA_STATUS_DATA, busy)) { + while(!(status & IO_MASK(R_ATA_STATUS_DATA, dav))) + status = *R_ATA_STATUS_DATA; + *ptr++ = (unsigned short)(status & 0xffff); + } +#endif +} + +static void +e100_atapi_output_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount) +{ + unsigned long data_reg = IDE_DATA_REG; + + D(printk("atapi_output_bytes, dreg 0x%x, buffer 0x%x, count %d\n", + data_reg, buffer, bytecount)); + + if(bytecount & 1) { + printk("odd bytecount %d in atapi_out_bytes!\n", bytecount); + bytecount++; + } + + /* make sure the DMA channel is available */ + RESET_DMA(ATA_TX_DMA_NBR); + WAIT_DMA(ATA_TX_DMA_NBR); + + /* setup DMA descriptor */ + + mydescr.sw_len = bytecount; + mydescr.ctrl = d_eol; + mydescr.buf = virt_to_phys(buffer); + + /* start the dma channel */ + + *R_DMA_CH2_FIRST = virt_to_phys(&mydescr); + *R_DMA_CH2_CMD = IO_STATE(R_DMA_CH2_CMD, cmd, start); + + /* initiate a multi word dma write using PIO handshaking */ + + *R_ATA_TRANSFER_CNT = IO_FIELD(R_ATA_TRANSFER_CNT, count, bytecount >> 1); + + *R_ATA_CTRL_DATA = data_reg | + IO_STATE(R_ATA_CTRL_DATA, rw, write) | + IO_STATE(R_ATA_CTRL_DATA, src_dst, dma) | + IO_STATE(R_ATA_CTRL_DATA, handsh, pio) | + IO_STATE(R_ATA_CTRL_DATA, multi, on) | + IO_STATE(R_ATA_CTRL_DATA, dma_size, word); + + /* wait for completion */ + + LED_DISK_WRITE(1); + WAIT_DMA(ATA_TX_DMA_NBR); + LED_DISK_WRITE(0); + +#if 0 + /* old polled write code - see comment in input_bytes */ + + /* wait for busy flag */ + while(*R_ATA_STATUS_DATA & IO_MASK(R_ATA_STATUS_DATA, busy)); + + /* initiate a multi word write */ + + *R_ATA_TRANSFER_CNT = bytecount >> 1; + + ctrl = data_reg | + IO_STATE(R_ATA_CTRL_DATA, rw, write) | + IO_STATE(R_ATA_CTRL_DATA, src_dst, register) | + IO_STATE(R_ATA_CTRL_DATA, handsh, pio) | + IO_STATE(R_ATA_CTRL_DATA, multi, on) | + IO_STATE(R_ATA_CTRL_DATA, dma_size, word); + + LED_DISK_WRITE(1); + + /* Etrax will set busy = 1 until the multi pio transfer has finished + * and tr_rdy = 1 after each successful word transfer. + * When the last byte has been transferred Etrax will first set tr_tdy = 1 + * and then busy = 0 (not in the same cycle). If we read busy before it + * has been set to 0 we will think that we should transfer more bytes + * and then tr_rdy would be 0 forever. This is solved by checking busy + * in the inner loop. + */ + + do { + *R_ATA_CTRL_DATA = ctrl | *ptr++; + while(!(*R_ATA_STATUS_DATA & IO_MASK(R_ATA_STATUS_DATA, tr_rdy)) && + (*R_ATA_STATUS_DATA & IO_MASK(R_ATA_STATUS_DATA, busy))); + } while(*R_ATA_STATUS_DATA & IO_MASK(R_ATA_STATUS_DATA, busy)); + + LED_DISK_WRITE(0); +#endif + +} + +/* + * This is used for most PIO data transfers *from* the IDE interface + */ +static void +e100_ide_input_data (ide_drive_t *drive, void *buffer, unsigned int wcount) +{ + e100_atapi_input_bytes(drive, buffer, wcount << 2); +} + +/* + * This is used for most PIO data transfers *to* the IDE interface + */ +static void +e100_ide_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount) +{ + e100_atapi_output_bytes(drive, buffer, wcount << 2); +} + +/* we only have one DMA channel on the chip for ATA, so we can keep these statically */ +static etrax_dma_descr ata_descrs[MAX_DMA_DESCRS]; +static unsigned int ata_tot_size; + +/* + * e100_ide_build_dmatable() prepares a dma request. + * Returns 0 if all went okay, returns 1 otherwise. + */ +static int e100_ide_build_dmatable (ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + struct scatterlist* sg; + struct request *rq = HWGROUP(drive)->rq; + unsigned long size, addr; + unsigned int count = 0; + int i = 0; + + sg = hwif->sg_table; + + ata_tot_size = 0; + + ide_map_sg(drive, rq); + + i = hwif->sg_nents; + + while(i) { + /* + * 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. + */ + /* group sequential buffers into one large buffer */ + addr = page_to_phys(sg->page) + sg->offset; + size = sg_dma_len(sg); + while (sg++, --i) { + if ((addr + size) != page_to_phys(sg->page) + sg->offset) + break; + size += sg_dma_len(sg); + } + + /* did we run out of descriptors? */ + + if(count >= MAX_DMA_DESCRS) { + printk("%s: too few DMA descriptors\n", drive->name); + return 1; + } + + /* however, this case is more difficult - R_ATA_TRANSFER_CNT cannot be more + than 65536 words per transfer, so in that case we need to either + 1) use a DMA interrupt to re-trigger R_ATA_TRANSFER_CNT and continue with + the descriptors, or + 2) simply do the request here, and get dma_intr to only ide_end_request on + those blocks that were actually set-up for transfer. + */ + + if(ata_tot_size + size > 131072) { + printk("too large total ATA DMA request, %d + %d!\n", ata_tot_size, (int)size); + return 1; + } + + /* If size > 65536 it has to be splitted into new descriptors. Since we don't handle + size > 131072 only one split is necessary */ + + if(size > 65536) { + /* ok we want to do IO at addr, size bytes. set up a new descriptor entry */ + ata_descrs[count].sw_len = 0; /* 0 means 65536, this is a 16-bit field */ + ata_descrs[count].ctrl = 0; + ata_descrs[count].buf = addr; + ata_descrs[count].next = virt_to_phys(&ata_descrs[count + 1]); + count++; + ata_tot_size += 65536; + /* size and addr should refere to not handled data */ + size -= 65536; + addr += 65536; + } + /* ok we want to do IO at addr, size bytes. set up a new descriptor entry */ + if(size == 65536) { + ata_descrs[count].sw_len = 0; /* 0 means 65536, this is a 16-bit field */ + } else { + ata_descrs[count].sw_len = size; + } + ata_descrs[count].ctrl = 0; + ata_descrs[count].buf = addr; + ata_descrs[count].next = virt_to_phys(&ata_descrs[count + 1]); + count++; + ata_tot_size += size; + } + + if (count) { + /* set the end-of-list flag on the last descriptor */ + ata_descrs[count - 1].ctrl |= d_eol; + /* return and say all is ok */ + return 0; + } + + printk("%s: empty DMA table?\n", drive->name); + return 1; /* let the PIO routines handle this weirdness */ +} + +static int config_drive_for_dma (ide_drive_t *drive) +{ + const char **list; + struct hd_driveid *id = drive->id; + + if (id && (id->capability & 1)) { + /* Enable DMA on any drive that supports mword2 DMA */ + if ((id->field_valid & 2) && (id->dma_mword & 0x404) == 0x404) { + drive->using_dma = 1; + return 0; /* DMA enabled */ + } + + /* Consult the list of known "good" drives */ + list = good_dma_drives; + while (*list) { + if (!strcmp(*list++,id->model)) { + drive->using_dma = 1; + return 0; /* DMA enabled */ + } + } + } + return 1; /* DMA not enabled */ +} + +/* + * etrax_dma_intr() is the handler for disk read/write DMA interrupts + */ +static ide_startstop_t etrax_dma_intr (ide_drive_t *drive) +{ + LED_DISK_READ(0); + LED_DISK_WRITE(0); + + return ide_dma_intr(drive); +} + +/* + * Functions below 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. + * + * 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. + */ + +static int e100_dma_check(ide_drive_t *drive) +{ + return config_drive_for_dma (drive); +} + +static int e100_dma_end(ide_drive_t *drive) +{ + /* TODO: check if something went wrong with the DMA */ + return 0; +} + +static void e100_dma_start(ide_drive_t *drive) +{ + if (e100_read_command) { + /* begin DMA */ + + /* need to do this before RX DMA due to a chip bug + * it is enough to just flush the part of the cache that + * corresponds to the buffers we start, but since HD transfers + * usually are more than 8 kB, it is easier to optimize for the + * normal case and just flush the entire cache. its the only + * way to be sure! (OB movie quote) + */ + flush_etrax_cache(); + *R_DMA_CH3_FIRST = virt_to_phys(ata_descrs); + *R_DMA_CH3_CMD = IO_STATE(R_DMA_CH3_CMD, cmd, start); + + /* initiate a multi word dma read using DMA handshaking */ + + *R_ATA_TRANSFER_CNT = + IO_FIELD(R_ATA_TRANSFER_CNT, count, ata_tot_size >> 1); + + *R_ATA_CTRL_DATA = + IO_FIELD(R_ATA_CTRL_DATA, data, IDE_DATA_REG) | + IO_STATE(R_ATA_CTRL_DATA, rw, read) | + IO_STATE(R_ATA_CTRL_DATA, src_dst, dma) | + IO_STATE(R_ATA_CTRL_DATA, handsh, dma) | + IO_STATE(R_ATA_CTRL_DATA, multi, on) | + IO_STATE(R_ATA_CTRL_DATA, dma_size, word); + + LED_DISK_READ(1); + + D(printk("dma read of %d bytes.\n", ata_tot_size)); + + } else { + /* writing */ + /* begin DMA */ + + *R_DMA_CH2_FIRST = virt_to_phys(ata_descrs); + *R_DMA_CH2_CMD = IO_STATE(R_DMA_CH2_CMD, cmd, start); + + /* initiate a multi word dma write using DMA handshaking */ + + *R_ATA_TRANSFER_CNT = + IO_FIELD(R_ATA_TRANSFER_CNT, count, ata_tot_size >> 1); + + *R_ATA_CTRL_DATA = + IO_FIELD(R_ATA_CTRL_DATA, data, IDE_DATA_REG) | + IO_STATE(R_ATA_CTRL_DATA, rw, write) | + IO_STATE(R_ATA_CTRL_DATA, src_dst, dma) | + IO_STATE(R_ATA_CTRL_DATA, handsh, dma) | + IO_STATE(R_ATA_CTRL_DATA, multi, on) | + IO_STATE(R_ATA_CTRL_DATA, dma_size, word); + + LED_DISK_WRITE(1); + + D(printk("dma write of %d bytes.\n", ata_tot_size)); + } +} diff --git a/drivers/ide/h8300/ide-h8300.c b/drivers/ide/h8300/ide-h8300.c new file mode 100644 index 0000000..fb91cb8 --- /dev/null +++ b/drivers/ide/h8300/ide-h8300.c @@ -0,0 +1,119 @@ +/* + * drivers/ide/ide-h8300.c + * H8/300 generic IDE interface + */ + +#include <linux/init.h> +#include <linux/ide.h> +#include <linux/config.h> + +#include <asm/io.h> +#include <asm/irq.h> + +#define bswap(d) \ +({ \ + u16 r; \ + __asm__("mov.b %w1,r1h\n\t" \ + "mov.b %x1,r1l\n\t" \ + "mov.w r1,%0" \ + :"=r"(r) \ + :"r"(d) \ + :"er1"); \ + (r); \ +}) + +static void mm_outw(u16 d, unsigned long a) +{ + __asm__("mov.b %w0,r2h\n\t" + "mov.b %x0,r2l\n\t" + "mov.w r2,@%1" + : + :"r"(d),"r"(a) + :"er2"); +} + +static u16 mm_inw(unsigned long a) +{ + register u16 r __asm__("er0"); + __asm__("mov.w @%1,r2\n\t" + "mov.b r2l,%x0\n\t" + "mov.b r2h,%w0" + :"=r"(r) + :"r"(a) + :"er2"); + return r; +} + +static void mm_outsw(unsigned long addr, void *buf, u32 len) +{ + unsigned short *bp = (unsigned short *)buf; + for (; len > 0; len--, bp++) + *(volatile u16 *)addr = bswap(*bp); +} + +static void mm_insw(unsigned long addr, void *buf, u32 len) +{ + unsigned short *bp = (unsigned short *)buf; + for (; len > 0; len--, bp++) + *bp = bswap(*(volatile u16 *)addr); +} + +#define H8300_IDE_GAP (2) + +static inline void hw_setup(hw_regs_t *hw) +{ + int i; + + memset(hw, 0, sizeof(hw_regs_t)); + for (i = 0; i <= IDE_STATUS_OFFSET; i++) + hw->io_ports[i] = CONFIG_H8300_IDE_BASE + H8300_IDE_GAP*i; + hw->io_ports[IDE_CONTROL_OFFSET] = CONFIG_H8300_IDE_ALT; + hw->irq = EXT_IRQ0 + CONFIG_H8300_IDE_IRQ; + hw->dma = NO_DMA; + hw->chipset = ide_generic; +} + +static inline void hwif_setup(ide_hwif_t *hwif) +{ + default_hwif_iops(hwif); + + hwif->mmio = 2; + hwif->OUTW = mm_outw; + hwif->OUTSW = mm_outsw; + hwif->INW = mm_inw; + hwif->INSW = mm_insw; + hwif->OUTL = NULL; + hwif->INL = NULL; + hwif->OUTSL = NULL; + hwif->INSL = NULL; +} + +void __init h8300_ide_init(void) +{ + hw_regs_t hw; + ide_hwif_t *hwif; + int idx; + + if (!request_region(CONFIG_H8300_IDE_BASE, H8300_IDE_GAP*8, "ide-h8300")) + goto out_busy; + if (!request_region(CONFIG_H8300_IDE_ALT, H8300_IDE_GAP, "ide-h8300")) { + release_region(CONFIG_H8300_IDE_BASE, H8300_IDE_GAP*8); + goto out_busy; + } + + hw_setup(&hw); + + /* register if */ + idx = ide_register_hw(&hw, &hwif); + if (idx == -1) { + printk(KERN_ERR "ide-h8300: IDE I/F register failed\n"); + return; + } + + hwif_setup(hwif); + printk(KERN_INFO "ide%d: H8/300 generic IDE interface\n", idx); + return; + +out_busy: + printk(KERN_ERR "ide-h8300: IDE I/F resource already used.\n"); +} diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c new file mode 100644 index 0000000..33a020f --- /dev/null +++ b/drivers/ide/ide-cd.c @@ -0,0 +1,3524 @@ +/* + * linux/drivers/ide/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-2000 Jens Axboe <axboe@suse.de> + * + * 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_atapi/spec/SFF8020-r2.6/PS/8020r26.ps + * ftp://ftp.avc-pioneer.com/Mtfuji4/Spec/Fuji4r10.pdf + * + * Drives that deviate from these standards will be accommodated 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 occurred. + * - 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 + * + * 4.57 Apr 7, 2000 - Fixed sense reporting. + * - Fixed possible oops in ide_cdrom_get_last_session() + * - Fix locking mania and make ide_cdrom_reset relock + * - Stop spewing errors to log when magicdev polls with + * TEST_UNIT_READY on some drives. + * - Various fixes from Tobias Ringstrom: + * tray if it was locked prior to the reset. + * - cdrom_read_capacity returns one frame too little. + * - Fix real capacity reporting. + * + * 4.58 May 1, 2000 - Clean up ACER50 stuff. + * - Fix small problem with ide_cdrom_capacity + * + * 4.59 Aug 11, 2000 - Fix changer problem in cdrom_read_toc, we weren't + * correctly sensing a disc change. + * - Rearranged some code + * - Use extended sense on drives that support it for + * correctly reporting tray status -- from + * Michael D Johnson <johnsom@orst.edu> + * 4.60 Dec 17, 2003 - Add mt rainier support + * - Bump timeout for packet commands, matches sr + * - Odd stuff + * 4.61 Jan 22, 2004 - support hardware sector sizes other than 2kB, + * Pascal Schmidt <der.eremit@email.de> + * + *************************************************************************/ + +#define IDECD_VERSION "4.61" + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/timer.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/errno.h> +#include <linux/cdrom.h> +#include <linux/ide.h> +#include <linux/completion.h> + +#include <scsi/scsi.h> /* For SCSI -> ATAPI command conversion */ + +#include <asm/irq.h> +#include <asm/io.h> +#include <asm/byteorder.h> +#include <asm/uaccess.h> +#include <asm/unaligned.h> + +#include "ide-cd.h" + +static DECLARE_MUTEX(idecd_ref_sem); + +#define to_ide_cd(obj) container_of(obj, struct cdrom_info, kref) + +#define ide_cd_g(disk) \ + container_of((disk)->private_data, struct cdrom_info, driver) + +static struct cdrom_info *ide_cd_get(struct gendisk *disk) +{ + struct cdrom_info *cd = NULL; + + down(&idecd_ref_sem); + cd = ide_cd_g(disk); + if (cd) + kref_get(&cd->kref); + up(&idecd_ref_sem); + return cd; +} + +static void ide_cd_release(struct kref *); + +static void ide_cd_put(struct cdrom_info *cd) +{ + down(&idecd_ref_sem); + kref_put(&cd->kref, ide_cd_release); + up(&idecd_ref_sem); +} + +/**************************************************************************** + * 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 int cdrom_log_sense(ide_drive_t *drive, struct request *rq, + struct request_sense *sense) +{ + int log = 0; + + if (!sense || !rq || (rq->flags & REQ_QUIET)) + return 0; + + switch (sense->sense_key) { + case NO_SENSE: case RECOVERED_ERROR: + break; + case NOT_READY: + /* + * don't care about tray state messages for + * e.g. capacity commands or in-progress or + * becoming ready + */ + if (sense->asc == 0x3a || sense->asc == 0x04) + break; + log = 1; + break; + case ILLEGAL_REQUEST: + /* + * don't log START_STOP unit with LoEj set, since + * we cannot reliably check if drive can auto-close + */ + if (rq->cmd[0] == GPCMD_START_STOP_UNIT && sense->asc == 0x24) + log = 0; + break; + case 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); + break; + default: + log = 1; + break; + } + return log; +} + +static +void cdrom_analyze_sense_data(ide_drive_t *drive, + struct request *failed_command, + struct request_sense *sense) +{ + if (!cdrom_log_sense(drive, failed_command, sense)) + return; + + /* + * If a read toc is executed for a CD-R or CD-RW medium where + * the first toc has not been recorded yet, it will fail with + * 05/24/00 (which is a confusing error) + */ + if (failed_command && failed_command->cmd[0] == GPCMD_READ_TOC_PMA_ATIP) + if (sense->sense_key == 0x05 && sense->asc == 0x24) + return; + +#if VERBOSE_IDE_CD_ERRORS + { + int i; + const char *s; + char buf[80]; + + printk ("ATAPI device %s:\n", drive->name); + if (sense->error_code==0x70) + printk(" Error: "); + else if (sense->error_code==0x71) + printk(" Deferred Error: "); + else if (sense->error_code == 0x7f) + printk(" Vendor-specific Error: "); + else + printk(" Unknown Error Type: "); + + if (sense->sense_key < ARY_LEN(sense_key_texts)) + s = sense_key_texts[sense->sense_key]; + else + s = "bad sense key!"; + + printk("%s -- (Sense key=0x%02x)\n", s, sense->sense_key); + + if (sense->asc == 0x40) { + sprintf(buf, "Diagnostic failure on component 0x%02x", + sense->ascq); + s = buf; + } else { + int lo = 0, mid, hi = ARY_LEN(sense_data_texts); + unsigned long key = (sense->sense_key << 16); + key |= (sense->asc << 8); + if (!(sense->ascq >= 0x80 && sense->ascq <= 0xdd)) + key |= sense->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 (sense->asc > 0x80) + s = "(vendor-specific error)"; + else + s = "(reserved error code)"; + } + + printk(KERN_ERR " %s -- (asc=0x%02x, ascq=0x%02x)\n", + s, sense->asc, sense->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->cmd[0]) { + s = packet_command_texts[mid].text; + break; + } + if (packet_command_texts[mid].packet_command > + failed_command->cmd[0]) + hi = mid; + else + lo = mid+1; + } + + printk (KERN_ERR " The failed \"%s\" packet command was: \n \"", s); + for (i=0; i<sizeof (failed_command->cmd); i++) + printk ("%02x ", failed_command->cmd[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 (sense->sense_key == NOT_READY && (sense->sks[0] & 0x80)) { + int progress = (sense->sks[1] << 8 | sense->sks[2]) * 100; + printk(KERN_ERR " Command is %02d%% complete\n", progress / 0xffff); + + } + + if (sense->sense_key == ILLEGAL_REQUEST && + (sense->sks[0] & 0x80) != 0) { + printk(KERN_ERR " Error in %s byte %d", + (sense->sks[0] & 0x40) != 0 ? + "command packet" : "command data", + (sense->sks[1] << 8) + sense->sks[2]); + + if ((sense->sks[0] & 0x40) != 0) + printk (" bit %d", sense->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 (sense->sense_key == UNIT_ATTENTION || + (sense->sense_key == NOT_READY && (sense->asc == 4 || + sense->asc == 0x3a))) + return; + + printk(KERN_ERR "%s: error code: 0x%02x sense_key: 0x%02x asc: 0x%02x ascq: 0x%02x\n", + drive->name, + sense->error_code, sense->sense_key, + sense->asc, sense->ascq); +#endif /* not VERBOSE_IDE_CD_ERRORS */ +} + +/* + * Initialize a ide-cd packet command request + */ +static void cdrom_prepare_request(ide_drive_t *drive, struct request *rq) +{ + struct cdrom_info *cd = drive->driver_data; + + ide_init_drive_cmd(rq); + rq->flags = REQ_PC; + rq->rq_disk = cd->disk; +} + +static void cdrom_queue_request_sense(ide_drive_t *drive, void *sense, + struct request *failed_command) +{ + struct cdrom_info *info = drive->driver_data; + struct request *rq = &info->request_sense_request; + + if (sense == NULL) + sense = &info->sense_data; + + /* stuff the sense request in front of our current request */ + cdrom_prepare_request(drive, rq); + + rq->data = sense; + rq->cmd[0] = GPCMD_REQUEST_SENSE; + rq->cmd[4] = rq->data_len = 18; + + rq->flags = REQ_SENSE; + + /* NOTE! Save the failed command in "rq->buffer" */ + rq->buffer = (void *) failed_command; + + (void) ide_do_drive_cmd(drive, rq, ide_preempt); +} + +static void cdrom_end_request (ide_drive_t *drive, int uptodate) +{ + struct request *rq = HWGROUP(drive)->rq; + int nsectors = rq->hard_cur_sectors; + + if ((rq->flags & REQ_SENSE) && uptodate) { + /* + * For REQ_SENSE, "rq->buffer" points to the original failed + * request + */ + struct request *failed = (struct request *) rq->buffer; + struct cdrom_info *info = drive->driver_data; + void *sense = &info->sense_data; + unsigned long flags; + + if (failed) { + if (failed->sense) { + sense = failed->sense; + failed->sense_len = rq->sense_len; + } + + /* + * now end failed request + */ + spin_lock_irqsave(&ide_lock, flags); + end_that_request_chunk(failed, 0, failed->data_len); + end_that_request_last(failed); + spin_unlock_irqrestore(&ide_lock, flags); + } + + cdrom_analyze_sense_data(drive, failed, sense); + } + + if (!rq->current_nr_sectors && blk_fs_request(rq)) + uptodate = 1; + /* make sure it's fully ended */ + if (blk_pc_request(rq)) + nsectors = (rq->data_len + 511) >> 9; + if (!nsectors) + nsectors = 1; + + ide_end_request(drive, uptodate, nsectors); +} + +/* Returns 0 if the request should be continued. + Returns 1 if the request was ended. */ +static int cdrom_decode_status(ide_drive_t *drive, int good_stat, int *stat_ret) +{ + struct request *rq = HWGROUP(drive)->rq; + int stat, err, sense_key; + + /* Check for errors. */ + stat = HWIF(drive)->INB(IDE_STATUS_REG); + if (stat_ret) + *stat_ret = stat; + + if (OK_STAT(stat, good_stat, BAD_R_STAT)) + return 0; + + /* Get the IDE error register. */ + err = HWIF(drive)->INB(IDE_ERROR_REG); + sense_key = err >> 4; + + if (rq == NULL) { + printk("%s: missing rq in cdrom_decode_status\n", drive->name); + return 1; + } + + if (rq->flags & REQ_SENSE) { + /* We got an error trying to get sense info + from the drive (probably while trying + to recover from a former error). Just give up. */ + + rq->flags |= REQ_FAILED; + cdrom_end_request(drive, 0); + ide_error(drive, "request sense failure", stat); + return 1; + + } else if (rq->flags & (REQ_PC | REQ_BLOCK_PC)) { + /* All other functions, except for READ. */ + unsigned long flags; + + /* + * if we have an error, pass back CHECK_CONDITION as the + * scsi status byte + */ + if ((rq->flags & REQ_BLOCK_PC) && !rq->errors) + rq->errors = SAM_STAT_CHECK_CONDITION; + + /* 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 if (!(rq->flags & REQ_QUIET)) { + /* Otherwise, print an error. */ + ide_dump_status(drive, "packet command error", stat); + } + + rq->flags |= REQ_FAILED; + + /* + * instead of playing games with moving completions around, + * remove failed request completely and end it when the + * request sense has completed + */ + if (stat & ERR_STAT) { + spin_lock_irqsave(&ide_lock, flags); + blkdev_dequeue_request(rq); + HWGROUP(drive)->rq = NULL; + spin_unlock_irqrestore(&ide_lock, flags); + + cdrom_queue_request_sense(drive, rq->sense, rq); + } else + cdrom_end_request(drive, 0); + + } else if (blk_fs_request(rq)) { + int do_end_request = 0; + + /* Handle errors from READ and WRITE requests. */ + + if (blk_noretry_request(rq)) + do_end_request = 1; + + if (sense_key == NOT_READY) { + /* Tray open. */ + if (rq_data_dir(rq) == READ) { + cdrom_saw_media_change (drive); + + /* Fail the request. */ + printk ("%s: tray open\n", drive->name); + do_end_request = 1; + } else { + struct cdrom_info *info = drive->driver_data; + + /* allow the drive 5 seconds to recover, some + * devices will return this error while flushing + * data from cache */ + if (!rq->errors) + info->write_timeout = jiffies + ATAPI_WAIT_WRITE_BUSY; + rq->errors = 1; + if (time_after(jiffies, info->write_timeout)) + do_end_request = 1; + else { + unsigned long flags; + + /* + * take a breather relying on the + * unplug timer to kick us again + */ + spin_lock_irqsave(&ide_lock, flags); + blk_plug_device(drive->queue); + spin_unlock_irqrestore(&ide_lock,flags); + return 1; + } + } + } 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) + do_end_request = 1; + } 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); + do_end_request = 1; + } else if (sense_key == MEDIUM_ERROR) { + /* No point in re-trying a zillion times on a bad + * sector... If we got here the error is not correctable */ + ide_dump_status (drive, "media error (bad sector)", stat); + do_end_request = 1; + } else if (sense_key == BLANK_CHECK) { + /* Disk appears blank ?? */ + ide_dump_status (drive, "media error (blank)", stat); + do_end_request = 1; + } else if ((err & ~ABRT_ERR) != 0) { + /* Go to the default handler + for other errors. */ + ide_error(drive, "cdrom_decode_status", stat); + return 1; + } else if ((++rq->errors > ERROR_MAX)) { + /* We've racked up too many retries. Abort. */ + do_end_request = 1; + } + + if (do_end_request) + cdrom_end_request(drive, 0); + + /* If we got a CHECK_CONDITION status, + queue a request sense command. */ + if ((stat & ERR_STAT) != 0) + cdrom_queue_request_sense(drive, NULL, NULL); + } else { + blk_dump_rq_flags(rq, "ide-cd: bad rq"); + cdrom_end_request(drive, 0); + } + + /* Retry, or handle the next request. */ + return 1; +} + +static int cdrom_timer_expiry(ide_drive_t *drive) +{ + struct request *rq = HWGROUP(drive)->rq; + unsigned long wait = 0; + + /* + * Some commands are *slow* and normally take a long time to + * complete. Usually we can use the ATAPI "disconnect" to bypass + * this, but not all commands/drives support that. Let + * ide_timer_expiry keep polling us for these. + */ + switch (rq->cmd[0]) { + case GPCMD_BLANK: + case GPCMD_FORMAT_UNIT: + case GPCMD_RESERVE_RZONE_TRACK: + case GPCMD_CLOSE_TRACK: + case GPCMD_FLUSH_CACHE: + wait = ATAPI_WAIT_PC; + break; + default: + if (!(rq->flags & REQ_QUIET)) + printk(KERN_INFO "ide-cd: cmd 0x%x timed out\n", rq->cmd[0]); + wait = 0; + break; + } + 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; + ide_hwif_t *hwif = drive->hwif; + + /* 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->dma_setup(drive); + + /* Set up the controller registers. */ + /* FIXME: for Virtual DMA we must check harder */ + HWIF(drive)->OUTB(info->dma, IDE_FEATURE_REG); + HWIF(drive)->OUTB(0, IDE_IREASON_REG); + HWIF(drive)->OUTB(0, IDE_SECTOR_REG); + + HWIF(drive)->OUTB(xferlen & 0xff, IDE_BCOUNTL_REG); + HWIF(drive)->OUTB(xferlen >> 8 , IDE_BCOUNTH_REG); + if (IDE_CONTROL_REG) + HWIF(drive)->OUTB(drive->ctl, IDE_CONTROL_REG); + + if (CDROM_CONFIG_FLAGS (drive)->drq_interrupt) { + /* packet command */ + ide_execute_command(drive, WIN_PACKETCMD, handler, ATAPI_WAIT_PC, cdrom_timer_expiry); + return ide_started; + } else { + unsigned long flags; + + /* packet command */ + spin_lock_irqsave(&ide_lock, flags); + hwif->OUTBSYNC(drive, WIN_PACKETCMD, IDE_COMMAND_REG); + ndelay(400); + spin_unlock_irqrestore(&ide_lock, flags); + + 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. */ +/* + * changed 5 parameters to 3 for dvd-ram + * struct packet_command *pc; now packet_command_t *pc; + */ +#define ATAPI_MIN_CDB_BYTES 12 +static ide_startstop_t cdrom_transfer_packet_command (ide_drive_t *drive, + struct request *rq, + ide_handler_t *handler) +{ + ide_hwif_t *hwif = drive->hwif; + int cmd_len; + struct cdrom_info *info = drive->driver_data; + ide_startstop_t startstop; + + 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. */ + + /* Check for errors. */ + if (cdrom_decode_status(drive, DRQ_STAT, NULL)) + return ide_stopped; + } else { + /* 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, rq->timeout, cdrom_timer_expiry); + + /* ATAPI commands get padded out to 12 bytes minimum */ + cmd_len = COMMAND_SIZE(rq->cmd[0]); + if (cmd_len < ATAPI_MIN_CDB_BYTES) + cmd_len = ATAPI_MIN_CDB_BYTES; + + /* Send the command to the device. */ + HWIF(drive)->atapi_output_bytes(drive, rq->cmd, cmd_len); + + /* Start the DMA if need be */ + if (info->dma) + hwif->dma_start(drive); + + 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_t(int, 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) { + HWIF(drive)->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) { + static char dum[SECTOR_SIZE]; + HWIF(drive)->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) +{ + if (ireason == 2) + return 0; + else if (ireason == 0) { + /* Whoops... The drive is expecting to receive data from us! */ + printk(KERN_ERR "%s: 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; + HWIF(drive)->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. + */ + (void) HWIF(drive)->INB(IDE_STATUS_REG); + return 0; + } else { + /* Drive wants a command packet, or invalid ireason... */ + printk(KERN_ERR "%s: read_intr: bad interrupt reason %x\n", drive->name, + ireason); + } + + cdrom_end_request(drive, 0); + 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; + u8 lowcyl = 0, highcyl = 0; + int dma = info->dma, dma_error = 0; + + struct request *rq = HWGROUP(drive)->rq; + + /* + * handle dma case + */ + if (dma) { + info->dma = 0; + if ((dma_error = HWIF(drive)->ide_dma_end(drive))) + __ide_dma_off(drive); + } + + if (cdrom_decode_status(drive, 0, &stat)) + return ide_stopped; + + if (dma) { + if (!dma_error) { + ide_end_request(drive, 1, rq->nr_sectors); + return ide_stopped; + } else + return ide_error(drive, "dma error", stat); + } + + /* Read the interrupt reason and the transfer length. */ + ireason = HWIF(drive)->INB(IDE_IREASON_REG) & 0x3; + lowcyl = HWIF(drive)->INB(IDE_BCOUNTL_REG); + highcyl = HWIF(drive)->INB(IDE_BCOUNTH_REG); + + len = lowcyl + (256 * highcyl); + + /* 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 (KERN_ERR "%s: cdrom_read_intr: data underrun (%d blocks)\n", + drive->name, rq->current_nr_sectors); + rq->flags |= REQ_FAILED; + cdrom_end_request(drive, 0); + } else + cdrom_end_request(drive, 1); + 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 (KERN_ERR "%s: cdrom_read_intr: Bad transfer size %d\n", + drive->name, len); + if (CDROM_CONFIG_FLAGS(drive)->limit_nframes) + printk (KERN_ERR " This drive is not supported by this version of the driver\n"); + else { + printk (KERN_ERR " Trying to limit transfer sizes\n"); + CDROM_CONFIG_FLAGS(drive)->limit_nframes = 1; + } + cdrom_end_request(drive, 0); + 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_t(int, rq->current_nr_sectors - bio_cur_sectors(rq->bio), sectors_to_transfer); + + while (nskip > 0) { + /* We need to throw away a sector. */ + static char dum[SECTOR_SIZE]; + HWIF(drive)->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) + cdrom_end_request(drive, 1); + + /* 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_t(int, sectors_to_transfer, + rq->current_nr_sectors); + + /* Read this_transfer sectors + into the current buffer. */ + while (this_transfer > 0) { + HWIF(drive)->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, ATAPI_WAIT_PC, 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; + unsigned short sectors_per_frame; + + sectors_per_frame = queue_hardsect_size(drive->queue) >> SECTOR_BITS; + + /* 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(drive, 1); + + 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(drive, 1); + return -1; + } + + /* Move on to the next buffer if needed. */ + if (rq->current_nr_sectors == 0) + cdrom_end_request(drive, 1); + + /* 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 < bio_cur_sectors(rq->bio) && + (rq->sector & (sectors_per_frame - 1))) { + printk(KERN_ERR "%s: cdrom_read_from_buffer: buffer botch (%ld)\n", + drive->name, (long)rq->sector); + cdrom_end_request(drive, 0); + 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 request *rq = HWGROUP(drive)->rq; + unsigned short sectors_per_frame; + int nskip; + + sectors_per_frame = queue_hardsect_size(drive->queue) >> SECTOR_BITS; + + /* 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 = rq->sector & (sectors_per_frame - 1); + if (nskip > 0) { + /* Sanity check... */ + if (rq->current_nr_sectors != bio_cur_sectors(rq->bio) && + (rq->sector & (sectors_per_frame - 1))) { + printk(KERN_ERR "%s: cdrom_start_read_continuation: buffer botch (%u)\n", + drive->name, rq->current_nr_sectors); + cdrom_end_request(drive, 0); + return ide_stopped; + } + rq->current_nr_sectors += nskip; + } + + /* Set up the command */ + rq->timeout = ATAPI_WAIT_PC; + + /* Send the command to the drive and return. */ + return cdrom_transfer_packet_command(drive, rq, &cdrom_read_intr); +} + + +#define IDECD_SEEK_THRESHOLD (1000) /* 1000 blocks */ +#define IDECD_SEEK_TIMER (5 * WAIT_MIN_SLEEP) /* 100 ms */ +#define IDECD_SEEK_TIMEOUT (2 * WAIT_CMD) /* 20 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; + + if (cdrom_decode_status(drive, 0, &stat)) + return ide_stopped; + CDROM_CONFIG_FLAGS(drive)->seeking = 1; + + if (retry && time_after(jiffies, info->start_seek + IDECD_SEEK_TIMER)) { + if (--retry == 0) { + /* + * this condition is far too common, to bother + * users about it + */ + /* 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 request *rq = HWGROUP(drive)->rq; + sector_t frame = rq->sector; + + sector_div(frame, queue_hardsect_size(drive->queue) >> SECTOR_BITS); + + memset(rq->cmd, 0, sizeof(rq->cmd)); + rq->cmd[0] = GPCMD_SEEK; + put_unaligned(cpu_to_be32(frame), (unsigned int *) &rq->cmd[2]); + + rq->timeout = ATAPI_WAIT_PC; + return cdrom_transfer_packet_command(drive, rq, &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->cmd = 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 != bio_data(rq->bio)) { + sector_t n = (rq->buffer - (char *) bio_data(rq->bio)) / SECTOR_SIZE; + + rq->buffer = bio_data(rq->bio); + rq->nr_sectors += n; + rq->sector -= n; + } + rq->hard_cur_sectors = rq->current_nr_sectors = bio_cur_sectors(rq->bio); + rq->hard_nr_sectors = rq->nr_sectors; + rq->hard_sector = rq->sector; + rq->q->prep_rq_fn(rq->q, rq); +} + +/* + * 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; + unsigned short sectors_per_frame; + + sectors_per_frame = queue_hardsect_size(drive->queue) >> SECTOR_BITS; + + /* 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; + + blk_attempt_remerge(drive->queue, rq); + + /* Clear the local sector buffer. */ + info->nsectors_buffered = 0; + + /* use dma, if possible. */ + info->dma = drive->using_dma; + if ((rq->sector & (sectors_per_frame - 1)) || + (rq->nr_sectors & (sectors_per_frame - 1))) + info->dma = 0; + + info->cmd = READ; + + /* Start sending the read request to the drive. */ + return cdrom_start_packet_command(drive, 32768, cdrom_start_read_continuation); +} + +/**************************************************************************** + * Execute all other packet commands. + */ + +/* Interrupt routine for packet command completion. */ +static ide_startstop_t cdrom_pc_intr (ide_drive_t *drive) +{ + int ireason, len, thislen; + struct request *rq = HWGROUP(drive)->rq; + u8 lowcyl = 0, highcyl = 0; + int stat; + + /* Check for errors. */ + if (cdrom_decode_status(drive, 0, &stat)) + return ide_stopped; + + /* Read the interrupt reason and the transfer length. */ + ireason = HWIF(drive)->INB(IDE_IREASON_REG); + lowcyl = HWIF(drive)->INB(IDE_BCOUNTL_REG); + highcyl = HWIF(drive)->INB(IDE_BCOUNTH_REG); + + len = lowcyl + (256 * highcyl); + + /* 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 (rq->cmd[0] == GPCMD_REQUEST_SENSE && + rq->data_len > 0 && + rq->data_len <= 5) { + while (rq->data_len > 0) { + *(unsigned char *)rq->data++ = 0; + --rq->data_len; + } + } + + if (rq->data_len == 0) + cdrom_end_request(drive, 1); + 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); + */ + rq->flags |= REQ_FAILED; + cdrom_end_request(drive, 0); + } + return ide_stopped; + } + + /* Figure out how much data to transfer. */ + thislen = rq->data_len; + if (thislen > len) thislen = len; + + /* The drive wants to be written to. */ + if ((ireason & 3) == 0) { + if (!rq->data) { + blk_dump_rq_flags(rq, "cdrom_pc_intr, write"); + goto confused; + } + /* Transfer the data. */ + HWIF(drive)->atapi_output_bytes(drive, rq->data, thislen); + + /* If we haven't moved enough data to satisfy the drive, + add some padding. */ + while (len > thislen) { + int dum = 0; + HWIF(drive)->atapi_output_bytes(drive, &dum, sizeof(dum)); + len -= sizeof(dum); + } + + /* Keep count of how much data we've moved. */ + rq->data += thislen; + rq->data_len -= thislen; + } + + /* Same drill for reading. */ + else if ((ireason & 3) == 2) { + if (!rq->data) { + blk_dump_rq_flags(rq, "cdrom_pc_intr, write"); + goto confused; + } + /* Transfer the data. */ + HWIF(drive)->atapi_input_bytes(drive, rq->data, thislen); + + /* If we haven't moved enough data to satisfy the drive, + add some padding. */ + while (len > thislen) { + int dum = 0; + HWIF(drive)->atapi_input_bytes(drive, &dum, sizeof(dum)); + len -= sizeof(dum); + } + + /* Keep count of how much data we've moved. */ + rq->data += thislen; + rq->data_len -= thislen; + + if (rq->flags & REQ_SENSE) + rq->sense_len += thislen; + } else { +confused: + printk (KERN_ERR "%s: cdrom_pc_intr: The drive " + "appears confused (ireason = 0x%02x)\n", + drive->name, ireason); + rq->flags |= REQ_FAILED; + } + + /* Now we wait for another interrupt. */ + ide_set_handler(drive, &cdrom_pc_intr, ATAPI_WAIT_PC, cdrom_timer_expiry); + return ide_started; +} + +static ide_startstop_t cdrom_do_pc_continuation (ide_drive_t *drive) +{ + struct request *rq = HWGROUP(drive)->rq; + + if (!rq->timeout) + rq->timeout = ATAPI_WAIT_PC; + + /* Send the command to the drive and return. */ + return cdrom_transfer_packet_command(drive, rq, &cdrom_pc_intr); +} + + +static ide_startstop_t cdrom_do_packet_command (ide_drive_t *drive) +{ + int len; + struct request *rq = HWGROUP(drive)->rq; + struct cdrom_info *info = drive->driver_data; + + info->dma = 0; + info->cmd = 0; + rq->flags &= ~REQ_FAILED; + len = rq->data_len; + + /* Start sending the command to the drive. */ + return cdrom_start_packet_command(drive, len, cdrom_do_pc_continuation); +} + + +static +int cdrom_queue_packet_command(ide_drive_t *drive, struct request *rq) +{ + struct request_sense sense; + int retries = 10; + unsigned int flags = rq->flags; + + if (rq->sense == NULL) + rq->sense = &sense; + + /* Start of retry loop. */ + do { + int error; + unsigned long time = jiffies; + rq->flags = flags; + + error = ide_do_drive_cmd(drive, rq, ide_wait); + time = jiffies - time; + + /* FIXME: we should probably abort/retry or something + * in case of failure */ + if (rq->flags & REQ_FAILED) { + /* The request failed. Retry if it was due to a unit + attention status + (usually means media was changed). */ + struct request_sense *reqbuf = rq->sense; + + 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. */ + ssleep(2); + } else { + /* Otherwise, don't retry. */ + retries = 0; + } + --retries; + } + + /* End of retry loop. */ + } while ((rq->flags & REQ_FAILED) && retries >= 0); + + /* Return an error if the command failed. */ + return (rq->flags & REQ_FAILED) ? -EIO : 0; +} + +/* + * Write handling + */ +static inline int cdrom_write_check_ireason(ide_drive_t *drive, int len, int ireason) +{ + /* Two notes about IDE interrupt reason here - 0 means that + * the drive wants to receive data from us, 2 means that + * the drive is expecting to transfer data to us. + */ + if (ireason == 0) + return 0; + else if (ireason == 2) { + /* Whoops... The drive wants to send data. */ + printk(KERN_ERR "%s: write_intr: wrong transfer direction!\n", + drive->name); + + while (len > 0) { + int dum = 0; + HWIF(drive)->atapi_input_bytes(drive, &dum, sizeof(dum)); + len -= sizeof(dum); + } + } else { + /* Drive wants a command packet, or invalid ireason... */ + printk(KERN_ERR "%s: write_intr: bad interrupt reason %x\n", + drive->name, ireason); + } + + cdrom_end_request(drive, 0); + return 1; +} + +static void post_transform_command(struct request *req) +{ + u8 *c = req->cmd; + char *ibuf; + + if (!blk_pc_request(req)) + return; + + if (req->bio) + ibuf = bio_data(req->bio); + else + ibuf = req->data; + + if (!ibuf) + return; + + /* + * set ansi-revision and response data as atapi + */ + if (c[0] == GPCMD_INQUIRY) { + ibuf[2] |= 2; + ibuf[3] = (ibuf[3] & 0xf0) | 2; + } +} + +typedef void (xfer_func_t)(ide_drive_t *, void *, u32); + +/* + * best way to deal with dma that is not sector aligned right now... note + * that in this path we are not using ->data or ->buffer at all. this irs + * can replace cdrom_pc_intr, cdrom_read_intr, and cdrom_write_intr in the + * future. + */ +static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive) +{ + struct cdrom_info *info = drive->driver_data; + struct request *rq = HWGROUP(drive)->rq; + int dma_error, dma, stat, ireason, len, thislen; + u8 lowcyl, highcyl; + xfer_func_t *xferfunc; + unsigned long flags; + + /* Check for errors. */ + dma_error = 0; + dma = info->dma; + if (dma) { + info->dma = 0; + dma_error = HWIF(drive)->ide_dma_end(drive); + } + + if (cdrom_decode_status(drive, 0, &stat)) + return ide_stopped; + + /* + * using dma, transfer is complete now + */ + if (dma) { + if (dma_error) { + printk(KERN_ERR "ide-cd: dma error\n"); + __ide_dma_off(drive); + return ide_error(drive, "dma error", stat); + } + + end_that_request_chunk(rq, 1, rq->data_len); + rq->data_len = 0; + goto end_request; + } + + /* + * ok we fall to pio :/ + */ + ireason = HWIF(drive)->INB(IDE_IREASON_REG) & 0x3; + lowcyl = HWIF(drive)->INB(IDE_BCOUNTL_REG); + highcyl = HWIF(drive)->INB(IDE_BCOUNTH_REG); + + len = lowcyl + (256 * highcyl); + thislen = rq->data_len; + if (thislen > len) + thislen = len; + + /* + * If DRQ is clear, the command has completed. + */ + if ((stat & DRQ_STAT) == 0) + goto end_request; + + /* + * check which way to transfer data + */ + if (rq_data_dir(rq) == WRITE) { + /* + * write to drive + */ + if (cdrom_write_check_ireason(drive, len, ireason)) + return ide_stopped; + + xferfunc = HWIF(drive)->atapi_output_bytes; + } else { + /* + * read from drive + */ + if (cdrom_read_check_ireason(drive, len, ireason)) + return ide_stopped; + + xferfunc = HWIF(drive)->atapi_input_bytes; + } + + /* + * transfer data + */ + while (thislen > 0) { + int blen = blen = rq->data_len; + char *ptr = rq->data; + + /* + * bio backed? + */ + if (rq->bio) { + ptr = bio_data(rq->bio); + blen = bio_iovec(rq->bio)->bv_len; + } + + if (!ptr) { + printk(KERN_ERR "%s: confused, missing data\n", drive->name); + break; + } + + if (blen > thislen) + blen = thislen; + + xferfunc(drive, ptr, blen); + + thislen -= blen; + len -= blen; + rq->data_len -= blen; + + if (rq->bio) + end_that_request_chunk(rq, 1, blen); + else + rq->data += blen; + } + + /* + * pad, if necessary + */ + if (len > 0) { + while (len > 0) { + int pad = 0; + + xferfunc(drive, &pad, sizeof(pad)); + len -= sizeof(pad); + } + } + + if (HWGROUP(drive)->handler != NULL) + BUG(); + + ide_set_handler(drive, cdrom_newpc_intr, rq->timeout, NULL); + return ide_started; + +end_request: + if (!rq->data_len) + post_transform_command(rq); + + spin_lock_irqsave(&ide_lock, flags); + blkdev_dequeue_request(rq); + end_that_request_last(rq); + HWGROUP(drive)->rq = NULL; + spin_unlock_irqrestore(&ide_lock, flags); + return ide_stopped; +} + +static ide_startstop_t cdrom_write_intr(ide_drive_t *drive) +{ + int stat, ireason, len, sectors_to_transfer, uptodate; + struct cdrom_info *info = drive->driver_data; + int dma_error = 0, dma = info->dma; + u8 lowcyl = 0, highcyl = 0; + + struct request *rq = HWGROUP(drive)->rq; + + /* Check for errors. */ + if (dma) { + info->dma = 0; + if ((dma_error = HWIF(drive)->ide_dma_end(drive))) { + printk(KERN_ERR "ide-cd: write dma error\n"); + __ide_dma_off(drive); + } + } + + if (cdrom_decode_status(drive, 0, &stat)) + return ide_stopped; + + /* + * using dma, transfer is complete now + */ + if (dma) { + if (dma_error) + return ide_error(drive, "dma error", stat); + + ide_end_request(drive, 1, rq->nr_sectors); + return ide_stopped; + } + + /* Read the interrupt reason and the transfer length. */ + ireason = HWIF(drive)->INB(IDE_IREASON_REG); + lowcyl = HWIF(drive)->INB(IDE_BCOUNTL_REG); + highcyl = HWIF(drive)->INB(IDE_BCOUNTH_REG); + + len = lowcyl + (256 * highcyl); + + /* If DRQ is clear, the command has completed. */ + if ((stat & DRQ_STAT) == 0) { + /* If we're not done writing, complain. + * Otherwise, complete the command normally. + */ + uptodate = 1; + if (rq->current_nr_sectors > 0) { + printk(KERN_ERR "%s: write_intr: data underrun (%d blocks)\n", + drive->name, rq->current_nr_sectors); + uptodate = 0; + } + cdrom_end_request(drive, uptodate); + return ide_stopped; + } + + /* Check that the drive is expecting to do the same thing we are. */ + if (cdrom_write_check_ireason(drive, len, ireason)) + return ide_stopped; + + sectors_to_transfer = len / SECTOR_SIZE; + + /* + * now loop and write out the data + */ + while (sectors_to_transfer > 0) { + int this_transfer; + + if (!rq->current_nr_sectors) { + printk(KERN_ERR "ide-cd: write_intr: oops\n"); + break; + } + + /* + * Figure out how many sectors we can transfer + */ + this_transfer = min_t(int, sectors_to_transfer, rq->current_nr_sectors); + + while (this_transfer > 0) { + HWIF(drive)->atapi_output_bytes(drive, rq->buffer, SECTOR_SIZE); + rq->buffer += SECTOR_SIZE; + --rq->nr_sectors; + --rq->current_nr_sectors; + ++rq->sector; + --this_transfer; + --sectors_to_transfer; + } + + /* + * current buffer complete, move on + */ + if (rq->current_nr_sectors == 0 && rq->nr_sectors) + cdrom_end_request(drive, 1); + } + + /* re-arm handler */ + ide_set_handler(drive, &cdrom_write_intr, ATAPI_WAIT_PC, NULL); + return ide_started; +} + +static ide_startstop_t cdrom_start_write_cont(ide_drive_t *drive) +{ + struct request *rq = HWGROUP(drive)->rq; + +#if 0 /* the immediate bit */ + rq->cmd[1] = 1 << 3; +#endif + rq->timeout = ATAPI_WAIT_PC; + + return cdrom_transfer_packet_command(drive, rq, cdrom_write_intr); +} + +static ide_startstop_t cdrom_start_write(ide_drive_t *drive, struct request *rq) +{ + struct cdrom_info *info = drive->driver_data; + struct gendisk *g = info->disk; + unsigned short sectors_per_frame = queue_hardsect_size(drive->queue) >> SECTOR_BITS; + + /* + * writes *must* be hardware frame aligned + */ + if ((rq->nr_sectors & (sectors_per_frame - 1)) || + (rq->sector & (sectors_per_frame - 1))) { + cdrom_end_request(drive, 0); + return ide_stopped; + } + + /* + * disk has become write protected + */ + if (g->policy) { + cdrom_end_request(drive, 0); + return ide_stopped; + } + + /* + * for dvd-ram and such media, it's a really big deal to get + * big writes all the time. so scour the queue and attempt to + * remerge requests, often the plugging will not have had time + * to do this properly + */ + blk_attempt_remerge(drive->queue, rq); + + info->nsectors_buffered = 0; + + /* use dma, if possible. we don't need to check more, since we + * know that the transfer is always (at least!) frame aligned */ + info->dma = drive->using_dma ? 1 : 0; + info->cmd = WRITE; + + info->devinfo.media_written = 1; + + /* Start sending the write request to the drive. */ + return cdrom_start_packet_command(drive, 32768, cdrom_start_write_cont); +} + +static ide_startstop_t cdrom_do_newpc_cont(ide_drive_t *drive) +{ + struct request *rq = HWGROUP(drive)->rq; + + if (!rq->timeout) + rq->timeout = ATAPI_WAIT_PC; + + return cdrom_transfer_packet_command(drive, rq, cdrom_newpc_intr); +} + +static ide_startstop_t cdrom_do_block_pc(ide_drive_t *drive, struct request *rq) +{ + struct cdrom_info *info = drive->driver_data; + + rq->flags |= REQ_QUIET; + + info->dma = 0; + info->cmd = 0; + + /* + * sg request + */ + if (rq->bio) { + int mask = drive->queue->dma_alignment; + unsigned long addr = (unsigned long) page_address(bio_page(rq->bio)); + + info->cmd = rq_data_dir(rq); + info->dma = drive->using_dma; + + /* + * check if dma is safe + */ + if ((rq->data_len & mask) || (addr & mask)) + info->dma = 0; + } + + /* Start sending the command to the drive. */ + return cdrom_start_packet_command(drive, rq->data_len, cdrom_do_newpc_cont); +} + +/**************************************************************************** + * cdrom driver request routine. + */ +static ide_startstop_t +ide_do_rw_cdrom (ide_drive_t *drive, struct request *rq, sector_t block) +{ + ide_startstop_t action; + struct cdrom_info *info = drive->driver_data; + + if (blk_fs_request(rq)) { + if (CDROM_CONFIG_FLAGS(drive)->seeking) { + unsigned long elapsed = jiffies - info->start_seek; + int stat = HWIF(drive)->INB(IDE_STATUS_REG); + + if ((stat & SEEK_STAT) != SEEK_STAT) { + if (elapsed < IDECD_SEEK_TIMEOUT) { + ide_stall_queue(drive, IDECD_SEEK_TIMER); + return ide_stopped; + } + printk (KERN_ERR "%s: DSC timeout\n", drive->name); + } + CDROM_CONFIG_FLAGS(drive)->seeking = 0; + } + if ((rq_data_dir(rq) == READ) && IDE_LARGE_SEEK(info->last_block, block, IDECD_SEEK_THRESHOLD) && drive->dsc_overlap) { + action = cdrom_start_seek(drive, block); + } else { + if (rq_data_dir(rq) == READ) + action = cdrom_start_read(drive, block); + else + action = cdrom_start_write(drive, rq); + } + info->last_block = block; + return action; + } else if (rq->flags & (REQ_PC | REQ_SENSE)) { + return cdrom_do_packet_command(drive); + } else if (rq->flags & REQ_BLOCK_PC) { + return cdrom_do_block_pc(drive, rq); + } else if (rq->flags & REQ_SPECIAL) { + /* + * right now this can only be a reset... + */ + cdrom_end_request(drive, 1); + return ide_stopped; + } + + blk_dump_rq_flags(rq, "ide-cd bad flags"); + cdrom_end_request(drive, 0); + 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 request_sense *sense) +{ + struct request req; + struct cdrom_info *info = drive->driver_data; + struct cdrom_device_info *cdi = &info->devinfo; + + cdrom_prepare_request(drive, &req); + + req.sense = sense; + req.cmd[0] = GPCMD_TEST_UNIT_READY; + req.flags |= REQ_QUIET; + +#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 */ + + req.cmd[7] = cdi->sanyo_slot % 3; +#endif /* not STANDARD_ATAPI */ + + return cdrom_queue_packet_command(drive, &req); +} + + +/* 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 request_sense my_sense; + struct request req; + int stat; + + if (sense == NULL) + sense = &my_sense; + + /* If the drive cannot lock the door, just pretend. */ + if (CDROM_CONFIG_FLAGS(drive)->no_doorlock) { + stat = 0; + } else { + cdrom_prepare_request(drive, &req); + req.sense = sense; + req.cmd[0] = GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL; + req.cmd[4] = lockflag ? 1 : 0; + stat = cdrom_queue_packet_command(drive, &req); + } + + /* 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 (KERN_ERR "%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 request_sense *sense) +{ + struct request req; + char loej = 0x02; + + 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; + + cdrom_prepare_request(drive, &req); + + /* only tell drive to close tray if open, if it can do that */ + if (ejectflag && !CDROM_CONFIG_FLAGS(drive)->close_tray) + loej = 0; + + req.sense = sense; + req.cmd[0] = GPCMD_START_STOP_UNIT; + req.cmd[4] = loej | (ejectflag != 0); + return cdrom_queue_packet_command(drive, &req); +} + +static int cdrom_read_capacity(ide_drive_t *drive, unsigned long *capacity, + unsigned long *sectors_per_frame, + struct request_sense *sense) +{ + struct { + __u32 lba; + __u32 blocklen; + } capbuf; + + int stat; + struct request req; + + cdrom_prepare_request(drive, &req); + + req.sense = sense; + req.cmd[0] = GPCMD_READ_CDVD_CAPACITY; + req.data = (char *)&capbuf; + req.data_len = sizeof(capbuf); + + stat = cdrom_queue_packet_command(drive, &req); + if (stat == 0) { + *capacity = 1 + be32_to_cpu(capbuf.lba); + *sectors_per_frame = + be32_to_cpu(capbuf.blocklen) >> SECTOR_BITS; + } + + return stat; +} + +static int cdrom_read_tocentry(ide_drive_t *drive, int trackno, int msf_flag, + int format, char *buf, int buflen, + struct request_sense *sense) +{ + struct request req; + + cdrom_prepare_request(drive, &req); + + req.sense = sense; + req.data = buf; + req.data_len = buflen; + req.flags |= REQ_QUIET; + req.cmd[0] = GPCMD_READ_TOC_PMA_ATIP; + req.cmd[6] = trackno; + req.cmd[7] = (buflen >> 8); + req.cmd[8] = (buflen & 0xff); + req.cmd[9] = (format << 6); + + if (msf_flag) + req.cmd[1] = 2; + + return cdrom_queue_packet_command(drive, &req); +} + + +/* Try to read the entire TOC for the disk into our internal buffer. */ +static int cdrom_read_toc(ide_drive_t *drive, struct request_sense *sense) +{ + int stat, ntracks, i; + struct cdrom_info *info = drive->driver_data; + struct cdrom_device_info *cdi = &info->devinfo; + struct atapi_toc *toc = info->toc; + struct { + struct atapi_toc_header hdr; + struct atapi_toc_entry ent; + } ms_tmp; + long last_written; + unsigned long sectors_per_frame = SECTORS_PER_FRAME; + + 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 (KERN_ERR "%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. */ + (void) cdrom_check_status(drive, sense); + + if (CDROM_STATE_FLAGS(drive)->toc_valid) + return 0; + + /* Try to get the total cdrom capacity and sector size. */ + stat = cdrom_read_capacity(drive, &toc->capacity, §ors_per_frame, + sense); + if (stat) + toc->capacity = 0x1fffff; + + set_capacity(info->disk, toc->capacity * sectors_per_frame); + blk_queue_hardsect_size(drive->queue, + sectors_per_frame << SECTOR_BITS); + + /* 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), sense); + 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), sense); + + 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), + sense); + 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; + } + } + + 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, 0, 1, (char *)&ms_tmp, + sizeof(ms_tmp), sense); + if (stat) return stat; + + toc->last_session_lba = be32_to_cpu(ms_tmp.ent.addr.lba); + } else { + ms_tmp.hdr.first_track = ms_tmp.hdr.last_track = CDROM_LEADOUT; + toc->last_session_lba = msf_to_lba(0, 2, 0); /* 0m 2s 0f */ + } + +#if ! STANDARD_ATAPI + if (CDROM_CONFIG_FLAGS(drive)->tocaddr_as_bcd) { + /* Re-read multisession information using MSF format */ + stat = cdrom_read_tocentry(drive, 0, 1, 1, (char *)&ms_tmp, + sizeof(ms_tmp), sense); + if (stat) + return stat; + + msf_from_bcd (&ms_tmp.ent.addr.msf); + 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); + } +#endif /* not STANDARD_ATAPI */ + + toc->xa_flag = (ms_tmp.hdr.first_track != ms_tmp.hdr.last_track); + + /* Now try to get the total cdrom capacity. */ + stat = cdrom_get_last_written(cdi, &last_written); + if (!stat && (last_written > toc->capacity)) { + toc->capacity = last_written; + set_capacity(info->disk, toc->capacity * sectors_per_frame); + } + + /* Remember that we've read this stuff. */ + CDROM_STATE_FLAGS(drive)->toc_valid = 1; + + return 0; +} + + +static int cdrom_read_subchannel(ide_drive_t *drive, int format, char *buf, + int buflen, struct request_sense *sense) +{ + struct request req; + + cdrom_prepare_request(drive, &req); + + req.sense = sense; + req.data = buf; + req.data_len = buflen; + req.cmd[0] = GPCMD_READ_SUBCHANNEL; + req.cmd[1] = 2; /* MSF addressing */ + req.cmd[2] = 0x40; /* request subQ data */ + req.cmd[3] = format; + req.cmd[7] = (buflen >> 8); + req.cmd[8] = (buflen & 0xff); + return cdrom_queue_packet_command(drive, &req); +} + +/* 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 request_sense *sense) +{ + struct request req; + cdrom_prepare_request(drive, &req); + + req.sense = sense; + if (speed == 0) + speed = 0xffff; /* set to max */ + else + speed *= 177; /* Nx to kbytes/s */ + + req.cmd[0] = GPCMD_SET_SPEED; + /* Read Drive speed in kbytes/second MSB */ + req.cmd[2] = (speed >> 8) & 0xff; + /* Read Drive speed in kbytes/second LSB */ + req.cmd[3] = speed & 0xff; + if (CDROM_CONFIG_FLAGS(drive)->cd_r || + CDROM_CONFIG_FLAGS(drive)->cd_rw || + CDROM_CONFIG_FLAGS(drive)->dvd_r) { + /* Write Drive speed in kbytes/second MSB */ + req.cmd[4] = (speed >> 8) & 0xff; + /* Write Drive speed in kbytes/second LSB */ + req.cmd[5] = speed & 0xff; + } + + return cdrom_queue_packet_command(drive, &req); +} + +static int cdrom_play_audio(ide_drive_t *drive, int lba_start, int lba_end) +{ + struct request_sense sense; + struct request req; + + cdrom_prepare_request(drive, &req); + + req.sense = &sense; + req.cmd[0] = GPCMD_PLAY_AUDIO_MSF; + lba_to_msf(lba_start, &req.cmd[3], &req.cmd[4], &req.cmd[5]); + lba_to_msf(lba_end-1, &req.cmd[6], &req.cmd[7], &req.cmd[8]); + + return cdrom_queue_packet_command(drive, &req); +} + +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; + + /* + * don't serve cached data, if the toc isn't valid + */ + if (!CDROM_STATE_FLAGS(drive)->toc_valid) + return -EINVAL; + + /* 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 packet_command *cgc) +{ + struct request req; + ide_drive_t *drive = (ide_drive_t*) cdi->handle; + + if (cgc->timeout <= 0) + cgc->timeout = ATAPI_WAIT_PC; + + /* here we queue the commands from the uniform CD-ROM + layer. the packet must be complete, as we do not + touch it at all. */ + cdrom_prepare_request(drive, &req); + memcpy(req.cmd, cgc->cmd, CDROM_PACKET_SIZE); + if (cgc->sense) + memset(cgc->sense, 0, sizeof(struct request_sense)); + req.data = cgc->buffer; + req.data_len = cgc->buflen; + req.timeout = cgc->timeout; + + if (cgc->quiet) + req.flags |= REQ_QUIET; + + req.sense = cgc->sense; + cgc->stat = cdrom_queue_packet_command(drive, &req); + if (!cgc->stat) + cgc->buflen -= req.data_len; + return cgc->stat; +} + +static +int ide_cdrom_dev_ioctl (struct cdrom_device_info *cdi, + unsigned int cmd, unsigned long arg) +{ + struct packet_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 __user *) 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 __user *) 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; + int stat; + + switch (cmd) { + /* + * emulate PLAY_AUDIO_TI command with PLAY_AUDIO_10, since + * atapi doesn't support it + */ + case CDROMPLAYTRKIND: { + unsigned long lba_start, lba_end; + struct cdrom_ti *ti = (struct cdrom_ti *)arg; + struct atapi_toc_entry *first_toc, *last_toc; + + stat = cdrom_get_toc_entry(drive, ti->cdti_trk0, &first_toc); + if (stat) + return stat; + + stat = cdrom_get_toc_entry(drive, ti->cdti_trk1, &last_toc); + if (stat) + return stat; + + if (ti->cdti_trk1 != CDROM_LEADOUT) + ++last_toc; + lba_start = first_toc->addr.lba; + lba_end = last_toc->addr.lba; + + if (lba_end <= lba_start) + return -EINVAL; + + return cdrom_play_audio(drive, lba_start, lba_end); + } + + case CDROMREADTOCHDR: { + struct cdrom_tochdr *tochdr = (struct cdrom_tochdr *) arg; + struct atapi_toc *toc; + + /* Make sure our saved TOC is valid. */ + stat = cdrom_read_toc(drive, NULL); + 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: { + 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_sense sense; + struct request req; + int ret; + + cdrom_prepare_request(drive, &req); + req.flags = REQ_SPECIAL | REQ_QUIET; + ret = ide_do_drive_cmd(drive, &req, ide_wait); + + /* + * A reset will unlock the door. If it was previously locked, + * lock it again. + */ + if (CDROM_STATE_FLAGS(drive)->door_locked) + (void) cdrom_lockdoor(drive, 1, &sense); + + return ret; +} + + +static +int ide_cdrom_tray_move (struct cdrom_device_info *cdi, int position) +{ + ide_drive_t *drive = (ide_drive_t*) cdi->handle; + struct request_sense sense; + + if (position) { + int stat = cdrom_lockdoor(drive, 0, &sense); + if (stat) return stat; + } + + return cdrom_eject(drive, !position, &sense); +} + +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, NULL); +} + +static +int ide_cdrom_select_speed (struct cdrom_device_info *cdi, int speed) +{ + ide_drive_t *drive = (ide_drive_t*) cdi->handle; + struct request_sense sense; + int stat; + + if ((stat = cdrom_select_speed(drive, speed, &sense)) < 0) + return stat; + + cdi->speed = CDROM_STATE_FLAGS(drive)->current_speed; + return 0; +} + +/* + * add logic to try GET_EVENT command first to check for media and tray + * status. this should be supported by newer cd-r/w and all DVD etc + * drives + */ +static +int ide_cdrom_drive_status (struct cdrom_device_info *cdi, int slot_nr) +{ + ide_drive_t *drive = (ide_drive_t*) cdi->handle; + struct media_event_desc med; + struct request_sense sense; + int stat; + + if (slot_nr != CDSL_CURRENT) + return -EINVAL; + + stat = cdrom_check_status(drive, &sense); + if (!stat || sense.sense_key == UNIT_ATTENTION) + return CDS_DISC_OK; + + if (!cdrom_get_media_event(cdi, &med)) { + if (med.media_present) + return CDS_DISC_OK; + else if (med.door_open) + return CDS_TRAY_OPEN; + else + return CDS_NO_DISC; + } + + if (sense.sense_key == NOT_READY && sense.asc == 0x04 && sense.ascq == 0x04) + return CDS_DISC_OK; + + /* + * If not using Mt Fuji extended media tray reports, + * just return TRAY_OPEN since ATAPI doesn't provide + * any other way to detect this... + */ + if (sense.sense_key == NOT_READY) { + if (sense.asc == 0x3a) { + if (sense.ascq == 1) + return CDS_NO_DISC; + else if (sense.ascq == 0 || sense.ascq == 2) + return CDS_TRAY_OPEN; + } + } + + return CDS_DRIVE_NOT_READY; +} + +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; + struct request_sense sense; + int ret; + + if (!CDROM_STATE_FLAGS(drive)->toc_valid || info->toc == NULL) + if ((ret = cdrom_read_toc(drive, &sense))) + return ret; + + 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), NULL))) + 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; + int retval; + + if (slot_nr == CDSL_CURRENT) { + (void) cdrom_check_status(drive, NULL); + retval = CDROM_STATE_FLAGS(drive)->media_changed; + CDROM_STATE_FLAGS(drive)->media_changed = 0; + return retval; + } 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) +{ + ide_drive_t *drive = cdi->handle; + + if (!cdi->use_count) + CDROM_STATE_FLAGS(drive)->toc_valid = 0; +} + + + +/**************************************************************************** + * Device initialization. + */ +static struct cdrom_device_ops ide_cdrom_dops = { + .open = ide_cdrom_open_real, + .release = ide_cdrom_release_real, + .drive_status = ide_cdrom_drive_status, + .media_changed = ide_cdrom_check_media_change_real, + .tray_move = ide_cdrom_tray_move, + .lock_door = ide_cdrom_lock_door, + .select_speed = ide_cdrom_select_speed, + .get_last_session = ide_cdrom_get_last_session, + .get_mcn = ide_cdrom_get_mcn, + .reset = ide_cdrom_reset, + .audio_ioctl = ide_cdrom_audio_ioctl, + .dev_ioctl = ide_cdrom_dev_ioctl, + .capability = 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 | CDC_MO_DRIVE | CDC_MRW | + CDC_MRW_W | CDC_RAM, + .generic_packet = 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; + + devinfo->ops = &ide_cdrom_dops; + devinfo->mask = 0; + devinfo->speed = CDROM_STATE_FLAGS(drive)->current_speed; + 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; + if (!CDROM_CONFIG_FLAGS(drive)->mo_drive) + devinfo->mask |= CDC_MO_DRIVE; + + devinfo->disk = info->disk; + return register_cdrom(devinfo); +} + +static +int ide_cdrom_get_capabilities(ide_drive_t *drive, struct atapi_capabilities_page *cap) +{ + struct cdrom_info *info = drive->driver_data; + struct cdrom_device_info *cdi = &info->devinfo; + struct packet_command cgc; + int stat, attempts = 3, size = sizeof(*cap); + + /* + * ACER50 (and others?) require the full spec length mode sense + * page capabilities size, but older drives break. + */ + if (!(!strcmp(drive->id->model, "ATAPI CD ROM DRIVE 50X MAX") || + !strcmp(drive->id->model, "WPI CDS-32X"))) + size -= sizeof(cap->pad); + + init_cdrom_command(&cgc, cap, size, CGC_DATA_UNKNOWN); + do { /* we seem to get stat=0x01,err=0x00 the first time (??) */ + stat = cdrom_mode_sense(cdi, &cgc, GPMODE_CAPABILITIES_PAGE, 0); + if (!stat) + break; + } while (--attempts); + return stat; +} + +static +int ide_cdrom_probe_capabilities (ide_drive_t *drive) +{ + struct cdrom_info *info = drive->driver_data; + struct cdrom_device_info *cdi = &info->devinfo; + struct atapi_capabilities_page cap; + int nslots = 1; + + if (drive->media == ide_optical) { + CDROM_CONFIG_FLAGS(drive)->mo_drive = 1; + CDROM_CONFIG_FLAGS(drive)->ram = 1; + printk(KERN_ERR "%s: ATAPI magneto-optical drive\n", drive->name); + return nslots; + } + + if (CDROM_CONFIG_FLAGS(drive)->nec260 || + !strcmp(drive->id->model,"STINGRAY 8422 IDE 8X CD-ROM 7-27-95")) { + CDROM_CONFIG_FLAGS(drive)->no_eject = 0; + CDROM_CONFIG_FLAGS(drive)->audio_play = 1; + return nslots; + } + + /* + * 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; + + if (ide_cdrom_get_capabilities(drive, &cap)) + return 0; + + if (cap.lock == 0) + CDROM_CONFIG_FLAGS(drive)->no_doorlock = 1; + if (cap.eject) + CDROM_CONFIG_FLAGS(drive)->no_eject = 0; + if (cap.cd_r_write) + CDROM_CONFIG_FLAGS(drive)->cd_r = 1; + if (cap.cd_rw_write) { + CDROM_CONFIG_FLAGS(drive)->cd_rw = 1; + CDROM_CONFIG_FLAGS(drive)->ram = 1; + } + if (cap.test_write) + CDROM_CONFIG_FLAGS(drive)->test_write = 1; + if (cap.dvd_ram_read || cap.dvd_r_read || cap.dvd_rom) + CDROM_CONFIG_FLAGS(drive)->dvd = 1; + if (cap.dvd_ram_write) { + CDROM_CONFIG_FLAGS(drive)->dvd_ram = 1; + CDROM_CONFIG_FLAGS(drive)->ram = 1; + } + if (cap.dvd_r_write) + CDROM_CONFIG_FLAGS(drive)->dvd_r = 1; + if (cap.audio_play) + CDROM_CONFIG_FLAGS(drive)->audio_play = 1; + if (cap.mechtype == mechtype_caddy || cap.mechtype == mechtype_popup) + CDROM_CONFIG_FLAGS(drive)->close_tray = 0; + + /* Some drives used by Apple don't advertise audio play + * but they do support reading TOC & audio datas + */ + if (strcmp(drive->id->model, "MATSHITADVD-ROM SR-8187") == 0 || + strcmp(drive->id->model, "MATSHITADVD-ROM SR-8186") == 0 || + strcmp(drive->id->model, "MATSHITADVD-ROM SR-8176") == 0 || + strcmp(drive->id->model, "MATSHITADVD-ROM SR-8174") == 0) + CDROM_CONFIG_FLAGS(drive)->audio_play = 1; + +#if ! STANDARD_ATAPI + if (cdi->sanyo_slot > 0) { + CDROM_CONFIG_FLAGS(drive)->is_changer = 1; + nslots = 3; + } + + else +#endif /* not STANDARD_ATAPI */ + if (cap.mechtype == mechtype_individual_changer || + 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; + } + } + + /* The ACER/AOpen 24X cdrom has the speed fields byte-swapped */ + if (!drive->id->model[0] && + !strncmp(drive->id->fw_rev, "241N", 4)) { + CDROM_STATE_FLAGS(drive)->current_speed = + (((unsigned int)cap.curspeed) + (176/2)) / 176; + CDROM_CONFIG_FLAGS(drive)->max_speed = + (((unsigned int)cap.maxspeed) + (176/2)) / 176; + } else { + CDROM_STATE_FLAGS(drive)->current_speed = + (ntohs(cap.curspeed) + (176/2)) / 176; + CDROM_CONFIG_FLAGS(drive)->max_speed = + (ntohs(cap.maxspeed) + (176/2)) / 176; + } + + /* don't print speed if the drive reported 0. + */ + printk(KERN_INFO "%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)? "-RAM" : ""); + + 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(cap.buffer_size)); + + if (drive->using_dma) + ide_dma_verbose(drive); + + printk("\n"); + + return nslots; +} + +static void ide_cdrom_add_settings(ide_drive_t *drive) +{ + ide_add_setting(drive, "dsc_overlap", SETTING_RW, -1, -1, TYPE_BYTE, 0, 1, 1, 1, &drive->dsc_overlap, NULL); +} + +/* + * standard prep_rq_fn that builds 10 byte cmds + */ +static int ide_cdrom_prep_fs(request_queue_t *q, struct request *rq) +{ + int hard_sect = queue_hardsect_size(q); + long block = (long)rq->hard_sector / (hard_sect >> 9); + unsigned long blocks = rq->hard_nr_sectors / (hard_sect >> 9); + + memset(rq->cmd, 0, sizeof(rq->cmd)); + + if (rq_data_dir(rq) == READ) + rq->cmd[0] = GPCMD_READ_10; + else + rq->cmd[0] = GPCMD_WRITE_10; + + /* + * fill in lba + */ + rq->cmd[2] = (block >> 24) & 0xff; + rq->cmd[3] = (block >> 16) & 0xff; + rq->cmd[4] = (block >> 8) & 0xff; + rq->cmd[5] = block & 0xff; + + /* + * and transfer length + */ + rq->cmd[7] = (blocks >> 8) & 0xff; + rq->cmd[8] = blocks & 0xff; + rq->cmd_len = 10; + return BLKPREP_OK; +} + +/* + * Most of the SCSI commands are supported directly by ATAPI devices. + * This transform handles the few exceptions. + */ +static int ide_cdrom_prep_pc(struct request *rq) +{ + u8 *c = rq->cmd; + + /* + * Transform 6-byte read/write commands to the 10-byte version + */ + if (c[0] == READ_6 || c[0] == WRITE_6) { + c[8] = c[4]; + c[5] = c[3]; + c[4] = c[2]; + c[3] = c[1] & 0x1f; + c[2] = 0; + c[1] &= 0xe0; + c[0] += (READ_10 - READ_6); + rq->cmd_len = 10; + return BLKPREP_OK; + } + + /* + * it's silly to pretend we understand 6-byte sense commands, just + * reject with ILLEGAL_REQUEST and the caller should take the + * appropriate action + */ + if (c[0] == MODE_SENSE || c[0] == MODE_SELECT) { + rq->errors = ILLEGAL_REQUEST; + return BLKPREP_KILL; + } + + return BLKPREP_OK; +} + +static int ide_cdrom_prep_fn(request_queue_t *q, struct request *rq) +{ + if (rq->flags & REQ_CMD) + return ide_cdrom_prep_fs(q, rq); + else if (rq->flags & REQ_BLOCK_PC) + return ide_cdrom_prep_pc(rq); + + return 0; +} + +static +int ide_cdrom_setup (ide_drive_t *drive) +{ + struct cdrom_info *info = drive->driver_data; + struct cdrom_device_info *cdi = &info->devinfo; + int nslots; + + blk_queue_prep_rq(drive->queue, ide_cdrom_prep_fn); + blk_queue_dma_alignment(drive->queue, 31); + drive->queue->unplug_delay = (1 * HZ) / 1000; + if (!drive->queue->unplug_delay) + drive->queue->unplug_delay = 1; + + drive->special.all = 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 + + CDROM_CONFIG_FLAGS(drive)->drq_interrupt = ((drive->id->config & 0x0060) == 0x20); + 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; + /* 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 (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); + + /* + * set correct block size + */ + blk_queue_hardsect_size(drive->queue, CD_FRAMESIZE); + + if (drive->autotune == IDE_TUNE_DEFAULT || + drive->autotune == IDE_TUNE_AUTO) + drive->dsc_overlap = (drive->next != drive); +#if 0 + drive->dsc_overlap = (HWIF(drive)->no_dsc) ? 0 : 1; + if (HWIF(drive)->no_dsc) { + printk(KERN_INFO "ide-cd: %s: disabling DSC overlap\n", + drive->name); + drive->dsc_overlap = 0; + } +#endif + + if (ide_cdrom_register(drive, nslots)) { + printk (KERN_ERR "%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; +} + +static +sector_t ide_cdrom_capacity (ide_drive_t *drive) +{ + unsigned long capacity, sectors_per_frame; + + if (cdrom_read_capacity(drive, &capacity, §ors_per_frame, NULL)) + return 0; + + return capacity * sectors_per_frame; +} + +static +int ide_cdrom_cleanup(ide_drive_t *drive) +{ + struct cdrom_info *info = drive->driver_data; + + if (ide_unregister_subdriver(drive)) { + printk(KERN_ERR "%s: %s: failed to ide_unregister_subdriver\n", + __FUNCTION__, drive->name); + return 1; + } + + del_gendisk(info->disk); + + ide_cd_put(info); + + return 0; +} + +static void ide_cd_release(struct kref *kref) +{ + struct cdrom_info *info = to_ide_cd(kref); + struct cdrom_device_info *devinfo = &info->devinfo; + ide_drive_t *drive = info->drive; + struct gendisk *g = info->disk; + + 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(KERN_ERR "%s: %s failed to unregister device from the cdrom " + "driver.\n", __FUNCTION__, drive->name); + drive->dsc_overlap = 0; + drive->driver_data = NULL; + blk_queue_prep_rq(drive->queue, NULL); + g->private_data = NULL; + put_disk(g); + kfree(info); +} + +static int ide_cdrom_attach (ide_drive_t *drive); + +#ifdef CONFIG_PROC_FS +static int proc_idecd_read_capacity + (char *page, char **start, off_t off, int count, int *eof, void *data) +{ + ide_drive_t*drive = (ide_drive_t *)data; + int len; + + len = sprintf(page,"%llu\n", (long long)ide_cdrom_capacity(drive)); + PROC_IDE_READ_RETURN(page,start,off,count,eof,len); +} + +static ide_proc_entry_t idecd_proc[] = { + { "capacity", S_IFREG|S_IRUGO, proc_idecd_read_capacity, NULL }, + { NULL, 0, NULL, NULL } +}; +#else +# define idecd_proc NULL +#endif + +static ide_driver_t ide_cdrom_driver = { + .owner = THIS_MODULE, + .name = "ide-cdrom", + .version = IDECD_VERSION, + .media = ide_cdrom, + .busy = 0, + .supports_dsc_overlap = 1, + .cleanup = ide_cdrom_cleanup, + .do_request = ide_do_rw_cdrom, + .end_request = ide_end_request, + .error = __ide_error, + .abort = __ide_abort, + .proc = idecd_proc, + .attach = ide_cdrom_attach, + .drives = LIST_HEAD_INIT(ide_cdrom_driver.drives), +}; + +static int idecd_open(struct inode * inode, struct file * file) +{ + struct gendisk *disk = inode->i_bdev->bd_disk; + struct cdrom_info *info; + ide_drive_t *drive; + int rc = -ENOMEM; + + if (!(info = ide_cd_get(disk))) + return -ENXIO; + + drive = info->drive; + + drive->usage++; + + if (!info->buffer) + info->buffer = kmalloc(SECTOR_BUFFER_SIZE, + GFP_KERNEL|__GFP_REPEAT); + if (!info->buffer || (rc = cdrom_open(&info->devinfo, inode, file))) + drive->usage--; + + if (rc < 0) + ide_cd_put(info); + + return rc; +} + +static int idecd_release(struct inode * inode, struct file * file) +{ + struct gendisk *disk = inode->i_bdev->bd_disk; + struct cdrom_info *info = ide_cd_g(disk); + ide_drive_t *drive = info->drive; + + cdrom_release (&info->devinfo, file); + drive->usage--; + + ide_cd_put(info); + + return 0; +} + +static int idecd_ioctl (struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct block_device *bdev = inode->i_bdev; + struct cdrom_info *info = ide_cd_g(bdev->bd_disk); + int err; + + err = generic_ide_ioctl(info->drive, file, bdev, cmd, arg); + if (err == -EINVAL) + err = cdrom_ioctl(file, &info->devinfo, inode, cmd, arg); + + return err; +} + +static int idecd_media_changed(struct gendisk *disk) +{ + struct cdrom_info *info = ide_cd_g(disk); + return cdrom_media_changed(&info->devinfo); +} + +static int idecd_revalidate_disk(struct gendisk *disk) +{ + struct cdrom_info *info = ide_cd_g(disk); + struct request_sense sense; + cdrom_read_toc(info->drive, &sense); + return 0; +} + +static struct block_device_operations idecd_ops = { + .owner = THIS_MODULE, + .open = idecd_open, + .release = idecd_release, + .ioctl = idecd_ioctl, + .media_changed = idecd_media_changed, + .revalidate_disk= idecd_revalidate_disk +}; + +/* options */ +static char *ignore = NULL; + +module_param(ignore, charp, 0400); +MODULE_DESCRIPTION("ATAPI CD-ROM Driver"); + +static int ide_cdrom_attach (ide_drive_t *drive) +{ + struct cdrom_info *info; + struct gendisk *g; + struct request_sense sense; + + if (!strstr("ide-cdrom", drive->driver_req)) + goto failed; + if (!drive->present) + goto failed; + if (drive->media != ide_cdrom && drive->media != ide_optical) + goto failed; + /* skip drives that we were told to ignore */ + if (ignore != NULL) { + if (strstr(ignore, drive->name)) { + printk(KERN_INFO "ide-cd: ignoring drive %s\n", drive->name); + goto failed; + } + } + if (drive->scsi) { + printk(KERN_INFO "ide-cd: passing drive %s to ide-scsi emulation.\n", drive->name); + goto failed; + } + info = (struct cdrom_info *) kmalloc (sizeof (struct cdrom_info), GFP_KERNEL); + if (info == NULL) { + printk(KERN_ERR "%s: Can't allocate a cdrom structure\n", drive->name); + goto failed; + } + + g = alloc_disk(1 << PARTN_BITS); + if (!g) + goto out_free_cd; + + ide_init_disk(g, drive); + + if (ide_register_subdriver(drive, &ide_cdrom_driver)) { + printk(KERN_ERR "%s: Failed to register the driver with ide.c\n", + drive->name); + goto out_put_disk; + } + memset(info, 0, sizeof (struct cdrom_info)); + + kref_init(&info->kref); + + info->drive = drive; + info->driver = &ide_cdrom_driver; + info->disk = g; + + g->private_data = &info->driver; + + drive->driver_data = info; + + DRIVER(drive)->busy++; + g->minors = 1; + snprintf(g->devfs_name, sizeof(g->devfs_name), + "%s/cd", drive->devfs_name); + g->driverfs_dev = &drive->gendev; + g->flags = GENHD_FL_CD | GENHD_FL_REMOVABLE; + if (ide_cdrom_setup(drive)) { + struct cdrom_device_info *devinfo = &info->devinfo; + DRIVER(drive)->busy--; + ide_unregister_subdriver(drive); + 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 (KERN_ERR "%s: ide_cdrom_cleanup failed to unregister device from the cdrom driver.\n", drive->name); + kfree(info); + drive->driver_data = NULL; + goto failed; + } + DRIVER(drive)->busy--; + + cdrom_read_toc(drive, &sense); + g->fops = &idecd_ops; + g->flags |= GENHD_FL_REMOVABLE; + add_disk(g); + return 0; + +out_put_disk: + put_disk(g); +out_free_cd: + kfree(info); +failed: + return 1; +} + +static void __exit ide_cdrom_exit(void) +{ + ide_unregister_driver(&ide_cdrom_driver); +} + +static int ide_cdrom_init(void) +{ + ide_register_driver(&ide_cdrom_driver); + return 0; +} + +module_init(ide_cdrom_init); +module_exit(ide_cdrom_exit); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/ide-cd.h b/drivers/ide/ide-cd.h new file mode 100644 index 0000000..7ca3e5a --- /dev/null +++ b/drivers/ide/ide-cd.h @@ -0,0 +1,746 @@ +/* + * linux/drivers/ide/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 + +/* + * typical timeout for packet command + */ +#define ATAPI_WAIT_PC (60 * HZ) +#define ATAPI_WAIT_WRITE_BUSY (10 * HZ) + +/************************************************************************/ + +#define SECTOR_BITS 9 +#ifndef SECTOR_SIZE +#define SECTOR_SIZE (1 << SECTOR_BITS) +#endif +#define SECTORS_PER_FRAME (CD_FRAMESIZE >> SECTOR_BITS) +#define SECTOR_BUFFER_SIZE (CD_FRAMESIZE * 32) +#define SECTORS_BUFFER (SECTOR_BUFFER_SIZE >> SECTOR_BITS) +#define SECTORS_MAX (131072 >> SECTOR_BITS) + +#define BLOCKS_PER_FRAME (CD_FRAMESIZE / BLOCK_SIZE) + +/* 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 ram : 1; /* generic WRITE (dvd-ram/mrw) */ + __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 mo_drive : 1; /* drive is an MO device */ + __u8 reserved : 2; + 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)) + +/* 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 long 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 { + struct mode_page_header header; +#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; + char pad[4]; +}; + + +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; + __u16 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 { + ide_drive_t *drive; + ide_driver_t *driver; + struct gendisk *disk; + struct kref kref; + + /* 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; + int dma; + int cmd; + 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; + + unsigned long write_timeout; +}; + +/**************************************************************************** + * 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 BLANK_CHECK 0x08 +#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. */ +static 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. */ +static const char * const sense_key_texts[16] = { + "No sense data", + "Recovered error", + "Not ready", + "Medium error", + "Hardware error", + "Illegal request", + "Unit attention", + "Data protect", + "Blank check", + "(reserved)", + "(reserved)", + "Aborted command", + "(reserved)", + "(reserved)", + "Miscompare", + "(reserved)", +}; + +/* From Table 304 of the SFF8090 Ver. 3 (Mt. Fuji) draft standard. */ +static 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, "Logical unit not ready - format in progress" }, + { 0x020407, "Logical unit not ready - operation in progress" }, + { 0x020408, "Logical unit not ready - long write in progress" }, + { 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" }, + { 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-disk.c b/drivers/ide/ide-disk.c new file mode 100644 index 0000000..5d54f77 --- /dev/null +++ b/drivers/ide/ide-disk.c @@ -0,0 +1,1280 @@ +/* + * linux/drivers/ide/ide-disk.c Version 1.18 Mar 05, 2003 + * + * Copyright (C) 1994-1998 Linus Torvalds & authors (see below) + * Copyright (C) 1998-2002 Linux ATA Development + * Andre Hedrick <andre@linux-ide.org> + * Copyright (C) 2003 Red Hat <alan@redhat.com> + */ + +/* + * Mostly written by Mark Lord <mlord@pobox.com> + * and Gadi Oxman <gadio@netvision.net.il> + * and Andre Hedrick <andre@linux-ide.org> + * + * 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 + * Version 1.10 request queue changes, Ultra DMA 100 + * Version 1.11 added 48-bit lba + * Version 1.12 adding taskfile io access method + * Version 1.13 added standby and flush-cache for notifier + * Version 1.14 added acoustic-wcache + * Version 1.15 convert all calls to ide_raw_taskfile + * since args will return register content. + * Version 1.16 added suspend-resume-checkpower + * Version 1.17 do flush on standy, do flush on ATA < ATA6 + * fix wcache setup. + */ + +#define IDEDISK_VERSION "1.18" + +#undef REALLY_SLOW_IO /* most systems can safely undef this */ + +//#define DEBUG + +#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/slab.h> +#include <linux/delay.h> + +#define _IDE_DISK + +#include <linux/ide.h> + +#include <asm/byteorder.h> +#include <asm/irq.h> +#include <asm/uaccess.h> +#include <asm/io.h> +#include <asm/div64.h> + +struct ide_disk_obj { + ide_drive_t *drive; + ide_driver_t *driver; + struct gendisk *disk; + struct kref kref; +}; + +static DECLARE_MUTEX(idedisk_ref_sem); + +#define to_ide_disk(obj) container_of(obj, struct ide_disk_obj, kref) + +#define ide_disk_g(disk) \ + container_of((disk)->private_data, struct ide_disk_obj, driver) + +static struct ide_disk_obj *ide_disk_get(struct gendisk *disk) +{ + struct ide_disk_obj *idkp = NULL; + + down(&idedisk_ref_sem); + idkp = ide_disk_g(disk); + if (idkp) + kref_get(&idkp->kref); + up(&idedisk_ref_sem); + return idkp; +} + +static void ide_disk_release(struct kref *); + +static void ide_disk_put(struct ide_disk_obj *idkp) +{ + down(&idedisk_ref_sem); + kref_put(&idkp->kref, ide_disk_release); + up(&idedisk_ref_sem); +} + +/* + * 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 */ +} + +/* + * __ide_do_rw_disk() issues READ and WRITE commands to a disk, + * using LBA if supported, or CHS otherwise, to address sectors. + */ +static ide_startstop_t __ide_do_rw_disk(ide_drive_t *drive, struct request *rq, sector_t block) +{ + ide_hwif_t *hwif = HWIF(drive); + unsigned int dma = drive->using_dma; + u8 lba48 = (drive->addressing == 1) ? 1 : 0; + task_ioreg_t command = WIN_NOP; + ata_nsector_t nsectors; + + nsectors.all = (u16) rq->nr_sectors; + + if (hwif->no_lba48_dma && lba48 && dma) { + if (block + rq->nr_sectors > 1ULL << 28) + dma = 0; + else + lba48 = 0; + } + + if (!dma) { + ide_init_sg_cmd(drive, rq); + ide_map_sg(drive, rq); + } + + if (IDE_CONTROL_REG) + hwif->OUTB(drive->ctl, IDE_CONTROL_REG); + + /* FIXME: SELECT_MASK(drive, 0) ? */ + + if (drive->select.b.lba) { + if (lba48) { + task_ioreg_t tasklets[10]; + + pr_debug("%s: LBA=0x%012llx\n", drive->name, block); + + tasklets[0] = 0; + tasklets[1] = 0; + tasklets[2] = nsectors.b.low; + tasklets[3] = nsectors.b.high; + tasklets[4] = (task_ioreg_t) block; + tasklets[5] = (task_ioreg_t) (block>>8); + tasklets[6] = (task_ioreg_t) (block>>16); + tasklets[7] = (task_ioreg_t) (block>>24); + if (sizeof(block) == 4) { + tasklets[8] = (task_ioreg_t) 0; + tasklets[9] = (task_ioreg_t) 0; + } else { + tasklets[8] = (task_ioreg_t)((u64)block >> 32); + tasklets[9] = (task_ioreg_t)((u64)block >> 40); + } +#ifdef DEBUG + printk("%s: 0x%02x%02x 0x%02x%02x%02x%02x%02x%02x\n", + drive->name, tasklets[3], tasklets[2], + tasklets[9], tasklets[8], tasklets[7], + tasklets[6], tasklets[5], tasklets[4]); +#endif + hwif->OUTB(tasklets[1], IDE_FEATURE_REG); + hwif->OUTB(tasklets[3], IDE_NSECTOR_REG); + hwif->OUTB(tasklets[7], IDE_SECTOR_REG); + hwif->OUTB(tasklets[8], IDE_LCYL_REG); + hwif->OUTB(tasklets[9], IDE_HCYL_REG); + + hwif->OUTB(tasklets[0], IDE_FEATURE_REG); + hwif->OUTB(tasklets[2], IDE_NSECTOR_REG); + hwif->OUTB(tasklets[4], IDE_SECTOR_REG); + hwif->OUTB(tasklets[5], IDE_LCYL_REG); + hwif->OUTB(tasklets[6], IDE_HCYL_REG); + hwif->OUTB(0x00|drive->select.all,IDE_SELECT_REG); + } else { + hwif->OUTB(0x00, IDE_FEATURE_REG); + hwif->OUTB(nsectors.b.low, IDE_NSECTOR_REG); + hwif->OUTB(block, IDE_SECTOR_REG); + hwif->OUTB(block>>=8, IDE_LCYL_REG); + hwif->OUTB(block>>=8, IDE_HCYL_REG); + hwif->OUTB(((block>>8)&0x0f)|drive->select.all,IDE_SELECT_REG); + } + } else { + unsigned int sect,head,cyl,track; + track = (int)block / drive->sect; + sect = (int)block % drive->sect + 1; + hwif->OUTB(sect, IDE_SECTOR_REG); + head = track % drive->head; + cyl = track / drive->head; + + pr_debug("%s: CHS=%u/%u/%u\n", drive->name, cyl, head, sect); + + hwif->OUTB(0x00, IDE_FEATURE_REG); + hwif->OUTB(nsectors.b.low, IDE_NSECTOR_REG); + hwif->OUTB(cyl, IDE_LCYL_REG); + hwif->OUTB(cyl>>8, IDE_HCYL_REG); + hwif->OUTB(head|drive->select.all,IDE_SELECT_REG); + } + + if (dma) { + if (!hwif->dma_setup(drive)) { + if (rq_data_dir(rq)) { + command = lba48 ? WIN_WRITEDMA_EXT : WIN_WRITEDMA; + if (drive->vdma) + command = lba48 ? WIN_WRITE_EXT: WIN_WRITE; + } else { + command = lba48 ? WIN_READDMA_EXT : WIN_READDMA; + if (drive->vdma) + command = lba48 ? WIN_READ_EXT: WIN_READ; + } + hwif->dma_exec_cmd(drive, command); + hwif->dma_start(drive); + return ide_started; + } + /* fallback to PIO */ + ide_init_sg_cmd(drive, rq); + } + + if (rq_data_dir(rq) == READ) { + + if (drive->mult_count) { + hwif->data_phase = TASKFILE_MULTI_IN; + command = lba48 ? WIN_MULTREAD_EXT : WIN_MULTREAD; + } else { + hwif->data_phase = TASKFILE_IN; + command = lba48 ? WIN_READ_EXT : WIN_READ; + } + + ide_execute_command(drive, command, &task_in_intr, WAIT_CMD, NULL); + return ide_started; + } else { + if (drive->mult_count) { + hwif->data_phase = TASKFILE_MULTI_OUT; + command = lba48 ? WIN_MULTWRITE_EXT : WIN_MULTWRITE; + } else { + hwif->data_phase = TASKFILE_OUT; + command = lba48 ? WIN_WRITE_EXT : WIN_WRITE; + } + + /* FIXME: ->OUTBSYNC ? */ + hwif->OUTB(command, IDE_COMMAND_REG); + + return pre_task_out_intr(drive, rq); + } +} + +/* + * 268435455 == 137439 MB or 28bit limit + * 320173056 == 163929 MB or 48bit addressing + * 1073741822 == 549756 MB or 48bit addressing fake drive + */ + +static ide_startstop_t ide_do_rw_disk (ide_drive_t *drive, struct request *rq, sector_t block) +{ + ide_hwif_t *hwif = HWIF(drive); + + BUG_ON(drive->blocked); + + if (!blk_fs_request(rq)) { + blk_dump_rq_flags(rq, "ide_do_rw_disk - bad command"); + ide_end_request(drive, 0, 0); + return ide_stopped; + } + + pr_debug("%s: %sing: block=%llu, sectors=%lu, buffer=0x%08lx\n", + drive->name, rq_data_dir(rq) == READ ? "read" : "writ", + block, rq->nr_sectors, (unsigned long)rq->buffer); + + if (hwif->rw_disk) + hwif->rw_disk(drive, rq); + + return __ide_do_rw_disk(drive, rq, block); +} + +/* + * Queries for true maximum capacity of the drive. + * Returns maximum LBA address (> 0) of the drive, 0 if failed. + */ +static unsigned long idedisk_read_native_max_address(ide_drive_t *drive) +{ + ide_task_t args; + unsigned long addr = 0; + + /* Create IDE/ATA command request structure */ + memset(&args, 0, sizeof(ide_task_t)); + args.tfRegister[IDE_SELECT_OFFSET] = 0x40; + args.tfRegister[IDE_COMMAND_OFFSET] = WIN_READ_NATIVE_MAX; + args.command_type = IDE_DRIVE_TASK_NO_DATA; + args.handler = &task_no_data_intr; + /* submit command request */ + ide_raw_taskfile(drive, &args, NULL); + + /* if OK, compute maximum address value */ + if ((args.tfRegister[IDE_STATUS_OFFSET] & 0x01) == 0) { + addr = ((args.tfRegister[IDE_SELECT_OFFSET] & 0x0f) << 24) + | ((args.tfRegister[ IDE_HCYL_OFFSET] ) << 16) + | ((args.tfRegister[ IDE_LCYL_OFFSET] ) << 8) + | ((args.tfRegister[IDE_SECTOR_OFFSET] )); + addr++; /* since the return value is (maxlba - 1), we add 1 */ + } + return addr; +} + +static unsigned long long idedisk_read_native_max_address_ext(ide_drive_t *drive) +{ + ide_task_t args; + unsigned long long addr = 0; + + /* Create IDE/ATA command request structure */ + memset(&args, 0, sizeof(ide_task_t)); + + args.tfRegister[IDE_SELECT_OFFSET] = 0x40; + args.tfRegister[IDE_COMMAND_OFFSET] = WIN_READ_NATIVE_MAX_EXT; + args.command_type = IDE_DRIVE_TASK_NO_DATA; + args.handler = &task_no_data_intr; + /* submit command request */ + ide_raw_taskfile(drive, &args, NULL); + + /* if OK, compute maximum address value */ + if ((args.tfRegister[IDE_STATUS_OFFSET] & 0x01) == 0) { + u32 high = (args.hobRegister[IDE_HCYL_OFFSET] << 16) | + (args.hobRegister[IDE_LCYL_OFFSET] << 8) | + args.hobRegister[IDE_SECTOR_OFFSET]; + u32 low = ((args.tfRegister[IDE_HCYL_OFFSET])<<16) | + ((args.tfRegister[IDE_LCYL_OFFSET])<<8) | + (args.tfRegister[IDE_SECTOR_OFFSET]); + addr = ((__u64)high << 24) | low; + addr++; /* since the return value is (maxlba - 1), we add 1 */ + } + return addr; +} + +/* + * Sets maximum virtual LBA address of the drive. + * Returns new maximum virtual LBA address (> 0) or 0 on failure. + */ +static unsigned long idedisk_set_max_address(ide_drive_t *drive, unsigned long addr_req) +{ + ide_task_t args; + unsigned long addr_set = 0; + + addr_req--; + /* Create IDE/ATA command request structure */ + memset(&args, 0, sizeof(ide_task_t)); + args.tfRegister[IDE_SECTOR_OFFSET] = ((addr_req >> 0) & 0xff); + args.tfRegister[IDE_LCYL_OFFSET] = ((addr_req >> 8) & 0xff); + args.tfRegister[IDE_HCYL_OFFSET] = ((addr_req >> 16) & 0xff); + args.tfRegister[IDE_SELECT_OFFSET] = ((addr_req >> 24) & 0x0f) | 0x40; + args.tfRegister[IDE_COMMAND_OFFSET] = WIN_SET_MAX; + args.command_type = IDE_DRIVE_TASK_NO_DATA; + args.handler = &task_no_data_intr; + /* submit command request */ + ide_raw_taskfile(drive, &args, NULL); + /* if OK, read new maximum address value */ + if ((args.tfRegister[IDE_STATUS_OFFSET] & 0x01) == 0) { + addr_set = ((args.tfRegister[IDE_SELECT_OFFSET] & 0x0f) << 24) + | ((args.tfRegister[ IDE_HCYL_OFFSET] ) << 16) + | ((args.tfRegister[ IDE_LCYL_OFFSET] ) << 8) + | ((args.tfRegister[IDE_SECTOR_OFFSET] )); + addr_set++; + } + return addr_set; +} + +static unsigned long long idedisk_set_max_address_ext(ide_drive_t *drive, unsigned long long addr_req) +{ + ide_task_t args; + unsigned long long addr_set = 0; + + addr_req--; + /* Create IDE/ATA command request structure */ + memset(&args, 0, sizeof(ide_task_t)); + args.tfRegister[IDE_SECTOR_OFFSET] = ((addr_req >> 0) & 0xff); + args.tfRegister[IDE_LCYL_OFFSET] = ((addr_req >>= 8) & 0xff); + args.tfRegister[IDE_HCYL_OFFSET] = ((addr_req >>= 8) & 0xff); + args.tfRegister[IDE_SELECT_OFFSET] = 0x40; + args.tfRegister[IDE_COMMAND_OFFSET] = WIN_SET_MAX_EXT; + args.hobRegister[IDE_SECTOR_OFFSET] = (addr_req >>= 8) & 0xff; + args.hobRegister[IDE_LCYL_OFFSET] = (addr_req >>= 8) & 0xff; + args.hobRegister[IDE_HCYL_OFFSET] = (addr_req >>= 8) & 0xff; + args.hobRegister[IDE_SELECT_OFFSET] = 0x40; + args.hobRegister[IDE_CONTROL_OFFSET_HOB]= (drive->ctl|0x80); + args.command_type = IDE_DRIVE_TASK_NO_DATA; + args.handler = &task_no_data_intr; + /* submit command request */ + ide_raw_taskfile(drive, &args, NULL); + /* if OK, compute maximum address value */ + if ((args.tfRegister[IDE_STATUS_OFFSET] & 0x01) == 0) { + u32 high = (args.hobRegister[IDE_HCYL_OFFSET] << 16) | + (args.hobRegister[IDE_LCYL_OFFSET] << 8) | + args.hobRegister[IDE_SECTOR_OFFSET]; + u32 low = ((args.tfRegister[IDE_HCYL_OFFSET])<<16) | + ((args.tfRegister[IDE_LCYL_OFFSET])<<8) | + (args.tfRegister[IDE_SECTOR_OFFSET]); + addr_set = ((__u64)high << 24) | low; + addr_set++; + } + return addr_set; +} + +static unsigned long long sectors_to_MB(unsigned long long n) +{ + n <<= 9; /* make it bytes */ + do_div(n, 1000000); /* make it MB */ + return n; +} + +/* + * Bits 10 of command_set_1 and cfs_enable_1 must be equal, + * so on non-buggy drives we need test only one. + * However, we should also check whether these fields are valid. + */ +static inline int idedisk_supports_hpa(const struct hd_driveid *id) +{ + return (id->command_set_1 & 0x0400) && (id->cfs_enable_1 & 0x0400); +} + +/* + * The same here. + */ +static inline int idedisk_supports_lba48(const struct hd_driveid *id) +{ + return (id->command_set_2 & 0x0400) && (id->cfs_enable_2 & 0x0400) + && id->lba_capacity_2; +} + +static inline void idedisk_check_hpa(ide_drive_t *drive) +{ + unsigned long long capacity, set_max; + int lba48 = idedisk_supports_lba48(drive->id); + + capacity = drive->capacity64; + if (lba48) + set_max = idedisk_read_native_max_address_ext(drive); + else + set_max = idedisk_read_native_max_address(drive); + + if (set_max <= capacity) + return; + + printk(KERN_INFO "%s: Host Protected Area detected.\n" + "\tcurrent capacity is %llu sectors (%llu MB)\n" + "\tnative capacity is %llu sectors (%llu MB)\n", + drive->name, + capacity, sectors_to_MB(capacity), + set_max, sectors_to_MB(set_max)); + + if (lba48) + set_max = idedisk_set_max_address_ext(drive, set_max); + else + set_max = idedisk_set_max_address(drive, set_max); + if (set_max) { + drive->capacity64 = set_max; + printk(KERN_INFO "%s: Host Protected Area disabled.\n", + drive->name); + } +} + +/* + * Compute drive->capacity, the full capacity of the drive + * Called with drive->id != NULL. + * + * To compute capacity, this uses either of + * + * 1. CHS value set by user (whatever user sets will be trusted) + * 2. LBA value from target drive (require new ATA feature) + * 3. LBA value from system BIOS (new one is OK, old one may break) + * 4. CHS value from system BIOS (traditional style) + * + * in above order (i.e., if value of higher priority is available, + * reset will be ignored). + */ +static void init_idedisk_capacity (ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + /* + * If this drive supports the Host Protected Area feature set, + * then we may need to change our opinion about the drive's capacity. + */ + int hpa = idedisk_supports_hpa(id); + + if (idedisk_supports_lba48(id)) { + /* drive speaks 48-bit LBA */ + drive->select.b.lba = 1; + drive->capacity64 = id->lba_capacity_2; + if (hpa) + idedisk_check_hpa(drive); + } else if ((id->capability & 2) && lba_capacity_is_ok(id)) { + /* drive speaks 28-bit LBA */ + drive->select.b.lba = 1; + drive->capacity64 = id->lba_capacity; + if (hpa) + idedisk_check_hpa(drive); + } else { + /* drive speaks boring old 28-bit CHS */ + drive->capacity64 = drive->cyl * drive->head * drive->sect; + } +} + +static sector_t idedisk_capacity (ide_drive_t *drive) +{ + return drive->capacity64 - drive->sect0; +} + +#ifdef CONFIG_PROC_FS + +static int smart_enable(ide_drive_t *drive) +{ + ide_task_t args; + + memset(&args, 0, sizeof(ide_task_t)); + args.tfRegister[IDE_FEATURE_OFFSET] = SMART_ENABLE; + args.tfRegister[IDE_LCYL_OFFSET] = SMART_LCYL_PASS; + args.tfRegister[IDE_HCYL_OFFSET] = SMART_HCYL_PASS; + args.tfRegister[IDE_COMMAND_OFFSET] = WIN_SMART; + args.command_type = IDE_DRIVE_TASK_NO_DATA; + args.handler = &task_no_data_intr; + return ide_raw_taskfile(drive, &args, NULL); +} + +static int get_smart_values(ide_drive_t *drive, u8 *buf) +{ + ide_task_t args; + + memset(&args, 0, sizeof(ide_task_t)); + args.tfRegister[IDE_FEATURE_OFFSET] = SMART_READ_VALUES; + args.tfRegister[IDE_NSECTOR_OFFSET] = 0x01; + args.tfRegister[IDE_LCYL_OFFSET] = SMART_LCYL_PASS; + args.tfRegister[IDE_HCYL_OFFSET] = SMART_HCYL_PASS; + args.tfRegister[IDE_COMMAND_OFFSET] = WIN_SMART; + args.command_type = IDE_DRIVE_TASK_IN; + args.data_phase = TASKFILE_IN; + args.handler = &task_in_intr; + (void) smart_enable(drive); + return ide_raw_taskfile(drive, &args, buf); +} + +static int get_smart_thresholds(ide_drive_t *drive, u8 *buf) +{ + ide_task_t args; + memset(&args, 0, sizeof(ide_task_t)); + args.tfRegister[IDE_FEATURE_OFFSET] = SMART_READ_THRESHOLDS; + args.tfRegister[IDE_NSECTOR_OFFSET] = 0x01; + args.tfRegister[IDE_LCYL_OFFSET] = SMART_LCYL_PASS; + args.tfRegister[IDE_HCYL_OFFSET] = SMART_HCYL_PASS; + args.tfRegister[IDE_COMMAND_OFFSET] = WIN_SMART; + args.command_type = IDE_DRIVE_TASK_IN; + args.data_phase = TASKFILE_IN; + args.handler = &task_in_intr; + (void) smart_enable(drive); + return ide_raw_taskfile(drive, &args, 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_read) + 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_capacity + (char *page, char **start, off_t off, int count, int *eof, void *data) +{ + ide_drive_t*drive = (ide_drive_t *)data; + int len; + + len = sprintf(page,"%llu\n", (long long)idedisk_capacity(drive)); + 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; + 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; + 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 }, + { "capacity", S_IFREG|S_IRUGO, proc_idedisk_read_capacity, 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 void idedisk_end_flush(request_queue_t *q, struct request *flush_rq) +{ + ide_drive_t *drive = q->queuedata; + struct request *rq = flush_rq->end_io_data; + int good_sectors = rq->hard_nr_sectors; + int bad_sectors; + sector_t sector; + + if (flush_rq->errors & ABRT_ERR) { + printk(KERN_ERR "%s: barrier support doesn't work\n", drive->name); + blk_queue_ordered(drive->queue, QUEUE_ORDERED_NONE); + blk_queue_issue_flush_fn(drive->queue, NULL); + good_sectors = 0; + } else if (flush_rq->errors) { + good_sectors = 0; + if (blk_barrier_preflush(rq)) { + sector = ide_get_error_location(drive,flush_rq->buffer); + if ((sector >= rq->hard_sector) && + (sector < rq->hard_sector + rq->hard_nr_sectors)) + good_sectors = sector - rq->hard_sector; + } + } + + if (flush_rq->errors) + printk(KERN_ERR "%s: failed barrier write: " + "sector=%Lx(good=%d/bad=%d)\n", + drive->name, (unsigned long long)rq->sector, + good_sectors, + (int) (rq->hard_nr_sectors-good_sectors)); + + bad_sectors = rq->hard_nr_sectors - good_sectors; + + if (good_sectors) + __ide_end_request(drive, rq, 1, good_sectors); + if (bad_sectors) + __ide_end_request(drive, rq, 0, bad_sectors); +} + +static int idedisk_prepare_flush(request_queue_t *q, struct request *rq) +{ + ide_drive_t *drive = q->queuedata; + + if (!drive->wcache) + return 0; + + memset(rq->cmd, 0, sizeof(rq->cmd)); + + if (ide_id_has_flush_cache_ext(drive->id) && + (drive->capacity64 >= (1UL << 28))) + rq->cmd[0] = WIN_FLUSH_CACHE_EXT; + else + rq->cmd[0] = WIN_FLUSH_CACHE; + + + rq->flags |= REQ_DRIVE_TASK | REQ_SOFTBARRIER; + rq->buffer = rq->cmd; + return 1; +} + +static int idedisk_issue_flush(request_queue_t *q, struct gendisk *disk, + sector_t *error_sector) +{ + ide_drive_t *drive = q->queuedata; + struct request *rq; + int ret; + + if (!drive->wcache) + return 0; + + rq = blk_get_request(q, WRITE, __GFP_WAIT); + + idedisk_prepare_flush(q, rq); + + ret = blk_execute_rq(q, disk, rq); + + /* + * if we failed and caller wants error offset, get it + */ + if (ret && error_sector) + *error_sector = ide_get_error_location(drive, rq->cmd); + + blk_put_request(rq); + return ret; +} + +/* + * This is tightly woven into the driver->do_special can not touch. + * DON'T do it again until a total personality rewrite is committed. + */ +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); + rq.flags = REQ_DRIVE_CMD; + 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) +{ + if (ide_spin_wait_hwgroup(drive)) + return -EBUSY; + drive->nowerr = arg; + drive->bad_wstat = arg ? BAD_R_STAT : BAD_W_STAT; + spin_unlock_irq(&ide_lock); + return 0; +} + +static int write_cache(ide_drive_t *drive, int arg) +{ + ide_task_t args; + int err; + + if (!ide_id_has_flush_cache(drive->id)) + return 1; + + memset(&args, 0, sizeof(ide_task_t)); + args.tfRegister[IDE_FEATURE_OFFSET] = (arg) ? + SETFEATURES_EN_WCACHE : SETFEATURES_DIS_WCACHE; + args.tfRegister[IDE_COMMAND_OFFSET] = WIN_SETFEATURES; + args.command_type = IDE_DRIVE_TASK_NO_DATA; + args.handler = &task_no_data_intr; + + err = ide_raw_taskfile(drive, &args, NULL); + if (err) + return err; + + drive->wcache = arg; + return 0; +} + +static int do_idedisk_flushcache (ide_drive_t *drive) +{ + ide_task_t args; + + memset(&args, 0, sizeof(ide_task_t)); + if (ide_id_has_flush_cache_ext(drive->id)) + args.tfRegister[IDE_COMMAND_OFFSET] = WIN_FLUSH_CACHE_EXT; + else + args.tfRegister[IDE_COMMAND_OFFSET] = WIN_FLUSH_CACHE; + args.command_type = IDE_DRIVE_TASK_NO_DATA; + args.handler = &task_no_data_intr; + return ide_raw_taskfile(drive, &args, NULL); +} + +static int set_acoustic (ide_drive_t *drive, int arg) +{ + ide_task_t args; + + memset(&args, 0, sizeof(ide_task_t)); + args.tfRegister[IDE_FEATURE_OFFSET] = (arg) ? SETFEATURES_EN_AAM : + SETFEATURES_DIS_AAM; + args.tfRegister[IDE_NSECTOR_OFFSET] = arg; + args.tfRegister[IDE_COMMAND_OFFSET] = WIN_SETFEATURES; + args.command_type = IDE_DRIVE_TASK_NO_DATA; + args.handler = &task_no_data_intr; + ide_raw_taskfile(drive, &args, NULL); + drive->acoustic = arg; + return 0; +} + +/* + * drive->addressing: + * 0: 28-bit + * 1: 48-bit + * 2: 48-bit capable doing 28-bit + */ +static int set_lba_addressing(ide_drive_t *drive, int arg) +{ + drive->addressing = 0; + + if (HWIF(drive)->no_lba48) + return 0; + + if (!idedisk_supports_lba48(drive->id)) + return -EIO; + drive->addressing = arg; + return 0; +} + +static void idedisk_add_settings(ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + + 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, "address", SETTING_RW, HDIO_GET_ADDRESS, HDIO_SET_ADDRESS, TYPE_INTA, 0, 2, 1, 1, &drive->addressing, set_lba_addressing); + 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, 1, &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, "lun", SETTING_RW, -1, -1, TYPE_INT, 0, 7, 1, 1, &drive->lun, NULL); + ide_add_setting(drive, "wcache", SETTING_RW, HDIO_GET_WCACHE, HDIO_SET_WCACHE, TYPE_BYTE, 0, 1, 1, 1, &drive->wcache, write_cache); + ide_add_setting(drive, "acoustic", SETTING_RW, HDIO_GET_ACOUSTIC, HDIO_SET_ACOUSTIC, TYPE_BYTE, 0, 254, 1, 1, &drive->acoustic, set_acoustic); + ide_add_setting(drive, "failures", SETTING_RW, -1, -1, TYPE_INT, 0, 65535, 1, 1, &drive->failures, NULL); + ide_add_setting(drive, "max_failures", SETTING_RW, -1, -1, TYPE_INT, 0, 65535, 1, 1, &drive->max_failures, NULL); +} + +static void idedisk_setup (ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + unsigned long long capacity; + int barrier; + + idedisk_add_settings(drive); + + if (drive->id_read == 0) + 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_flash)) { + /* + * Removable disks (eg. SYQUEST); ignore 'WD' drives + */ + if (id->model[0] != 'W' || id->model[1] != 'D') { + drive->doorlocking = 1; + } + } + + (void)set_lba_addressing(drive, 1); + + if (drive->addressing == 1) { + ide_hwif_t *hwif = HWIF(drive); + int max_s = 2048; + + if (max_s > hwif->rqsize) + max_s = hwif->rqsize; + + blk_queue_max_sectors(drive->queue, max_s); + } + + printk(KERN_INFO "%s: max request size: %dKiB\n", drive->name, drive->queue->max_sectors / 2); + + /* calculate drive capacity, and select LBA if possible */ + init_idedisk_capacity (drive); + + /* limit drive capacity to 137GB if LBA48 cannot be used */ + if (drive->addressing == 0 && drive->capacity64 > 1ULL << 28) { + printk(KERN_WARNING "%s: cannot use LBA48 - full capacity " + "%llu sectors (%llu MB)\n", + drive->name, (unsigned long long)drive->capacity64, + sectors_to_MB(drive->capacity64)); + drive->capacity64 = 1ULL << 28; + } + + if (drive->hwif->no_lba48_dma && drive->addressing) { + if (drive->capacity64 > 1ULL << 28) { + printk(KERN_INFO "%s: cannot use LBA48 DMA - PIO mode will" + " be used for accessing sectors > %u\n", + drive->name, 1 << 28); + } else + drive->addressing = 0; + } + + /* + * if possible, give fdisk access to more of the drive, + * by correcting bios_cyls: + */ + capacity = idedisk_capacity (drive); + if (!drive->forced_geom) { + + if (idedisk_supports_lba48(drive->id)) { + /* compatibility */ + drive->bios_sect = 63; + drive->bios_head = 255; + } + + if (drive->bios_sect && drive->bios_head) { + unsigned int cap0 = capacity; /* truncate to 32 bits */ + unsigned int cylsz, cyl; + + if (cap0 != capacity) + drive->bios_cyl = 65535; + else { + cylsz = drive->bios_sect * drive->bios_head; + cyl = cap0 / cylsz; + if (cyl > 65535) + cyl = 65535; + if (cyl > drive->bios_cyl) + drive->bios_cyl = cyl; + } + } + } + printk(KERN_INFO "%s: %llu sectors (%llu MB)", + drive->name, capacity, sectors_to_MB(capacity)); + + /* Only print cache size when it was specified */ + if (id->buf_size) + printk (" w/%dKiB Cache", id->buf_size/2); + + printk(", CHS=%d/%d/%d", + drive->bios_cyl, drive->bios_head, drive->bios_sect); + if (drive->using_dma) + ide_dma_verbose(drive); + printk("\n"); + + drive->no_io_32bit = id->dword_io ? 1 : 0; + + /* write cache enabled? */ + if ((id->csfo & 1) || (id->cfs_enable_1 & (1 << 5))) + drive->wcache = 1; + + write_cache(drive, 1); + + /* + * We must avoid issuing commands a drive does not understand + * or we may crash it. We check flush cache is supported. We also + * check we have the LBA48 flush cache if the drive capacity is + * too large. By this time we have trimmed the drive capacity if + * LBA48 is not available so we don't need to recheck that. + */ + barrier = 0; + if (ide_id_has_flush_cache(id)) + barrier = 1; + if (drive->addressing == 1) { + /* Can't issue the correct flush ? */ + if (capacity > (1ULL << 28) && !ide_id_has_flush_cache_ext(id)) + barrier = 0; + } + + printk(KERN_INFO "%s: cache flushes %ssupported\n", + drive->name, barrier ? "" : "not "); + if (barrier) { + blk_queue_ordered(drive->queue, QUEUE_ORDERED_FLUSH); + drive->queue->prepare_flush_fn = idedisk_prepare_flush; + drive->queue->end_flush_fn = idedisk_end_flush; + blk_queue_issue_flush_fn(drive->queue, idedisk_issue_flush); + } +} + +static void ide_cacheflush_p(ide_drive_t *drive) +{ + if (!drive->wcache || !ide_id_has_flush_cache(drive->id)) + return; + + if (do_idedisk_flushcache(drive)) + printk(KERN_INFO "%s: wcache flush failed!\n", drive->name); +} + +static int idedisk_cleanup (ide_drive_t *drive) +{ + struct ide_disk_obj *idkp = drive->driver_data; + struct gendisk *g = idkp->disk; + + ide_cacheflush_p(drive); + if (ide_unregister_subdriver(drive)) + return 1; + del_gendisk(g); + + ide_disk_put(idkp); + + return 0; +} + +static void ide_disk_release(struct kref *kref) +{ + struct ide_disk_obj *idkp = to_ide_disk(kref); + ide_drive_t *drive = idkp->drive; + struct gendisk *g = idkp->disk; + + drive->driver_data = NULL; + drive->devfs_name[0] = '\0'; + g->private_data = NULL; + put_disk(g); + kfree(idkp); +} + +static int idedisk_attach(ide_drive_t *drive); + +static void ide_device_shutdown(struct device *dev) +{ + ide_drive_t *drive = container_of(dev, ide_drive_t, gendev); + +#ifdef CONFIG_ALPHA + /* On Alpha, halt(8) doesn't actually turn the machine off, + it puts you into the sort of firmware monitor. Typically, + it's used to boot another kernel image, so it's not much + different from reboot(8). Therefore, we don't need to + spin down the disk in this case, especially since Alpha + firmware doesn't handle disks in standby mode properly. + On the other hand, it's reasonably safe to turn the power + off when the shutdown process reaches the firmware prompt, + as the firmware initialization takes rather long time - + at least 10 seconds, which should be sufficient for + the disk to expire its write cache. */ + if (system_state != SYSTEM_POWER_OFF) { +#else + if (system_state == SYSTEM_RESTART) { +#endif + ide_cacheflush_p(drive); + return; + } + + printk("Shutdown: %s\n", drive->name); + dev->bus->suspend(dev, PMSG_SUSPEND); +} + +/* + * IDE subdriver functions, registered with ide.c + */ +static ide_driver_t idedisk_driver = { + .owner = THIS_MODULE, + .gen_driver = { + .shutdown = ide_device_shutdown, + }, + .name = "ide-disk", + .version = IDEDISK_VERSION, + .media = ide_disk, + .busy = 0, + .supports_dsc_overlap = 0, + .cleanup = idedisk_cleanup, + .do_request = ide_do_rw_disk, + .end_request = ide_end_request, + .error = __ide_error, + .abort = __ide_abort, + .proc = idedisk_proc, + .attach = idedisk_attach, + .drives = LIST_HEAD_INIT(idedisk_driver.drives), +}; + +static int idedisk_open(struct inode *inode, struct file *filp) +{ + struct gendisk *disk = inode->i_bdev->bd_disk; + struct ide_disk_obj *idkp; + ide_drive_t *drive; + + if (!(idkp = ide_disk_get(disk))) + return -ENXIO; + + drive = idkp->drive; + + drive->usage++; + if (drive->removable && drive->usage == 1) { + ide_task_t args; + memset(&args, 0, sizeof(ide_task_t)); + args.tfRegister[IDE_COMMAND_OFFSET] = WIN_DOORLOCK; + args.command_type = IDE_DRIVE_TASK_NO_DATA; + args.handler = &task_no_data_intr; + check_disk_change(inode->i_bdev); + /* + * 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_raw_taskfile(drive, &args, NULL)) + drive->doorlocking = 0; + } + return 0; +} + +static int idedisk_release(struct inode *inode, struct file *filp) +{ + struct gendisk *disk = inode->i_bdev->bd_disk; + struct ide_disk_obj *idkp = ide_disk_g(disk); + ide_drive_t *drive = idkp->drive; + + if (drive->usage == 1) + ide_cacheflush_p(drive); + if (drive->removable && drive->usage == 1) { + ide_task_t args; + memset(&args, 0, sizeof(ide_task_t)); + args.tfRegister[IDE_COMMAND_OFFSET] = WIN_DOORUNLOCK; + args.command_type = IDE_DRIVE_TASK_NO_DATA; + args.handler = &task_no_data_intr; + if (drive->doorlocking && ide_raw_taskfile(drive, &args, NULL)) + drive->doorlocking = 0; + } + drive->usage--; + + ide_disk_put(idkp); + + return 0; +} + +static int idedisk_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct block_device *bdev = inode->i_bdev; + struct ide_disk_obj *idkp = ide_disk_g(bdev->bd_disk); + return generic_ide_ioctl(idkp->drive, file, bdev, cmd, arg); +} + +static int idedisk_media_changed(struct gendisk *disk) +{ + struct ide_disk_obj *idkp = ide_disk_g(disk); + ide_drive_t *drive = idkp->drive; + + /* do not scan partitions twice if this is a removable device */ + if (drive->attach) { + drive->attach = 0; + return 0; + } + /* if removable, always assume it was changed */ + return drive->removable; +} + +static int idedisk_revalidate_disk(struct gendisk *disk) +{ + struct ide_disk_obj *idkp = ide_disk_g(disk); + set_capacity(disk, idedisk_capacity(idkp->drive)); + return 0; +} + +static struct block_device_operations idedisk_ops = { + .owner = THIS_MODULE, + .open = idedisk_open, + .release = idedisk_release, + .ioctl = idedisk_ioctl, + .media_changed = idedisk_media_changed, + .revalidate_disk= idedisk_revalidate_disk +}; + +MODULE_DESCRIPTION("ATA DISK Driver"); + +static int idedisk_attach(ide_drive_t *drive) +{ + struct ide_disk_obj *idkp; + struct gendisk *g; + + /* strstr("foo", "") is non-NULL */ + if (!strstr("ide-disk", drive->driver_req)) + goto failed; + if (!drive->present) + goto failed; + if (drive->media != ide_disk) + goto failed; + + idkp = kmalloc(sizeof(*idkp), GFP_KERNEL); + if (!idkp) + goto failed; + + g = alloc_disk(1 << PARTN_BITS); + if (!g) + goto out_free_idkp; + + ide_init_disk(g, drive); + + if (ide_register_subdriver(drive, &idedisk_driver)) { + printk (KERN_ERR "ide-disk: %s: Failed to register the driver with ide.c\n", drive->name); + goto out_put_disk; + } + + memset(idkp, 0, sizeof(*idkp)); + + kref_init(&idkp->kref); + + idkp->drive = drive; + idkp->driver = &idedisk_driver; + idkp->disk = g; + + g->private_data = &idkp->driver; + + drive->driver_data = idkp; + + DRIVER(drive)->busy++; + 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); + drive->attach = 0; + } else + drive->attach = 1; + DRIVER(drive)->busy--; + g->minors = 1 << PARTN_BITS; + strcpy(g->devfs_name, drive->devfs_name); + g->driverfs_dev = &drive->gendev; + g->flags = drive->removable ? GENHD_FL_REMOVABLE : 0; + set_capacity(g, idedisk_capacity(drive)); + g->fops = &idedisk_ops; + add_disk(g); + return 0; + +out_put_disk: + put_disk(g); +out_free_idkp: + kfree(idkp); +failed: + return 1; +} + +static void __exit idedisk_exit (void) +{ + ide_unregister_driver(&idedisk_driver); +} + +static int idedisk_init (void) +{ + return ide_register_driver(&idedisk_driver); +} + +module_init(idedisk_init); +module_exit(idedisk_exit); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c new file mode 100644 index 0000000..2d2eefb --- /dev/null +++ b/drivers/ide/ide-dma.c @@ -0,0 +1,959 @@ +/* + * linux/drivers/ide/ide-dma.c Version 4.10 June 9, 2000 + * + * Copyright (c) 1999-2000 Andre Hedrick <andre@linux-ide.org> + * 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. + * + * ATA-66/100 and recovery functions, I forgot the rest...... + * + */ + +#include <linux/config.h> +#include <linux/module.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 <linux/delay.h> +#include <linux/scatterlist.h> + +#include <asm/io.h> +#include <asm/irq.h> + +struct drive_list_entry { + const char *id_model; + const char *id_firmware; +}; + +static const struct drive_list_entry drive_whitelist [] = { + + { "Micropolis 2112A" , "ALL" }, + { "CONNER CTMA 4000" , "ALL" }, + { "CONNER CTT8000-A" , "ALL" }, + { "ST34342A" , "ALL" }, + { NULL , NULL } +}; + +static const 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" }, + { "Compaq CRD-8241B" , "ALL" }, + { "CRD-8400B" , "ALL" }, + { "CRD-8480B", "ALL" }, + { "CRD-8482B", "ALL" }, + { "CRD-84" , "ALL" }, + { "SanDisk SDP3B" , "ALL" }, + { "SanDisk SDP3B-64" , "ALL" }, + { "SANYO CD-ROM CRD" , "ALL" }, + { "HITACHI CDR-8" , "ALL" }, + { "HITACHI CDR-8335" , "ALL" }, + { "HITACHI CDR-8435" , "ALL" }, + { "Toshiba CD-ROM XM-6202B" , "ALL" }, + { "CD-532E-A" , "ALL" }, + { "E-IDE CD-ROM CR-840", "ALL" }, + { "CD-ROM Drive/F5A", "ALL" }, + { "WPI CDD-820", "ALL" }, + { "SAMSUNG CD-ROM SC-148C", "ALL" }, + { "SAMSUNG CD-ROM SC", "ALL" }, + { "SanDisk SDP3B-64" , "ALL" }, + { "SAMSUNG CD-ROM SN-124", "ALL" }, + { "ATAPI CD-ROM DRIVE 40X MAXIMUM", "ALL" }, + { "_NEC DV5800A", "ALL" }, + { NULL , NULL } + +}; + +/** + * in_drive_list - look for drive in black/white list + * @id: drive identifier + * @drive_table: list to inspect + * + * Look for a drive in the blacklist and the whitelist tables + * Returns 1 if the drive is found in the table. + */ + +static int in_drive_list(struct hd_driveid *id, const 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; +} + +/** + * ide_dma_intr - IDE DMA interrupt handler + * @drive: the drive the interrupt is for + * + * Handle an interrupt completing a read/write DMA transfer on an + * IDE device + */ + +ide_startstop_t ide_dma_intr (ide_drive_t *drive) +{ + u8 stat = 0, dma_stat = 0; + + dma_stat = HWIF(drive)->ide_dma_end(drive); + stat = HWIF(drive)->INB(IDE_STATUS_REG); /* get drive status */ + if (OK_STAT(stat,DRIVE_READY,drive->bad_wstat|DRQ_STAT)) { + if (!dma_stat) { + struct request *rq = HWGROUP(drive)->rq; + + if (rq->rq_disk) { + ide_driver_t *drv; + + drv = *(ide_driver_t **)rq->rq_disk->private_data;; + drv->end_request(drive, 1, rq->nr_sectors); + } else + ide_end_request(drive, 1, rq->nr_sectors); + return ide_stopped; + } + printk(KERN_ERR "%s: dma_intr: bad DMA status (dma_stat=%x)\n", + drive->name, dma_stat); + } + return ide_error(drive, "dma_intr", stat); +} + +EXPORT_SYMBOL_GPL(ide_dma_intr); + +#ifdef CONFIG_BLK_DEV_IDEDMA_PCI +/** + * ide_build_sglist - map IDE scatter gather for DMA I/O + * @drive: the drive to build the DMA table for + * @rq: the request holding the sg list + * + * Perform the PCI mapping magic necessary to access the source or + * target buffers of a request via PCI DMA. The lower layers of the + * kernel provide the necessary cache management so that we can + * operate in a portable fashion + */ + +int ide_build_sglist(ide_drive_t *drive, struct request *rq) +{ + ide_hwif_t *hwif = HWIF(drive); + struct scatterlist *sg = hwif->sg_table; + + if ((rq->flags & REQ_DRIVE_TASKFILE) && rq->nr_sectors > 256) + BUG(); + + ide_map_sg(drive, rq); + + if (rq_data_dir(rq) == READ) + hwif->sg_dma_direction = PCI_DMA_FROMDEVICE; + else + hwif->sg_dma_direction = PCI_DMA_TODEVICE; + + return pci_map_sg(hwif->pci_dev, sg, hwif->sg_nents, hwif->sg_dma_direction); +} + +EXPORT_SYMBOL_GPL(ide_build_sglist); + +/** + * ide_build_dmatable - build IDE DMA table + * + * ide_build_dmatable() prepares a dma request. We map the command + * to get the pci bus addresses of the buffers and then build up + * the PRD table that the IDE layer wants to be fed. The code + * knows about the 64K wrap bug in the CS5530. + * + * Returns the number of built PRD entries if all went okay, + * returns 0 otherwise. + * + * May also be invoked from trm290.c + */ + +int ide_build_dmatable (ide_drive_t *drive, struct request *rq) +{ + ide_hwif_t *hwif = HWIF(drive); + unsigned int *table = hwif->dmatable_cpu; + unsigned int is_trm290 = (hwif->chipset == ide_trm290) ? 1 : 0; + unsigned int count = 0; + int i; + struct scatterlist *sg; + + hwif->sg_nents = i = ide_build_sglist(drive, rq); + + if (!i) + return 0; + + sg = hwif->sg_table; + while (i) { + u32 cur_addr; + u32 cur_len; + + cur_addr = sg_dma_address(sg); + cur_len = sg_dma_len(sg); + + /* + * Fill in the dma table, without crossing any 64kB boundaries. + * Most hardware requires 16-bit alignment of all blocks, + * but the trm290 requires 32-bit alignment. + */ + + while (cur_len) { + if (count++ >= PRD_ENTRIES) { + printk(KERN_ERR "%s: DMA table too small\n", drive->name); + goto use_pio_instead; + } 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) + xcount = ((xcount >> 2) - 1) << 16; + if (xcount == 0x0000) { + /* + * Most chipsets correctly interpret a length of 0x0000 as 64KB, + * but at least one (e.g. CS5530) misinterprets it as zero (!). + * So here we break the 64KB entry into two 32KB entries instead. + */ + if (count++ >= PRD_ENTRIES) { + printk(KERN_ERR "%s: DMA table too small\n", drive->name); + goto use_pio_instead; + } + *table++ = cpu_to_le32(0x8000); + *table++ = cpu_to_le32(cur_addr + 0x8000); + xcount = 0x8000; + } + *table++ = cpu_to_le32(xcount); + cur_addr += bcount; + cur_len -= bcount; + } + } + + sg++; + i--; + } + + if (count) { + if (!is_trm290) + *--table |= cpu_to_le32(0x80000000); + return count; + } + printk(KERN_ERR "%s: empty DMA table?\n", drive->name); +use_pio_instead: + pci_unmap_sg(hwif->pci_dev, + hwif->sg_table, + hwif->sg_nents, + hwif->sg_dma_direction); + return 0; /* revert to PIO for this request */ +} + +EXPORT_SYMBOL_GPL(ide_build_dmatable); + +/** + * ide_destroy_dmatable - clean up DMA mapping + * @drive: The drive to unmap + * + * Teardown mappings after DMA has completed. This must be called + * after the completion of each use of ide_build_dmatable and before + * the next use of ide_build_dmatable. Failure to do so will cause + * an oops as only one mapping can be live for each target at a given + * time. + */ + +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); +} + +EXPORT_SYMBOL_GPL(ide_destroy_dmatable); + +/** + * config_drive_for_dma - attempt to activate IDE DMA + * @drive: the drive to place in DMA mode + * + * If the drive supports at least mode 2 DMA or UDMA of any kind + * then attempt to place it into DMA mode. Drives that are known to + * support DMA but predate the DMA properties or that are known + * to have DMA handling bugs are also set up appropriately based + * on the good/bad drive lists. + */ + +static int config_drive_for_dma (ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + ide_hwif_t *hwif = HWIF(drive); + + if ((id->capability & 1) && hwif->autodma) { + /* + * Enable DMA on any drive that has + * UltraDMA (mode 0/1/2/3/4/5/6) enabled + */ + if ((id->field_valid & 4) && ((id->dma_ultra >> 8) & 0x7f)) + return hwif->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->ide_dma_on(drive); + + /* Consult the list of known "good" drives */ + if (__ide_dma_good_drive(drive)) + return hwif->ide_dma_on(drive); + } +// if (hwif->tuneproc != NULL) hwif->tuneproc(drive, 255); + return hwif->ide_dma_off_quietly(drive); +} + +/** + * dma_timer_expiry - handle a DMA timeout + * @drive: Drive that timed out + * + * An IDE DMA transfer timed out. In the event of an error we ask + * the driver to resolve the problem, if a DMA transfer is still + * in progress we continue to wait (arguably we need to add a + * secondary 'I don't care what the drive thinks' timeout here) + * Finally if we have an interrupt we let it complete the I/O. + * But only one time - we clear expiry and if it's still not + * completed after WAIT_CMD, we error and retry in PIO. + * This can occur if an interrupt is lost or due to hang or bugs. + */ + +static int dma_timer_expiry (ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + u8 dma_stat = hwif->INB(hwif->dma_status); + + printk(KERN_WARNING "%s: dma_timer_expiry: dma status == 0x%02x\n", + drive->name, dma_stat); + + if ((dma_stat & 0x18) == 0x18) /* BUSY Stupid Early Timer !! */ + return WAIT_CMD; + + HWGROUP(drive)->expiry = NULL; /* one free ride for now */ + + /* 1 dmaing, 2 error, 4 intr */ + if (dma_stat & 2) /* ERROR */ + return -1; + + if (dma_stat & 1) /* DMAing */ + return WAIT_CMD; + + if (dma_stat & 4) /* Got an Interrupt */ + return WAIT_CMD; + + return 0; /* Status is unknown -- reset the bus */ +} + +/** + * __ide_dma_host_off - Generic DMA kill + * @drive: drive to control + * + * Perform the generic IDE controller DMA off operation. This + * works for most IDE bus mastering controllers + */ + +int __ide_dma_host_off (ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + u8 unit = (drive->select.b.unit & 0x01); + u8 dma_stat = hwif->INB(hwif->dma_status); + + hwif->OUTB((dma_stat & ~(1<<(5+unit))), hwif->dma_status); + return 0; +} + +EXPORT_SYMBOL(__ide_dma_host_off); + +/** + * __ide_dma_host_off_quietly - Generic DMA kill + * @drive: drive to control + * + * Turn off the current DMA on this IDE controller. + */ + +int __ide_dma_off_quietly (ide_drive_t *drive) +{ + drive->using_dma = 0; + ide_toggle_bounce(drive, 0); + + if (HWIF(drive)->ide_dma_host_off(drive)) + return 1; + + return 0; +} + +EXPORT_SYMBOL(__ide_dma_off_quietly); +#endif /* CONFIG_BLK_DEV_IDEDMA_PCI */ + +/** + * __ide_dma_off - disable DMA on a device + * @drive: drive to disable DMA on + * + * Disable IDE DMA for a device on this IDE controller. + * Inform the user that DMA has been disabled. + */ + +int __ide_dma_off (ide_drive_t *drive) +{ + printk(KERN_INFO "%s: DMA disabled\n", drive->name); + return HWIF(drive)->ide_dma_off_quietly(drive); +} + +EXPORT_SYMBOL(__ide_dma_off); + +#ifdef CONFIG_BLK_DEV_IDEDMA_PCI +/** + * __ide_dma_host_on - Enable DMA on a host + * @drive: drive to enable for DMA + * + * Enable DMA on an IDE controller following generic bus mastering + * IDE controller behaviour + */ + +int __ide_dma_host_on (ide_drive_t *drive) +{ + if (drive->using_dma) { + ide_hwif_t *hwif = HWIF(drive); + u8 unit = (drive->select.b.unit & 0x01); + u8 dma_stat = hwif->INB(hwif->dma_status); + + hwif->OUTB((dma_stat|(1<<(5+unit))), hwif->dma_status); + return 0; + } + return 1; +} + +EXPORT_SYMBOL(__ide_dma_host_on); + +/** + * __ide_dma_on - Enable DMA on a device + * @drive: drive to enable DMA on + * + * Enable IDE DMA for a device on this IDE controller. + */ + +int __ide_dma_on (ide_drive_t *drive) +{ + /* consult the list of known "bad" drives */ + if (__ide_dma_bad_drive(drive)) + return 1; + + drive->using_dma = 1; + ide_toggle_bounce(drive, 1); + + if (HWIF(drive)->ide_dma_host_on(drive)) + return 1; + + return 0; +} + +EXPORT_SYMBOL(__ide_dma_on); + +/** + * __ide_dma_check - check DMA setup + * @drive: drive to check + * + * Don't use - due for extermination + */ + +int __ide_dma_check (ide_drive_t *drive) +{ + return config_drive_for_dma(drive); +} + +EXPORT_SYMBOL(__ide_dma_check); + +/** + * ide_dma_setup - begin a DMA phase + * @drive: target device + * + * Build an IDE DMA PRD (IDE speak for scatter gather table) + * and then set up the DMA transfer registers for a device + * that follows generic IDE PCI DMA behaviour. Controllers can + * override this function if they need to + * + * Returns 0 on success. If a PIO fallback is required then 1 + * is returned. + */ + +int ide_dma_setup(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + struct request *rq = HWGROUP(drive)->rq; + unsigned int reading; + u8 dma_stat; + + if (rq_data_dir(rq)) + reading = 0; + else + reading = 1 << 3; + + /* fall back to pio! */ + if (!ide_build_dmatable(drive, rq)) { + ide_map_sg(drive, rq); + return 1; + } + + /* PRD table */ + hwif->OUTL(hwif->dmatable_dma, hwif->dma_prdtable); + + /* specify r/w */ + hwif->OUTB(reading, hwif->dma_command); + + /* read dma_status for INTR & ERROR flags */ + dma_stat = hwif->INB(hwif->dma_status); + + /* clear INTR & ERROR flags */ + hwif->OUTB(dma_stat|6, hwif->dma_status); + drive->waiting_for_dma = 1; + return 0; +} + +EXPORT_SYMBOL_GPL(ide_dma_setup); + +static void ide_dma_exec_cmd(ide_drive_t *drive, u8 command) +{ + /* issue cmd to drive */ + ide_execute_command(drive, command, &ide_dma_intr, 2*WAIT_CMD, dma_timer_expiry); +} + +void ide_dma_start(ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + u8 dma_cmd = hwif->INB(hwif->dma_command); + + /* 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. + */ + /* start DMA */ + hwif->OUTB(dma_cmd|1, hwif->dma_command); + hwif->dma = 1; + wmb(); +} + +EXPORT_SYMBOL_GPL(ide_dma_start); + +/* returns 1 on error, 0 otherwise */ +int __ide_dma_end (ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + u8 dma_stat = 0, dma_cmd = 0; + + drive->waiting_for_dma = 0; + /* get dma_command mode */ + dma_cmd = hwif->INB(hwif->dma_command); + /* stop DMA */ + hwif->OUTB(dma_cmd&~1, hwif->dma_command); + /* get DMA status */ + dma_stat = hwif->INB(hwif->dma_status); + /* clear the INTR & ERROR bits */ + hwif->OUTB(dma_stat|6, hwif->dma_status); + /* purge DMA mappings */ + ide_destroy_dmatable(drive); + /* verify good DMA status */ + hwif->dma = 0; + wmb(); + return (dma_stat & 7) != 4 ? (0x10 | dma_stat) : 0; +} + +EXPORT_SYMBOL(__ide_dma_end); + +/* returns 1 if dma irq issued, 0 otherwise */ +static int __ide_dma_test_irq(ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + u8 dma_stat = hwif->INB(hwif->dma_status); + +#if 0 /* do not set unless you know what you are doing */ + if (dma_stat & 4) { + u8 stat = hwif->INB(IDE_STATUS_REG); + hwif->OUTB(hwif->dma_status, dma_stat & 0xE4); + } +#endif + /* return 1 if INTR asserted */ + if ((dma_stat & 4) == 4) + return 1; + if (!drive->waiting_for_dma) + printk(KERN_WARNING "%s: (%s) called while not waiting\n", + drive->name, __FUNCTION__); + return 0; +} +#endif /* CONFIG_BLK_DEV_IDEDMA_PCI */ + +int __ide_dma_bad_drive (ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + + int blacklist = in_drive_list(id, drive_blacklist); + if (blacklist) { + printk(KERN_WARNING "%s: Disabling (U)DMA for %s (blacklisted)\n", + drive->name, id->model); + return blacklist; + } + return 0; +} + +EXPORT_SYMBOL(__ide_dma_bad_drive); + +int __ide_dma_good_drive (ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + return in_drive_list(id, drive_whitelist); +} + +EXPORT_SYMBOL(__ide_dma_good_drive); + +int ide_use_dma(ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + ide_hwif_t *hwif = drive->hwif; + + /* consult the list of known "bad" drives */ + if (__ide_dma_bad_drive(drive)) + return 0; + + /* capable of UltraDMA modes */ + if (id->field_valid & 4) { + if (hwif->ultra_mask & id->dma_ultra) + return 1; + } + + /* capable of regular DMA modes */ + if (id->field_valid & 2) { + if (hwif->mwdma_mask & id->dma_mword) + return 1; + if (hwif->swdma_mask & id->dma_1word) + return 1; + } + + /* consult the list of known "good" drives */ + if (__ide_dma_good_drive(drive) && id->eide_dma_time < 150) + return 1; + + return 0; +} + +EXPORT_SYMBOL_GPL(ide_use_dma); + +void ide_dma_verbose(ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + ide_hwif_t *hwif = HWIF(drive); + + if (id->field_valid & 4) { + if ((id->dma_ultra >> 8) && (id->dma_mword >> 8)) + goto bug_dma_off; + if (id->dma_ultra & ((id->dma_ultra >> 8) & hwif->ultra_mask)) { + if (((id->dma_ultra >> 11) & 0x1F) && + eighty_ninty_three(drive)) { + if ((id->dma_ultra >> 15) & 1) { + printk(", UDMA(mode 7)"); + } else if ((id->dma_ultra >> 14) & 1) { + printk(", UDMA(133)"); + } else if ((id->dma_ultra >> 13) & 1) { + printk(", UDMA(100)"); + } else if ((id->dma_ultra >> 12) & 1) { + printk(", UDMA(66)"); + } else if ((id->dma_ultra >> 11) & 1) { + printk(", UDMA(44)"); + } else + goto mode_two; + } else { + mode_two: + if ((id->dma_ultra >> 10) & 1) { + printk(", UDMA(33)"); + } else if ((id->dma_ultra >> 9) & 1) { + printk(", UDMA(25)"); + } else if ((id->dma_ultra >> 8) & 1) { + printk(", UDMA(16)"); + } + } + } else { + printk(", (U)DMA"); /* Can be BIOS-enabled! */ + } + } else if (id->field_valid & 2) { + if ((id->dma_mword >> 8) && (id->dma_1word >> 8)) + goto bug_dma_off; + printk(", DMA"); + } else if (id->field_valid & 1) { + printk(", BUG"); + } + return; +bug_dma_off: + printk(", BUG DMA OFF"); + hwif->ide_dma_off_quietly(drive); + return; +} + +EXPORT_SYMBOL(ide_dma_verbose); + +#ifdef CONFIG_BLK_DEV_IDEDMA_PCI +int __ide_dma_lostirq (ide_drive_t *drive) +{ + printk("%s: DMA interrupt recovery\n", drive->name); + return 1; +} + +EXPORT_SYMBOL(__ide_dma_lostirq); + +int __ide_dma_timeout (ide_drive_t *drive) +{ + printk(KERN_ERR "%s: timeout waiting for DMA\n", drive->name); + if (HWIF(drive)->ide_dma_test_irq(drive)) + return 0; + + return HWIF(drive)->ide_dma_end(drive); +} + +EXPORT_SYMBOL(__ide_dma_timeout); + +/* + * Needed for allowing full modular support of ide-driver + */ +static int ide_release_dma_engine(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; + } + return 1; +} + +static int ide_release_iomio_dma(ide_hwif_t *hwif) +{ + if ((hwif->dma_extra) && (hwif->channel == 0)) + release_region((hwif->dma_base + 16), hwif->dma_extra); + release_region(hwif->dma_base, 8); + if (hwif->dma_base2) + release_region(hwif->dma_base, 8); + return 1; +} + +/* + * Needed for allowing full modular support of ide-driver + */ +int ide_release_dma (ide_hwif_t *hwif) +{ + if (hwif->mmio == 2) + return 1; + if (hwif->chipset == ide_etrax100) + return 1; + + ide_release_dma_engine(hwif); + return ide_release_iomio_dma(hwif); +} + +static int ide_allocate_dma_engine(ide_hwif_t *hwif) +{ + hwif->dmatable_cpu = pci_alloc_consistent(hwif->pci_dev, + PRD_ENTRIES * PRD_BYTES, + &hwif->dmatable_dma); + + if (hwif->dmatable_cpu) + return 0; + + printk(KERN_ERR "%s: -- Error, unable to allocate%s DMA table(s).\n", + hwif->cds->name, !hwif->dmatable_cpu ? " CPU" : ""); + + ide_release_dma_engine(hwif); + return 1; +} + +static int ide_mapped_mmio_dma(ide_hwif_t *hwif, unsigned long base, unsigned int ports) +{ + printk(KERN_INFO " %s: MMIO-DMA ", hwif->name); + + hwif->dma_base = base; + if (hwif->cds->extra && hwif->channel == 0) + hwif->dma_extra = hwif->cds->extra; + + if(hwif->mate) + hwif->dma_master = (hwif->channel) ? hwif->mate->dma_base : base; + else + hwif->dma_master = base; + return 0; +} + +static int ide_iomio_dma(ide_hwif_t *hwif, unsigned long base, unsigned int ports) +{ + printk(KERN_INFO " %s: BM-DMA at 0x%04lx-0x%04lx", + hwif->name, base, base + ports - 1); + if (!request_region(base, ports, hwif->name)) { + printk(" -- Error, ports in use.\n"); + return 1; + } + hwif->dma_base = base; + if ((hwif->cds->extra) && (hwif->channel == 0)) { + request_region(base+16, hwif->cds->extra, hwif->cds->name); + hwif->dma_extra = hwif->cds->extra; + } + + if(hwif->mate) + hwif->dma_master = (hwif->channel) ? hwif->mate->dma_base : base; + else + hwif->dma_master = base; + if (hwif->dma_base2) { + if (!request_region(hwif->dma_base2, ports, hwif->name)) + { + printk(" -- Error, secondary ports in use.\n"); + release_region(base, ports); + return 1; + } + } + return 0; +} + +static int ide_dma_iobase(ide_hwif_t *hwif, unsigned long base, unsigned int ports) +{ + if (hwif->mmio == 2) + return ide_mapped_mmio_dma(hwif, base,ports); + BUG_ON(hwif->mmio == 1); + return ide_iomio_dma(hwif, base, ports); +} + +/* + * 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) +{ + if (ide_dma_iobase(hwif, dma_base, num_ports)) + return; + + if (ide_allocate_dma_engine(hwif)) { + ide_release_dma(hwif); + return; + } + + if (!(hwif->dma_command)) + hwif->dma_command = hwif->dma_base; + if (!(hwif->dma_vendor1)) + hwif->dma_vendor1 = (hwif->dma_base + 1); + if (!(hwif->dma_status)) + hwif->dma_status = (hwif->dma_base + 2); + if (!(hwif->dma_vendor3)) + hwif->dma_vendor3 = (hwif->dma_base + 3); + if (!(hwif->dma_prdtable)) + hwif->dma_prdtable = (hwif->dma_base + 4); + + if (!hwif->ide_dma_off_quietly) + hwif->ide_dma_off_quietly = &__ide_dma_off_quietly; + if (!hwif->ide_dma_host_off) + hwif->ide_dma_host_off = &__ide_dma_host_off; + if (!hwif->ide_dma_on) + hwif->ide_dma_on = &__ide_dma_on; + if (!hwif->ide_dma_host_on) + hwif->ide_dma_host_on = &__ide_dma_host_on; + if (!hwif->ide_dma_check) + hwif->ide_dma_check = &__ide_dma_check; + if (!hwif->dma_setup) + hwif->dma_setup = &ide_dma_setup; + if (!hwif->dma_exec_cmd) + hwif->dma_exec_cmd = &ide_dma_exec_cmd; + if (!hwif->dma_start) + hwif->dma_start = &ide_dma_start; + if (!hwif->ide_dma_end) + hwif->ide_dma_end = &__ide_dma_end; + if (!hwif->ide_dma_test_irq) + hwif->ide_dma_test_irq = &__ide_dma_test_irq; + if (!hwif->ide_dma_timeout) + hwif->ide_dma_timeout = &__ide_dma_timeout; + if (!hwif->ide_dma_lostirq) + hwif->ide_dma_lostirq = &__ide_dma_lostirq; + + if (hwif->chipset != ide_trm290) { + u8 dma_stat = hwif->INB(hwif->dma_status); + 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"); + + if (!(hwif->dma_master)) + BUG(); +} + +EXPORT_SYMBOL_GPL(ide_setup_dma); +#endif /* CONFIG_BLK_DEV_IDEDMA_PCI */ diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c new file mode 100644 index 0000000..36c0b74 --- /dev/null +++ b/drivers/ide/ide-floppy.c @@ -0,0 +1,2211 @@ +/* + * linux/drivers/ide/ide-floppy.c Version 0.99 Feb 24 2002 + * + * Copyright (C) 1996 - 1999 Gadi Oxman <gadio@netvision.net.il> + * Copyright (C) 2000 - 2002 Paul Bristow <paul@paulbristow.net> + */ + +/* + * IDE ATAPI floppy driver. + * + * The driver currently doesn't have any fancy features, just the bare + * minimum read/write support. + * + * This driver supports the following IDE floppy drives: + * + * LS-120/240 SuperDisk + * Iomega Zip 100/250 + * Iomega PC Card Clik!/PocketZip + * + * 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. + * Ver 0.9.sv Jan 6 01 Sam Varshavchik <mrsam@courier-mta.com> + * Implement low level formatting. Reimplemented + * IDEFLOPPY_CAPABILITIES_PAGE, since we need the srfp + * bit. My LS-120 drive barfs on + * IDEFLOPPY_CAPABILITIES_PAGE, but maybe it's just me. + * Compromise by not reporting a failure to get this + * mode page. Implemented four IOCTLs in order to + * implement formatting. IOCTls begin with 0x4600, + * 0x46 is 'F' as in Format. + * Jan 9 01 Userland option to select format verify. + * Added PC_SUPPRESS_ERROR flag - some idefloppy drives + * do not implement IDEFLOPPY_CAPABILITIES_PAGE, and + * return a sense error. Suppress error reporting in + * this particular case in order to avoid spurious + * errors in syslog. The culprit is + * idefloppy_get_capability_page(), so move it to + * idefloppy_begin_format() so that it's not used + * unless absolutely necessary. + * If drive does not support format progress indication + * monitor the dsc bit in the status register. + * Also, O_NDELAY on open will allow the device to be + * opened without a disk available. This can be used to + * open an unformatted disk, or get the device capacity. + * Ver 0.91 Dec 11 99 Added IOMEGA Clik! drive support by + * <paul@paulbristow.net> + * Ver 0.92 Oct 22 00 Paul Bristow became official maintainer for this + * driver. Included Powerbook internal zip kludge. + * Ver 0.93 Oct 24 00 Fixed bugs for Clik! drive + * no disk on insert and disk change now works + * Ver 0.94 Oct 27 00 Tidied up to remove strstr(Clik) everywhere + * Ver 0.95 Nov 7 00 Brought across to kernel 2.4 + * Ver 0.96 Jan 7 01 Actually in line with release version of 2.4.0 + * including set_bit patch from Rusty Russell + * Ver 0.97 Jul 22 01 Merge 0.91-0.96 onto 0.9.sv for ac series + * Ver 0.97.sv Aug 3 01 Backported from 2.4.7-ac3 + * Ver 0.98 Oct 26 01 Split idefloppy_transfer_pc into two pieces to + * fix a lost interrupt problem. It appears the busy + * bit was being deasserted by my IOMEGA ATAPI ZIP 100 + * drive before the drive was actually ready. + * Ver 0.98a Oct 29 01 Expose delay value so we can play. + * Ver 0.99 Feb 24 02 Remove duplicate code, modify clik! detection code + * to support new PocketZip drives + */ + +#define IDEFLOPPY_VERSION "0.99.newide" + +#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/slab.h> +#include <linux/cdrom.h> +#include <linux/ide.h> +#include <linux/bitops.h> + +#include <asm/byteorder.h> +#include <asm/irq.h> +#include <asm/uaccess.h> +#include <asm/io.h> +#include <asm/unaligned.h> + +/* + * The following are used to debug the driver. + */ +#define IDEFLOPPY_DEBUG_LOG 0 +#define IDEFLOPPY_DEBUG_INFO 0 +#define IDEFLOPPY_DEBUG_BUGS 1 + +/* #define IDEFLOPPY_DEBUG(fmt, args...) printk(KERN_INFO fmt, ## args) */ +#define IDEFLOPPY_DEBUG( fmt, args... ) + +#if IDEFLOPPY_DEBUG_LOG +#define debug_log printk +#else +#define debug_log(fmt, args... ) do {} while(0) +#endif + + +/* + * 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 */ + int b_count; /* Missing/Available data on the current buffer */ + struct request *rq; /* The corresponding request */ + u8 *buffer; /* Data buffer */ + u8 *current_position; /* Pointer into the above buffer */ + void (*callback) (ide_drive_t *); /* Called when this packet command is completed */ + u8 pc_buffer[IDEFLOPPY_PC_BUFFER_SIZE]; /* Temporary buffer */ + unsigned long flags; /* Status/Action bit flags: long for set_bit */ +} 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 */ + +#define PC_SUPPRESS_ERROR 6 /* Suppress error reporting */ + +/* + * 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_floppy_obj { + ide_drive_t *drive; + ide_driver_t *driver; + struct gendisk *disk; + struct kref kref; + + /* Current packet command */ + idefloppy_pc_t *pc; + /* Last failed packet command */ + idefloppy_pc_t *failed_pc; + /* Packet command stack */ + idefloppy_pc_t pc_stack[IDEFLOPPY_PC_STACK]; + /* Next free packet command storage space */ + int pc_stack_index; + struct request rq_stack[IDEFLOPPY_PC_STACK]; + /* We implement a circular array */ + int rq_stack_index; + + /* + * Last error information + */ + u8 sense_key, asc, ascq; + /* delay this long before sending packet command */ + u8 ticks; + int progress_indication; + + /* + * Device information + */ + /* Current format */ + int blocks, block_size, bs_factor; + /* Last format capacity */ + idefloppy_capacity_descriptor_t capacity; + /* Copy of the flexible disk page */ + idefloppy_flexible_disk_page_t flexible_disk_page; + /* Write protect */ + int wp; + /* Supports format progress report */ + int srfp; + /* Status/Action flags */ + unsigned long flags; +} idefloppy_floppy_t; + +#define IDEFLOPPY_TICKS_DELAY 3 /* default delay for ZIP 100 */ + +/* + * 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 */ +#define IDEFLOPPY_FORMAT_IN_PROGRESS 3 /* Format in progress */ +#define IDEFLOPPY_CLIK_DRIVE 4 /* Avoid commands not supported in Clik drive */ +#define IDEFLOPPY_ZIP_DRIVE 5 /* Requires BH algorithm for packets */ + +/* + * 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 + +/* + * IOCTLs used in low-level formatting. + */ + +#define IDEFLOPPY_IOCTL_FORMAT_SUPPORTED 0x4600 +#define IDEFLOPPY_IOCTL_FORMAT_GET_CAPACITY 0x4601 +#define IDEFLOPPY_IOCTL_FORMAT_START 0x4602 +#define IDEFLOPPY_IOCTL_FORMAT_GET_PROGRESS 0x4603 + +#if 0 +/* + * 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)) + +#endif + +/* + * Error codes which are returned in rq->errors to the higher part + * of the driver. + */ +#define IDEFLOPPY_ERROR_GENERAL 101 + +/* + * 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 sksv[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; + +static DECLARE_MUTEX(idefloppy_ref_sem); + +#define to_ide_floppy(obj) container_of(obj, struct ide_floppy_obj, kref) + +#define ide_floppy_g(disk) \ + container_of((disk)->private_data, struct ide_floppy_obj, driver) + +static struct ide_floppy_obj *ide_floppy_get(struct gendisk *disk) +{ + struct ide_floppy_obj *floppy = NULL; + + down(&idefloppy_ref_sem); + floppy = ide_floppy_g(disk); + if (floppy) + kref_get(&floppy->kref); + up(&idefloppy_ref_sem); + return floppy; +} + +static void ide_floppy_release(struct kref *); + +static void ide_floppy_put(struct ide_floppy_obj *floppy) +{ + down(&idefloppy_ref_sem); + kref_put(&floppy->kref, ide_floppy_release); + up(&idefloppy_ref_sem); +} + +/* + * 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--) + (void) HWIF(drive)->INB(IDE_DATA_REG); +} + +#if IDEFLOPPY_DEBUG_BUGS +static void idefloppy_write_zeros (ide_drive_t *drive, unsigned int bcount) +{ + while (bcount--) + HWIF(drive)->OUTB(0, IDE_DATA_REG); +} +#endif /* IDEFLOPPY_DEBUG_BUGS */ + + +/* + * idefloppy_do_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 int idefloppy_do_end_request(ide_drive_t *drive, int uptodate, int nsecs) +{ + idefloppy_floppy_t *floppy = drive->driver_data; + struct request *rq = HWGROUP(drive)->rq; + int error; + + debug_log(KERN_INFO "Reached idefloppy_end_request\n"); + + 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 0; + if (!(rq->flags & REQ_SPECIAL)) { //if (!IDEFLOPPY_RQ_CMD (rq->cmd)) { + /* our real local end request function */ + ide_end_request(drive, uptodate, nsecs); + return 0; + } + rq->errors = error; + /* fixme: need to move this local also */ + ide_end_drive_cmd(drive, 0, 0); + return 0; +} + +static void idefloppy_input_buffers (ide_drive_t *drive, idefloppy_pc_t *pc, unsigned int bcount) +{ + struct request *rq = pc->rq; + struct bio_vec *bvec; + struct bio *bio; + unsigned long flags; + char *data; + int count, i, done = 0; + + rq_for_each_bio(bio, rq) { + bio_for_each_segment(bvec, bio, i) { + if (!bcount) + break; + + count = min(bvec->bv_len, bcount); + + data = bvec_kmap_irq(bvec, &flags); + drive->hwif->atapi_input_bytes(drive, data, count); + bvec_kunmap_irq(data, &flags); + + bcount -= count; + pc->b_count += count; + done += count; + } + } + + idefloppy_do_end_request(drive, 1, done >> 9); + + if (bcount) { + printk(KERN_ERR "%s: leftover data in idefloppy_input_buffers, bcount == %d\n", drive->name, bcount); + idefloppy_discard_data(drive, bcount); + } +} + +static void idefloppy_output_buffers (ide_drive_t *drive, idefloppy_pc_t *pc, unsigned int bcount) +{ + struct request *rq = pc->rq; + struct bio *bio; + struct bio_vec *bvec; + unsigned long flags; + int count, i, done = 0; + char *data; + + rq_for_each_bio(bio, rq) { + bio_for_each_segment(bvec, bio, i) { + if (!bcount) + break; + + count = min(bvec->bv_len, bcount); + + data = bvec_kmap_irq(bvec, &flags); + drive->hwif->atapi_output_bytes(drive, data, count); + bvec_kunmap_irq(data, &flags); + + bcount -= count; + pc->b_count += count; + done += count; + } + } + + idefloppy_do_end_request(drive, 1, done >> 9); + + if (bcount) { + printk(KERN_ERR "%s: leftover data in idefloppy_output_buffers, bcount == %d\n", drive->name, bcount); + idefloppy_write_zeros(drive, bcount); + } +} + +static void idefloppy_update_buffers (ide_drive_t *drive, idefloppy_pc_t *pc) +{ + struct request *rq = pc->rq; + struct bio *bio = rq->bio; + + while ((bio = rq->bio) != NULL) + idefloppy_do_end_request(drive, 1, 0); +} + +/* + * 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) +{ + struct ide_floppy_obj *floppy = drive->driver_data; + + ide_init_drive_cmd(rq); + rq->buffer = (char *) pc; + rq->flags = REQ_SPECIAL; //rq->cmd = IDEFLOPPY_PC_RQ; + rq->rq_disk = floppy->disk; + (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; + floppy->progress_indication = result->sksv[0] & 0x80 ? + (u16)get_unaligned((u16 *)(result->sksv+1)):0x10000; + if (floppy->failed_pc) + debug_log(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 + debug_log(KERN_INFO "ide-floppy: sense key = %x, asc = %x, " + "ascq = %x\n", result->sense_key, + result->asc, result->ascq); +} + +static void idefloppy_request_sense_callback (ide_drive_t *drive) +{ + idefloppy_floppy_t *floppy = drive->driver_data; + + debug_log(KERN_INFO "ide-floppy: Reached %s\n", __FUNCTION__); + + if (!floppy->pc->error) { + idefloppy_analyze_error(drive,(idefloppy_request_sense_result_t *) floppy->pc->buffer); + idefloppy_do_end_request(drive, 1, 0); + } else { + printk(KERN_ERR "Error in REQUEST SENSE itself - Aborting request!\n"); + idefloppy_do_end_request(drive, 0, 0); + } +} + +/* + * General packet command callback function. + */ +static void idefloppy_pc_callback (ide_drive_t *drive) +{ + idefloppy_floppy_t *floppy = drive->driver_data; + + debug_log(KERN_INFO "ide-floppy: Reached %s\n", __FUNCTION__); + + idefloppy_do_end_request(drive, floppy->pc->error ? 0 : 1, 0); +} + +/* + * 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->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; + atapi_error_t error; + + error.all = HWIF(drive)->INB(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; + atapi_status_t status; + atapi_bcount_t bcount; + atapi_ireason_t ireason; + idefloppy_pc_t *pc = floppy->pc; + struct request *rq = pc->rq; + unsigned int temp; + + debug_log(KERN_INFO "ide-floppy: Reached %s interrupt handler\n", + __FUNCTION__); + + if (test_bit(PC_DMA_IN_PROGRESS, &pc->flags)) { + if (HWIF(drive)->ide_dma_end(drive)) { + set_bit(PC_DMA_ERROR, &pc->flags); + } else { + pc->actually_transferred = pc->request_transfer; + idefloppy_update_buffers(drive, pc); + } + debug_log(KERN_INFO "ide-floppy: DMA finished\n"); + } + + /* Clear the interrupt */ + status.all = HWIF(drive)->INB(IDE_STATUS_REG); + + if (!status.b.drq) { /* No more interrupts */ + debug_log(KERN_INFO "Packet command completed, %d bytes " + "transferred\n", pc->actually_transferred); + clear_bit(PC_DMA_IN_PROGRESS, &pc->flags); + + local_irq_enable(); + + if (status.b.check || test_bit(PC_DMA_ERROR, &pc->flags)) { + /* Error detected */ + debug_log(KERN_INFO "ide-floppy: %s: I/O error\n", + drive->name); + 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); + } + /* Retry operation */ + idefloppy_retry_pc(drive); + /* queued, but not started */ + return ide_stopped; + } + pc->error = 0; + if (floppy->failed_pc == pc) + floppy->failed_pc = NULL; + /* Command finished - Call the callback function */ + pc->callback(drive); + return ide_stopped; + } + + 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)__ide_dma_off(drive); + return ide_do_reset(drive); + } + + /* Get the number of bytes to transfer */ + bcount.b.high = HWIF(drive)->INB(IDE_BCOUNTH_REG); + bcount.b.low = HWIF(drive)->INB(IDE_BCOUNTL_REG); + /* on this interrupt */ + ireason.all = HWIF(drive)->INB(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); + if (HWGROUP(drive)->handler != NULL) + BUG(); + ide_set_handler(drive, + &idefloppy_pc_intr, + IDEFLOPPY_WAIT_CMD, + NULL); + return ide_started; + } + debug_log(KERN_NOTICE "ide-floppy: The floppy wants to " + "send us more data than expected - " + "allowing transfer\n"); + } + } + if (test_bit(PC_WRITING, &pc->flags)) { + if (pc->buffer != NULL) + /* Write the current buffer */ + HWIF(drive)->atapi_output_bytes(drive, + pc->current_position, + bcount.all); + else + idefloppy_output_buffers(drive, pc, bcount.all); + } else { + if (pc->buffer != NULL) + /* Read the current buffer */ + HWIF(drive)->atapi_input_bytes(drive, + pc->current_position, + bcount.all); + else + idefloppy_input_buffers(drive, pc, bcount.all); + } + /* Update the current position */ + pc->actually_transferred += bcount.all; + pc->current_position += bcount.all; + + if (HWGROUP(drive)->handler != NULL) + BUG(); + ide_set_handler(drive, &idefloppy_pc_intr, IDEFLOPPY_WAIT_CMD, NULL); /* And set the interrupt handler again */ + return ide_started; +} + +/* + * This is the original routine that did the packet transfer. + * It fails at high speeds on the Iomega ZIP drive, so there's a slower version + * for that drive below. The algorithm is chosen based on drive type + */ +static ide_startstop_t idefloppy_transfer_pc (ide_drive_t *drive) +{ + ide_startstop_t startstop; + idefloppy_floppy_t *floppy = drive->driver_data; + atapi_ireason_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 = HWIF(drive)->INB(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); + } + if (HWGROUP(drive)->handler != NULL) + BUG(); + /* Set the interrupt routine */ + ide_set_handler(drive, &idefloppy_pc_intr, IDEFLOPPY_WAIT_CMD, NULL); + /* Send the actual packet */ + HWIF(drive)->atapi_output_bytes(drive, floppy->pc->c, 12); + return ide_started; +} + + +/* + * What we have here is a classic case of a top half / bottom half + * interrupt service routine. In interrupt mode, the device sends + * an interrupt to signal it's ready to receive a packet. However, + * we need to delay about 2-3 ticks before issuing the packet or we + * gets in trouble. + * + * So, follow carefully. transfer_pc1 is called as an interrupt (or + * directly). In either case, when the device says it's ready for a + * packet, we schedule the packet transfer to occur about 2-3 ticks + * later in transfer_pc2. + */ +static int idefloppy_transfer_pc2 (ide_drive_t *drive) +{ + idefloppy_floppy_t *floppy = drive->driver_data; + + /* Send the actual packet */ + HWIF(drive)->atapi_output_bytes(drive, floppy->pc->c, 12); + /* Timeout for the packet command */ + return IDEFLOPPY_WAIT_CMD; +} + +static ide_startstop_t idefloppy_transfer_pc1 (ide_drive_t *drive) +{ + idefloppy_floppy_t *floppy = drive->driver_data; + ide_startstop_t startstop; + atapi_ireason_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 = HWIF(drive)->INB(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); + } + /* + * The following delay solves a problem with ATAPI Zip 100 drives + * where the Busy flag was apparently being deasserted before the + * unit was ready to receive data. This was happening on a + * 1200 MHz Athlon system. 10/26/01 25msec is too short, + * 40 and 50msec work well. idefloppy_pc_intr will not be actually + * used until after the packet is moved in about 50 msec. + */ + if (HWGROUP(drive)->handler != NULL) + BUG(); + ide_set_handler(drive, + &idefloppy_pc_intr, /* service routine for packet command */ + floppy->ticks, /* wait this long before "failing" */ + &idefloppy_transfer_pc2); /* fail == transfer_pc2 */ + return ide_started; +} + +/** + * idefloppy_should_report_error() + * + * Supresses error messages resulting from Medium not present + */ +static inline int idefloppy_should_report_error(idefloppy_floppy_t *floppy) +{ + if (floppy->sense_key == 0x02 && + floppy->asc == 0x3a && + floppy->ascq == 0x00) + return 0; + return 1; +} + +/* + * 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; + ide_hwif_t *hwif = drive->hwif; + atapi_feature_t feature; + atapi_bcount_t bcount; + ide_handler_t *pkt_xfer_routine; + +#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; + /* Set the current packet command */ + floppy->pc = pc; + + 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)) { + if (!test_bit(PC_SUPPRESS_ERROR, &pc->flags)) { + if (idefloppy_should_report_error(floppy)) + 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); + } + /* Giving up */ + pc->error = IDEFLOPPY_ERROR_GENERAL; + } + floppy->failed_pc = NULL; + pc->callback(drive); + return ide_stopped; + } + + debug_log(KERN_INFO "Retry number - %d\n",pc->retries); + + pc->retries++; + /* We haven't transferred any data yet */ + pc->actually_transferred = 0; + pc->current_position = pc->buffer; + bcount.all = min(pc->request_transfer, 63 * 1024); + + if (test_and_clear_bit(PC_DMA_ERROR, &pc->flags)) { + (void)__ide_dma_off(drive); + } + feature.all = 0; + + if (test_bit(PC_DMA_RECOMMENDED, &pc->flags) && drive->using_dma) + feature.b.dma = !hwif->dma_setup(drive); + + if (IDE_CONTROL_REG) + HWIF(drive)->OUTB(drive->ctl, IDE_CONTROL_REG); + /* Use PIO/DMA */ + HWIF(drive)->OUTB(feature.all, IDE_FEATURE_REG); + HWIF(drive)->OUTB(bcount.b.high, IDE_BCOUNTH_REG); + HWIF(drive)->OUTB(bcount.b.low, IDE_BCOUNTL_REG); + HWIF(drive)->OUTB(drive->select.all, IDE_SELECT_REG); + + if (feature.b.dma) { /* Begin DMA, if necessary */ + set_bit(PC_DMA_IN_PROGRESS, &pc->flags); + hwif->dma_start(drive); + } + + /* Can we transfer the packet when we get the interrupt or wait? */ + if (test_bit(IDEFLOPPY_ZIP_DRIVE, &floppy->flags)) { + /* wait */ + pkt_xfer_routine = &idefloppy_transfer_pc1; + } else { + /* immediate */ + pkt_xfer_routine = &idefloppy_transfer_pc; + } + + if (test_bit (IDEFLOPPY_DRQ_INTERRUPT, &floppy->flags)) { + /* Issue the packet command */ + ide_execute_command(drive, WIN_PACKETCMD, + pkt_xfer_routine, + IDEFLOPPY_WAIT_CMD, + NULL); + return ide_started; + } else { + /* Issue the packet command */ + HWIF(drive)->OUTB(WIN_PACKETCMD, IDE_COMMAND_REG); + return (*pkt_xfer_routine) (drive); + } +} + +static void idefloppy_rw_callback (ide_drive_t *drive) +{ + debug_log(KERN_INFO "ide-floppy: Reached idefloppy_rw_callback\n"); + + idefloppy_do_end_request(drive, 1, 0); + return; +} + +static void idefloppy_create_prevent_cmd (idefloppy_pc_t *pc, int prevent) +{ + debug_log(KERN_INFO "ide-floppy: creating prevent removal command, " + "prevent = %d\n", prevent); + + 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; +} + +static void idefloppy_create_format_unit_cmd (idefloppy_pc_t *pc, int b, int l, + int flags) +{ + idefloppy_init_pc(pc); + pc->c[0] = IDEFLOPPY_FORMAT_UNIT_CMD; + pc->c[1] = 0x17; + + memset(pc->buffer, 0, 12); + pc->buffer[1] = 0xA2; + /* Default format list header, u8 1: FOV/DCRT/IMM bits set */ + + if (flags & 1) /* Verify bit on... */ + pc->buffer[1] ^= 0x20; /* ... turn off DCRT bit */ + pc->buffer[3] = 8; + + put_unaligned(htonl(b), (unsigned int *)(&pc->buffer[4])); + put_unaligned(htonl(l), (unsigned int *)(&pc->buffer[8])); + pc->buffer_size=12; + set_bit(PC_WRITING, &pc->flags); +} + +/* + * A mode sense command is used to "sense" floppy parameters. + */ +static void idefloppy_create_mode_sense_cmd (idefloppy_pc_t *pc, u8 page_code, u8 type) +{ + u16 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), (u16 *) &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; + int cmd = rq_data_dir(rq); + + debug_log("create_rw1%d_cmd: block == %d, blocks == %d\n", + 2 * test_bit (IDEFLOPPY_USE_READ12, &floppy->flags), + block, blocks); + + idefloppy_init_pc(pc); + if (test_bit(IDEFLOPPY_USE_READ12, &floppy->flags)) { + pc->c[0] = cmd == READ ? IDEFLOPPY_READ12_CMD : IDEFLOPPY_WRITE12_CMD; + put_unaligned(htonl(blocks), (unsigned int *) &pc->c[6]); + } else { + pc->c[0] = 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_count = cmd == READ ? 0 : rq->bio->bi_size; + if (rq->flags & REQ_RW) + 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); +} + +static int +idefloppy_blockpc_cmd(idefloppy_floppy_t *floppy, idefloppy_pc_t *pc, struct request *rq) +{ + /* + * just support eject for now, it would not be hard to make the + * REQ_BLOCK_PC support fully-featured + */ + if (rq->cmd[0] != IDEFLOPPY_START_STOP_CMD) + return 1; + + idefloppy_init_pc(pc); + memcpy(pc->c, rq->cmd, sizeof(pc->c)); + return 0; +} + +/* + * idefloppy_do_request is our request handling function. + */ +static ide_startstop_t idefloppy_do_request (ide_drive_t *drive, struct request *rq, sector_t block_s) +{ + idefloppy_floppy_t *floppy = drive->driver_data; + idefloppy_pc_t *pc; + unsigned long block = (unsigned long)block_s; + + debug_log(KERN_INFO "rq_status: %d, dev: %s, flags: %lx, errors: %d\n", + rq->rq_status, + rq->rq_disk ? rq->rq_disk->disk_name ? "?", + rq->flags, rq->errors); + debug_log(KERN_INFO "sector: %ld, nr_sectors: %ld, " + "current_nr_sectors: %d\n", (long)rq->sector, + rq->nr_sectors, rq->current_nr_sectors); + + if (rq->errors >= ERROR_MAX) { + if (floppy->failed_pc != NULL) { + if (idefloppy_should_report_error(floppy)) + 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_do_end_request(drive, 0, 0); + return ide_stopped; + } + if (rq->flags & REQ_CMD) { + if (((long)rq->sector % floppy->bs_factor) || + (rq->nr_sectors % floppy->bs_factor)) { + printk("%s: unsupported r/w request size\n", + drive->name); + idefloppy_do_end_request(drive, 0, 0); + return ide_stopped; + } + pc = idefloppy_next_pc_storage(drive); + idefloppy_create_rw_cmd(floppy, pc, rq, block); + } else if (rq->flags & REQ_SPECIAL) { + pc = (idefloppy_pc_t *) rq->buffer; + } else if (rq->flags & REQ_BLOCK_PC) { + pc = idefloppy_next_pc_storage(drive); + if (idefloppy_blockpc_cmd(floppy, pc, rq)) { + idefloppy_do_end_request(drive, 0, 0); + return ide_stopped; + } + } else { + blk_dump_rq_flags(rq, + "ide-floppy: unsupported command in queue"); + idefloppy_do_end_request(drive, 0, 0); + 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 ide_floppy_obj *floppy = drive->driver_data; + struct request rq; + + ide_init_drive_cmd (&rq); + rq.buffer = (char *) pc; + rq.flags = REQ_SPECIAL; // rq.cmd = IDEFLOPPY_PC_RQ; + rq.rq_disk = floppy->disk; + + 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; + set_disk_ro(floppy->disk, floppy->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; +} + +static int idefloppy_get_capability_page(ide_drive_t *drive) +{ + idefloppy_floppy_t *floppy = drive->driver_data; + idefloppy_pc_t pc; + idefloppy_mode_parameter_header_t *header; + idefloppy_capabilities_page_t *page; + + floppy->srfp = 0; + idefloppy_create_mode_sense_cmd(&pc, IDEFLOPPY_CAPABILITIES_PAGE, + MODE_SENSE_CURRENT); + + set_bit(PC_SUPPRESS_ERROR, &pc.flags); + if (idefloppy_queue_pc_tail(drive,&pc)) { + return 1; + } + + header = (idefloppy_mode_parameter_header_t *) pc.buffer; + page= (idefloppy_capabilities_page_t *)(header+1); + floppy->srfp = page->srfp; + 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; + set_capacity(floppy->disk, 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) + { + switch (descriptor->dc) { + /* Clik! drive returns this instead of CAPACITY_CURRENT */ + case CAPACITY_UNFORMATTED: + if (!test_bit(IDEFLOPPY_CLIK_DRIVE, &floppy->flags)) + /* + * If it is not a clik drive, break out + * (maintains previous driver behaviour) + */ + break; + case CAPACITY_CURRENT: + /* Normal Zip/LS-120 disks */ + if (memcmp(descriptor, &floppy->capacity, sizeof (idefloppy_capacity_descriptor_t))) + printk(KERN_INFO "%s: %dkB, %d blocks, %d " + "sector size\n", drive->name, + blocks * length / 1024, blocks, length); + floppy->capacity = *descriptor; + if (!length || length % 512) { + printk(KERN_NOTICE "%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; + } + break; + case CAPACITY_NO_CARTRIDGE: + /* + * This is a KERN_ERR so it appears on screen + * for the user to see + */ + printk(KERN_ERR "%s: No disk in drive\n", drive->name); + break; + case CAPACITY_INVALID: + printk(KERN_ERR "%s: Invalid capacity for disk " + "in drive\n", drive->name); + break; + } + } + if (!i) { + debug_log( "Descriptor 0 Code: %d\n", + descriptor->dc); + } + debug_log( "Descriptor %d: %dkB, %d blocks, %d " + "sector size\n", i, blocks * length / 1024, blocks, + length); + } + + /* Clik! disk does not support get_flexible_disk_page */ + if (!test_bit(IDEFLOPPY_CLIK_DRIVE, &floppy->flags)) { + (void) idefloppy_get_flexible_disk_page(drive); + } + + set_capacity(floppy->disk, floppy->blocks * floppy->bs_factor); + return rc; +} + +/* +** Obtain the list of formattable capacities. +** Very similar to idefloppy_get_capacity, except that we push the capacity +** descriptors to userland, instead of our own structures. +** +** Userland gives us the following structure: +** +** struct idefloppy_format_capacities { +** int nformats; +** struct { +** int nblocks; +** int blocksize; +** } formats[]; +** } ; +** +** userland initializes nformats to the number of allocated formats[] +** records. On exit we set nformats to the number of records we've +** actually initialized. +** +*/ + +static int idefloppy_get_format_capacities(ide_drive_t *drive, int __user *arg) +{ + idefloppy_pc_t pc; + idefloppy_capacity_header_t *header; + idefloppy_capacity_descriptor_t *descriptor; + int i, descriptors, blocks, length; + int u_array_size; + int u_index; + int __user *argp; + + if (get_user(u_array_size, arg)) + return (-EFAULT); + + if (u_array_size <= 0) + return (-EINVAL); + + 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 (-EIO); + } + header = (idefloppy_capacity_header_t *) pc.buffer; + descriptors = header->length / + sizeof(idefloppy_capacity_descriptor_t); + descriptor = (idefloppy_capacity_descriptor_t *) (header + 1); + + u_index = 0; + argp = arg + 1; + + /* + ** We always skip the first capacity descriptor. That's the + ** current capacity. We are interested in the remaining descriptors, + ** the formattable capacities. + */ + + for (i=0; i<descriptors; i++, descriptor++) { + if (u_index >= u_array_size) + break; /* User-supplied buffer too small */ + if (i == 0) + continue; /* Skip the first descriptor */ + + blocks = ntohl(descriptor->blocks); + length = ntohs(descriptor->length); + + if (put_user(blocks, argp)) + return(-EFAULT); + ++argp; + + if (put_user(length, argp)) + return (-EFAULT); + ++argp; + + ++u_index; + } + + if (put_user(u_index, arg)) + return (-EFAULT); + return (0); +} + +/* +** Send ATAPI_FORMAT_UNIT to the drive. +** +** Userland gives us the following structure: +** +** struct idefloppy_format_command { +** int nblocks; +** int blocksize; +** int flags; +** } ; +** +** flags is a bitmask, currently, the only defined flag is: +** +** 0x01 - verify media after format. +*/ + +static int idefloppy_begin_format(ide_drive_t *drive, int __user *arg) +{ + int blocks; + int length; + int flags; + idefloppy_pc_t pc; + + if (get_user(blocks, arg) || + get_user(length, arg+1) || + get_user(flags, arg+2)) { + return (-EFAULT); + } + + /* Get the SFRP bit */ + (void) idefloppy_get_capability_page(drive); + idefloppy_create_format_unit_cmd(&pc, blocks, length, flags); + if (idefloppy_queue_pc_tail(drive, &pc)) { + return (-EIO); + } + + return (0); +} + +/* +** Get ATAPI_FORMAT_UNIT progress indication. +** +** Userland gives a pointer to an int. The int is set to a progresss +** indicator 0-65536, with 65536=100%. +** +** If the drive does not support format progress indication, we just check +** the dsc bit, and return either 0 or 65536. +*/ + +static int idefloppy_get_format_progress(ide_drive_t *drive, int __user *arg) +{ + idefloppy_floppy_t *floppy = drive->driver_data; + idefloppy_pc_t pc; + int progress_indication = 0x10000; + + if (floppy->srfp) { + idefloppy_create_request_sense_cmd(&pc); + if (idefloppy_queue_pc_tail(drive, &pc)) { + return (-EIO); + } + + if (floppy->sense_key == 2 && + floppy->asc == 4 && + floppy->ascq == 4) { + progress_indication = floppy->progress_indication; + } + /* Else assume format_unit has finished, and we're + ** at 0x10000 */ + } else { + atapi_status_t status; + unsigned long flags; + + local_irq_save(flags); + status.all = HWIF(drive)->INB(IDE_STATUS_REG); + local_irq_restore(flags); + + progress_indication = !status.b.dsc ? 0 : 0x10000; + } + if (put_user(progress_indication, arg)) + return (-EFAULT); + + return (0); +} + +/* + * Return the current floppy capacity. + */ +static sector_t 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 + u16 mask,i; + char buffer[80]; +#endif /* IDEFLOPPY_DEBUG_INFO */ + + *((u16 *) &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) +{ + idefloppy_floppy_t *floppy = 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, "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, "ticks", SETTING_RW, -1, -1, TYPE_BYTE, 0, 255, 1, 1, &floppy->ticks, NULL); +} + +/* + * Driver initialization. + */ +static void idefloppy_setup (ide_drive_t *drive, idefloppy_floppy_t *floppy) +{ + struct idefloppy_id_gcw gcw; + + *((u16 *) &gcw) = drive->id->config; + 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 (!strncmp(drive->id->model, "IOMEGA ZIP 100 ATAPI", 20)) { + set_bit(IDEFLOPPY_ZIP_DRIVE, &floppy->flags); + /* This value will be visible in the /proc/ide/hdx/settings */ + floppy->ticks = IDEFLOPPY_TICKS_DELAY; + blk_queue_max_sectors(drive->queue, 64); + } + + /* + * Guess what? The IOMEGA Clik! drive also needs the + * above fix. It makes nasty clicking noises without + * it, so please don't remove this. + */ + if (strncmp(drive->id->model, "IOMEGA Clik!", 11) == 0) { + blk_queue_max_sectors(drive->queue, 64); + set_bit(IDEFLOPPY_CLIK_DRIVE, &floppy->flags); + } + + + (void) idefloppy_get_capacity(drive); + idefloppy_add_settings(drive); +} + +static int idefloppy_cleanup (ide_drive_t *drive) +{ + idefloppy_floppy_t *floppy = drive->driver_data; + struct gendisk *g = floppy->disk; + + if (ide_unregister_subdriver(drive)) + return 1; + + del_gendisk(g); + + ide_floppy_put(floppy); + + return 0; +} + +static void ide_floppy_release(struct kref *kref) +{ + struct ide_floppy_obj *floppy = to_ide_floppy(kref); + ide_drive_t *drive = floppy->drive; + struct gendisk *g = floppy->disk; + + drive->driver_data = NULL; + g->private_data = NULL; + put_disk(g); + kfree(floppy); +} + +#ifdef CONFIG_PROC_FS + +static int proc_idefloppy_read_capacity + (char *page, char **start, off_t off, int count, int *eof, void *data) +{ + ide_drive_t*drive = (ide_drive_t *)data; + int len; + + len = sprintf(page,"%llu\n", (long long)idefloppy_capacity(drive)); + PROC_IDE_READ_RETURN(page,start,off,count,eof,len); +} + +static ide_proc_entry_t idefloppy_proc[] = { + { "capacity", S_IFREG|S_IRUGO, proc_idefloppy_read_capacity, NULL }, + { "geometry", S_IFREG|S_IRUGO, proc_ide_read_geometry, NULL }, + { NULL, 0, NULL, NULL } +}; + +#else + +#define idefloppy_proc NULL + +#endif /* CONFIG_PROC_FS */ + +static int idefloppy_attach(ide_drive_t *drive); + +/* + * IDE subdriver functions, registered with ide.c + */ +static ide_driver_t idefloppy_driver = { + .owner = THIS_MODULE, + .name = "ide-floppy", + .version = IDEFLOPPY_VERSION, + .media = ide_floppy, + .busy = 0, + .supports_dsc_overlap = 0, + .cleanup = idefloppy_cleanup, + .do_request = idefloppy_do_request, + .end_request = idefloppy_do_end_request, + .error = __ide_error, + .abort = __ide_abort, + .proc = idefloppy_proc, + .attach = idefloppy_attach, + .drives = LIST_HEAD_INIT(idefloppy_driver.drives), +}; + +static int idefloppy_open(struct inode *inode, struct file *filp) +{ + struct gendisk *disk = inode->i_bdev->bd_disk; + struct ide_floppy_obj *floppy; + ide_drive_t *drive; + idefloppy_pc_t pc; + int ret = 0; + + debug_log(KERN_INFO "Reached idefloppy_open\n"); + + if (!(floppy = ide_floppy_get(disk))) + return -ENXIO; + + drive = floppy->drive; + + drive->usage++; + + if (drive->usage == 1) { + clear_bit(IDEFLOPPY_FORMAT_IN_PROGRESS, &floppy->flags); + /* Just in case */ + + 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) + && (filp->f_flags & O_NDELAY) == 0 + /* + ** Allow O_NDELAY to open a drive without a disk, or with + ** an unreadable disk, so that we can get the format + ** capacity of the drive or begin the format - Sam + */ + ) { + drive->usage--; + ret = -EIO; + goto out_put_floppy; + } + + if (floppy->wp && (filp->f_mode & 2)) { + drive->usage--; + ret = -EROFS; + goto out_put_floppy; + } + set_bit(IDEFLOPPY_MEDIA_CHANGED, &floppy->flags); + /* IOMEGA Clik! drives do not support lock/unlock commands */ + if (!test_bit(IDEFLOPPY_CLIK_DRIVE, &floppy->flags)) { + idefloppy_create_prevent_cmd(&pc, 1); + (void) idefloppy_queue_pc_tail(drive, &pc); + } + check_disk_change(inode->i_bdev); + } else if (test_bit(IDEFLOPPY_FORMAT_IN_PROGRESS, &floppy->flags)) { + drive->usage--; + ret = -EBUSY; + goto out_put_floppy; + } + return 0; + +out_put_floppy: + ide_floppy_put(floppy); + return ret; +} + +static int idefloppy_release(struct inode *inode, struct file *filp) +{ + struct gendisk *disk = inode->i_bdev->bd_disk; + struct ide_floppy_obj *floppy = ide_floppy_g(disk); + ide_drive_t *drive = floppy->drive; + idefloppy_pc_t pc; + + debug_log(KERN_INFO "Reached idefloppy_release\n"); + + if (drive->usage == 1) { + /* IOMEGA Clik! drives do not support lock/unlock commands */ + if (!test_bit(IDEFLOPPY_CLIK_DRIVE, &floppy->flags)) { + idefloppy_create_prevent_cmd(&pc, 0); + (void) idefloppy_queue_pc_tail(drive, &pc); + } + + clear_bit(IDEFLOPPY_FORMAT_IN_PROGRESS, &floppy->flags); + } + drive->usage--; + + ide_floppy_put(floppy); + + return 0; +} + +static int idefloppy_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct block_device *bdev = inode->i_bdev; + struct ide_floppy_obj *floppy = ide_floppy_g(bdev->bd_disk); + ide_drive_t *drive = floppy->drive; + void __user *argp = (void __user *)arg; + int err = generic_ide_ioctl(drive, file, bdev, cmd, arg); + int prevent = (arg) ? 1 : 0; + idefloppy_pc_t pc; + if (err != -EINVAL) + return err; + + switch (cmd) { + case CDROMEJECT: + prevent = 0; + /* fall through */ + case CDROM_LOCKDOOR: + if (drive->usage > 1) + return -EBUSY; + + /* The IOMEGA Clik! Drive doesn't support this command - no room for an eject mechanism */ + if (!test_bit(IDEFLOPPY_CLIK_DRIVE, &floppy->flags)) { + idefloppy_create_prevent_cmd(&pc, prevent); + (void) idefloppy_queue_pc_tail(drive, &pc); + } + if (cmd == CDROMEJECT) { + idefloppy_create_start_stop_cmd(&pc, 2); + (void) idefloppy_queue_pc_tail(drive, &pc); + } + return 0; + case IDEFLOPPY_IOCTL_FORMAT_SUPPORTED: + return 0; + case IDEFLOPPY_IOCTL_FORMAT_GET_CAPACITY: + return idefloppy_get_format_capacities(drive, argp); + case IDEFLOPPY_IOCTL_FORMAT_START: + + if (!(file->f_mode & 2)) + return -EPERM; + + if (drive->usage > 1) { + /* Don't format if someone is using the disk */ + + clear_bit(IDEFLOPPY_FORMAT_IN_PROGRESS, + &floppy->flags); + return -EBUSY; + } + + set_bit(IDEFLOPPY_FORMAT_IN_PROGRESS, &floppy->flags); + + err = idefloppy_begin_format(drive, argp); + if (err) + clear_bit(IDEFLOPPY_FORMAT_IN_PROGRESS, &floppy->flags); + return err; + /* + ** Note, the bit will be cleared when the device is + ** closed. This is the cleanest way to handle the + ** situation where the drive does not support + ** format progress reporting. + */ + case IDEFLOPPY_IOCTL_FORMAT_GET_PROGRESS: + return idefloppy_get_format_progress(drive, argp); + } + return -EINVAL; +} + +static int idefloppy_media_changed(struct gendisk *disk) +{ + struct ide_floppy_obj *floppy = ide_floppy_g(disk); + ide_drive_t *drive = floppy->drive; + + /* do not scan partitions twice if this is a removable device */ + if (drive->attach) { + drive->attach = 0; + return 0; + } + return test_and_clear_bit(IDEFLOPPY_MEDIA_CHANGED, &floppy->flags); +} + +static int idefloppy_revalidate_disk(struct gendisk *disk) +{ + struct ide_floppy_obj *floppy = ide_floppy_g(disk); + set_capacity(disk, idefloppy_capacity(floppy->drive)); + return 0; +} + +static struct block_device_operations idefloppy_ops = { + .owner = THIS_MODULE, + .open = idefloppy_open, + .release = idefloppy_release, + .ioctl = idefloppy_ioctl, + .media_changed = idefloppy_media_changed, + .revalidate_disk= idefloppy_revalidate_disk +}; + +static int idefloppy_attach (ide_drive_t *drive) +{ + idefloppy_floppy_t *floppy; + struct gendisk *g; + + if (!strstr("ide-floppy", drive->driver_req)) + goto failed; + if (!drive->present) + goto failed; + if (drive->media != ide_floppy) + goto failed; + if (!idefloppy_identify_device (drive, drive->id)) { + printk (KERN_ERR "ide-floppy: %s: not supported by this version of ide-floppy\n", drive->name); + goto failed; + } + if (drive->scsi) { + printk("ide-floppy: passing drive %s to ide-scsi emulation.\n", drive->name); + goto failed; + } + 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); + goto failed; + } + + g = alloc_disk(1 << PARTN_BITS); + if (!g) + goto out_free_floppy; + + ide_init_disk(g, drive); + + if (ide_register_subdriver(drive, &idefloppy_driver)) { + printk (KERN_ERR "ide-floppy: %s: Failed to register the driver with ide.c\n", drive->name); + goto out_put_disk; + } + + memset(floppy, 0, sizeof(*floppy)); + + kref_init(&floppy->kref); + + floppy->drive = drive; + floppy->driver = &idefloppy_driver; + floppy->disk = g; + + g->private_data = &floppy->driver; + + drive->driver_data = floppy; + + DRIVER(drive)->busy++; + idefloppy_setup (drive, floppy); + DRIVER(drive)->busy--; + g->minors = 1 << PARTN_BITS; + g->driverfs_dev = &drive->gendev; + strcpy(g->devfs_name, drive->devfs_name); + g->flags = drive->removable ? GENHD_FL_REMOVABLE : 0; + g->fops = &idefloppy_ops; + drive->attach = 1; + add_disk(g); + return 0; + +out_put_disk: + put_disk(g); +out_free_floppy: + kfree(floppy); +failed: + return 1; +} + +MODULE_DESCRIPTION("ATAPI FLOPPY Driver"); + +static void __exit idefloppy_exit (void) +{ + ide_unregister_driver(&idefloppy_driver); +} + +/* + * idefloppy_init will register the driver for each floppy. + */ +static int idefloppy_init (void) +{ + printk("ide-floppy driver " IDEFLOPPY_VERSION "\n"); + ide_register_driver(&idefloppy_driver); + return 0; +} + +module_init(idefloppy_init); +module_exit(idefloppy_exit); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/ide-generic.c b/drivers/ide/ide-generic.c new file mode 100644 index 0000000..99fd561 --- /dev/null +++ b/drivers/ide/ide-generic.c @@ -0,0 +1,32 @@ +/* + * generic/default IDE host driver + * + * Copyright (C) 2004 Bartlomiej Zolnierkiewicz + * This code was split off from ide.c. See it for original copyrights. + * + * May be copied or modified under the terms of the GNU General Public License. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/ide.h> + +static int __init ide_generic_init(void) +{ + if (ide_hwifs[0].io_ports[IDE_DATA_OFFSET]) + ide_get_lock(NULL, NULL); /* for atari only */ + + (void)ideprobe_init(); + + if (ide_hwifs[0].io_ports[IDE_DATA_OFFSET]) + ide_release_lock(); /* for atari only */ + + create_proc_ide_interfaces(); + + return 0; +} + +module_init(ide_generic_init); + +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c new file mode 100644 index 0000000..248e3cc --- /dev/null +++ b/drivers/ide/ide-io.c @@ -0,0 +1,1681 @@ +/* + * IDE I/O functions + * + * Basic PIO and command management functionality. + * + * This code was split off from ide.c. See ide.c for history and original + * copyrights. + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * For the avoidance of doubt the "preferred form" of this code is one which + * is in an open non patent encumbered format. Where cryptographic key signing + * forms part of the process of creating an executable the information + * including keys needed to generate an equivalently functional executable + * are deemed to be part of the source code. + */ + + +#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/slab.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/ide.h> +#include <linux/completion.h> +#include <linux/reboot.h> +#include <linux/cdrom.h> +#include <linux/seq_file.h> +#include <linux/device.h> +#include <linux/kmod.h> +#include <linux/scatterlist.h> + +#include <asm/byteorder.h> +#include <asm/irq.h> +#include <asm/uaccess.h> +#include <asm/io.h> +#include <asm/bitops.h> + +int __ide_end_request(ide_drive_t *drive, struct request *rq, int uptodate, + int nr_sectors) +{ + int ret = 1; + + BUG_ON(!(rq->flags & REQ_STARTED)); + + /* + * if failfast is set on a request, override number of sectors and + * complete the whole request right now + */ + if (blk_noretry_request(rq) && end_io_error(uptodate)) + nr_sectors = rq->hard_nr_sectors; + + if (!blk_fs_request(rq) && end_io_error(uptodate) && !rq->errors) + rq->errors = -EIO; + + /* + * decide whether to reenable DMA -- 3 is a random magic for now, + * if we DMA timeout more than 3 times, just stay in PIO + */ + if (drive->state == DMA_PIO_RETRY && drive->retry_pio <= 3) { + drive->state = 0; + HWGROUP(drive)->hwif->ide_dma_on(drive); + } + + if (!end_that_request_first(rq, uptodate, nr_sectors)) { + add_disk_randomness(rq->rq_disk); + + if (blk_rq_tagged(rq)) + blk_queue_end_tag(drive->queue, rq); + + blkdev_dequeue_request(rq); + HWGROUP(drive)->rq = NULL; + end_that_request_last(rq); + ret = 0; + } + return ret; +} +EXPORT_SYMBOL(__ide_end_request); + +/** + * ide_end_request - complete an IDE I/O + * @drive: IDE device for the I/O + * @uptodate: + * @nr_sectors: number of sectors completed + * + * This is our end_request wrapper function. We complete the I/O + * update random number input and dequeue the request, which if + * it was tagged may be out of order. + */ + +int ide_end_request (ide_drive_t *drive, int uptodate, int nr_sectors) +{ + struct request *rq; + unsigned long flags; + int ret = 1; + + spin_lock_irqsave(&ide_lock, flags); + rq = HWGROUP(drive)->rq; + + if (!nr_sectors) + nr_sectors = rq->hard_cur_sectors; + + if (blk_complete_barrier_rq_locked(drive->queue, rq, nr_sectors)) + ret = rq->nr_sectors != 0; + else + ret = __ide_end_request(drive, rq, uptodate, nr_sectors); + + spin_unlock_irqrestore(&ide_lock, flags); + return ret; +} +EXPORT_SYMBOL(ide_end_request); + +/* + * Power Management state machine. This one is rather trivial for now, + * we should probably add more, like switching back to PIO on suspend + * to help some BIOSes, re-do the door locking on resume, etc... + */ + +enum { + ide_pm_flush_cache = ide_pm_state_start_suspend, + idedisk_pm_standby, + + idedisk_pm_idle = ide_pm_state_start_resume, + ide_pm_restore_dma, +}; + +static void ide_complete_power_step(ide_drive_t *drive, struct request *rq, u8 stat, u8 error) +{ + if (drive->media != ide_disk) + return; + + switch (rq->pm->pm_step) { + case ide_pm_flush_cache: /* Suspend step 1 (flush cache) complete */ + if (rq->pm->pm_state == 4) + rq->pm->pm_step = ide_pm_state_completed; + else + rq->pm->pm_step = idedisk_pm_standby; + break; + case idedisk_pm_standby: /* Suspend step 2 (standby) complete */ + rq->pm->pm_step = ide_pm_state_completed; + break; + case idedisk_pm_idle: /* Resume step 1 (idle) complete */ + rq->pm->pm_step = ide_pm_restore_dma; + break; + } +} + +static ide_startstop_t ide_start_power_step(ide_drive_t *drive, struct request *rq) +{ + ide_task_t *args = rq->special; + + memset(args, 0, sizeof(*args)); + + if (drive->media != ide_disk) { + /* skip idedisk_pm_idle for ATAPI devices */ + if (rq->pm->pm_step == idedisk_pm_idle) + rq->pm->pm_step = ide_pm_restore_dma; + } + + switch (rq->pm->pm_step) { + case ide_pm_flush_cache: /* Suspend step 1 (flush cache) */ + if (drive->media != ide_disk) + break; + /* Not supported? Switch to next step now. */ + if (!drive->wcache || !ide_id_has_flush_cache(drive->id)) { + ide_complete_power_step(drive, rq, 0, 0); + return ide_stopped; + } + if (ide_id_has_flush_cache_ext(drive->id)) + args->tfRegister[IDE_COMMAND_OFFSET] = WIN_FLUSH_CACHE_EXT; + else + args->tfRegister[IDE_COMMAND_OFFSET] = WIN_FLUSH_CACHE; + args->command_type = IDE_DRIVE_TASK_NO_DATA; + args->handler = &task_no_data_intr; + return do_rw_taskfile(drive, args); + + case idedisk_pm_standby: /* Suspend step 2 (standby) */ + args->tfRegister[IDE_COMMAND_OFFSET] = WIN_STANDBYNOW1; + args->command_type = IDE_DRIVE_TASK_NO_DATA; + args->handler = &task_no_data_intr; + return do_rw_taskfile(drive, args); + + case idedisk_pm_idle: /* Resume step 1 (idle) */ + args->tfRegister[IDE_COMMAND_OFFSET] = WIN_IDLEIMMEDIATE; + args->command_type = IDE_DRIVE_TASK_NO_DATA; + args->handler = task_no_data_intr; + return do_rw_taskfile(drive, args); + + case ide_pm_restore_dma: /* Resume step 2 (restore DMA) */ + /* + * Right now, all we do is call hwif->ide_dma_check(drive), + * we could be smarter and check for current xfer_speed + * in struct drive etc... + */ + if ((drive->id->capability & 1) == 0) + break; + if (drive->hwif->ide_dma_check == NULL) + break; + drive->hwif->ide_dma_check(drive); + break; + } + rq->pm->pm_step = ide_pm_state_completed; + return ide_stopped; +} + +/** + * ide_complete_pm_request - end the current Power Management request + * @drive: target drive + * @rq: request + * + * This function cleans up the current PM request and stops the queue + * if necessary. + */ +static void ide_complete_pm_request (ide_drive_t *drive, struct request *rq) +{ + unsigned long flags; + +#ifdef DEBUG_PM + printk("%s: completing PM request, %s\n", drive->name, + blk_pm_suspend_request(rq) ? "suspend" : "resume"); +#endif + spin_lock_irqsave(&ide_lock, flags); + if (blk_pm_suspend_request(rq)) { + blk_stop_queue(drive->queue); + } else { + drive->blocked = 0; + blk_start_queue(drive->queue); + } + blkdev_dequeue_request(rq); + HWGROUP(drive)->rq = NULL; + end_that_request_last(rq); + spin_unlock_irqrestore(&ide_lock, flags); +} + +/* + * FIXME: probably move this somewhere else, name is bad too :) + */ +u64 ide_get_error_location(ide_drive_t *drive, char *args) +{ + u32 high, low; + u8 hcyl, lcyl, sect; + u64 sector; + + high = 0; + hcyl = args[5]; + lcyl = args[4]; + sect = args[3]; + + if (ide_id_has_flush_cache_ext(drive->id)) { + low = (hcyl << 16) | (lcyl << 8) | sect; + HWIF(drive)->OUTB(drive->ctl|0x80, IDE_CONTROL_REG); + high = ide_read_24(drive); + } else { + u8 cur = HWIF(drive)->INB(IDE_SELECT_REG); + if (cur & 0x40) { + high = cur & 0xf; + low = (hcyl << 16) | (lcyl << 8) | sect; + } else { + low = hcyl * drive->head * drive->sect; + low += lcyl * drive->sect; + low += sect - 1; + } + } + + sector = ((u64) high << 24) | low; + return sector; +} +EXPORT_SYMBOL(ide_get_error_location); + +/** + * ide_end_drive_cmd - end an explicit drive command + * @drive: command + * @stat: status bits + * @err: error bits + * + * Clean up after success/failure of an explicit drive command. + * These get thrown onto the queue so they are synchronized with + * real I/O operations on the drive. + * + * In LBA48 mode we have to read the register set twice to get + * all the extra information out. + */ + +void ide_end_drive_cmd (ide_drive_t *drive, u8 stat, u8 err) +{ + ide_hwif_t *hwif = HWIF(drive); + unsigned long flags; + struct request *rq; + + spin_lock_irqsave(&ide_lock, flags); + rq = HWGROUP(drive)->rq; + spin_unlock_irqrestore(&ide_lock, flags); + + if (rq->flags & REQ_DRIVE_CMD) { + u8 *args = (u8 *) rq->buffer; + if (rq->errors == 0) + rq->errors = !OK_STAT(stat,READY_STAT,BAD_STAT); + + if (args) { + args[0] = stat; + args[1] = err; + args[2] = hwif->INB(IDE_NSECTOR_REG); + } + } else if (rq->flags & REQ_DRIVE_TASK) { + u8 *args = (u8 *) rq->buffer; + if (rq->errors == 0) + rq->errors = !OK_STAT(stat,READY_STAT,BAD_STAT); + + if (args) { + args[0] = stat; + args[1] = err; + args[2] = hwif->INB(IDE_NSECTOR_REG); + args[3] = hwif->INB(IDE_SECTOR_REG); + args[4] = hwif->INB(IDE_LCYL_REG); + args[5] = hwif->INB(IDE_HCYL_REG); + args[6] = hwif->INB(IDE_SELECT_REG); + } + } else if (rq->flags & REQ_DRIVE_TASKFILE) { + ide_task_t *args = (ide_task_t *) rq->special; + if (rq->errors == 0) + rq->errors = !OK_STAT(stat,READY_STAT,BAD_STAT); + + if (args) { + if (args->tf_in_flags.b.data) { + u16 data = hwif->INW(IDE_DATA_REG); + args->tfRegister[IDE_DATA_OFFSET] = (data) & 0xFF; + args->hobRegister[IDE_DATA_OFFSET] = (data >> 8) & 0xFF; + } + args->tfRegister[IDE_ERROR_OFFSET] = err; + /* be sure we're looking at the low order bits */ + hwif->OUTB(drive->ctl & ~0x80, IDE_CONTROL_REG); + args->tfRegister[IDE_NSECTOR_OFFSET] = hwif->INB(IDE_NSECTOR_REG); + args->tfRegister[IDE_SECTOR_OFFSET] = hwif->INB(IDE_SECTOR_REG); + args->tfRegister[IDE_LCYL_OFFSET] = hwif->INB(IDE_LCYL_REG); + args->tfRegister[IDE_HCYL_OFFSET] = hwif->INB(IDE_HCYL_REG); + args->tfRegister[IDE_SELECT_OFFSET] = hwif->INB(IDE_SELECT_REG); + args->tfRegister[IDE_STATUS_OFFSET] = stat; + + if (drive->addressing == 1) { + hwif->OUTB(drive->ctl|0x80, IDE_CONTROL_REG); + args->hobRegister[IDE_FEATURE_OFFSET] = hwif->INB(IDE_FEATURE_REG); + args->hobRegister[IDE_NSECTOR_OFFSET] = hwif->INB(IDE_NSECTOR_REG); + args->hobRegister[IDE_SECTOR_OFFSET] = hwif->INB(IDE_SECTOR_REG); + args->hobRegister[IDE_LCYL_OFFSET] = hwif->INB(IDE_LCYL_REG); + args->hobRegister[IDE_HCYL_OFFSET] = hwif->INB(IDE_HCYL_REG); + } + } + } else if (blk_pm_request(rq)) { +#ifdef DEBUG_PM + printk("%s: complete_power_step(step: %d, stat: %x, err: %x)\n", + drive->name, rq->pm->pm_step, stat, err); +#endif + ide_complete_power_step(drive, rq, stat, err); + if (rq->pm->pm_step == ide_pm_state_completed) + ide_complete_pm_request(drive, rq); + return; + } + + spin_lock_irqsave(&ide_lock, flags); + blkdev_dequeue_request(rq); + HWGROUP(drive)->rq = NULL; + rq->errors = err; + end_that_request_last(rq); + spin_unlock_irqrestore(&ide_lock, flags); +} + +EXPORT_SYMBOL(ide_end_drive_cmd); + +/** + * try_to_flush_leftover_data - flush junk + * @drive: drive to flush + * + * 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]; + u32 wcount = (i > 16) ? 16 : i; + + i -= wcount; + HWIF(drive)->ata_input_data(drive, buffer, wcount); + } +} + +static void ide_kill_rq(ide_drive_t *drive, struct request *rq) +{ + if (rq->rq_disk) { + ide_driver_t *drv; + + drv = *(ide_driver_t **)rq->rq_disk->private_data; + drv->end_request(drive, 0, 0); + } else + ide_end_request(drive, 0, 0); +} + +static ide_startstop_t ide_ata_error(ide_drive_t *drive, struct request *rq, u8 stat, u8 err) +{ + ide_hwif_t *hwif = drive->hwif; + + if (stat & BUSY_STAT || ((stat & WRERR_STAT) && !drive->nowerr)) { + /* other bits are useless when BUSY */ + rq->errors |= ERROR_RESET; + } else if (stat & ERR_STAT) { + /* err has different meaning on cdrom and tape */ + if (err == ABRT_ERR) { + if (drive->select.b.lba && + /* some newer drives don't support WIN_SPECIFY */ + hwif->INB(IDE_COMMAND_REG) == WIN_SPECIFY) + return ide_stopped; + } else if ((err & BAD_CRC) == BAD_CRC) { + /* UDMA crc error, just retry the operation */ + drive->crc_count++; + } 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_data_dir(rq) == READ) + try_to_flush_leftover_data(drive); + + if (hwif->INB(IDE_STATUS_REG) & (BUSY_STAT|DRQ_STAT)) + /* force an abort */ + hwif->OUTB(WIN_IDLEIMMEDIATE, IDE_COMMAND_REG); + + if (rq->errors >= ERROR_MAX || blk_noretry_request(rq)) + ide_kill_rq(drive, rq); + 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; +} + +static ide_startstop_t ide_atapi_error(ide_drive_t *drive, struct request *rq, u8 stat, u8 err) +{ + ide_hwif_t *hwif = drive->hwif; + + if (stat & BUSY_STAT || ((stat & WRERR_STAT) && !drive->nowerr)) { + /* other bits are useless when BUSY */ + rq->errors |= ERROR_RESET; + } else { + /* add decoding error stuff */ + } + + if (hwif->INB(IDE_STATUS_REG) & (BUSY_STAT|DRQ_STAT)) + /* force an abort */ + hwif->OUTB(WIN_IDLEIMMEDIATE, IDE_COMMAND_REG); + + if (rq->errors >= ERROR_MAX) { + ide_kill_rq(drive, rq); + } else { + if ((rq->errors & ERROR_RESET) == ERROR_RESET) { + ++rq->errors; + return ide_do_reset(drive); + } + ++rq->errors; + } + + return ide_stopped; +} + +ide_startstop_t +__ide_error(ide_drive_t *drive, struct request *rq, u8 stat, u8 err) +{ + if (drive->media == ide_disk) + return ide_ata_error(drive, rq, stat, err); + return ide_atapi_error(drive, rq, stat, err); +} + +EXPORT_SYMBOL_GPL(__ide_error); + +/** + * ide_error - handle an error on the IDE + * @drive: drive the error occurred on + * @msg: message to report + * @stat: status bits + * + * ide_error() takes action based on the error returned by the drive. + * For normal I/O that may well include retries. We deal with + * both new-style (taskfile) and old style command handling here. + * In the case of taskfile command handling there is work left to + * do + */ + +ide_startstop_t ide_error (ide_drive_t *drive, const char *msg, u8 stat) +{ + struct request *rq; + u8 err; + + err = ide_dump_status(drive, msg, stat); + + if ((rq = HWGROUP(drive)->rq) == NULL) + return ide_stopped; + + /* retry only "normal" I/O: */ + if (rq->flags & (REQ_DRIVE_CMD | REQ_DRIVE_TASK | REQ_DRIVE_TASKFILE)) { + rq->errors = 1; + ide_end_drive_cmd(drive, stat, err); + return ide_stopped; + } + + if (rq->rq_disk) { + ide_driver_t *drv; + + drv = *(ide_driver_t **)rq->rq_disk->private_data; + return drv->error(drive, rq, stat, err); + } else + return __ide_error(drive, rq, stat, err); +} + +EXPORT_SYMBOL_GPL(ide_error); + +ide_startstop_t __ide_abort(ide_drive_t *drive, struct request *rq) +{ + if (drive->media != ide_disk) + rq->errors |= ERROR_RESET; + + ide_kill_rq(drive, rq); + + return ide_stopped; +} + +EXPORT_SYMBOL_GPL(__ide_abort); + +/** + * ide_abort - abort pending IDE operatins + * @drive: drive the error occurred on + * @msg: message to report + * + * ide_abort kills and cleans up when we are about to do a + * host initiated reset on active commands. Longer term we + * want handlers to have sensible abort handling themselves + * + * This differs fundamentally from ide_error because in + * this case the command is doing just fine when we + * blow it away. + */ + +ide_startstop_t ide_abort(ide_drive_t *drive, const char *msg) +{ + struct request *rq; + + if (drive == NULL || (rq = HWGROUP(drive)->rq) == NULL) + return ide_stopped; + + /* retry only "normal" I/O: */ + if (rq->flags & (REQ_DRIVE_CMD | REQ_DRIVE_TASK | REQ_DRIVE_TASKFILE)) { + rq->errors = 1; + ide_end_drive_cmd(drive, BUSY_STAT, 0); + return ide_stopped; + } + + if (rq->rq_disk) { + ide_driver_t *drv; + + drv = *(ide_driver_t **)rq->rq_disk->private_data; + return drv->abort(drive, rq); + } else + return __ide_abort(drive, rq); +} + +/** + * ide_cmd - issue a simple drive command + * @drive: drive the command is for + * @cmd: command byte + * @nsect: sector byte + * @handler: handler for the command completion + * + * Issue a simple drive command with interrupts. + * The drive must be selected beforehand. + */ + +static void ide_cmd (ide_drive_t *drive, u8 cmd, u8 nsect, + ide_handler_t *handler) +{ + ide_hwif_t *hwif = HWIF(drive); + if (IDE_CONTROL_REG) + hwif->OUTB(drive->ctl,IDE_CONTROL_REG); /* clear nIEN */ + SELECT_MASK(drive,0); + hwif->OUTB(nsect,IDE_NSECTOR_REG); + ide_execute_command(drive, cmd, handler, WAIT_CMD, NULL); +} + +/** + * drive_cmd_intr - drive command completion interrupt + * @drive: drive the completion interrupt occurred on + * + * drive_cmd_intr() is invoked on completion of a special DRIVE_CMD. + * We do any necessary daya reading and then wait for the drive to + * go non busy. At that point we may read the error data and complete + * the request + */ + +static ide_startstop_t drive_cmd_intr (ide_drive_t *drive) +{ + struct request *rq = HWGROUP(drive)->rq; + ide_hwif_t *hwif = HWIF(drive); + u8 *args = (u8 *) rq->buffer; + u8 stat = hwif->INB(IDE_STATUS_REG); + int retries = 10; + + local_irq_enable(); + if ((stat & DRQ_STAT) && args && args[3]) { + u8 io_32bit = drive->io_32bit; + drive->io_32bit = 0; + hwif->ata_input_data(drive, &args[4], args[3] * SECTOR_WORDS); + drive->io_32bit = io_32bit; + while (((stat = hwif->INB(IDE_STATUS_REG)) & 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, hwif->INB(IDE_ERROR_REG)); + return ide_stopped; +} + +static void ide_init_specify_cmd(ide_drive_t *drive, ide_task_t *task) +{ + task->tfRegister[IDE_NSECTOR_OFFSET] = drive->sect; + task->tfRegister[IDE_SECTOR_OFFSET] = drive->sect; + task->tfRegister[IDE_LCYL_OFFSET] = drive->cyl; + task->tfRegister[IDE_HCYL_OFFSET] = drive->cyl>>8; + task->tfRegister[IDE_SELECT_OFFSET] = ((drive->head-1)|drive->select.all)&0xBF; + task->tfRegister[IDE_COMMAND_OFFSET] = WIN_SPECIFY; + + task->handler = &set_geometry_intr; +} + +static void ide_init_restore_cmd(ide_drive_t *drive, ide_task_t *task) +{ + task->tfRegister[IDE_NSECTOR_OFFSET] = drive->sect; + task->tfRegister[IDE_COMMAND_OFFSET] = WIN_RESTORE; + + task->handler = &recal_intr; +} + +static void ide_init_setmult_cmd(ide_drive_t *drive, ide_task_t *task) +{ + task->tfRegister[IDE_NSECTOR_OFFSET] = drive->mult_req; + task->tfRegister[IDE_COMMAND_OFFSET] = WIN_SETMULT; + + task->handler = &set_multmode_intr; +} + +static ide_startstop_t ide_disk_special(ide_drive_t *drive) +{ + special_t *s = &drive->special; + ide_task_t args; + + memset(&args, 0, sizeof(ide_task_t)); + args.command_type = IDE_DRIVE_TASK_NO_DATA; + + if (s->b.set_geometry) { + s->b.set_geometry = 0; + ide_init_specify_cmd(drive, &args); + } else if (s->b.recalibrate) { + s->b.recalibrate = 0; + ide_init_restore_cmd(drive, &args); + } else if (s->b.set_multmode) { + s->b.set_multmode = 0; + if (drive->mult_req > drive->id->max_multsect) + drive->mult_req = drive->id->max_multsect; + ide_init_setmult_cmd(drive, &args); + } 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; + } + + do_rw_taskfile(drive, &args); + + return ide_started; +} + +/** + * do_special - issue some special commands + * @drive: drive the command is for + * + * 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) { + s->b.set_tune = 0; + if (HWIF(drive)->tuneproc != NULL) + HWIF(drive)->tuneproc(drive, drive->tune_req); + return ide_stopped; + } else { + if (drive->media == ide_disk) + return ide_disk_special(drive); + + s->all = 0; + drive->mult_req = 0; + return ide_stopped; + } +} + +void ide_map_sg(ide_drive_t *drive, struct request *rq) +{ + ide_hwif_t *hwif = drive->hwif; + struct scatterlist *sg = hwif->sg_table; + + if (hwif->sg_mapped) /* needed by ide-scsi */ + return; + + if ((rq->flags & REQ_DRIVE_TASKFILE) == 0) { + hwif->sg_nents = blk_rq_map_sg(drive->queue, rq, sg); + } else { + sg_init_one(sg, rq->buffer, rq->nr_sectors * SECTOR_SIZE); + hwif->sg_nents = 1; + } +} + +EXPORT_SYMBOL_GPL(ide_map_sg); + +void ide_init_sg_cmd(ide_drive_t *drive, struct request *rq) +{ + ide_hwif_t *hwif = drive->hwif; + + hwif->nsect = hwif->nleft = rq->nr_sectors; + hwif->cursg = hwif->cursg_ofs = 0; +} + +EXPORT_SYMBOL_GPL(ide_init_sg_cmd); + +/** + * execute_drive_command - issue special drive command + * @drive: the drive to issue th command on + * @rq: the request structure holding the command + * + * execute_drive_cmd() issues a special drive command, usually + * initiated by ioctl() from the external hdparm program. The + * command can be a drive command, drive task or taskfile + * operation. Weirdly you can call it with NULL to wait for + * all commands to finish. Don't do this as that is due to change + */ + +static ide_startstop_t execute_drive_cmd (ide_drive_t *drive, + struct request *rq) +{ + ide_hwif_t *hwif = HWIF(drive); + if (rq->flags & REQ_DRIVE_TASKFILE) { + ide_task_t *args = rq->special; + + if (!args) + goto done; + + hwif->data_phase = args->data_phase; + + switch (hwif->data_phase) { + case TASKFILE_MULTI_OUT: + case TASKFILE_OUT: + case TASKFILE_MULTI_IN: + case TASKFILE_IN: + ide_init_sg_cmd(drive, rq); + ide_map_sg(drive, rq); + default: + break; + } + + if (args->tf_out_flags.all != 0) + return flagged_taskfile(drive, args); + return do_rw_taskfile(drive, args); + } else if (rq->flags & REQ_DRIVE_TASK) { + u8 *args = rq->buffer; + u8 sel; + + if (!args) + goto done; +#ifdef DEBUG + printk("%s: DRIVE_TASK_CMD ", drive->name); + printk("cmd=0x%02x ", args[0]); + printk("fr=0x%02x ", args[1]); + printk("ns=0x%02x ", args[2]); + printk("sc=0x%02x ", args[3]); + printk("lcyl=0x%02x ", args[4]); + printk("hcyl=0x%02x ", args[5]); + printk("sel=0x%02x\n", args[6]); +#endif + hwif->OUTB(args[1], IDE_FEATURE_REG); + hwif->OUTB(args[3], IDE_SECTOR_REG); + hwif->OUTB(args[4], IDE_LCYL_REG); + hwif->OUTB(args[5], IDE_HCYL_REG); + sel = (args[6] & ~0x10); + if (drive->select.b.unit) + sel |= 0x10; + hwif->OUTB(sel, IDE_SELECT_REG); + ide_cmd(drive, args[0], args[2], &drive_cmd_intr); + return ide_started; + } else if (rq->flags & REQ_DRIVE_CMD) { + u8 *args = rq->buffer; + + if (!args) + goto done; +#ifdef DEBUG + printk("%s: DRIVE_CMD ", drive->name); + printk("cmd=0x%02x ", args[0]); + printk("sc=0x%02x ", args[1]); + printk("fr=0x%02x ", args[2]); + printk("xx=0x%02x\n", args[3]); +#endif + if (args[0] == WIN_SMART) { + hwif->OUTB(0x4f, IDE_LCYL_REG); + hwif->OUTB(0xc2, IDE_HCYL_REG); + hwif->OUTB(args[2],IDE_FEATURE_REG); + hwif->OUTB(args[1],IDE_SECTOR_REG); + ide_cmd(drive, args[0], args[3], &drive_cmd_intr); + return ide_started; + } + hwif->OUTB(args[2],IDE_FEATURE_REG); + ide_cmd(drive, args[0], args[1], &drive_cmd_intr); + return ide_started; + } + +done: + /* + * 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, + hwif->INB(IDE_STATUS_REG), + hwif->INB(IDE_ERROR_REG)); + return ide_stopped; +} + +/** + * start_request - start of I/O and command issuing for IDE + * + * start_request() initiates handling of a new I/O request. It + * accepts commands and I/O (read/write) requests. It also does + * the final remapping for weird stuff like EZDrive. Once + * device mapper can work sector level the EZDrive stuff can go away + * + * FIXME: this function needs a rename + */ + +static ide_startstop_t start_request (ide_drive_t *drive, struct request *rq) +{ + ide_startstop_t startstop; + sector_t block; + + BUG_ON(!(rq->flags & REQ_STARTED)); + +#ifdef DEBUG + printk("%s: start_request: current=0x%08lx\n", + HWIF(drive)->name, (unsigned long) rq); +#endif + + /* bail early if we've exceeded max_failures */ + if (drive->max_failures && (drive->failures > drive->max_failures)) { + goto kill_rq; + } + + block = rq->sector; + if (blk_fs_request(rq) && + (drive->media == ide_disk || drive->media == ide_floppy)) { + block += 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 (blk_pm_suspend_request(rq) && + rq->pm->pm_step == ide_pm_state_start_suspend) + /* Mark drive blocked when starting the suspend sequence. */ + drive->blocked = 1; + else if (blk_pm_resume_request(rq) && + rq->pm->pm_step == ide_pm_state_start_resume) { + /* + * The first thing we do on wakeup is to wait for BSY bit to + * go away (with a looong timeout) as a drive on this hwif may + * just be POSTing itself. + * We do that before even selecting as the "other" device on + * the bus may be broken enough to walk on our toes at this + * point. + */ + int rc; +#ifdef DEBUG_PM + printk("%s: Wakeup request inited, waiting for !BSY...\n", drive->name); +#endif + rc = ide_wait_not_busy(HWIF(drive), 35000); + if (rc) + printk(KERN_WARNING "%s: bus not ready on wakeup\n", drive->name); + SELECT_DRIVE(drive); + HWIF(drive)->OUTB(8, HWIF(drive)->io_ports[IDE_CONTROL_OFFSET]); + rc = ide_wait_not_busy(HWIF(drive), 10000); + if (rc) + printk(KERN_WARNING "%s: drive not ready on wakeup\n", drive->name); + } + + SELECT_DRIVE(drive); + if (ide_wait_stat(&startstop, drive, drive->ready_stat, BUSY_STAT|DRQ_STAT, WAIT_READY)) { + printk(KERN_ERR "%s: drive not ready for command\n", drive->name); + return startstop; + } + if (!drive->special.all) { + ide_driver_t *drv; + + if (rq->flags & (REQ_DRIVE_CMD | REQ_DRIVE_TASK)) + return execute_drive_cmd(drive, rq); + else if (rq->flags & REQ_DRIVE_TASKFILE) + return execute_drive_cmd(drive, rq); + else if (blk_pm_request(rq)) { +#ifdef DEBUG_PM + printk("%s: start_power_step(step: %d)\n", + drive->name, rq->pm->pm_step); +#endif + startstop = ide_start_power_step(drive, rq); + if (startstop == ide_stopped && + rq->pm->pm_step == ide_pm_state_completed) + ide_complete_pm_request(drive, rq); + return startstop; + } + + drv = *(ide_driver_t **)rq->rq_disk->private_data; + return drv->do_request(drive, rq, block); + } + return do_special(drive); +kill_rq: + ide_kill_rq(drive, rq); + return ide_stopped; +} + +/** + * ide_stall_queue - pause an IDE device + * @drive: drive to stall + * @timeout: time to stall for (jiffies) + * + * 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; + drive->sleeping = 1; +} + +EXPORT_SYMBOL(ide_stall_queue); + +#define WAKEUP(drive) ((drive)->service_start + 2 * (drive)->service_time) + +/** + * choose_drive - select a drive to service + * @hwgroup: hardware group to select on + * + * choose_drive() selects the next drive which will be serviced. + * This is necessary because the IDE layer can't issue commands + * to both drives on the same cable, unlike SCSI. + */ + +static inline ide_drive_t *choose_drive (ide_hwgroup_t *hwgroup) +{ + ide_drive_t *drive, *best; + +repeat: + best = NULL; + drive = hwgroup->drive; + + /* + * drive is doing pre-flush, ordered write, post-flush sequence. even + * though that is 3 requests, it must be seen as a single transaction. + * we must not preempt this drive until that is complete + */ + if (blk_queue_flushing(drive->queue)) { + /* + * small race where queue could get replugged during + * the 3-request flush cycle, just yank the plug since + * we want it to finish asap + */ + blk_remove_plug(drive->queue); + return drive; + } + + do { + if ((!drive->sleeping || time_after_eq(jiffies, drive->sleep)) + && !elv_queue_empty(drive->queue)) { + if (!best + || (drive->sleeping && (!best->sleeping || time_before(drive->sleep, best->sleep))) + || (!best->sleeping && time_before(WAKEUP(drive), WAKEUP(best)))) + { + if (!blk_queue_plugged(drive->queue)) + best = drive; + } + } + } while ((drive = drive->next) != hwgroup->drive); + if (best && best->nice1 && !best->sleeping && 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->sleeping + && time_before(jiffies - best->service_time, WAKEUP(drive)) + && time_before(WAKEUP(drive), jiffies + t)) + { + ide_stall_queue(best, min_t(long, 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(&ide_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 ide_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 ide_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; + struct request *rq; + ide_startstop_t startstop; + + /* for atari only: POSSIBLY BROKEN HERE(?) */ + ide_get_lock(ide_intr, hwgroup); + + /* caller must own ide_lock */ + BUG_ON(!irqs_disabled()); + + while (!hwgroup->busy) { + hwgroup->busy = 1; + drive = choose_drive(hwgroup); + if (drive == NULL) { + int sleeping = 0; + unsigned long sleep = 0; /* shut up, gcc */ + hwgroup->rq = NULL; + drive = hwgroup->drive; + do { + if (drive->sleeping && (!sleeping || time_before(drive->sleep, sleep))) { + sleeping = 1; + sleep = drive->sleep; + } + } while ((drive = drive->next) != hwgroup->drive); + if (sleeping) { + /* + * 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 (time_before(sleep, jiffies + WAIT_MIN_SLEEP)) + sleep = jiffies + WAIT_MIN_SLEEP; +#if 1 + if (timer_pending(&hwgroup->timer)) + printk(KERN_CRIT "ide_set_handler: timer already active\n"); +#endif + /* so that ide_timer_expiry knows what to do */ + hwgroup->sleeping = 1; + 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_disk? + */ + + /* for atari only */ + ide_release_lock(); + hwgroup->busy = 0; + } + + /* no more work for this hwgroup (for now) */ + return; + } + hwif = HWIF(drive); + if (hwgroup->hwif->sharing_irq && + hwif != hwgroup->hwif && + hwif->io_ports[IDE_CONTROL_OFFSET]) { + /* set nIEN for previous hwif */ + SELECT_INTERRUPT(drive); + } + hwgroup->hwif = hwif; + hwgroup->drive = drive; + drive->sleeping = 0; + drive->service_start = jiffies; + + if (blk_queue_plugged(drive->queue)) { + printk(KERN_ERR "ide: huh? queue was plugged!\n"); + break; + } + + /* + * we know that the queue isn't empty, but this can happen + * if the q->prep_rq_fn() decides to kill a request + */ + rq = elv_next_request(drive->queue); + if (!rq) { + hwgroup->busy = 0; + break; + } + + /* + * Sanity: don't accept a request that isn't a PM request + * if we are currently power managed. This is very important as + * blk_stop_queue() doesn't prevent the elv_next_request() + * above to return us whatever is in the queue. Since we call + * ide_do_request() ourselves, we end up taking requests while + * the queue is blocked... + * + * We let requests forced at head of queue with ide-preempt + * though. I hope that doesn't happen too much, hopefully not + * unless the subdriver triggers such a thing in its own PM + * state machine. + */ + if (drive->blocked && !blk_pm_request(rq) && !(rq->flags & REQ_PREEMPT)) { + /* We clear busy, there should be no pending ATA command at this point. */ + hwgroup->busy = 0; + break; + } + + hwgroup->rq = rq; + + /* + * 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 (masked_irq != IDE_NO_IRQ && hwif->irq != masked_irq) + disable_irq_nosync(hwif->irq); + spin_unlock(&ide_lock); + local_irq_enable(); + /* allow other IRQs while we start this request */ + startstop = start_request(drive, rq); + spin_lock_irq(&ide_lock); + if (masked_irq != IDE_NO_IRQ && hwif->irq != masked_irq) + enable_irq(hwif->irq); + if (startstop == ide_stopped) + hwgroup->busy = 0; + } +} + +/* + * Passes the stuff to ide_do_request + */ +void do_ide_request(request_queue_t *q) +{ + ide_drive_t *drive = q->queuedata; + + ide_do_request(HWGROUP(drive), IDE_NO_IRQ); +} + +/* + * un-busy the hwgroup etc, and clear any pending DMA status. we want to + * retry the current request in pio mode instead of risking tossing it + * all away + */ +static ide_startstop_t ide_dma_timeout_retry(ide_drive_t *drive, int error) +{ + ide_hwif_t *hwif = HWIF(drive); + struct request *rq; + ide_startstop_t ret = ide_stopped; + + /* + * end current dma transaction + */ + + if (error < 0) { + printk(KERN_WARNING "%s: DMA timeout error\n", drive->name); + (void)HWIF(drive)->ide_dma_end(drive); + ret = ide_error(drive, "dma timeout error", + hwif->INB(IDE_STATUS_REG)); + } else { + printk(KERN_WARNING "%s: DMA timeout retry\n", drive->name); + (void) hwif->ide_dma_timeout(drive); + } + + /* + * disable dma for now, but remember that we did so because of + * a timeout -- we'll reenable after we finish this next request + * (or rather the first chunk of it) in pio. + */ + drive->retry_pio++; + drive->state = DMA_PIO_RETRY; + (void) hwif->ide_dma_off_quietly(drive); + + /* + * un-busy drive etc (hwgroup->busy is cleared on return) and + * make sure request is sane + */ + rq = HWGROUP(drive)->rq; + HWGROUP(drive)->rq = NULL; + + rq->errors = 0; + + if (!rq->bio) + goto out; + + rq->sector = rq->bio->bi_sector; + rq->current_nr_sectors = bio_iovec(rq->bio)->bv_len >> 9; + rq->hard_cur_sectors = rq->current_nr_sectors; + rq->buffer = bio_data(rq->bio); +out: + return ret; +} + +/** + * ide_timer_expiry - handle lack of an IDE interrupt + * @data: timer callback magic (hwgroup) + * + * An IDE command has timed out before the expected drive return + * occurred. At this point we attempt to clean up the current + * mess. If the current handler includes an expiry handler then + * we invoke the expiry handler, and providing it is happy the + * work is done. If that fails we apply generic recovery rules + * invoking the handler and checking the drive DMA status. We + * have an excessively incestuous relationship with the DMA + * logic that wants cleaning up. + */ + +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 = -1; + + spin_lock_irqsave(&ide_lock, flags); + + if ((handler = hwgroup->handler) == NULL) { + /* + * Either a marginal timeout occurred + * (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(KERN_ERR "ide_timer_expiry: hwgroup->drive was NULL\n"); + hwgroup->handler = NULL; + } else { + ide_hwif_t *hwif; + ide_startstop_t startstop = ide_stopped; + if (!hwgroup->busy) { + hwgroup->busy = 1; /* paranoia */ + printk(KERN_ERR "%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(&ide_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(&ide_lock); + hwif = HWIF(drive); +#if DISABLE_IRQ_NOSYNC + disable_irq_nosync(hwif->irq); +#else + /* disable_irq_nosync ?? */ + disable_irq(hwif->irq); +#endif /* DISABLE_IRQ_NOSYNC */ + /* local CPU only, + * as if we were handling an interrupt */ + local_irq_disable(); + if (hwgroup->polling) { + startstop = handler(drive); + } else if (drive_is_ready(drive)) { + if (drive->waiting_for_dma) + (void) hwgroup->hwif->ide_dma_lostirq(drive); + (void)ide_ack_intr(hwif); + printk(KERN_WARNING "%s: lost interrupt\n", drive->name); + startstop = handler(drive); + } else { + if (drive->waiting_for_dma) { + startstop = ide_dma_timeout_retry(drive, wait); + } else + startstop = + ide_error(drive, "irq timeout", hwif->INB(IDE_STATUS_REG)); + } + drive->service_time = jiffies - drive->service_start; + spin_lock_irq(&ide_lock); + enable_irq(hwif->irq); + if (startstop == ide_stopped) + hwgroup->busy = 0; + } + } + ide_do_request(hwgroup, IDE_NO_IRQ); + spin_unlock_irqrestore(&ide_lock, flags); +} + +/** + * unexpected_intr - handle an unexpected IDE interrupt + * @irq: interrupt line + * @hwgroup: hwgroup being processed + * + * 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 accidentally invoked as a result of any valid command completion + * interrupt. + * + * Note that we must walk the entire hwgroup here. We know which hwif + * is doing the current command, but we don't know which hwif burped + * mysteriously. + */ + +static void unexpected_intr (int irq, ide_hwgroup_t *hwgroup) +{ + u8 stat; + ide_hwif_t *hwif = hwgroup->hwif; + + /* + * handle the unexpected interrupt + */ + do { + if (hwif->irq == irq) { + stat = hwif->INB(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, count; + ++count; + if (time_after(jiffies, last_msgtime + HZ)) { + last_msgtime = jiffies; + printk(KERN_ERR "%s%s: unexpected interrupt, " + "status=0x%02x, count=%ld\n", + hwif->name, + (hwif->next==hwgroup->hwif) ? "" : "(?)", stat, count); + } + } + } + } while ((hwif = hwif->next) != hwgroup->hwif); +} + +/** + * ide_intr - default IDE interrupt handler + * @irq: interrupt number + * @dev_id: hwif group + * @regs: unused weirdness from the kernel irq layer + * + * This is the default IRQ handler for the IDE layer. You should + * not need to override it. If you do be aware it is subtle in + * places + * + * hwgroup->hwif is the interface in the group currently performing + * a command. hwgroup->drive is the drive and hwgroup->handler is + * the IRQ handler to call. As we issue a command the handlers + * step through multiple states, reassigning the handler to the + * next step in the process. Unlike a smart SCSI controller IDE + * expects the main processor to sequence the various transfer + * stages. We also manage a poll timer to catch up with most + * timeout situations. There are still a few where the handlers + * don't ever decide to give up. + * + * The handler eventually returns ide_stopped to indicate the + * request completed. At this point we issue the next request + * on the hwgroup and the process begins again. + */ + +irqreturn_t 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(&ide_lock, flags); + hwif = hwgroup->hwif; + + if (!ide_ack_intr(hwif)) { + spin_unlock_irqrestore(&ide_lock, flags); + return IRQ_NONE; + } + + if ((handler = hwgroup->handler) == NULL || hwgroup->polling) { + /* + * 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. + * + * FIXME: unexpected_intr should be hwif-> then we can + * remove all the ifdef PCI crap + */ +#ifdef CONFIG_BLK_DEV_IDEPCI + if (hwif->pci_dev && !hwif->pci_dev->vendor) +#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) hwif->INB(hwif->io_ports[IDE_STATUS_OFFSET]); +#endif /* CONFIG_BLK_DEV_IDEPCI */ + } + spin_unlock_irqrestore(&ide_lock, flags); + return IRQ_NONE; + } + drive = hwgroup->drive; + if (!drive) { + /* + * This should NEVER happen, and there isn't much + * we could do about it here. + * + * [Note - this can occur if the drive is hot unplugged] + */ + spin_unlock_irqrestore(&ide_lock, flags); + return IRQ_HANDLED; + } + 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(&ide_lock, flags); + return IRQ_NONE; + } + if (!hwgroup->busy) { + hwgroup->busy = 1; /* paranoia */ + printk(KERN_ERR "%s: ide_intr: hwgroup->busy was 0 ??\n", drive->name); + } + hwgroup->handler = NULL; + del_timer(&hwgroup->timer); + spin_unlock(&ide_lock); + + if (drive->unmask) + local_irq_enable(); + /* service this interrupt, may set handler for next interrupt */ + startstop = handler(drive); + spin_lock_irq(&ide_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. + */ + 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(KERN_ERR "%s: ide_intr: huh? expected NULL handler " + "on exit\n", drive->name); + } + } + spin_unlock_irqrestore(&ide_lock, flags); + return IRQ_HANDLED; +} + +/** + * ide_init_drive_cmd - initialize a drive command request + * @rq: request object + * + * Initialize a request before we fill it in and send it down to + * ide_do_drive_cmd. Commands must be set up by this function. Right + * now it doesn't do a lot, but if that changes abusers will have a + * nasty suprise. + */ + +void ide_init_drive_cmd (struct request *rq) +{ + memset(rq, 0, sizeof(*rq)); + rq->flags = REQ_DRIVE_CMD; + rq->ref_count = 1; +} + +EXPORT_SYMBOL(ide_init_drive_cmd); + +/** + * ide_do_drive_cmd - issue IDE special command + * @drive: device to issue command + * @rq: request to issue + * @action: action for processing + * + * 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); + DECLARE_COMPLETION(wait); + int where = ELEVATOR_INSERT_BACK, err; + int must_wait = (action == ide_wait || action == ide_head_wait); + + rq->errors = 0; + rq->rq_status = RQ_ACTIVE; + + /* + * we need to hold an extra reference to request for safe inspection + * after completion + */ + if (must_wait) { + rq->ref_count++; + rq->waiting = &wait; + rq->end_io = blk_end_sync_rq; + } + + spin_lock_irqsave(&ide_lock, flags); + if (action == ide_preempt) + hwgroup->rq = NULL; + if (action == ide_preempt || action == ide_head_wait) { + where = ELEVATOR_INSERT_FRONT; + rq->flags |= REQ_PREEMPT; + } + __elv_add_request(drive->queue, rq, where, 0); + ide_do_request(hwgroup, IDE_NO_IRQ); + spin_unlock_irqrestore(&ide_lock, flags); + + err = 0; + if (must_wait) { + wait_for_completion(&wait); + rq->waiting = NULL; + if (rq->errors) + err = -EIO; + + blk_put_request(rq); + } + + return err; +} + +EXPORT_SYMBOL(ide_do_drive_cmd); diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c new file mode 100644 index 0000000..5302494 --- /dev/null +++ b/drivers/ide/ide-iops.c @@ -0,0 +1,1285 @@ +/* + * linux/drivers/ide/ide-iops.c Version 0.37 Mar 05, 2003 + * + * Copyright (C) 2000-2002 Andre Hedrick <andre@linux-ide.org> + * Copyright (C) 2003 Red Hat <alan@redhat.com> + * + */ + +#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/slab.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/hdreg.h> +#include <linux/ide.h> +#include <linux/bitops.h> + +#include <asm/byteorder.h> +#include <asm/irq.h> +#include <asm/uaccess.h> +#include <asm/io.h> + +/* + * Conventional PIO operations for ATA devices + */ + +static u8 ide_inb (unsigned long port) +{ + return (u8) inb(port); +} + +static u16 ide_inw (unsigned long port) +{ + return (u16) inw(port); +} + +static void ide_insw (unsigned long port, void *addr, u32 count) +{ + insw(port, addr, count); +} + +static u32 ide_inl (unsigned long port) +{ + return (u32) inl(port); +} + +static void ide_insl (unsigned long port, void *addr, u32 count) +{ + insl(port, addr, count); +} + +static void ide_outb (u8 val, unsigned long port) +{ + outb(val, port); +} + +static void ide_outbsync (ide_drive_t *drive, u8 addr, unsigned long port) +{ + outb(addr, port); +} + +static void ide_outw (u16 val, unsigned long port) +{ + outw(val, port); +} + +static void ide_outsw (unsigned long port, void *addr, u32 count) +{ + outsw(port, addr, count); +} + +static void ide_outl (u32 val, unsigned long port) +{ + outl(val, port); +} + +static void ide_outsl (unsigned long port, void *addr, u32 count) +{ + outsl(port, addr, count); +} + +void default_hwif_iops (ide_hwif_t *hwif) +{ + hwif->OUTB = ide_outb; + hwif->OUTBSYNC = ide_outbsync; + hwif->OUTW = ide_outw; + hwif->OUTL = ide_outl; + hwif->OUTSW = ide_outsw; + hwif->OUTSL = ide_outsl; + hwif->INB = ide_inb; + hwif->INW = ide_inw; + hwif->INL = ide_inl; + hwif->INSW = ide_insw; + hwif->INSL = ide_insl; +} + +EXPORT_SYMBOL(default_hwif_iops); + +/* + * MMIO operations, typically used for SATA controllers + */ + +static u8 ide_mm_inb (unsigned long port) +{ + return (u8) readb((void __iomem *) port); +} + +static u16 ide_mm_inw (unsigned long port) +{ + return (u16) readw((void __iomem *) port); +} + +static void ide_mm_insw (unsigned long port, void *addr, u32 count) +{ + __ide_mm_insw((void __iomem *) port, addr, count); +} + +static u32 ide_mm_inl (unsigned long port) +{ + return (u32) readl((void __iomem *) port); +} + +static void ide_mm_insl (unsigned long port, void *addr, u32 count) +{ + __ide_mm_insl((void __iomem *) port, addr, count); +} + +static void ide_mm_outb (u8 value, unsigned long port) +{ + writeb(value, (void __iomem *) port); +} + +static void ide_mm_outbsync (ide_drive_t *drive, u8 value, unsigned long port) +{ + writeb(value, (void __iomem *) port); +} + +static void ide_mm_outw (u16 value, unsigned long port) +{ + writew(value, (void __iomem *) port); +} + +static void ide_mm_outsw (unsigned long port, void *addr, u32 count) +{ + __ide_mm_outsw((void __iomem *) port, addr, count); +} + +static void ide_mm_outl (u32 value, unsigned long port) +{ + writel(value, (void __iomem *) port); +} + +static void ide_mm_outsl (unsigned long port, void *addr, u32 count) +{ + __ide_mm_outsl((void __iomem *) port, addr, count); +} + +void default_hwif_mmiops (ide_hwif_t *hwif) +{ + hwif->OUTB = ide_mm_outb; + /* Most systems will need to override OUTBSYNC, alas however + this one is controller specific! */ + hwif->OUTBSYNC = ide_mm_outbsync; + hwif->OUTW = ide_mm_outw; + hwif->OUTL = ide_mm_outl; + hwif->OUTSW = ide_mm_outsw; + hwif->OUTSL = ide_mm_outsl; + hwif->INB = ide_mm_inb; + hwif->INW = ide_mm_inw; + hwif->INL = ide_mm_inl; + hwif->INSW = ide_mm_insw; + hwif->INSL = ide_mm_insl; +} + +EXPORT_SYMBOL(default_hwif_mmiops); + +u32 ide_read_24 (ide_drive_t *drive) +{ + u8 hcyl = HWIF(drive)->INB(IDE_HCYL_REG); + u8 lcyl = HWIF(drive)->INB(IDE_LCYL_REG); + u8 sect = HWIF(drive)->INB(IDE_SECTOR_REG); + return (hcyl<<16)|(lcyl<<8)|sect; +} + +void SELECT_DRIVE (ide_drive_t *drive) +{ + if (HWIF(drive)->selectproc) + HWIF(drive)->selectproc(drive); + HWIF(drive)->OUTB(drive->select.all, IDE_SELECT_REG); +} + +EXPORT_SYMBOL(SELECT_DRIVE); + +void SELECT_INTERRUPT (ide_drive_t *drive) +{ + if (HWIF(drive)->intrproc) + HWIF(drive)->intrproc(drive); + else + HWIF(drive)->OUTB(drive->ctl|2, IDE_CONTROL_REG); +} + +void SELECT_MASK (ide_drive_t *drive, int mask) +{ + if (HWIF(drive)->maskproc) + HWIF(drive)->maskproc(drive, mask); +} + +void QUIRK_LIST (ide_drive_t *drive) +{ + if (HWIF(drive)->quirkproc) + drive->quirk_list = HWIF(drive)->quirkproc(drive); +} + +/* + * 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 void ata_vlb_sync(ide_drive_t *drive, unsigned long port) +{ + (void) HWIF(drive)->INB(port); + (void) HWIF(drive)->INB(port); + (void) HWIF(drive)->INB(port); +} + +/* + * This is used for most PIO data transfers *from* the IDE interface + */ +static void ata_input_data(ide_drive_t *drive, void *buffer, u32 wcount) +{ + ide_hwif_t *hwif = HWIF(drive); + u8 io_32bit = drive->io_32bit; + + if (io_32bit) { + if (io_32bit & 2) { + unsigned long flags; + local_irq_save(flags); + ata_vlb_sync(drive, IDE_NSECTOR_REG); + hwif->INSL(IDE_DATA_REG, buffer, wcount); + local_irq_restore(flags); + } else + hwif->INSL(IDE_DATA_REG, buffer, wcount); + } else { + hwif->INSW(IDE_DATA_REG, buffer, wcount<<1); + } +} + +/* + * This is used for most PIO data transfers *to* the IDE interface + */ +static void ata_output_data(ide_drive_t *drive, void *buffer, u32 wcount) +{ + ide_hwif_t *hwif = HWIF(drive); + u8 io_32bit = drive->io_32bit; + + if (io_32bit) { + if (io_32bit & 2) { + unsigned long flags; + local_irq_save(flags); + ata_vlb_sync(drive, IDE_NSECTOR_REG); + hwif->OUTSL(IDE_DATA_REG, buffer, wcount); + local_irq_restore(flags); + } else + hwif->OUTSL(IDE_DATA_REG, buffer, wcount); + } else { + hwif->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. + */ + +static void atapi_input_bytes(ide_drive_t *drive, void *buffer, u32 bytecount) +{ + ide_hwif_t *hwif = HWIF(drive); + + ++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 || CONFIG_Q40 */ + hwif->ata_input_data(drive, buffer, bytecount / 4); + if ((bytecount & 0x03) >= 2) + hwif->INSW(IDE_DATA_REG, ((u8 *)buffer)+(bytecount & ~0x03), 1); +} + +static void atapi_output_bytes(ide_drive_t *drive, void *buffer, u32 bytecount) +{ + ide_hwif_t *hwif = HWIF(drive); + + ++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 || CONFIG_Q40 */ + hwif->ata_output_data(drive, buffer, bytecount / 4); + if ((bytecount & 0x03) >= 2) + hwif->OUTSW(IDE_DATA_REG, ((u8*)buffer)+(bytecount & ~0x03), 1); +} + +void default_hwif_transport(ide_hwif_t *hwif) +{ + hwif->ata_input_data = ata_input_data; + hwif->ata_output_data = ata_output_data; + hwif->atapi_input_bytes = atapi_input_bytes; + hwif->atapi_output_bytes = atapi_output_bytes; +} + +EXPORT_SYMBOL(default_hwif_transport); + +/* + * Beginning of Taskfile OPCODE Library and feature sets. + */ +void ide_fix_driveid (struct hd_driveid *id) +{ +#ifndef __LITTLE_ENDIAN +# ifdef __BIG_ENDIAN + int i; + u16 *stringcast; + + id->config = __le16_to_cpu(id->config); + id->cyls = __le16_to_cpu(id->cyls); + id->reserved2 = __le16_to_cpu(id->reserved2); + id->heads = __le16_to_cpu(id->heads); + id->track_bytes = __le16_to_cpu(id->track_bytes); + id->sector_bytes = __le16_to_cpu(id->sector_bytes); + id->sectors = __le16_to_cpu(id->sectors); + id->vendor0 = __le16_to_cpu(id->vendor0); + id->vendor1 = __le16_to_cpu(id->vendor1); + id->vendor2 = __le16_to_cpu(id->vendor2); + stringcast = (u16 *)&id->serial_no[0]; + for (i = 0; i < (20/2); i++) + stringcast[i] = __le16_to_cpu(stringcast[i]); + id->buf_type = __le16_to_cpu(id->buf_type); + id->buf_size = __le16_to_cpu(id->buf_size); + id->ecc_bytes = __le16_to_cpu(id->ecc_bytes); + stringcast = (u16 *)&id->fw_rev[0]; + for (i = 0; i < (8/2); i++) + stringcast[i] = __le16_to_cpu(stringcast[i]); + stringcast = (u16 *)&id->model[0]; + for (i = 0; i < (40/2); i++) + stringcast[i] = __le16_to_cpu(stringcast[i]); + id->dword_io = __le16_to_cpu(id->dword_io); + id->reserved50 = __le16_to_cpu(id->reserved50); + id->field_valid = __le16_to_cpu(id->field_valid); + id->cur_cyls = __le16_to_cpu(id->cur_cyls); + id->cur_heads = __le16_to_cpu(id->cur_heads); + id->cur_sectors = __le16_to_cpu(id->cur_sectors); + id->cur_capacity0 = __le16_to_cpu(id->cur_capacity0); + id->cur_capacity1 = __le16_to_cpu(id->cur_capacity1); + id->lba_capacity = __le32_to_cpu(id->lba_capacity); + id->dma_1word = __le16_to_cpu(id->dma_1word); + id->dma_mword = __le16_to_cpu(id->dma_mword); + id->eide_pio_modes = __le16_to_cpu(id->eide_pio_modes); + id->eide_dma_min = __le16_to_cpu(id->eide_dma_min); + id->eide_dma_time = __le16_to_cpu(id->eide_dma_time); + id->eide_pio = __le16_to_cpu(id->eide_pio); + id->eide_pio_iordy = __le16_to_cpu(id->eide_pio_iordy); + for (i = 0; i < 2; ++i) + id->words69_70[i] = __le16_to_cpu(id->words69_70[i]); + for (i = 0; i < 4; ++i) + id->words71_74[i] = __le16_to_cpu(id->words71_74[i]); + id->queue_depth = __le16_to_cpu(id->queue_depth); + for (i = 0; i < 4; ++i) + id->words76_79[i] = __le16_to_cpu(id->words76_79[i]); + id->major_rev_num = __le16_to_cpu(id->major_rev_num); + id->minor_rev_num = __le16_to_cpu(id->minor_rev_num); + id->command_set_1 = __le16_to_cpu(id->command_set_1); + id->command_set_2 = __le16_to_cpu(id->command_set_2); + id->cfsse = __le16_to_cpu(id->cfsse); + id->cfs_enable_1 = __le16_to_cpu(id->cfs_enable_1); + id->cfs_enable_2 = __le16_to_cpu(id->cfs_enable_2); + id->csf_default = __le16_to_cpu(id->csf_default); + id->dma_ultra = __le16_to_cpu(id->dma_ultra); + id->trseuc = __le16_to_cpu(id->trseuc); + id->trsEuc = __le16_to_cpu(id->trsEuc); + id->CurAPMvalues = __le16_to_cpu(id->CurAPMvalues); + id->mprc = __le16_to_cpu(id->mprc); + id->hw_config = __le16_to_cpu(id->hw_config); + id->acoustic = __le16_to_cpu(id->acoustic); + id->msrqs = __le16_to_cpu(id->msrqs); + id->sxfert = __le16_to_cpu(id->sxfert); + id->sal = __le16_to_cpu(id->sal); + id->spg = __le32_to_cpu(id->spg); + id->lba_capacity_2 = __le64_to_cpu(id->lba_capacity_2); + for (i = 0; i < 22; i++) + id->words104_125[i] = __le16_to_cpu(id->words104_125[i]); + id->last_lun = __le16_to_cpu(id->last_lun); + id->word127 = __le16_to_cpu(id->word127); + id->dlf = __le16_to_cpu(id->dlf); + id->csfo = __le16_to_cpu(id->csfo); + for (i = 0; i < 26; i++) + id->words130_155[i] = __le16_to_cpu(id->words130_155[i]); + id->word156 = __le16_to_cpu(id->word156); + for (i = 0; i < 3; i++) + id->words157_159[i] = __le16_to_cpu(id->words157_159[i]); + id->cfa_power = __le16_to_cpu(id->cfa_power); + for (i = 0; i < 14; i++) + id->words161_175[i] = __le16_to_cpu(id->words161_175[i]); + for (i = 0; i < 31; i++) + id->words176_205[i] = __le16_to_cpu(id->words176_205[i]); + for (i = 0; i < 48; i++) + id->words206_254[i] = __le16_to_cpu(id->words206_254[i]); + id->integrity_word = __le16_to_cpu(id->integrity_word); +# else +# error "Please fix <asm/byteorder.h>" +# endif +#endif +} + +/* FIXME: exported for use by the USB storage (isd200.c) code only */ +EXPORT_SYMBOL(ide_fix_driveid); + +void ide_fixstring (u8 *s, const int bytecount, const int byteswap) +{ + u8 *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'; +} + +EXPORT_SYMBOL(ide_fixstring); + +/* + * Needed for PCI irq sharing + */ +int drive_is_ready (ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + u8 stat = 0; + + if (drive->waiting_for_dma) + return hwif->ide_dma_test_irq(drive); + +#if 0 + /* need to guarantee 400ns since last command was issued */ + udelay(1); +#endif + +#ifdef CONFIG_IDEPCI_SHARE_IRQ + /* + * We do a passive status test under shared PCI interrupts on + * cards that truly share the ATA side interrupt, but may also share + * an interrupt with another pci card/device. We make no assumptions + * about possible isa-pnp and pci-pnp issues yet. + */ + if (IDE_CONTROL_REG) + stat = hwif->INB(IDE_ALTSTATUS_REG); + else +#endif /* CONFIG_IDEPCI_SHARE_IRQ */ + /* Note: this may clear a pending IRQ!! */ + stat = hwif->INB(IDE_STATUS_REG); + + if (stat & BUSY_STAT) + /* drive busy: definitely not interrupting */ + return 0; + + /* drive ready: *might* be interrupting */ + return 1; +} + +EXPORT_SYMBOL(drive_is_ready); + +/* + * Global for All, and taken from ide-pmac.c. Can be called + * with spinlock held & IRQs disabled, so don't schedule ! + */ +int wait_for_ready (ide_drive_t *drive, int timeout) +{ + ide_hwif_t *hwif = HWIF(drive); + u8 stat = 0; + + while(--timeout) { + stat = hwif->INB(IDE_STATUS_REG); + 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(KERN_ERR "%s: wait_for_ready, " + "error status: %x\n", drive->name, stat); + } + return 1; + } + return 0; +} + +EXPORT_SYMBOL(wait_for_ready); + +/* + * 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, u8 good, u8 bad, unsigned long timeout) +{ + ide_hwif_t *hwif = HWIF(drive); + u8 stat; + int i; + unsigned long flags; + + /* bail early if we've exceeded max_failures */ + if (drive->max_failures && (drive->failures > drive->max_failures)) { + *startstop = ide_stopped; + return 1; + } + + udelay(1); /* spec allows drive 400ns to assert "BUSY" */ + if ((stat = hwif->INB(IDE_STATUS_REG)) & BUSY_STAT) { + local_irq_set(flags); + timeout += jiffies; + while ((stat = hwif->INB(IDE_STATUS_REG)) & BUSY_STAT) { + if (time_after(jiffies, timeout)) { + /* + * One last read after the timeout in case + * heavy interrupt load made us not make any + * progress during the timeout.. + */ + stat = hwif->INB(IDE_STATUS_REG); + if (!(stat & BUSY_STAT)) + break; + + local_irq_restore(flags); + *startstop = ide_error(drive, "status timeout", stat); + return 1; + } + } + local_irq_restore(flags); + } + /* + * 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 = hwif->INB(IDE_STATUS_REG)), good, bad)) + return 0; + } + *startstop = ide_error(drive, "status error", stat); + return 1; +} + +EXPORT_SYMBOL(ide_wait_stat); + +/* + * All hosts that use the 80c ribbon must use! + * The name is derived from upper byte of word 93 and the 80c ribbon. + */ +u8 eighty_ninty_three (ide_drive_t *drive) +{ +#if 0 + if (!HWIF(drive)->udma_four) + return 0; + + if (drive->id->major_rev_num) { + int hssbd = 0; + int i; + /* + * Determine highest Supported SPEC + */ + for (i=1; i<=15; i++) + if (drive->id->major_rev_num & (1<<i)) + hssbd++; + + switch (hssbd) { + case 7: + case 6: + case 5: + /* ATA-4 and older do not support above Ultra 33 */ + default: + return 0; + } + } + + return ((u8) ( +#ifndef CONFIG_IDEDMA_IVB + (drive->id->hw_config & 0x4000) && +#endif /* CONFIG_IDEDMA_IVB */ + (drive->id->hw_config & 0x6000)) ? 1 : 0); + +#else + + return ((u8) ((HWIF(drive)->udma_four) && +#ifndef CONFIG_IDEDMA_IVB + (drive->id->hw_config & 0x4000) && +#endif /* CONFIG_IDEDMA_IVB */ + (drive->id->hw_config & 0x6000)) ? 1 : 0); +#endif +} + +EXPORT_SYMBOL(eighty_ninty_three); + +int ide_ata66_check (ide_drive_t *drive, ide_task_t *args) +{ + if ((args->tfRegister[IDE_COMMAND_OFFSET] == WIN_SETFEATURES) && + (args->tfRegister[IDE_SECTOR_OFFSET] > XFER_UDMA_2) && + (args->tfRegister[IDE_FEATURE_OFFSET] == SETFEATURES_XFER)) { +#ifndef CONFIG_IDEDMA_IVB + if ((drive->id->hw_config & 0x6000) == 0) { +#else /* !CONFIG_IDEDMA_IVB */ + if (((drive->id->hw_config & 0x2000) == 0) || + ((drive->id->hw_config & 0x4000) == 0)) { +#endif /* CONFIG_IDEDMA_IVB */ + printk("%s: Speed warnings UDMA 3/4/5 is not " + "functional.\n", drive->name); + return 1; + } + if (!HWIF(drive)->udma_four) { + printk("%s: Speed warnings UDMA 3/4/5 is not " + "functional.\n", + HWIF(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, ide_task_t *args) +{ + if ((args->tfRegister[IDE_COMMAND_OFFSET] == WIN_SETFEATURES) && + (args->tfRegister[IDE_SECTOR_OFFSET] >= XFER_SW_DMA_0) && + (args->tfRegister[IDE_FEATURE_OFFSET] == SETFEATURES_XFER) && + (drive->id->dma_ultra || + drive->id->dma_mword || + drive->id->dma_1word)) + return 1; + + return 0; +} + +#ifdef CONFIG_BLK_DEV_IDEDMA +static u8 ide_auto_reduce_xfer (ide_drive_t *drive) +{ + if (!drive->crc_count) + return drive->current_speed; + drive->crc_count = 0; + + switch(drive->current_speed) { + case XFER_UDMA_7: return XFER_UDMA_6; + case XFER_UDMA_6: return XFER_UDMA_5; + case XFER_UDMA_5: return XFER_UDMA_4; + case XFER_UDMA_4: return XFER_UDMA_3; + case XFER_UDMA_3: return XFER_UDMA_2; + case XFER_UDMA_2: return XFER_UDMA_1; + case XFER_UDMA_1: return XFER_UDMA_0; + /* + * OOPS we do not goto non Ultra DMA modes + * without iCRC's available we force + * the system to PIO and make the user + * invoke the ATA-1 ATA-2 DMA modes. + */ + case XFER_UDMA_0: + default: return XFER_PIO_4; + } +} +#endif /* CONFIG_BLK_DEV_IDEDMA */ + +/* + * Update the + */ +int ide_driveid_update (ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + struct hd_driveid *id; +#if 0 + id = kmalloc(SECTOR_WORDS*4, GFP_ATOMIC); + if (!id) + return 0; + + taskfile_lib_get_identify(drive, (char *)&id); + + ide_fix_driveid(id); + if (id) { + drive->id->dma_ultra = id->dma_ultra; + drive->id->dma_mword = id->dma_mword; + drive->id->dma_1word = id->dma_1word; + /* anything more ? */ + kfree(id); + } + return 1; +#else + /* + * Re-read drive->id for possible DMA mode + * change (copied from ide-probe.c) + */ + unsigned long timeout, flags; + + SELECT_MASK(drive, 1); + if (IDE_CONTROL_REG) + hwif->OUTB(drive->ctl,IDE_CONTROL_REG); + msleep(50); + hwif->OUTB(WIN_IDENTIFY, IDE_COMMAND_REG); + timeout = jiffies + WAIT_WORSTCASE; + do { + if (time_after(jiffies, timeout)) { + SELECT_MASK(drive, 0); + return 0; /* drive timed-out */ + } + msleep(50); /* give drive a breather */ + } while (hwif->INB(IDE_ALTSTATUS_REG) & BUSY_STAT); + msleep(50); /* wait for IRQ and DRQ_STAT */ + if (!OK_STAT(hwif->INB(IDE_STATUS_REG),DRQ_STAT,BAD_R_STAT)) { + SELECT_MASK(drive, 0); + printk("%s: CHECK for good STATUS\n", drive->name); + return 0; + } + local_irq_save(flags); + SELECT_MASK(drive, 0); + id = kmalloc(SECTOR_WORDS*4, GFP_ATOMIC); + if (!id) { + local_irq_restore(flags); + return 0; + } + ata_input_data(drive, id, SECTOR_WORDS); + (void) hwif->INB(IDE_STATUS_REG); /* clear drive IRQ */ + local_irq_enable(); + local_irq_restore(flags); + ide_fix_driveid(id); + if (id) { + drive->id->dma_ultra = id->dma_ultra; + drive->id->dma_mword = id->dma_mword; + drive->id->dma_1word = id->dma_1word; + /* anything more ? */ + kfree(id); + } + + return 1; +#endif +} + +/* + * 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, u8 speed) +{ + ide_hwif_t *hwif = HWIF(drive); + int i, error = 1; + u8 stat; + +// while (HWGROUP(drive)->busy) +// msleep(50); + +#ifdef CONFIG_BLK_DEV_IDEDMA + if (hwif->ide_dma_check) /* check if host supports DMA */ + hwif->ide_dma_host_off(drive); +#endif + + /* + * 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_nosync(hwif->irq); + + /* + * FIXME: we race against the running IRQ here if + * this is called from non IRQ context. If we use + * disable_irq() we hang on the error path. Work + * is needed. + */ + + udelay(1); + SELECT_DRIVE(drive); + SELECT_MASK(drive, 0); + udelay(1); + if (IDE_CONTROL_REG) + hwif->OUTB(drive->ctl | 2, IDE_CONTROL_REG); + hwif->OUTB(speed, IDE_NSECTOR_REG); + hwif->OUTB(SETFEATURES_XFER, IDE_FEATURE_REG); + hwif->OUTB(WIN_SETFEATURES, IDE_COMMAND_REG); + if ((IDE_CONTROL_REG) && (drive->quirk_list == 2)) + hwif->OUTB(drive->ctl, IDE_CONTROL_REG); + udelay(1); + /* + * Wait for drive to become non-BUSY + */ + if ((stat = hwif->INB(IDE_STATUS_REG)) & BUSY_STAT) { + unsigned long flags, timeout; + local_irq_set(flags); + timeout = jiffies + WAIT_CMD; + while ((stat = hwif->INB(IDE_STATUS_REG)) & BUSY_STAT) { + if (time_after(jiffies, timeout)) + break; + } + local_irq_restore(flags); + } + + /* + * 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 = hwif->INB(IDE_STATUS_REG)), DRIVE_READY, BUSY_STAT|DRQ_STAT|ERR_STAT)) { + error = 0; + break; + } + } + + SELECT_MASK(drive, 0); + + 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; + +#ifdef CONFIG_BLK_DEV_IDEDMA + if (speed >= XFER_SW_DMA_0) + hwif->ide_dma_host_on(drive); + else if (hwif->ide_dma_check) /* check if host supports DMA */ + hwif->ide_dma_off_quietly(drive); +#endif + + 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; + } + if (!drive->init_speed) + drive->init_speed = speed; + drive->current_speed = speed; + return error; +} + +EXPORT_SYMBOL(ide_config_drive_speed); + + +/* + * 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). + * + * See also ide_execute_command + */ +static void __ide_set_handler (ide_drive_t *drive, ide_handler_t *handler, + unsigned int timeout, ide_expiry_t *expiry) +{ + ide_hwgroup_t *hwgroup = HWGROUP(drive); + + if (hwgroup->handler != NULL) { + printk(KERN_CRIT "%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); +} + +void ide_set_handler (ide_drive_t *drive, ide_handler_t *handler, + unsigned int timeout, ide_expiry_t *expiry) +{ + unsigned long flags; + spin_lock_irqsave(&ide_lock, flags); + __ide_set_handler(drive, handler, timeout, expiry); + spin_unlock_irqrestore(&ide_lock, flags); +} + +EXPORT_SYMBOL(ide_set_handler); + +/** + * ide_execute_command - execute an IDE command + * @drive: IDE drive to issue the command against + * @command: command byte to write + * @handler: handler for next phase + * @timeout: timeout for command + * @expiry: handler to run on timeout + * + * Helper function to issue an IDE command. This handles the + * atomicity requirements, command timing and ensures that the + * handler and IRQ setup do not race. All IDE command kick off + * should go via this function or do equivalent locking. + */ + +void ide_execute_command(ide_drive_t *drive, task_ioreg_t cmd, ide_handler_t *handler, unsigned timeout, ide_expiry_t *expiry) +{ + unsigned long flags; + ide_hwgroup_t *hwgroup = HWGROUP(drive); + ide_hwif_t *hwif = HWIF(drive); + + spin_lock_irqsave(&ide_lock, flags); + + if(hwgroup->handler) + BUG(); + hwgroup->handler = handler; + hwgroup->expiry = expiry; + hwgroup->timer.expires = jiffies + timeout; + add_timer(&hwgroup->timer); + hwif->OUTBSYNC(drive, cmd, IDE_COMMAND_REG); + /* Drive takes 400nS to respond, we must avoid the IRQ being + serviced before that. + + FIXME: we could skip this delay with care on non shared + devices + */ + ndelay(400); + spin_unlock_irqrestore(&ide_lock, flags); +} + +EXPORT_SYMBOL(ide_execute_command); + + +/* needed below */ +static ide_startstop_t do_reset1 (ide_drive_t *, int); + +/* + * 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); + ide_hwif_t *hwif = HWIF(drive); + u8 stat; + + SELECT_DRIVE(drive); + udelay (10); + + if (OK_STAT(stat = hwif->INB(IDE_STATUS_REG), 0, BUSY_STAT)) { + printk("%s: ATAPI reset complete\n", drive->name); + } else { + if (time_before(jiffies, hwgroup->poll_timeout)) { + if (HWGROUP(drive)->handler != NULL) + BUG(); + ide_set_handler(drive, &atapi_reset_pollfunc, HZ/20, NULL); + /* continue polling */ + return ide_started; + } + /* end of polling */ + hwgroup->polling = 0; + printk("%s: ATAPI reset timed-out, status=0x%02x\n", + drive->name, stat); + /* do it the old fashioned way */ + return do_reset1(drive, 1); + } + /* done polling */ + hwgroup->polling = 0; + 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); + u8 tmp; + + if (hwif->reset_poll != NULL) { + if (hwif->reset_poll(drive)) { + printk(KERN_ERR "%s: host reset_poll failure for %s.\n", + hwif->name, drive->name); + return ide_stopped; + } + } + + if (!OK_STAT(tmp = hwif->INB(IDE_STATUS_REG), 0, BUSY_STAT)) { + if (time_before(jiffies, hwgroup->poll_timeout)) { + if (HWGROUP(drive)->handler != NULL) + BUG(); + ide_set_handler(drive, &reset_pollfunc, HZ/20, NULL); + /* continue polling */ + return ide_started; + } + printk("%s: reset timed-out, status=0x%02x\n", hwif->name, tmp); + drive->failures++; + } else { + printk("%s: reset: ", hwif->name); + if ((tmp = hwif->INB(IDE_ERROR_REG)) == 1) { + printk("success\n"); + drive->failures = 0; + } else { + drive->failures++; + 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"); + } + } + hwgroup->polling = 0; /* done polling */ + return ide_stopped; +} + +static void check_dma_crc(ide_drive_t *drive) +{ +#ifdef CONFIG_BLK_DEV_IDEDMA + if (drive->crc_count) { + (void) HWIF(drive)->ide_dma_off_quietly(drive); + ide_set_xfer_rate(drive, ide_auto_reduce_xfer(drive)); + if (drive->current_speed >= XFER_SW_DMA_0) + (void) HWIF(drive)->ide_dma_on(drive); + } else + (void)__ide_dma_off(drive); +#endif +} + +static void ide_disk_pre_reset(ide_drive_t *drive) +{ + int legacy = (drive->id->cfs_enable_2 & 0x0400) ? 0 : 1; + + drive->special.all = 0; + drive->special.b.set_geometry = legacy; + drive->special.b.recalibrate = legacy; + 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; +} + +static void pre_reset(ide_drive_t *drive) +{ + if (drive->media == ide_disk) + ide_disk_pre_reset(drive); + else + drive->post_reset = 1; + + if (!drive->keep_settings) { + if (drive->using_dma) { + check_dma_crc(drive); + } else { + drive->unmask = 0; + drive->io_32bit = 0; + } + return; + } + if (drive->using_dma) + check_dma_crc(drive); + + if (HWIF(drive)->pre_reset != NULL) + HWIF(drive)->pre_reset(drive); + +} + +/* + * 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; + ide_hwgroup_t *hwgroup; + + spin_lock_irqsave(&ide_lock, flags); + hwif = HWIF(drive); + hwgroup = HWGROUP(drive); + + /* We must not reset with running handlers */ + if(hwgroup->handler != NULL) + BUG(); + + /* For an ATAPI device, first try an ATAPI SRST. */ + if (drive->media != ide_disk && !do_not_try_atapi) { + pre_reset(drive); + SELECT_DRIVE(drive); + udelay (20); + hwif->OUTB(WIN_SRST, IDE_COMMAND_REG); + hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE; + hwgroup->polling = 1; + __ide_set_handler(drive, &atapi_reset_pollfunc, HZ/20, NULL); + spin_unlock_irqrestore(&ide_lock, flags); + 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) { + spin_unlock_irqrestore(&ide_lock, 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. + */ + /* set SRST and nIEN */ + hwif->OUTBSYNC(drive, drive->ctl|6,IDE_CONTROL_REG); + /* more than enough time */ + udelay(10); + if (drive->quirk_list == 2) { + /* clear SRST and nIEN */ + hwif->OUTBSYNC(drive, drive->ctl, IDE_CONTROL_REG); + } else { + /* clear SRST, leave nIEN */ + hwif->OUTBSYNC(drive, drive->ctl|2, IDE_CONTROL_REG); + } + /* more than enough time */ + udelay(10); + hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE; + hwgroup->polling = 1; + __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 */ + + spin_unlock_irqrestore(&ide_lock, flags); + 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); +} + +EXPORT_SYMBOL(ide_do_reset); + +/* + * ide_wait_not_busy() waits for the currently selected device on the hwif + * to report a non-busy status, see comments in probe_hwif(). + */ +int ide_wait_not_busy(ide_hwif_t *hwif, unsigned long timeout) +{ + u8 stat = 0; + + while(timeout--) { + /* + * Turn this into a schedule() sleep once I'm sure + * about locking issues (2.5 work ?). + */ + mdelay(1); + stat = hwif->INB(hwif->io_ports[IDE_STATUS_OFFSET]); + if ((stat & BUSY_STAT) == 0) + return 0; + /* + * Assume a value of 0xff means nothing is connected to + * the interface and it doesn't implement the pull-down + * resistor on D7. + */ + if (stat == 0xff) + return -ENODEV; + } + return -EBUSY; +} + +EXPORT_SYMBOL_GPL(ide_wait_not_busy); + diff --git a/drivers/ide/ide-lib.c b/drivers/ide/ide-lib.c new file mode 100644 index 0000000..6806d40 --- /dev/null +++ b/drivers/ide/ide-lib.c @@ -0,0 +1,622 @@ +#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/slab.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/hdreg.h> +#include <linux/ide.h> +#include <linux/bitops.h> + +#include <asm/byteorder.h> +#include <asm/irq.h> +#include <asm/uaccess.h> +#include <asm/io.h> + +/* + * IDE library routines. These are plug in code that most + * drivers can use but occasionally may be weird enough + * to want to do their own thing with + * + * Add common non I/O op stuff here. Make sure it has proper + * kernel-doc function headers or your patch will be rejected + */ + + +/** + * ide_xfer_verbose - return IDE mode names + * @xfer_rate: rate to name + * + * Returns a constant string giving the name of the mode + * requested. + */ + +char *ide_xfer_verbose (u8 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"); + } +} + +EXPORT_SYMBOL(ide_xfer_verbose); + +/** + * ide_dma_speed - compute DMA speed + * @drive: drive + * @mode; intended mode + * + * Checks the drive capabilities and returns the speed to use + * for the transfer. Returns -1 if the requested mode is unknown + * (eg PIO) + */ + +u8 ide_dma_speed(ide_drive_t *drive, u8 mode) +{ + struct hd_driveid *id = drive->id; + ide_hwif_t *hwif = HWIF(drive); + u8 speed = 0; + + if (drive->media != ide_disk && hwif->atapi_dma == 0) + return 0; + + switch(mode) { + case 0x04: + if ((id->dma_ultra & 0x0040) && + (id->dma_ultra & hwif->ultra_mask)) + { speed = XFER_UDMA_6; break; } + case 0x03: + if ((id->dma_ultra & 0x0020) && + (id->dma_ultra & hwif->ultra_mask)) + { speed = XFER_UDMA_5; break; } + case 0x02: + if ((id->dma_ultra & 0x0010) && + (id->dma_ultra & hwif->ultra_mask)) + { speed = XFER_UDMA_4; break; } + if ((id->dma_ultra & 0x0008) && + (id->dma_ultra & hwif->ultra_mask)) + { speed = XFER_UDMA_3; break; } + case 0x01: + if ((id->dma_ultra & 0x0004) && + (id->dma_ultra & hwif->ultra_mask)) + { speed = XFER_UDMA_2; break; } + if ((id->dma_ultra & 0x0002) && + (id->dma_ultra & hwif->ultra_mask)) + { speed = XFER_UDMA_1; break; } + if ((id->dma_ultra & 0x0001) && + (id->dma_ultra & hwif->ultra_mask)) + { speed = XFER_UDMA_0; break; } + case 0x00: + if ((id->dma_mword & 0x0004) && + (id->dma_mword & hwif->mwdma_mask)) + { speed = XFER_MW_DMA_2; break; } + if ((id->dma_mword & 0x0002) && + (id->dma_mword & hwif->mwdma_mask)) + { speed = XFER_MW_DMA_1; break; } + if ((id->dma_mword & 0x0001) && + (id->dma_mword & hwif->mwdma_mask)) + { speed = XFER_MW_DMA_0; break; } + if ((id->dma_1word & 0x0004) && + (id->dma_1word & hwif->swdma_mask)) + { speed = XFER_SW_DMA_2; break; } + if ((id->dma_1word & 0x0002) && + (id->dma_1word & hwif->swdma_mask)) + { speed = XFER_SW_DMA_1; break; } + if ((id->dma_1word & 0x0001) && + (id->dma_1word & hwif->swdma_mask)) + { speed = XFER_SW_DMA_0; break; } + } + +// printk("%s: %s: mode 0x%02x, speed 0x%02x\n", +// __FUNCTION__, drive->name, mode, speed); + + return speed; +} + +EXPORT_SYMBOL(ide_dma_speed); + + +/** + * ide_rate_filter - return best speed for mode + * @mode: modes available + * @speed: desired speed + * + * Given the available DMA/UDMA mode this function returns + * the best available speed at or below the speed requested. + */ + +u8 ide_rate_filter (u8 mode, u8 speed) +{ +#ifdef CONFIG_BLK_DEV_IDEDMA + static u8 speed_max[] = { + XFER_MW_DMA_2, XFER_UDMA_2, XFER_UDMA_4, + XFER_UDMA_5, XFER_UDMA_6 + }; + +// printk("%s: mode 0x%02x, speed 0x%02x\n", __FUNCTION__, mode, speed); + + /* So that we remember to update this if new modes appear */ + if (mode > 4) + BUG(); + return min(speed, speed_max[mode]); +#else /* !CONFIG_BLK_DEV_IDEDMA */ + return min(speed, (u8)XFER_PIO_4); +#endif /* CONFIG_BLK_DEV_IDEDMA */ +} + +EXPORT_SYMBOL(ide_rate_filter); + +int ide_dma_enable (ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + struct hd_driveid *id = drive->id; + + return ((int) ((((id->dma_ultra >> 8) & hwif->ultra_mask) || + ((id->dma_mword >> 8) & hwif->mwdma_mask) || + ((id->dma_1word >> 8) & hwif->swdma_mask)) ? 1 : 0)); +} + +EXPORT_SYMBOL(ide_dma_enable); + +/* + * 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). + */ +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) */ +}; + +EXPORT_SYMBOL_GPL(ide_pio_timings); + +/* + * 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. + */ + +/* + * 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_540", 3 }, /* Older Quantum Fireballs don't work */ + { "QUANTUM FIREBALL_640", 3 }, + { "QUANTUM FIREBALL_1080", 3 }, + { "QUANTUM FIREBALL_1280", 3 }, + { NULL, 0 } +}; + +/** + * ide_scan_pio_blacklist - check for a blacklisted drive + * @model: Drive model string + * + * 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[]. + */ + +static 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; +} + +/** + * ide_get_best_pio_mode - get PIO mode from drive + * @driver: drive to consider + * @mode_wanted: preferred mode + * @max_mode: highest allowed + * @d: pio data + * + * 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 + */ + +u8 ide_get_best_pio_mode (ide_drive_t *drive, u8 mode_wanted, u8 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) printk("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; +} + +EXPORT_SYMBOL_GPL(ide_get_best_pio_mode); + +/** + * ide_toggle_bounce - handle bounce buffering + * @drive: drive to update + * @on: on/off boolean + * + * Enable or disable bounce buffering for the device. Drives move + * between PIO and DMA and that changes the rules we need. + */ + +void ide_toggle_bounce(ide_drive_t *drive, int on) +{ + u64 addr = BLK_BOUNCE_HIGH; /* dma64_addr_t */ + + if (on && drive->media == ide_disk) { + if (!PCI_DMA_BUS_IS_PHYS) + addr = BLK_BOUNCE_ANY; + else if (HWIF(drive)->pci_dev) + addr = HWIF(drive)->pci_dev->dma_mask; + } + + if (drive->queue) + blk_queue_bounce_limit(drive->queue, addr); +} + +/** + * ide_set_xfer_rate - set transfer rate + * @drive: drive to set + * @speed: speed to attempt to set + * + * General helper for setting the speed of an IDE device. This + * function knows about user enforced limits from the configuration + * which speedproc() does not. High level drivers should never + * invoke speedproc() directly. + */ + +int ide_set_xfer_rate(ide_drive_t *drive, u8 rate) +{ +#ifndef CONFIG_BLK_DEV_IDEDMA + rate = min(rate, (u8) XFER_PIO_4); +#endif + if(HWIF(drive)->speedproc) + return HWIF(drive)->speedproc(drive, rate); + else + return -1; +} + +EXPORT_SYMBOL_GPL(ide_set_xfer_rate); + +static void ide_dump_opcode(ide_drive_t *drive) +{ + struct request *rq; + u8 opcode = 0; + int found = 0; + + spin_lock(&ide_lock); + rq = NULL; + if (HWGROUP(drive)) + rq = HWGROUP(drive)->rq; + spin_unlock(&ide_lock); + if (!rq) + return; + if (rq->flags & (REQ_DRIVE_CMD | REQ_DRIVE_TASK)) { + char *args = rq->buffer; + if (args) { + opcode = args[0]; + found = 1; + } + } else if (rq->flags & REQ_DRIVE_TASKFILE) { + ide_task_t *args = rq->special; + if (args) { + task_struct_t *tf = (task_struct_t *) args->tfRegister; + opcode = tf->command; + found = 1; + } + } + + printk("ide: failed opcode was: "); + if (!found) + printk("unknown\n"); + else + printk("0x%02x\n", opcode); +} + +static u8 ide_dump_ata_status(ide_drive_t *drive, const char *msg, u8 stat) +{ + ide_hwif_t *hwif = HWIF(drive); + unsigned long flags; + u8 err = 0; + + local_irq_set(flags); + printk("%s: %s: status=0x%02x", drive->name, msg, stat); + 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("}"); + printk("\n"); + if ((stat & (BUSY_STAT|ERR_STAT)) == ERR_STAT) { + err = hwif->INB(IDE_ERROR_REG); + printk("%s: %s: error=0x%02x", drive->name, msg, err); + printk(" { "); + if (err & ABRT_ERR) printk("DriveStatusError "); + if (err & ICRC_ERR) + printk("Bad%s ", (err & ABRT_ERR) ? "CRC" : "Sector"); + 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))) { + if (drive->addressing == 1) { + __u64 sectors = 0; + u32 low = 0, high = 0; + low = ide_read_24(drive); + hwif->OUTB(drive->ctl|0x80, IDE_CONTROL_REG); + high = ide_read_24(drive); + sectors = ((__u64)high << 24) | low; + printk(", LBAsect=%llu, high=%d, low=%d", + (unsigned long long) sectors, + high, low); + } else { + u8 cur = hwif->INB(IDE_SELECT_REG); + if (cur & 0x40) { /* using LBA? */ + printk(", LBAsect=%ld", (unsigned long) + ((cur&0xf)<<24) + |(hwif->INB(IDE_HCYL_REG)<<16) + |(hwif->INB(IDE_LCYL_REG)<<8) + | hwif->INB(IDE_SECTOR_REG)); + } else { + printk(", CHS=%d/%d/%d", + (hwif->INB(IDE_HCYL_REG)<<8) + + hwif->INB(IDE_LCYL_REG), + cur & 0xf, + hwif->INB(IDE_SECTOR_REG)); + } + } + if (HWGROUP(drive) && HWGROUP(drive)->rq) + printk(", sector=%llu", + (unsigned long long)HWGROUP(drive)->rq->sector); + } + } + printk("\n"); + ide_dump_opcode(drive); + local_irq_restore(flags); + return err; +} + +/** + * ide_dump_atapi_status - print human readable atapi status + * @drive: drive that status applies to + * @msg: text message to print + * @stat: status byte to decode + * + * Error reporting, in human readable form (luxurious, but a memory hog). + */ + +static u8 ide_dump_atapi_status(ide_drive_t *drive, const char *msg, u8 stat) +{ + unsigned long flags; + + atapi_status_t status; + atapi_error_t error; + + status.all = stat; + error.all = 0; + local_irq_set(flags); + printk("%s: %s: status=0x%02x { ", drive->name, msg, stat); + if (status.b.bsy) + printk("Busy "); + else { + if (status.b.drdy) printk("DriveReady "); + if (status.b.df) printk("DeviceFault "); + if (status.b.dsc) printk("SeekComplete "); + if (status.b.drq) printk("DataRequest "); + if (status.b.corr) printk("CorrectedError "); + if (status.b.idx) printk("Index "); + if (status.b.check) printk("Error "); + } + printk("}\n"); + if (status.b.check && !status.b.bsy) { + error.all = HWIF(drive)->INB(IDE_ERROR_REG); + printk("%s: %s: error=0x%02x { ", drive->name, msg, error.all); + if (error.b.ili) printk("IllegalLengthIndication "); + if (error.b.eom) printk("EndOfMedia "); + if (error.b.abrt) printk("AbortedCommand "); + if (error.b.mcr) printk("MediaChangeRequested "); + if (error.b.sense_key) printk("LastFailedSense=0x%02x ", + error.b.sense_key); + printk("}\n"); + } + ide_dump_opcode(drive); + local_irq_restore(flags); + return error.all; +} + +/** + * ide_dump_status - translate ATA/ATAPI error + * @drive: drive the error occured on + * @msg: information string + * @stat: status byte + * + * Error reporting, in human readable form (luxurious, but a memory hog). + * Combines the drive name, message and status byte to provide a + * user understandable explanation of the device error. + */ + +u8 ide_dump_status(ide_drive_t *drive, const char *msg, u8 stat) +{ + if (drive->media == ide_disk) + return ide_dump_ata_status(drive, msg, stat); + return ide_dump_atapi_status(drive, msg, stat); +} + +EXPORT_SYMBOL(ide_dump_status); diff --git a/drivers/ide/ide-pnp.c b/drivers/ide/ide-pnp.c new file mode 100644 index 0000000..df7d150 --- /dev/null +++ b/drivers/ide/ide-pnp.c @@ -0,0 +1,75 @@ +/* + * linux/drivers/ide/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@donpac.ru> + * + * 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/init.h> +#include <linux/pnp.h> +#include <linux/ide.h> + +/* Add your devices here :)) */ +static struct pnp_device_id idepnp_devices[] = { + /* Generic ESDI/IDE/ATA compatible hard disk controller */ + {.id = "PNP0600", .driver_data = 0}, + {.id = ""} +}; + +static int idepnp_probe(struct pnp_dev * dev, const struct pnp_device_id *dev_id) +{ + hw_regs_t hw; + ide_hwif_t *hwif; + int index; + + if (!(pnp_port_valid(dev, 0) && pnp_port_valid(dev, 1) && pnp_irq_valid(dev, 0))) + return -1; + + memset(&hw, 0, sizeof(hw)); + ide_std_init_ports(&hw, pnp_port_start(dev, 0), + pnp_port_start(dev, 1)); + hw.irq = pnp_irq(dev, 0); + hw.dma = NO_DMA; + + index = ide_register_hw(&hw, &hwif); + + if (index != -1) { + printk(KERN_INFO "ide%d: generic PnP IDE interface\n", index); + pnp_set_drvdata(dev,hwif); + return 0; + } + + return -1; +} + +static void idepnp_remove(struct pnp_dev * dev) +{ + ide_hwif_t *hwif = pnp_get_drvdata(dev); + if (hwif) { + ide_unregister(hwif->index); + } else + printk(KERN_ERR "idepnp: Unable to remove device, please report.\n"); +} + +static struct pnp_driver idepnp_driver = { + .name = "ide", + .id_table = idepnp_devices, + .probe = idepnp_probe, + .remove = idepnp_remove, +}; + +void __init pnpide_init(void) +{ + pnp_register_driver(&idepnp_driver); +} diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c new file mode 100644 index 0000000..554473a --- /dev/null +++ b/drivers/ide/ide-probe.c @@ -0,0 +1,1421 @@ +/* + * linux/drivers/ide/ide-probe.c Version 1.11 Mar 05, 2003 + * + * Copyright (C) 1994-1998 Linus Torvalds & authors (see below) + */ + +/* + * Mostly written by Mark Lord <mlord@pobox.com> + * and Gadi Oxman <gadio@netvision.net.il> + * and Andre Hedrick <andre@linux-ide.org> + * + * 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; + * Version 1.06 stream line request queue and prep for cascade project. + * Version 1.07 max_sect <= 255; slower disks would get behind and + * then fall over when they get to 256. Paul G. + * Version 1.10 Update set for new IDE. drive->id is now always + * valid after probe time even with noprobe + */ + +#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/slab.h> +#include <linux/delay.h> +#include <linux/ide.h> +#include <linux/spinlock.h> +#include <linux/kmod.h> +#include <linux/pci.h> + +#include <asm/byteorder.h> +#include <asm/irq.h> +#include <asm/uaccess.h> +#include <asm/io.h> + +/** + * generic_id - add a generic drive id + * @drive: drive to make an ID block for + * + * Add a fake id field to the drive we are passed. This allows + * use to skip a ton of NULL checks (which people always miss) + * and make drive properties unconditional outside of this file + */ + +static void generic_id(ide_drive_t *drive) +{ + drive->id->cyls = drive->cyl; + drive->id->heads = drive->head; + drive->id->sectors = drive->sect; + drive->id->cur_cyls = drive->cyl; + drive->id->cur_heads = drive->head; + drive->id->cur_sectors = drive->sect; +} + +static void ide_disk_init_chs(ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + + /* 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; + } +} + +static void ide_disk_init_mult_count(ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + + 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_is_flashcard - check for compact flash + * @drive: drive to check + * + * 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.4.x). + * At that time, we might also consider parameterizing the timeouts and + * retries, since these are MUCH faster than mechanical drives. -M.Lord + */ + +static inline int drive_is_flashcard (ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + + if (drive->removable) { + 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) /* old SanDisk */ + || !strncmp(id->model, "SanDisk SDCFB", 13) /* SanDisk */ + || !strncmp(id->model, "HAGIWARA HPC", 12) /* Hagiwara */ + || !strncmp(id->model, "LEXAR ATA_FLASH", 15) /* Lexar */ + || !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 */ +} + +/** + * do_identify - identify a drive + * @drive: drive to identify + * @cmd: command used + * + * Called when we have issued a drive identify command to + * read and parse the results. This function is run with + * interrupts disabled. + */ + +static inline void do_identify (ide_drive_t *drive, u8 cmd) +{ + ide_hwif_t *hwif = HWIF(drive); + int bswap = 1; + struct hd_driveid *id; + + id = drive->id; + /* read 512 bytes of id info */ + hwif->ata_input_data(drive, id, SECTOR_WORDS); + + drive->id_read = 1; + local_irq_enable(); + ide_fix_driveid(id); + +#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); + goto err_misc; + } +#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 */ + /* Vertos drives may still be weird */ + bswap ^= 1; + } + 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")) + goto err_misc; + + /* we depend on this a lot! */ + id->model[sizeof(id->model)-1] = '\0'; + printk("%s: %s, ", drive->name, id->model); + drive->present = 1; + drive->dead = 0; + + /* + * Check for an ATAPI device + */ + if (cmd == WIN_PIDENTIFY) { + u8 type = (id->config >> 8) & 0x1f; + printk("ATAPI "); + 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"); + drive->removable = 1; + break; + } + } + /* Early cdrom models used zero */ + type = ide_cdrom; + 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 ("CD/DVD-ROM"); + 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; + /* an ATAPI device ignores DRDY */ + drive->ready_stat = 0; + return; + } + + /* + * Not an ATAPI device: looks like a "regular" hard disk + */ + if (id->config & (1<<7)) + drive->removable = 1; + + if (drive_is_flashcard(drive)) + drive->is_flash = 1; + drive->media = ide_disk; + printk("%s DISK drive\n", (drive->is_flash) ? "CFA" : "ATA" ); + QUIRK_LIST(drive); + return; + +err_misc: + kfree(id); + drive->present = 0; + return; +} + +/** + * actual_try_to_identify - send ata/atapi identify + * @drive: drive to identify + * @cmd: command to use + * + * 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 actual_try_to_identify (ide_drive_t *drive, u8 cmd) +{ + ide_hwif_t *hwif = HWIF(drive); + int rc; + unsigned long hd_status; + unsigned long timeout; + u8 s = 0, a = 0; + + /* take a deep breath */ + msleep(50); + + if (IDE_CONTROL_REG) { + a = hwif->INB(IDE_ALTSTATUS_REG); + s = hwif->INB(IDE_STATUS_REG); + if ((a ^ s) & ~INDEX_STAT) { + printk(KERN_INFO "%s: probing with STATUS(0x%02x) instead of " + "ALTSTATUS(0x%02x)\n", drive->name, s, a); + /* ancient Seagate drives, broken interfaces */ + hd_status = IDE_STATUS_REG; + } else { + /* use non-intrusive polling */ + hd_status = IDE_ALTSTATUS_REG; + } + } else + hd_status = IDE_STATUS_REG; + + /* set features register for atapi + * identify command to be sure of reply + */ + if ((cmd == WIN_PIDENTIFY)) + /* disable dma & overlap */ + hwif->OUTB(0, IDE_FEATURE_REG); + + /* ask drive for ID */ + hwif->OUTB(cmd, IDE_COMMAND_REG); + + timeout = ((cmd == WIN_IDENTIFY) ? WAIT_WORSTCASE : WAIT_PIDENTIFY) / 2; + timeout += jiffies; + do { + if (time_after(jiffies, timeout)) { + /* drive timed-out */ + return 1; + } + /* give drive a breather */ + msleep(50); + } while ((hwif->INB(hd_status)) & BUSY_STAT); + + /* wait for IRQ and DRQ_STAT */ + msleep(50); + if (OK_STAT((hwif->INB(IDE_STATUS_REG)), DRQ_STAT, BAD_R_STAT)) { + unsigned long flags; + + /* local CPU only; some systems need this */ + local_irq_save(flags); + /* drive returned ID */ + do_identify(drive, cmd); + /* drive responded with ID */ + rc = 0; + /* clear drive IRQ */ + (void) hwif->INB(IDE_STATUS_REG); + local_irq_restore(flags); + } else { + /* drive refused ID */ + rc = 2; + } + return rc; +} + +/** + * try_to_identify - try to identify a drive + * @drive: drive to probe + * @cmd: command to use + * + * Issue the identify command and then do IRQ probing to + * complete the identification when needed by finding the + * IRQ the drive is attached to + */ + +static int try_to_identify (ide_drive_t *drive, u8 cmd) +{ + ide_hwif_t *hwif = HWIF(drive); + int retval; + int autoprobe = 0; + unsigned long cookie = 0; + + /* + * Disable device irq unless we need to + * probe for it. Otherwise we'll get spurious + * interrupts during the identify-phase that + * the irq handler isn't expecting. + */ + if (IDE_CONTROL_REG) { + u8 ctl = drive->ctl | 2; + if (!hwif->irq) { + autoprobe = 1; + cookie = probe_irq_on(); + /* enable device irq */ + ctl &= ~2; + } + hwif->OUTB(ctl, IDE_CONTROL_REG); + } + + retval = actual_try_to_identify(drive, cmd); + + if (autoprobe) { + int irq; + /* mask device irq */ + hwif->OUTB(drive->ctl|2, IDE_CONTROL_REG); + /* clear drive IRQ */ + (void) hwif->INB(IDE_STATUS_REG); + udelay(5); + irq = probe_irq_off(cookie); + if (!hwif->irq) { + if (irq > 0) { + hwif->irq = irq; + } else { + /* Mmmm.. multiple IRQs.. + * don't know which was ours + */ + printk("%s: IRQ probe failed (0x%lx)\n", + drive->name, cookie); + } + } + } + return retval; +} + + +/** + * do_probe - probe an IDE device + * @drive: drive to probe + * @cmd: command to use + * + * 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, u8 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 + + /* needed for some systems + * (e.g. crw9624 as drive0 with disk as slave) + */ + msleep(50); + SELECT_DRIVE(drive); + msleep(50); + if (hwif->INB(IDE_SELECT_REG) != drive->select.all && !drive->present) { + if (drive->select.b.unit != 0) { + /* exit with drive0 selected */ + SELECT_DRIVE(&hwif->drives[0]); + /* allow BUSY_STAT to assert & clear */ + msleep(50); + } + /* no i/f present: mmm.. this should be a 4 -ml */ + return 3; + } + + if (OK_STAT((hwif->INB(IDE_STATUS_REG)), READY_STAT, BUSY_STAT) || + drive->present || cmd == WIN_PIDENTIFY) { + /* send cmd and wait */ + if ((rc = try_to_identify(drive, cmd))) { + /* failed: try again */ + rc = try_to_identify(drive,cmd); + } + if (hwif->INB(IDE_STATUS_REG) == (BUSY_STAT|READY_STAT)) + return 4; + + if ((rc == 1 && cmd == WIN_PIDENTIFY) && + ((drive->autotune == IDE_TUNE_DEFAULT) || + (drive->autotune == IDE_TUNE_AUTO))) { + unsigned long timeout; + printk("%s: no response (status = 0x%02x), " + "resetting drive\n", drive->name, + hwif->INB(IDE_STATUS_REG)); + msleep(50); + hwif->OUTB(drive->select.all, IDE_SELECT_REG); + msleep(50); + hwif->OUTB(WIN_SRST, IDE_COMMAND_REG); + timeout = jiffies; + while (((hwif->INB(IDE_STATUS_REG)) & BUSY_STAT) && + time_before(jiffies, timeout + WAIT_WORSTCASE)) + msleep(50); + rc = try_to_identify(drive, cmd); + } + if (rc == 1) + printk("%s: no response (status = 0x%02x)\n", + drive->name, hwif->INB(IDE_STATUS_REG)); + /* ensure drive irq is clear */ + (void) hwif->INB(IDE_STATUS_REG); + } else { + /* not present or maybe ATAPI */ + rc = 3; + } + if (drive->select.b.unit != 0) { + /* exit with drive0 selected */ + SELECT_DRIVE(&hwif->drives[0]); + msleep(50); + /* ensure drive irq is clear */ + (void) hwif->INB(IDE_STATUS_REG); + } + return rc; +} + +/* + * + */ +static void enable_nest (ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + unsigned long timeout; + + printk("%s: enabling %s -- ", hwif->name, drive->id->model); + SELECT_DRIVE(drive); + msleep(50); + hwif->OUTB(EXABYTE_ENABLE_NEST, IDE_COMMAND_REG); + timeout = jiffies + WAIT_WORSTCASE; + do { + if (time_after(jiffies, timeout)) { + printk("failed (timeout)\n"); + return; + } + msleep(50); + } while ((hwif->INB(IDE_STATUS_REG)) & BUSY_STAT); + + msleep(50); + + if (!OK_STAT((hwif->INB(IDE_STATUS_REG)), 0, BAD_STAT)) { + printk("failed (status = 0x%02x)\n", hwif->INB(IDE_STATUS_REG)); + } else { + printk("success\n"); + } + + /* if !(success||timed-out) */ + if (do_probe(drive, WIN_IDENTIFY) >= 2) { + /* look for ATAPI device */ + (void) do_probe(drive, WIN_PIDENTIFY); + } +} + +/** + * probe_for_drives - upper level drive probe + * @drive: drive to probe for + * + * probe_for_drive() tests for existence of a given drive using do_probe() + * and presents things to the user as needed. + * + * Returns: 0 no device was found + * 1 device was found (note: drive->present might + * still be 0) + */ + +static inline u8 probe_for_drive (ide_drive_t *drive) +{ + /* + * In order to keep things simple we have an id + * block for all drives at all times. If the device + * is pre ATA or refuses ATA/ATAPI identify we + * will add faked data to this. + * + * Also note that 0 everywhere means "can't do X" + */ + + drive->id = kmalloc(SECTOR_WORDS *4, GFP_KERNEL); + drive->id_read = 0; + if(drive->id == NULL) + { + printk(KERN_ERR "ide: out of memory for id data.\n"); + return 0; + } + memset(drive->id, 0, SECTOR_WORDS * 4); + strcpy(drive->id->model, "UNKNOWN"); + + /* skip probing? */ + if (!drive->noprobe) + { + /* if !(success||timed-out) */ + if (do_probe(drive, WIN_IDENTIFY) >= 2) { + /* look for ATAPI device */ + (void) do_probe(drive, WIN_PIDENTIFY); + } + if (strstr(drive->id->model, "E X A B Y T E N E S T")) + enable_nest(drive); + if (!drive->present) + /* drive not found */ + return 0; + + /* identification failed? */ + if (!drive->id_read) { + if (drive->media == ide_disk) { + printk(KERN_INFO "%s: non-IDE drive, CHS=%d/%d/%d\n", + drive->name, drive->cyl, + drive->head, drive->sect); + } else if (drive->media == ide_cdrom) { + printk(KERN_INFO "%s: ATAPI cdrom (?)\n", drive->name); + } else { + /* nuke it */ + printk(KERN_WARNING "%s: Unknown device on bus refused identification. Ignoring.\n", drive->name); + drive->present = 0; + } + } + /* drive was found */ + } + if(!drive->present) + return 0; + /* The drive wasn't being helpful. Add generic info only */ + if (drive->id_read == 0) { + generic_id(drive); + return 1; + } + + if (drive->media == ide_disk) { + ide_disk_init_chs(drive); + ide_disk_init_mult_count(drive); + } + + return drive->present; +} + +static void hwif_release_dev (struct device *dev) +{ + ide_hwif_t *hwif = container_of(dev, ide_hwif_t, gendev); + + up(&hwif->gendev_rel_sem); +} + +static void hwif_register (ide_hwif_t *hwif) +{ + /* register with global device tree */ + strlcpy(hwif->gendev.bus_id,hwif->name,BUS_ID_SIZE); + hwif->gendev.driver_data = hwif; + if (hwif->gendev.parent == NULL) { + if (hwif->pci_dev) + hwif->gendev.parent = &hwif->pci_dev->dev; + else + /* Would like to do = &device_legacy */ + hwif->gendev.parent = NULL; + } + hwif->gendev.release = hwif_release_dev; + device_register(&hwif->gendev); +} + +static int wait_hwif_ready(ide_hwif_t *hwif) +{ + int rc; + + printk(KERN_DEBUG "Probing IDE interface %s...\n", hwif->name); + + /* Let HW settle down a bit from whatever init state we + * come from */ + mdelay(2); + + /* Wait for BSY bit to go away, spec timeout is 30 seconds, + * I know of at least one disk who takes 31 seconds, I use 35 + * here to be safe + */ + rc = ide_wait_not_busy(hwif, 35000); + if (rc) + return rc; + + /* Now make sure both master & slave are ready */ + SELECT_DRIVE(&hwif->drives[0]); + hwif->OUTB(8, hwif->io_ports[IDE_CONTROL_OFFSET]); + mdelay(2); + rc = ide_wait_not_busy(hwif, 10000); + if (rc) + return rc; + SELECT_DRIVE(&hwif->drives[1]); + hwif->OUTB(8, hwif->io_ports[IDE_CONTROL_OFFSET]); + mdelay(2); + rc = ide_wait_not_busy(hwif, 10000); + + /* Exit function with master reselected (let's be sane) */ + SELECT_DRIVE(&hwif->drives[0]); + + return rc; +} + +/** + * ide_undecoded_slave - look for bad CF adapters + * @hwif: interface + * + * Analyse the drives on the interface and attempt to decide if we + * have the same drive viewed twice. This occurs with crap CF adapters + * and PCMCIA sometimes. + */ + +void ide_undecoded_slave(ide_hwif_t *hwif) +{ + ide_drive_t *drive0 = &hwif->drives[0]; + ide_drive_t *drive1 = &hwif->drives[1]; + + if (drive0->present == 0 || drive1->present == 0) + return; + + /* If the models don't match they are not the same product */ + if (strcmp(drive0->id->model, drive1->id->model)) + return; + + /* Serial numbers do not match */ + if (strncmp(drive0->id->serial_no, drive1->id->serial_no, 20)) + return; + + /* No serial number, thankfully very rare for CF */ + if (drive0->id->serial_no[0] == 0) + return; + + /* Appears to be an IDE flash adapter with decode bugs */ + printk(KERN_WARNING "ide-probe: ignoring undecoded slave\n"); + + drive1->present = 0; +} + +EXPORT_SYMBOL_GPL(ide_undecoded_slave); + +/* + * 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; + unsigned int irqd; + + if (hwif->noprobe) + return; + + if ((hwif->chipset != ide_4drives || !hwif->mate || !hwif->mate->present) && + (ide_hwif_request_regions(hwif))) { + u16 msgout = 0; + for (unit = 0; unit < MAX_DRIVES; ++unit) { + ide_drive_t *drive = &hwif->drives[unit]; + if (drive->present) { + drive->present = 0; + printk(KERN_ERR "%s: ERROR, PORTS ALREADY IN USE\n", + drive->name); + msgout = 1; + } + } + if (!msgout) + printk(KERN_ERR "%s: ports already in use, skipping probe\n", + hwif->name); + return; + } + + /* + * We must always disable IRQ, as probe_for_drive will assert IRQ, but + * we'll install our IRQ driver much later... + */ + irqd = hwif->irq; + if (irqd) + disable_irq(hwif->irq); + + local_irq_set(flags); + + /* This is needed on some PPCs and a bunch of BIOS-less embedded + * platforms. Typical cases are: + * + * - The firmware hard reset the disk before booting the kernel, + * the drive is still doing it's poweron-reset sequence, that + * can take up to 30 seconds + * - The firmware does nothing (or no firmware), the device is + * still in POST state (same as above actually). + * - Some CD/DVD/Writer combo drives tend to drive the bus during + * their reset sequence even when they are non-selected slave + * devices, thus preventing discovery of the main HD + * + * Doing this wait-for-busy should not harm any existing configuration + * (at least things won't be worse than what current code does, that + * is blindly go & talk to the drive) and fix some issues like the + * above. + * + * BenH. + */ + if (wait_hwif_ready(hwif) == -EBUSY) + printk(KERN_DEBUG "%s: Wait for ready failed before probe !\n", hwif->name); + + /* + * 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]; + drive->dn = (hwif->channel ? 2 : 0) + unit; + (void) probe_for_drive(drive); + if (drive->present && !hwif->present) { + hwif->present = 1; + if (hwif->chipset != ide_4drives || + !hwif->mate || + !hwif->mate->present) { + hwif_register(hwif); + } + } + } + if (hwif->io_ports[IDE_CONTROL_OFFSET] && hwif->reset) { + unsigned long timeout = jiffies + WAIT_WORSTCASE; + u8 stat; + + printk(KERN_WARNING "%s: reset\n", hwif->name); + hwif->OUTB(12, hwif->io_ports[IDE_CONTROL_OFFSET]); + udelay(10); + hwif->OUTB(8, hwif->io_ports[IDE_CONTROL_OFFSET]); + do { + msleep(50); + stat = hwif->INB(hwif->io_ports[IDE_STATUS_OFFSET]); + } while ((stat & BUSY_STAT) && time_after(timeout, jiffies)); + + } + local_irq_restore(flags); + /* + * Use cached IRQ number. It might be (and is...) changed by probe + * code above + */ + if (irqd) + enable_irq(irqd); + + if (!hwif->present) { + ide_hwif_release_regions(hwif); + return; + } + + for (unit = 0; unit < MAX_DRIVES; ++unit) { + ide_drive_t *drive = &hwif->drives[unit]; + + if (drive->present) { + if (hwif->tuneproc != NULL && + drive->autotune == IDE_TUNE_AUTO) + /* auto-tune PIO mode */ + hwif->tuneproc(drive, 255); + + if (drive->autotune != IDE_TUNE_DEFAULT && + drive->autotune != IDE_TUNE_AUTO) + continue; + + drive->nice1 = 1; + + /* + * MAJOR HACK BARF :-/ + * + * FIXME: chipsets own this cruft! + */ + /* + * Move here to prevent module loading clashing. + */ + // drive->autodma = hwif->autodma; + if (hwif->ide_dma_check) { + /* + * Force DMAing for the beginning of the check. + * Some chipsets appear to do interesting + * things, if not checked and cleared. + * PARANOIA!!! + */ + hwif->ide_dma_off_quietly(drive); +#ifdef CONFIG_IDEDMA_ONLYDISK + if (drive->media == ide_disk) +#endif + hwif->ide_dma_check(drive); + } + } + } +} + +static int hwif_init(ide_hwif_t *hwif); + +int probe_hwif_init_with_fixup(ide_hwif_t *hwif, void (*fixup)(ide_hwif_t *hwif)) +{ + probe_hwif(hwif); + + if (fixup) + fixup(hwif); + + if (!hwif_init(hwif)) { + printk(KERN_INFO "%s: failed to initialize IDE interface\n", + hwif->name); + return -1; + } + + if (hwif->present) { + u16 unit = 0; + for (unit = 0; unit < MAX_DRIVES; ++unit) { + ide_drive_t *drive = &hwif->drives[unit]; + /* For now don't attach absent drives, we may + want them on default or a new "empty" class + for hotplug reprobing ? */ + if (drive->present) { + ata_attach(drive); + } + } + } + return 0; +} + +int probe_hwif_init(ide_hwif_t *hwif) +{ + return probe_hwif_init_with_fixup(hwif, NULL); +} + +EXPORT_SYMBOL(probe_hwif_init); + +#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 */ + +/* + * init request queue + */ +static int ide_init_queue(ide_drive_t *drive) +{ + request_queue_t *q; + ide_hwif_t *hwif = HWIF(drive); + int max_sectors = 256; + int max_sg_entries = PRD_ENTRIES; + + /* + * Our default set up assumes the normal IDE case, + * that is 64K segmenting, standard PRD setup + * and LBA28. Some drivers then impose their own + * limits and LBA48 we could raise it but as yet + * do not. + */ + + q = blk_init_queue(do_ide_request, &ide_lock); + if (!q) + return 1; + + q->queuedata = drive; + blk_queue_segment_boundary(q, 0xffff); + + if (!hwif->rqsize) { + if (hwif->no_lba48 || hwif->no_lba48_dma) + hwif->rqsize = 256; + else + hwif->rqsize = 65536; + } + if (hwif->rqsize < max_sectors) + max_sectors = hwif->rqsize; + blk_queue_max_sectors(q, max_sectors); + +#ifdef CONFIG_PCI + /* When we have an IOMMU, we may have a problem where pci_map_sg() + * creates segments that don't completely match our boundary + * requirements and thus need to be broken up again. Because it + * doesn't align properly either, we may actually have to break up + * to more segments than what was we got in the first place, a max + * worst case is twice as many. + * This will be fixed once we teach pci_map_sg() about our boundary + * requirements, hopefully soon. *FIXME* + */ + if (!PCI_DMA_BUS_IS_PHYS) + max_sg_entries >>= 1; +#endif /* CONFIG_PCI */ + + blk_queue_max_hw_segments(q, max_sg_entries); + blk_queue_max_phys_segments(q, max_sg_entries); + + /* assign drive queue */ + drive->queue = q; + + /* needs drive->queue to be set */ + ide_toggle_bounce(drive, 1); + + /* enable led activity for disk drives only */ + if (drive->media == ide_disk && hwif->led_act) + blk_queue_activity_fn(q, hwif->led_act, drive); + + return 0; +} + +/* + * 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 int index; + ide_hwgroup_t *hwgroup; + ide_hwif_t *match = NULL; + + + BUG_ON(in_interrupt()); + BUG_ON(irqs_disabled()); + down(&ide_cfg_sem); + 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; + hwif->hwgroup = hwgroup; + /* + * Link us into the hwgroup. + * This must be done early, do ensure that unexpected_intr + * can find the hwif and prevent irq storms. + * No drives are attached to the new hwif, choose_drive + * can't do anything stupid (yet). + * Add ourself as the 2nd entry to the hwgroup->hwif + * linked list, the first entry is the hwif that owns + * hwgroup->handler - do not change that. + */ + spin_lock_irq(&ide_lock); + hwif->next = hwgroup->hwif->next; + hwgroup->hwif->next = hwif; + spin_unlock_irq(&ide_lock); + } else { + hwgroup = kmalloc(sizeof(ide_hwgroup_t),GFP_KERNEL); + if (!hwgroup) + goto out_up; + + hwif->hwgroup = hwgroup; + + 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) { + int sa = SA_INTERRUPT; +#if defined(__mc68000__) || defined(CONFIG_APUS) + sa = SA_SHIRQ; +#endif /* __mc68000__ || CONFIG_APUS */ + + if (IDE_CHIPSET_IS_PCI(hwif->chipset)) { + sa = SA_SHIRQ; +#ifndef CONFIG_IDEPCI_SHARE_IRQ + sa |= SA_INTERRUPT; +#endif /* CONFIG_IDEPCI_SHARE_IRQ */ + } + + if (hwif->io_ports[IDE_CONTROL_OFFSET]) + /* clear nIEN */ + hwif->OUTB(0x08, hwif->io_ports[IDE_CONTROL_OFFSET]); + + if (request_irq(hwif->irq,&ide_intr,sa,hwif->name,hwgroup)) + goto out_unlink; + } + + /* + * For any present drive: + * - allocate the block device queue + * - link drive into the hwgroup + */ + for (index = 0; index < MAX_DRIVES; ++index) { + ide_drive_t *drive = &hwif->drives[index]; + if (!drive->present) + continue; + if (ide_init_queue(drive)) { + printk(KERN_ERR "ide: failed to init %s\n",drive->name); + continue; + } + spin_lock_irq(&ide_lock); + if (!hwgroup->drive) { + /* first drive for hwgroup. */ + drive->next = drive; + hwgroup->drive = drive; + hwgroup->hwif = HWIF(hwgroup->drive); + } else { + drive->next = hwgroup->drive->next; + hwgroup->drive->next = drive; + } + spin_unlock_irq(&ide_lock); + } + +#if !defined(__mc68000__) && !defined(CONFIG_APUS) && !defined(__sparc__) + printk("%s at 0x%03lx-0x%03lx,0x%03lx 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 0x%08lx on irq %d", 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"); + up(&ide_cfg_sem); + return 0; +out_unlink: + spin_lock_irq(&ide_lock); + if (hwif->next == hwif) { + BUG_ON(match); + BUG_ON(hwgroup->hwif != hwif); + kfree(hwgroup); + } else { + ide_hwif_t *g; + g = hwgroup->hwif; + while (g->next != hwif) + g = g->next; + g->next = hwif->next; + if (hwgroup->hwif == hwif) { + /* Impossible. */ + printk(KERN_ERR "Duh. Uninitialized hwif listed as active hwif.\n"); + hwgroup->hwif = g; + } + BUG_ON(hwgroup->hwif == hwif); + } + spin_unlock_irq(&ide_lock); +out_up: + up(&ide_cfg_sem); + return 1; +} + +static int ata_lock(dev_t dev, void *data) +{ + /* FIXME: we want to pin hwif down */ + return 0; +} + +static struct kobject *ata_probe(dev_t dev, int *part, void *data) +{ + ide_hwif_t *hwif = data; + int unit = *part >> PARTN_BITS; + ide_drive_t *drive = &hwif->drives[unit]; + if (!drive->present) + return NULL; + + if (drive->media == ide_disk) + request_module("ide-disk"); + if (drive->scsi) + request_module("ide-scsi"); + if (drive->media == ide_cdrom || drive->media == ide_optical) + request_module("ide-cd"); + if (drive->media == ide_tape) + request_module("ide-tape"); + if (drive->media == ide_floppy) + request_module("ide-floppy"); + + return NULL; +} + +static struct kobject *exact_match(dev_t dev, int *part, void *data) +{ + struct gendisk *p = data; + *part &= (1 << PARTN_BITS) - 1; + return &p->kobj; +} + +static int exact_lock(dev_t dev, void *data) +{ + struct gendisk *p = data; + + if (!get_disk(p)) + return -1; + return 0; +} + +void ide_register_region(struct gendisk *disk) +{ + blk_register_region(MKDEV(disk->major, disk->first_minor), + disk->minors, NULL, exact_match, exact_lock, disk); +} + +EXPORT_SYMBOL_GPL(ide_register_region); + +void ide_unregister_region(struct gendisk *disk) +{ + blk_unregister_region(MKDEV(disk->major, disk->first_minor), + disk->minors); +} + +EXPORT_SYMBOL_GPL(ide_unregister_region); + +void ide_init_disk(struct gendisk *disk, ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + unsigned int unit = (drive->select.all >> 4) & 1; + + disk->major = hwif->major; + disk->first_minor = unit << PARTN_BITS; + sprintf(disk->disk_name, "hd%c", 'a' + hwif->index * MAX_DRIVES + unit); + disk->queue = drive->queue; +} + +EXPORT_SYMBOL_GPL(ide_init_disk); + +static void drive_release_dev (struct device *dev) +{ + ide_drive_t *drive = container_of(dev, ide_drive_t, gendev); + + up(&drive->gendev_rel_sem); +} + +/* + * 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) +{ + unsigned int unit; + + for (unit = 0; unit < MAX_DRIVES; ++unit) { + ide_drive_t * drive = &hwif->drives[unit]; + ide_add_generic_settings(drive); + snprintf(drive->gendev.bus_id,BUS_ID_SIZE,"%u.%u", + hwif->index,unit); + drive->gendev.parent = &hwif->gendev; + drive->gendev.bus = &ide_bus_type; + drive->gendev.driver_data = drive; + drive->gendev.release = drive_release_dev; + if (drive->present) { + device_register(&drive->gendev); + sprintf(drive->devfs_name, "ide/host%d/bus%d/target%d/lun%d", + (hwif->channel && hwif->mate) ? + hwif->mate->index : hwif->index, + hwif->channel, unit, drive->lun); + } + } + blk_register_region(MKDEV(hwif->major, 0), MAX_DRIVES << PARTN_BITS, + THIS_MODULE, ata_probe, ata_lock, hwif); +} + +static int hwif_init(ide_hwif_t *hwif) +{ + int old_irq; + + /* Return success if no device is connected */ + if (!hwif->present) + return 1; + + 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 */ + + /* we set it back to 1 if all is ok below */ + hwif->present = 0; + + if (register_blkdev(hwif->major, hwif->name)) + return 0; + + if (!hwif->sg_max_nents) + hwif->sg_max_nents = PRD_ENTRIES; + + hwif->sg_table = kmalloc(sizeof(struct scatterlist)*hwif->sg_max_nents, + GFP_KERNEL); + if (!hwif->sg_table) { + printk(KERN_ERR "%s: unable to allocate SG table.\n", hwif->name); + goto out; + } + + if (init_irq(hwif) == 0) + goto done; + + old_irq = 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, old_irq); + goto out; + } + if (init_irq(hwif)) { + printk("%s: probed IRQ %d and default IRQ %d failed.\n", + hwif->name, old_irq, hwif->irq); + goto out; + } + printk("%s: probed IRQ %d failed, using default.\n", + hwif->name, hwif->irq); + +done: + init_gendisk(hwif); + hwif->present = 1; /* success */ + return 1; + +out: + unregister_blkdev(hwif->major, hwif->name); + return 0; +} + +int ideprobe_init (void) +{ + unsigned int index; + int probe[MAX_HWIFS]; + + memset(probe, 0, MAX_HWIFS * sizeof(int)); + for (index = 0; index < MAX_HWIFS; ++index) + probe[index] = !ide_hwifs[index].present; + + 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]); + for (index = 0; index < MAX_HWIFS; ++index) { + if (probe[index]) { + ide_hwif_t *hwif = &ide_hwifs[index]; + int unit; + if (!hwif->present) + continue; + if (hwif->chipset == ide_unknown || hwif->chipset == ide_forced) + hwif->chipset = ide_generic; + for (unit = 0; unit < MAX_DRIVES; ++unit) + if (hwif->drives[unit].present) + ata_attach(&hwif->drives[unit]); + } + } + return 0; +} + +EXPORT_SYMBOL_GPL(ideprobe_init); diff --git a/drivers/ide/ide-proc.c b/drivers/ide/ide-proc.c new file mode 100644 index 0000000..bdff5ac --- /dev/null +++ b/drivers/ide/ide-proc.c @@ -0,0 +1,521 @@ +/* + * linux/drivers/ide/ide-proc.c Version 1.05 Mar 05, 2003 + * + * Copyright (C) 1997-1998 Mark Lord + * Copyright (C) 2003 Red Hat <alan@redhat.com> + */ + +/* + * This is the /proc/ide/ filesystem implementation. + * + * 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. + * + */ + +#include <linux/config.h> +#include <linux/module.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/hdreg.h> +#include <linux/ide.h> +#include <linux/seq_file.h> + +#include <asm/io.h> + +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; + + /* + * Neither ide_unknown nor ide_forced should be set at this point. + */ + switch (hwif->chipset) { + 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_qd65xx: name = "qd65xx"; break; + case ide_umc8672: name = "umc8672"; break; + case ide_ht6560b: name = "ht6560b"; 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_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; + int err = 0; + + len = sprintf(page, "\n"); + + if (drive) { + unsigned short *val = (unsigned short *) page; + + err = taskfile_lib_get_identify(drive, page); + if (!err) { + char *out = ((char *)page) + (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 void proc_ide_settings_warn(void) +{ + static int warned = 0; + + if (warned) + return; + + printk(KERN_WARNING "Warning: /proc/ide/hd?/settings interface is " + "obsolete, and will be removed soon!\n"); + warned = 1; +} + +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; + + proc_ide_settings_warn(); + + down(&ide_setting_sem); + 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; + up(&ide_setting_sem); + 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 __user *buffer, + unsigned long count, void *data) +{ + ide_drive_t *drive = (ide_drive_t *) data; + char name[MAX_LEN + 1]; + int for_real = 0; + unsigned long n; + ide_settings_t *setting; + char *buf, *s; + + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + proc_ide_settings_warn(); + + if (count >= PAGE_SIZE) + return -EINVAL; + + s = buf = (char *)__get_free_page(GFP_USER); + if (!buf) + return -ENOMEM; + + if (copy_from_user(buf, buffer, count)) { + free_page((unsigned long)buf); + return -EFAULT; + } + + buf[count] = '\0'; + + /* + * Skip over leading whitespace + */ + while (count && isspace(*s)) { + --count; + ++s; + } + /* + * Do one full pass to verify all parameters, + * then do another to actually write the new settings. + */ + do { + char *p = s; + n = count; + while (n > 0) { + unsigned val; + char *q = p; + + while (n > 0 && *p != ':') { + --n; + p++; + } + if (*p != ':') + goto parse_error; + if (p - q > MAX_LEN) + goto parse_error; + memcpy(name, q, p - q); + name[p - q] = 0; + + if (n > 0) { + --n; + p++; + } else + goto parse_error; + + val = simple_strtoul(p, &q, 10); + n -= q - p; + p = q; + if (n > 0 && !isspace(*p)) + goto parse_error; + while (n > 0 && isspace(*p)) { + --n; + ++p; + } + + down(&ide_setting_sem); + setting = ide_find_setting_by_name(drive, name); + if (!setting) + { + up(&ide_setting_sem); + goto parse_error; + } + if (for_real) + ide_write_setting(drive, setting, val * setting->div_factor / setting->mul_factor); + up(&ide_setting_sem); + } + } while (!for_real++); + free_page((unsigned long)buf); + return count; +parse_error: + free_page((unsigned long)buf); + 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) +{ + int len = sprintf(page,"%llu\n", (long long)0x7fffffff); + PROC_IDE_READ_RETURN(page,start,off,count,eof,len); +} + +EXPORT_SYMBOL_GPL(proc_ide_read_capacity); + +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); +} + +EXPORT_SYMBOL(proc_ide_read_geometry); + +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 = drive->driver; + int len; + + if (driver) { + len = sprintf(page, "%s version %s\n", + driver->name, driver->version); + } else + len = sprintf(page, "ide-default version 0.9.newide\n"); + PROC_IDE_READ_RETURN(page,start,off,count,eof,len); +} + +static int proc_ide_write_driver + (struct file *file, const char __user *buffer, unsigned long count, void *data) +{ + ide_drive_t *drive = (ide_drive_t *) data; + char name[32]; + + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + if (count > 31) + count = 31; + if (copy_from_user(name, buffer, count)) + return -EFAULT; + name[count] = '\0'; + if (ide_replace_subdriver(drive, name)) + 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]; + + 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); + sprintf(name,"ide%d/%s", (drive->name[2]-'a')/2, drive->name); + ent = proc_symlink(drive->name, proc_ide_root, name); + if (!ent) return; + } +} + +static void destroy_proc_ide_device(ide_hwif_t *hwif, ide_drive_t *drive) +{ + if (drive->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 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]; + if (drive->proc) + destroy_proc_ide_device(hwif, drive); + } +} + +static ide_proc_entry_t hwif_entries[] = { + { "channel", S_IFREG|S_IRUGO, proc_ide_read_channel, NULL }, + { "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); + } +} + +EXPORT_SYMBOL(create_proc_ide_interfaces); + +#ifdef CONFIG_BLK_DEV_IDEPCI +void ide_pci_create_host_proc(const char *name, get_info_t *get_info) +{ + create_proc_info_entry(name, 0, proc_ide_root, get_info); +} + +EXPORT_SYMBOL_GPL(ide_pci_create_host_proc); +#endif + +void destroy_proc_ide_interface(ide_hwif_t *hwif) +{ + if (hwif->proc) { + destroy_proc_ide_drives(hwif); + ide_remove_proc_entries(hwif->proc, hwif_entries); + remove_proc_entry(hwif->name, proc_ide_root); + hwif->proc = NULL; + } +} + +extern struct seq_operations ide_drivers_op; +static int ide_drivers_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &ide_drivers_op); +} +static struct file_operations ide_drivers_operations = { + .open = ide_drivers_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +void proc_ide_create(void) +{ + struct proc_dir_entry *entry; + + if (!proc_ide_root) + return; + + create_proc_ide_interfaces(); + + entry = create_proc_entry("drivers", 0, proc_ide_root); + if (entry) + entry->proc_fops = &ide_drivers_operations; +} + +void proc_ide_destroy(void) +{ + remove_proc_entry("ide/drivers", proc_ide_root); + remove_proc_entry("ide", NULL); +} diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c new file mode 100644 index 0000000..4825448 --- /dev/null +++ b/drivers/ide/ide-tape.c @@ -0,0 +1,4937 @@ +/* + * linux/drivers/ide/ide-tape.c Version 1.19 Nov, 2003 + * + * Copyright (C) 1995 - 1999 Gadi Oxman <gadio@netvision.net.il> + * + * $Header$ + * + * 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. + * Ver 1.17 Nov 2000 / Jan 2001 Marcel Mol, marcel@mesa.nl + * - Add idetape_onstream_mode_sense_tape_parameter_page + * function to get tape capacity in frames: tape->capacity. + * - Add support for DI-50 drives( or any DI- drive). + * - 'workaround' for read error/blank block around block 3000. + * - Implement Early warning for end of media for Onstream. + * - Cosmetic code changes for readability. + * - Idetape_position_tape should not use SKIP bit during + * Onstream read recovery. + * - Add capacity, logical_blk_num and first/last_frame_position + * to /proc/ide/hd?/settings. + * - Module use count was gone in the Linux 2.4 driver. + * Ver 1.17a Apr 2001 Willem Riede osst@riede.org + * - Get drive's actual block size from mode sense block descriptor + * - Limit size of pipeline + * Ver 1.17b Oct 2002 Alan Stern <stern@rowland.harvard.edu> + * Changed IDETAPE_MIN_PIPELINE_STAGES to 1 and actually used + * it in the code! + * Actually removed aborted stages in idetape_abort_pipeline + * instead of just changing the command code. + * Made the transfer byte count for Request Sense equal to the + * actual length of the data transfer. + * Changed handling of partial data transfers: they do not + * cause DMA errors. + * Moved initiation of DMA transfers to the correct place. + * Removed reference to unallocated memory. + * Made __idetape_discard_read_pipeline return the number of + * sectors skipped, not the number of stages. + * Replaced errant kfree() calls with __idetape_kfree_stage(). + * Fixed off-by-one error in testing the pipeline length. + * Fixed handling of filemarks in the read pipeline. + * Small code optimization for MTBSF and MTBSFM ioctls. + * Don't try to unlock the door during device close if is + * already unlocked! + * Cosmetic fixes to miscellaneous debugging output messages. + * Set the minimum /proc/ide/hd?/settings values for "pipeline", + * "pipeline_min", and "pipeline_max" to 1. + * + * 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.19" + +#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/slab.h> +#include <linux/pci.h> +#include <linux/ide.h> +#include <linux/smp_lock.h> +#include <linux/completion.h> +#include <linux/bitops.h> + +#include <asm/byteorder.h> +#include <asm/irq.h> +#include <asm/uaccess.h> +#include <asm/io.h> +#include <asm/unaligned.h> + +/* + * 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; + +#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 is illegal: the pipelined mode + * cannot be disabled (calculate_speeds() divides by tape->max_stages.) + */ +#define IDETAPE_MIN_PIPELINE_STAGES 1 +#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 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 drives (for example, Seagate STT3401A Travan) require a very long + * timeout, because they don't return an interrupt or clear their busy bit + * until after the command completes (even retension commands). + */ +#define IDETAPE_WAIT_CMD (900*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; + +struct idetape_bh { + unsigned short b_size; + atomic_t b_count; + struct idetape_bh *b_reqnext; + char *b_data; +}; + +/* + * 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 idetape_bh *bh; + char *b_data; + int b_count; + u8 *buffer; /* Data buffer */ + u8 *current_position; /* Pointer into the above buffer */ + ide_startstop_t (*callback) (ide_drive_t *); /* Called when this packet command is completed */ + u8 pc_buffer[IDETAPE_PC_BUFFER_SIZE]; /* Temporary buffer */ + unsigned long flags; /* Status/Action bit flags: long for set_bit */ +} idetape_pc_t; + +/* + * Packet command flag bits. + */ +/* Set when an error is considered normal - We won't retry */ +#define PC_ABORT 0 +/* 1 When polling for DSC on a media access command */ +#define PC_WAIT_FOR_DSC 1 +/* 1 when we prefer to use DMA if possible */ +#define PC_DMA_RECOMMENDED 2 +/* 1 while DMA in progress */ +#define PC_DMA_IN_PROGRESS 3 +/* 1 when encountered problem during DMA */ +#define PC_DMA_ERROR 4 +/* Data direction */ +#define PC_WRITING 5 + +/* + * 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 idetape_bh *bh; /* The data buffers */ + struct idetape_stage_s *next; /* Pointer to the next stage */ +} 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_tape_obj { + ide_drive_t *drive; + ide_driver_t *driver; + struct gendisk *disk; + struct kref kref; + + /* + * 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. + */ + /* Current packet command */ + idetape_pc_t *pc; + /* Last failed packet command */ + idetape_pc_t *failed_pc; + /* Packet command stack */ + idetape_pc_t pc_stack[IDETAPE_PC_STACK]; + /* Next free packet command storage space */ + int pc_stack_index; + struct request rq_stack[IDETAPE_PC_STACK]; + /* We implement a circular array */ + int rq_stack_index; + + /* + * 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; + /* The time in which we started polling for DSC */ + unsigned long dsc_polling_start; + /* Timer used to poll for dsc */ + struct timer_list dsc_timer; + /* Read/Write dsc polling frequency */ + unsigned long best_dsc_rw_frequency; + /* The current polling frequency */ + unsigned long dsc_polling_frequency; + /* Maximum waiting time */ + unsigned long dsc_timeout; + + /* + * Read position information + */ + u8 partition; + /* Current block */ + unsigned int first_frame_position; + unsigned int last_frame_position; + unsigned int blocks_in_buffer; + + /* + * Last error information + */ + u8 sense_key, asc, ascq; + + /* + * Character device operation + */ + unsigned int minor; + /* device name */ + char name[4]; + /* Current character device data transfer direction */ + idetape_chrdev_direction_t chrdev_direction; + + /* + * Device information + */ + /* Usually 512 or 1024 bytes */ + unsigned short tape_block_size; + int user_bs_factor; + /* Copy of the tape's Capabilities and Mechanical Page */ + idetape_capabilities_page_t capabilities; + + /* + * 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. + */ + /* Pointer to the request which is waiting in the device request queue */ + struct request *active_data_request; + /* Data buffer size (chosen based on the tape's recommendation */ + int stage_size; + idetape_stage_t *merge_stage; + int merge_stage_size; + struct idetape_bh *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). + */ + /* Number of currently used stages */ + int nr_stages; + /* Number of pending stages */ + int nr_pending_stages; + /* We will not allocate more than this number of stages */ + int max_stages, min_pipeline, max_pipeline; + /* The first stage which will be removed from the pipeline */ + idetape_stage_t *first_stage; + /* The currently active stage */ + idetape_stage_t *active_stage; + /* Will be serviced after the currently active request */ + idetape_stage_t *next_stage; + /* New requests will be added to the pipeline here */ + idetape_stage_t *last_stage; + /* Optional free stage which we can use */ + idetape_stage_t *cache_stage; + int pages_per_stage; + /* Wasted space in each stage */ + int excess_bh_size; + + /* Status/Action flags: long for set_bit */ + unsigned long flags; + /* protects the ide-tape queue */ + spinlock_t spinlock; + + /* + * Measures average tape speed + */ + unsigned long avg_time; + int avg_size; + int avg_speed; + + /* last sense information */ + idetape_request_sense_result_t sense; + + char vendor_id[10]; + char product_id[18]; + char firmware_revision[6]; + int firmware_revision_num; + + /* the door is currently locked */ + int door_locked; + /* the tape hardware is write protected */ + char drv_write_prot; + /* the tape is write protected (hardware or opened as read-only) */ + char write_prot; + + /* + * Limit the number of times a request can + * be postponed, to avoid an infinite postpone + * deadlock. + */ + /* request postpone count limit */ + int postpone_cnt; + + /* + * Measures number of frames: + * + * 1. written/read to/from the driver pipeline (pipeline_head). + * 2. written/read to/from the tape buffers (idetape_bh). + * 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; + int controlled_pipeline_head_speed; + int uncontrolled_pipeline_head_speed; + int controlled_last_pipeline_head; + int uncontrolled_last_pipeline_head; + unsigned long uncontrolled_pipeline_head_time; + unsigned long controlled_pipeline_head_time; + int controlled_previous_pipeline_head; + int uncontrolled_previous_pipeline_head; + unsigned long controlled_previous_head_time; + unsigned long 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; + +static DECLARE_MUTEX(idetape_ref_sem); + +#define to_ide_tape(obj) container_of(obj, struct ide_tape_obj, kref) + +#define ide_tape_g(disk) \ + container_of((disk)->private_data, struct ide_tape_obj, driver) + +static struct ide_tape_obj *ide_tape_get(struct gendisk *disk) +{ + struct ide_tape_obj *tape = NULL; + + down(&idetape_ref_sem); + tape = ide_tape_g(disk); + if (tape) + kref_get(&tape->kref); + up(&idetape_ref_sem); + return tape; +} + +static void ide_tape_release(struct kref *); + +static void ide_tape_put(struct ide_tape_obj *tape) +{ + down(&idetape_ref_sem); + kref_put(&tape->kref, ide_tape_release); + up(&idetape_ref_sem); +} + +/* + * 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 */ +/* 0 = no tape is loaded, so we don't rewind after ejecting */ +#define IDETAPE_MEDIUM_PRESENT 9 + +/* + * 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. + */ + +enum { + REQ_IDETAPE_PC1 = (1 << 0), /* packet command (first stage) */ + REQ_IDETAPE_PC2 = (1 << 1), /* packet command (second stage) */ + REQ_IDETAPE_READ = (1 << 2), + REQ_IDETAPE_WRITE = (1 << 3), + REQ_IDETAPE_READ_BUFFER = (1 << 4), +}; + +/* + * 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 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_BLOCK_DESCRIPTOR 0 +#define IDETAPE_CAPABILITIES_PAGE 0x2a +#define IDETAPE_PARAMTR_PAGE 0x2b /* Onstream DI-x0 only */ +#define IDETAPE_BLOCK_SIZE_PAGE 0x30 +#define IDETAPE_BUFFER_FILLING_PAGE 0x33 + +/* + * 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 struct ide_tape_obj * idetape_devs[MAX_HWIFS * MAX_DRIVES]; + +#define ide_tape_f(file) ((file)->private_data) + +static struct ide_tape_obj *ide_tape_chrdev_get(unsigned int i) +{ + struct ide_tape_obj *tape = NULL; + + down(&idetape_ref_sem); + tape = idetape_devs[i]; + if (tape) + kref_get(&tape->kref); + up(&idetape_ref_sem); + return tape; +} + +/* + * Function declarations + * + */ +static int idetape_chrdev_release (struct inode *inode, struct file *filp); +static void idetape_write_release (ide_drive_t *drive, unsigned int minor); + +/* + * 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--) + (void) HWIF(drive)->INB(IDE_DATA_REG); +} + +static void idetape_input_buffers (ide_drive_t *drive, idetape_pc_t *pc, unsigned int bcount) +{ + struct idetape_bh *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 = min((unsigned int)(bh->b_size - atomic_read(&bh->b_count)), bcount); + HWIF(drive)->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 idetape_bh *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 = min((unsigned int)pc->b_count, (unsigned int)bcount); + HWIF(drive)->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); + } + } + } +} + +static void idetape_update_buffers (idetape_pc_t *pc) +{ + struct idetape_bh *bh = pc->bh; + int count; + unsigned int 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 = min((unsigned int)bh->b_size, (unsigned int)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; +} + +/* + * 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); +#endif /* IDETAPE_DEBUG_LOG */ + + /* + * 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); + } + + /* + * If error was the result of a zero-length read or write command, + * with sense key=5, asc=0x22, ascq=0, let it slide. Some drives + * (i.e. Seagate STT3401A Travan) don't support 0-length read/writes. + */ + if ((pc->c[0] == IDETAPE_READ_CMD || pc->c[0] == IDETAPE_WRITE_CMD) + && pc->c[4] == 0 && pc->c[3] == 0 && pc->c[2] == 0) { /* length==0 */ + if (result->sense_key == 5) { + /* don't report an error, everything's ok */ + pc->error = 0; + /* don't retry read/write */ + set_bit(PC_ABORT, &pc->flags); + } + } + 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) && + pc->actually_transferred) + pc->retries = IDETAPE_MAX_PC_RETRIES + 1; + } +} + +/* + * 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->rq_disk = tape->disk; + rq->buffer = NULL; + rq->special = (void *)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 += max(increase, 1); + tape->max_stages = max(tape->max_stages, tape->min_pipeline); + tape->max_stages = 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 idetape_bh *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 */ + } +} + +/* + * This will free all the pipeline stages starting from new_last_stage->next + * to the end of the list, and point tape->last_stage to new_last_stage. + */ +static void idetape_abort_pipeline(ide_drive_t *drive, + idetape_stage_t *new_last_stage) +{ + idetape_tape_t *tape = drive->driver_data; + idetape_stage_t *stage = new_last_stage->next; + idetape_stage_t *nstage; + +#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) { + nstage = stage->next; + idetape_kfree_stage(tape, stage); + --tape->nr_stages; + --tape->nr_pending_stages; + stage = nstage; + } + if (new_last_stage) + new_last_stage->next = NULL; + tape->last_stage = new_last_stage; + tape->next_stage = NULL; +} + +/* + * idetape_end_request is used to finish servicing a request, and to + * insert a pending pipeline request into the main device queue. + */ +static int idetape_end_request(ide_drive_t *drive, int uptodate, int nr_sects) +{ + struct request *rq = HWGROUP(drive)->rq; + idetape_tape_t *tape = drive->driver_data; + unsigned long flags; + int error; + int remove_stage = 0; + idetape_stage_t *active_stage; + +#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); + + /* The request was a pipelined data transfer request */ + if (tape->active_data_request == rq) { + active_stage = tape->active_stage; + tape->active_stage = NULL; + tape->active_data_request = NULL; + tape->nr_pending_stages--; + if (rq->cmd[0] & REQ_IDETAPE_WRITE) { + remove_stage = 1; + if (error) { + set_bit(IDETAPE_PIPELINE_ERROR, &tape->flags); + if (error == IDETAPE_ERROR_EOD) + idetape_abort_pipeline(drive, active_stage); + } + } else if (rq->cmd[0] & REQ_IDETAPE_READ) { + if (error == IDETAPE_ERROR_EOD) { + set_bit(IDETAPE_PIPELINE_ERROR, &tape->flags); + idetape_abort_pipeline(drive, active_stage); + } + } + if (tape->next_stage != NULL) { + 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) { + idetape_increase_max_pipeline_stages(drive); + } + } + ide_end_drive_cmd(drive, 0, 0); +// blkdev_dequeue_request(rq); +// drive->rq = NULL; +// end_that_request_last(rq); + + 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); + return 0; +} + +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(drive, 1, 0); + } else { + printk(KERN_ERR "ide-tape: Error in REQUEST SENSE itself - Aborting request!\n"); + idetape_end_request(drive, 0, 0); + } + 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 = 20; + pc->callback = &idetape_request_sense_callback; +} + +static void idetape_init_rq(struct request *rq, u8 cmd) +{ + memset(rq, 0, sizeof(*rq)); + rq->flags = REQ_SPECIAL; + rq->cmd[0] = cmd; +} + +/* + * 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) +{ + struct ide_tape_obj *tape = drive->driver_data; + + idetape_init_rq(rq, REQ_IDETAPE_PC1); + rq->buffer = (char *) pc; + rq->rq_disk = tape->disk; + (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; + atapi_error_t error; + + error.all = HWIF(drive)->INB(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) +{ + ide_hwif_t *hwif = drive->hwif; + idetape_tape_t *tape = drive->driver_data; + atapi_status_t status; + atapi_bcount_t bcount; + atapi_ireason_t ireason; + idetape_pc_t *pc = tape->pc; + + unsigned int temp; +#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 */ + + /* Clear the interrupt */ + status.all = HWIF(drive)->INB(IDE_STATUS_REG); + + if (test_bit(PC_DMA_IN_PROGRESS, &pc->flags)) { + if (HWIF(drive)->ide_dma_end(drive) || status.b.check) { + /* + * 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). + */ + + /* + * On the contrary, a DMA error is never expected; + * it usually indicates a hardware error or abort. + * If the tape crosses a filemark during a READ + * command, it will issue an irq and position itself + * after the filemark (not before). Only a partial + * data transfer will occur, but no DMA error. + * (AS, 19 Apr 2001) + */ + set_bit(PC_DMA_ERROR, &pc->flags); + } else { + 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 */ + } + + /* No more interrupts */ + if (!status.b.drq) { +#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); + + local_irq_enable(); + +#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\n", + 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 + /* Retry operation */ + return idetape_retry_pc(drive); + } + pc->error = 0; + if (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; + /* Allow ide.c to handle other requests */ + idetape_postpone_request(drive); + return ide_stopped; + } + if (tape->failed_pc == pc) + tape->failed_pc = NULL; + /* Command finished - Call the callback function */ + return pc->callback(drive); + } + 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)__ide_dma_off(drive); + return ide_do_reset(drive); + } + /* Get the number of bytes to transfer on this interrupt. */ + bcount.b.high = hwif->INB(IDE_BCOUNTH_REG); + bcount.b.low = hwif->INB(IDE_BCOUNTL_REG); + + ireason.all = hwif->INB(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 + /* Write the current buffer */ + HWIF(drive)->atapi_output_bytes(drive, pc->current_position, bcount.all); + } else { + if (pc->bh != NULL) + idetape_input_buffers(drive, pc, bcount.all); + else + /* Read the current buffer */ + HWIF(drive)->atapi_input_bytes(drive, pc->current_position, bcount.all); + } + /* Update the current position */ + pc->actually_transferred += bcount.all; + 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 + /* And set the interrupt handler again */ + ide_set_handler(drive, &idetape_pc_intr, IDETAPE_WAIT_CMD, NULL); + 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) +{ + ide_hwif_t *hwif = drive->hwif; + idetape_tape_t *tape = drive->driver_data; + idetape_pc_t *pc = tape->pc; + atapi_ireason_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 = hwif->INB(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 = hwif->INB(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); + } + /* Set the interrupt routine */ + ide_set_handler(drive, &idetape_pc_intr, IDETAPE_WAIT_CMD, NULL); +#ifdef CONFIG_BLK_DEV_IDEDMA + /* Begin DMA, if necessary */ + if (test_bit(PC_DMA_IN_PROGRESS, &pc->flags)) + hwif->dma_start(drive); +#endif + /* Send the actual packet */ + HWIF(drive)->atapi_output_bytes(drive, pc->c, 12); + return ide_started; +} + +static ide_startstop_t idetape_issue_packet_command (ide_drive_t *drive, idetape_pc_t *pc) +{ + ide_hwif_t *hwif = drive->hwif; + idetape_tape_t *tape = drive->driver_data; + atapi_bcount_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; + /* Set the current packet command */ + tape->pc = pc; + + 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 end of the media, for example). + */ + if (!test_bit(PC_ABORT, &pc->flags)) { + if (!(pc->c[0] == IDETAPE_TEST_UNIT_READY_CMD && + 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); + } + /* Giving up */ + pc->error = IDETAPE_ERROR_GENERAL; + } + 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, cmd = %02X\n", pc->retries, pc->c[0]); +#endif /* IDETAPE_DEBUG_LOG */ + + pc->retries++; + /* We haven't transferred any data yet */ + pc->actually_transferred = 0; + pc->current_position = pc->buffer; + /* Request to transfer the entire buffer at once */ + bcount.all = pc->request_transfer; + + if (test_and_clear_bit(PC_DMA_ERROR, &pc->flags)) { + printk(KERN_WARNING "ide-tape: DMA disabled, " + "reverting to PIO\n"); + (void)__ide_dma_off(drive); + } + if (test_bit(PC_DMA_RECOMMENDED, &pc->flags) && drive->using_dma) + dma_ok = !hwif->dma_setup(drive); + + if (IDE_CONTROL_REG) + hwif->OUTB(drive->ctl, IDE_CONTROL_REG); + hwif->OUTB(dma_ok ? 1 : 0, IDE_FEATURE_REG); /* Use PIO/DMA */ + hwif->OUTB(bcount.b.high, IDE_BCOUNTH_REG); + hwif->OUTB(bcount.b.low, IDE_BCOUNTL_REG); + hwif->OUTB(drive->select.all, IDE_SELECT_REG); + if (dma_ok) /* Will begin DMA later */ + set_bit(PC_DMA_IN_PROGRESS, &pc->flags); + if (test_bit(IDETAPE_DRQ_INTERRUPT, &tape->flags)) { + ide_set_handler(drive, &idetape_transfer_pc, IDETAPE_WAIT_CMD, NULL); + hwif->OUTB(WIN_PACKETCMD, IDE_COMMAND_REG); + return ide_started; + } else { + hwif->OUTB(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(drive, tape->pc->error ? 0 : 1, 0); + return ide_stopped; +} + +/* + * A mode sense command is used to "sense" tape parameters. + */ +static void idetape_create_mode_sense_cmd (idetape_pc_t *pc, u8 page_code) +{ + idetape_init_pc(pc); + pc->c[0] = IDETAPE_MODE_SENSE_CMD; + if (page_code != IDETAPE_BLOCK_DESCRIPTOR) + pc->c[1] = 8; /* DBD = 1 - Don't return block descriptors */ + pc->c[2] = page_code; + /* + * Changed pc->c[3] to 0 (255 will at best return unused info). + * + * For SCSI this byte is defined as subpage instead of high byte + * of length and some IDE drives seem to interpret it this way + * and return an error when 255 is used. + */ + pc->c[3] = 0; + pc->c[4] = 255; /* (We will just discard data in that case) */ + if (page_code == IDETAPE_BLOCK_DESCRIPTOR) + pc->request_transfer = 12; + else if (page_code == IDETAPE_CAPABILITIES_PAGE) + pc->request_transfer = 24; + else + pc->request_transfer = 50; + pc->callback = &idetape_pc_callback; +} + +static void calculate_speeds(ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + int full = 125, empty = 75; + + if (time_after(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 (time_after(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 (time_after(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 (time_after(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 (time_after(jiffies, tape->uncontrolled_pipeline_head_time + 30 * HZ)) { + tape->uncontrolled_pipeline_head_time = jiffies; + } + } + tape->pipeline_head_speed = 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 = 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; + atapi_status_t status; + + status.all = HWIF(drive)->INB(IDE_STATUS_REG); + if (status.b.dsc) { + if (status.b.check) { + /* Error detected */ + if (pc->c[0] != IDETAPE_TEST_UNIT_READY_CMD) + printk(KERN_ERR "ide-tape: %s: I/O error, ", + tape->name); + /* Retry operation */ + return idetape_retry_pc(drive); + } + 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 (time_after(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(drive, 1, 0); + else + idetape_end_request(drive, tape->pc->error, 0); + return ide_stopped; +} + +static void idetape_create_read_cmd(idetape_tape_t *tape, idetape_pc_t *pc, unsigned int length, struct idetape_bh *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; + 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); +} + +static void idetape_create_read_buffer_cmd(idetape_tape_t *tape, idetape_pc_t *pc, unsigned int length, struct idetape_bh *bh) +{ + int size = 32768; + struct idetape_bh *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 idetape_bh *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); + pc->bh = bh; + pc->b_data = bh->b_data; + pc->b_count = atomic_read(&bh->b_count); + pc->buffer = NULL; + 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); +} + +/* + * idetape_do_request is our request handling function. + */ +static ide_startstop_t idetape_do_request(ide_drive_t *drive, + struct request *rq, sector_t block) +{ + idetape_tape_t *tape = drive->driver_data; + idetape_pc_t *pc = NULL; + struct request *postponed_rq = tape->postponed_rq; + atapi_status_t status; + +#if IDETAPE_DEBUG_LOG +#if 0 + if (tape->debug_level >= 5) + printk(KERN_INFO "ide-tape: rq_status: %d, " + "dev: %s, cmd: %ld, errors: %d\n", rq->rq_status, + rq->rq_disk->disk_name, rq->cmd[0], rq->errors); +#endif + if (tape->debug_level >= 2) + printk(KERN_INFO "ide-tape: sector: %ld, " + "nr_sectors: %ld, current_nr_sectors: %d\n", + rq->sector, rq->nr_sectors, rq->current_nr_sectors); +#endif /* IDETAPE_DEBUG_LOG */ + + if ((rq->flags & REQ_SPECIAL) == 0) { + /* + * We do not support buffer cache originated requests. + */ + printk(KERN_NOTICE "ide-tape: %s: Unsupported request in " + "request queue (%ld)\n", drive->name, rq->flags); + ide_end_request(drive, 0, 0); + 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(drive, 0, 0); + 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 = HWIF(drive)->INB(IDE_STATUS_REG); + + if (!drive->dsc_overlap && !(rq->cmd[0] & REQ_IDETAPE_PC2)) + set_bit(IDETAPE_IGNORE_DSC, &tape->flags); + + if (drive->post_reset == 1) { + set_bit(IDETAPE_IGNORE_DSC, &tape->flags); + drive->post_reset = 0; + } + + if (tape->tape_still_time > 100 && tape->tape_still_time < 200) + tape->measure_insert_time = 1; + if (time_after(jiffies, tape->insert_time)) + tape->insert_speed = tape->insert_size / 1024 * HZ / (jiffies - tape->insert_time); + calculate_speeds(drive); + 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 (time_after(jiffies, tape->dsc_timeout)) { + printk(KERN_ERR "ide-tape: %s: DSC timeout\n", + tape->name); + if (rq->cmd[0] & REQ_IDETAPE_PC2) { + 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; + } + if (rq->cmd[0] & REQ_IDETAPE_READ) { + 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; + pc = idetape_next_pc_storage(drive); + idetape_create_read_cmd(tape, pc, rq->current_nr_sectors, (struct idetape_bh *)rq->special); + goto out; + } + if (rq->cmd[0] & REQ_IDETAPE_WRITE) { + 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; + pc = idetape_next_pc_storage(drive); + idetape_create_write_cmd(tape, pc, rq->current_nr_sectors, (struct idetape_bh *)rq->special); + goto out; + } + if (rq->cmd[0] & REQ_IDETAPE_READ_BUFFER) { + tape->postpone_cnt = 0; + pc = idetape_next_pc_storage(drive); + idetape_create_read_buffer_cmd(tape, pc, rq->current_nr_sectors, (struct idetape_bh *)rq->special); + goto out; + } + if (rq->cmd[0] & REQ_IDETAPE_PC1) { + pc = (idetape_pc_t *) rq->buffer; + rq->cmd[0] &= ~(REQ_IDETAPE_PC1); + rq->cmd[0] |= REQ_IDETAPE_PC2; + goto out; + } + if (rq->cmd[0] & REQ_IDETAPE_PC2) { + idetape_media_access_finished(drive); + return ide_stopped; + } + BUG(); +out: + 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 idetape_bh *prev_bh, *bh; + int pages = tape->pages_per_stage; + char *b_data = NULL; + + if ((stage = (idetape_stage_t *) kmalloc (sizeof (idetape_stage_t),GFP_KERNEL)) == NULL) + return NULL; + stage->next = NULL; + + bh = stage->bh = (struct idetape_bh *)kmalloc(sizeof(struct idetape_bh), 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); + + 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 idetape_bh *)kmalloc(sizeof(struct idetape_bh), 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); + prev_bh->b_reqnext = bh; + } + bh->b_size -= tape->excess_bh_size; + if (full) + atomic_sub(tape->excess_bh_size, &bh->b_count); + 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 __user *buf, int n) +{ + struct idetape_bh *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 = min((unsigned int)(bh->b_size - atomic_read(&bh->b_count)), (unsigned int)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 __user *buf, idetape_stage_t *stage, int n) +{ + struct idetape_bh *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 = 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 idetape_bh *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 idetape_bh *tmp; + + tmp = stage->bh; + stage->bh = tape->merge_stage->bh; + tape->merge_stage->bh = tmp; + 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); +} + +/* + * idetape_wait_for_request installs a completion 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 completion (usually by disabling interrupts). + */ +static void idetape_wait_for_request (ide_drive_t *drive, struct request *rq) +{ + DECLARE_COMPLETION(wait); + idetape_tape_t *tape = drive->driver_data; + +#if IDETAPE_DEBUG_BUGS + if (rq == NULL || (rq->flags & REQ_SPECIAL) == 0) { + printk (KERN_ERR "ide-tape: bug: Trying to sleep on non-valid request\n"); + return; + } +#endif /* IDETAPE_DEBUG_BUGS */ + rq->waiting = &wait; + rq->end_io = blk_end_sync_rq; + spin_unlock_irq(&tape->spinlock); + wait_for_completion(&wait); + /* The stage and its struct request have been deallocated */ + 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(drive, 0, 0); + } 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(drive, 1, 0); + } + } else { + idetape_end_request(drive, 0, 0); + } + 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_init_pc(pc); + pc->c[0] = IDETAPE_WRITE_FILEMARK_CMD; + 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 ide_tape_obj *tape = drive->driver_data; + struct request rq; + + idetape_init_rq(&rq, REQ_IDETAPE_PC1); + rq.buffer = (char *) pc; + rq.rq_disk = tape->disk; + 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_init_pc(pc); + pc->c[0] = IDETAPE_LOAD_UNLOAD_CMD; + pc->c[4] = cmd; + set_bit(PC_WAIT_FOR_DSC, &pc->flags); + pc->callback = &idetape_pc_callback; +} + +static int idetape_wait_ready(ide_drive_t *drive, unsigned long timeout) +{ + idetape_tape_t *tape = drive->driver_data; + idetape_pc_t pc; + int load_attempted = 0; + + /* + * Wait for the tape to become ready + */ + set_bit(IDETAPE_MEDIUM_PRESENT, &tape->flags); + timeout += jiffies; + while (time_before(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) + || (tape->asc == 0x3A)) { /* no media */ + if (load_attempted) + return -ENOMEDIUM; + idetape_create_load_unload_cmd(drive, &pc, IDETAPE_LU_LOAD_MASK); + __idetape_queue_pc_tail(drive, &pc); + load_attempted = 1; + /* not about to be ready */ + } else if (!(tape->sense_key == 2 && tape->asc == 4 && + (tape->ascq == 1 || tape->ascq == 8))) + return -EIO; + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(HZ / 10); + } + return -EIO; +} + +static int idetape_queue_pc_tail (ide_drive_t *drive,idetape_pc_t *pc) +{ + return __idetape_queue_pc_tail(drive, pc); +} + +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; + +#if IDETAPE_DEBUG_LOG + if (tape->debug_level >= 4) + printk(KERN_INFO "ide-tape: Reached idetape_read_position\n"); +#endif /* IDETAPE_DEBUG_LOG */ + + idetape_create_read_position_cmd(&pc); + if (idetape_queue_pc_tail(drive, &pc)) + return -1; + position = tape->first_frame_position; + return position; +} + +static void idetape_create_locate_cmd (ide_drive_t *drive, idetape_pc_t *pc, unsigned int block, u8 partition, int skip) +{ + idetape_init_pc(pc); + pc->c[0] = IDETAPE_LOCATE_CMD; + pc->c[1] = 2; + put_unaligned(htonl(block), (unsigned int *) &pc->c[3]); + pc->c[8] = partition; + set_bit(PC_WAIT_FOR_DSC, &pc->flags); + pc->callback = &idetape_pc_callback; +} + +static int idetape_create_prevent_cmd (ide_drive_t *drive, idetape_pc_t *pc, int prevent) +{ + idetape_tape_t *tape = drive->driver_data; + + if (!tape->capabilities.lock) + return 0; + + idetape_init_pc(pc); + pc->c[0] = IDETAPE_PREVENT_CMD; + pc->c[4] = prevent; + pc->callback = &idetape_pc_callback; + return 1; +} + +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; + + /* Remove merge stage. */ + cnt = tape->merge_stage_size / tape->tape_block_size; + if (test_and_clear_bit(IDETAPE_FILEMARK, &tape->flags)) + ++cnt; /* Filemarks count as 1 sector */ + tape->merge_stage_size = 0; + if (tape->merge_stage != NULL) { + __idetape_kfree_stage(tape->merge_stage); + tape->merge_stage = NULL; + } + + /* Clear pipeline flags. */ + clear_bit(IDETAPE_PIPELINE_ERROR, &tape->flags); + tape->chrdev_direction = idetape_direction_none; + + /* Remove pipeline stages. */ + 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); + + while (tape->first_stage != NULL) { + struct request *rq_ptr = &tape->first_stage->rq; + + cnt += rq_ptr->nr_sectors - rq_ptr->current_nr_sectors; + if (rq_ptr->errors == IDETAPE_ERROR_FILEMARK) + ++cnt; + 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, u8 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); + 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; + } + } +} + +/* + * 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 idetape_bh *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 */ + + idetape_init_rq(&rq, cmd); + rq.rq_disk = tape->disk; + rq.special = (void *)bh; + rq.sector = tape->first_frame_position; + rq.nr_sectors = rq.current_nr_sectors = blocks; + (void) ide_do_drive_cmd(drive, &rq, ide_wait); + + if ((cmd & (REQ_IDETAPE_READ | REQ_IDETAPE_WRITE)) == 0) + 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)); +} + +/* + * 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)) { + 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_init_pc(pc); + pc->c[0] = IDETAPE_REWIND_CMD; + set_bit(PC_WAIT_FOR_DSC, &pc->flags); + pc->callback = &idetape_pc_callback; +} + +#if 0 +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; +} +#endif + +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, u8 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; +} + +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, REQ_IDETAPE_WRITE, blocks, tape->merge_stage->bh); + } + } + rq = &new_stage->rq; + idetape_init_rq(rq, REQ_IDETAPE_WRITE); + /* Doesn't actually matter - We always assume sequential access */ + rq->sector = tape->first_frame_position; + rq->nr_sectors = rq->current_nr_sectors = blocks; + + idetape_switch_buffers(tape, new_stage); + 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. + */ + 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); + } + } + 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, min; + struct idetape_bh *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) { + unsigned int i; + + 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 = min(i, (unsigned int)(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->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; + + /* Initialize read operation */ + if (tape->chrdev_direction != idetape_direction_read) { + 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; + + /* + * Issue a read 0 command to ensure that DSC handshake + * is switched from completion mode to buffer available + * mode. + * No point in issuing this if DSC overlap isn't supported, + * some drives (Seagate STT3401A) will return an error. + */ + if (drive->dsc_overlap) { + bytes_read = idetape_queue_rw_tail(drive, REQ_IDETAPE_READ, 0, tape->merge_stage->bh); + if (bytes_read < 0) { + __idetape_kfree_stage(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); + idetape_init_rq(&rq, REQ_IDETAPE_READ); + 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); + } + } + return 0; +} + +/* + * 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 */ + + /* + * If we are at a filemark, return a read length of 0 + */ + if (test_bit(IDETAPE_FILEMARK, &tape->flags)) + return 0; + + /* + * Wait for the next block to be available at the head + * of the pipeline + */ + idetape_initiate_read(drive, tape->max_stages); + if (tape->first_stage == NULL) { + if (test_bit(IDETAPE_PIPELINE_ERROR, &tape->flags)) + return 0; + return idetape_queue_rw_tail(drive, REQ_IDETAPE_READ, blocks, tape->merge_stage->bh); + } + idetape_wait_first_stage(drive); + 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 (rq_ptr->errors == IDETAPE_ERROR_EOD) + return 0; + else { + idetape_switch_buffers(tape, tape->first_stage); + if (rq_ptr->errors == IDETAPE_ERROR_FILEMARK) + set_bit(IDETAPE_FILEMARK, &tape->flags); + spin_lock_irqsave(&tape->spinlock, flags); + idetape_remove_stage_head(drive); + spin_unlock_irqrestore(&tape->spinlock, flags); + 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 idetape_bh *bh; + int blocks; + + while (bcount) { + unsigned int count; + + bh = tape->merge_stage->bh; + count = min(tape->stage_size, bcount); + bcount -= count; + blocks = count / tape->tape_block_size; + while (count) { + atomic_set(&bh->b_count, min(count, (unsigned int)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, REQ_IDETAPE_WRITE, 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; +#if IDETAPE_DEBUG_LOG + idetape_tape_t *tape = drive->driver_data; + 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; + 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, unsigned int cmd, unsigned long arg) +{ + idetape_tape_t *tape = drive->driver_data; + idetape_config_t config; + void __user *argp = (void __user *)arg; + +#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(&config, argp, 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(argp, &config, sizeof (idetape_config_t))) + return -EFAULT; + break; + default: + return -EIO; + } + 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; + + if (mt_count == 0) + return 0; + if (MTBSF == mt_op || MTBSFM == mt_op) { + if (!tape->capabilities.sprev) + return -EIO; + mt_count = - mt_count; + } + + if (tape->chrdev_direction == idetape_direction_read) { + /* + * We have a read-ahead buffer. Scan it for crossed + * filemarks. + */ + tape->merge_stage_size = 0; + if (test_and_clear_bit(IDETAPE_FILEMARK, &tape->flags)) + ++count; + while (tape->first_stage != NULL) { + if (count == mt_count) { + if (mt_op == MTFSFM) + set_bit(IDETAPE_FILEMARK, &tape->flags); + return 0; + } + spin_lock_irqsave(&tape->spinlock, flags); + if (tape->first_stage == tape->active_stage) { + /* + * We have reached the active stage in the read pipeline. + * There is no point in allowing the drive to continue + * reading any farther, so we stop the pipeline. + * + * This section should be moved to a separate subroutine, + * because a similar function is performed in + * __idetape_discard_read_pipeline(), for example. + */ + tape->next_stage = NULL; + spin_unlock_irqrestore(&tape->spinlock, flags); + idetape_wait_first_stage(drive); + tape->next_stage = tape->first_stage->next; + } else + spin_unlock_irqrestore(&tape->spinlock, flags); + if (tape->first_stage->rq.errors == IDETAPE_ERROR_FILEMARK) + ++count; + idetape_remove_stage_head(drive); + } + idetape_discard_read_pipeline(drive, 0); + } + + /* + * The filemark was not found in our internal pipeline. + * Now we can issue the space command. + */ + switch (mt_op) { + case MTFSF: + case MTBSF: + idetape_create_space_cmd(&pc,mt_count-count,IDETAPE_SPACE_OVER_FILEMARK); + return (idetape_queue_pc_tail(drive, &pc)); + case MTFSFM: + case MTBSFM: + if (!tape->capabilities.sprev) + return (-EIO); + retval = idetape_space_over_filemarks(drive, MTFSF, mt_count-count); + if (retval) return (retval); + count = (MTBSFM == mt_op ? 1 : -1); + return (idetape_space_over_filemarks(drive, MTFSF, count)); + 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 __user *buf, + size_t count, loff_t *ppos) +{ + struct ide_tape_obj *tape = ide_tape_f(file); + ide_drive_t *drive = tape->drive; + ssize_t bytes_read,temp, actually_read = 0, rc; + +#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 = min((unsigned int)(tape->merge_stage_size), (unsigned int)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 = min((unsigned long)count, (unsigned long)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; + } + return actually_read; +} + +static ssize_t idetape_chrdev_write (struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct ide_tape_obj *tape = ide_tape_f(file); + ide_drive_t *drive = tape->drive; + ssize_t retval, actually_written = 0; + + /* The drive is write protected. */ + if (tape->write_prot) + return -EACCES; + +#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 */ + + /* Initialize write operation */ + if (tape->chrdev_direction != idetape_direction_write) { + 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); + + /* + * Issue a write 0 command to ensure that DSC handshake + * is switched from completion mode to buffer available + * mode. + * No point in issuing this if DSC overlap isn't supported, + * some drives (Seagate STT3401A) will return an error. + */ + if (drive->dsc_overlap) { + retval = idetape_queue_rw_tail(drive, REQ_IDETAPE_WRITE, 0, tape->merge_stage->bh); + if (retval < 0) { + __idetape_kfree_stage(tape->merge_stage); + tape->merge_stage = NULL; + tape->chrdev_direction = idetape_direction_none; + return retval; + } + } + } + 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 = min((unsigned int)(tape->stage_size - tape->merge_stage_size), (unsigned int)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_pc_t pc; + + /* Write a filemark */ + idetape_create_write_filemark_cmd(drive, &pc, 1); + if (idetape_queue_pc_tail(drive, &pc)) { + printk(KERN_ERR "ide-tape: Couldn't write a filemark\n"); + return -EIO; + } + 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 + * support 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: + if (tape->write_prot) + return -EACCES; + 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; + 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: + /* + * If door is locked, attempt to unlock before + * attempting to eject. + */ + if (tape->door_locked) { + if (idetape_create_prevent_cmd(drive, &pc, 0)) + if (!idetape_queue_pc_tail(drive, &pc)) + tape->door_locked = DOOR_UNLOCKED; + } + idetape_discard_read_pipeline(drive, 0); + idetape_create_load_unload_cmd(drive, &pc,!IDETAPE_LU_LOAD_MASK); + retval = idetape_queue_pc_tail(drive, &pc); + if (!retval) + clear_bit(IDETAPE_MEDIUM_PRESENT, &tape->flags); + return retval; + 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: + idetape_create_space_cmd(&pc, 0, IDETAPE_SPACE_TO_EOD); + return (idetape_queue_pc_tail(drive, &pc)); + case MTERASE: + (void) idetape_rewind_tape(drive); + idetape_create_erase_cmd(&pc); + return (idetape_queue_pc_tail(drive, &pc)); + case MTSETBLK: + 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: + idetape_discard_read_pipeline(drive, 0); + return idetape_position_tape(drive, mt_count * tape->user_bs_factor, tape->partition, 0); + case MTSETPART: + idetape_discard_read_pipeline(drive, 0); + return (idetape_position_tape(drive, 0, mt_count, 0)); + case MTFSR: + case MTBSR: + case MTLOCK: + if (!idetape_create_prevent_cmd(drive, &pc, 1)) + return 0; + retval = idetape_queue_pc_tail(drive, &pc); + if (retval) return retval; + tape->door_locked = DOOR_EXPLICITLY_LOCKED; + return 0; + case MTUNLOCK: + if (!idetape_create_prevent_cmd(drive, &pc, 0)) + return 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) +{ + struct ide_tape_obj *tape = ide_tape_f(file); + ide_drive_t *drive = tape->drive; + struct mtop mtop; + struct mtget mtget; + struct mtpos mtpos; + int block_offset = 0, position = tape->first_frame_position; + void __user *argp = (void __user *)arg; + +#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(&mtop, argp, 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; + mtget.mt_blkno = position / tape->user_bs_factor - block_offset; + mtget.mt_dsreg = ((tape->tape_block_size * tape->user_bs_factor) << MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK; + if (tape->drv_write_prot) { + mtget.mt_gstat |= GMT_WR_PROT(0xffffffff); + } + if (copy_to_user(argp, &mtget, sizeof(struct mtget))) + return -EFAULT; + return 0; + case MTIOCPOS: + mtpos.mt_blkno = position / tape->user_bs_factor - block_offset; + if (copy_to_user(argp, &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, cmd, arg); + } +} + +static void idetape_get_blocksize_from_block_descriptor(ide_drive_t *drive); + +/* + * Our character device open function. + */ +static int idetape_chrdev_open (struct inode *inode, struct file *filp) +{ + unsigned int minor = iminor(inode), i = minor & ~0xc0; + ide_drive_t *drive; + idetape_tape_t *tape; + idetape_pc_t pc; + int retval; + + /* + * We really want to do nonseekable_open(inode, filp); here, but some + * versions of tar incorrectly call lseek on tapes and bail out if that + * fails. So we disallow pread() and pwrite(), but permit lseeks. + */ + filp->f_mode &= ~(FMODE_PREAD | FMODE_PWRITE); + +#if IDETAPE_DEBUG_LOG + printk(KERN_INFO "ide-tape: Reached idetape_chrdev_open\n"); +#endif /* IDETAPE_DEBUG_LOG */ + + if (i >= MAX_HWIFS * MAX_DRIVES) + return -ENXIO; + + if (!(tape = ide_tape_chrdev_get(i))) + return -ENXIO; + + drive = tape->drive; + + filp->private_data = tape; + + if (test_and_set_bit(IDETAPE_BUSY, &tape->flags)) { + retval = -EBUSY; + goto out_put_tape; + } + + retval = idetape_wait_ready(drive, 60 * HZ); + if (retval) { + clear_bit(IDETAPE_BUSY, &tape->flags); + printk(KERN_ERR "ide-tape: %s: drive not ready\n", tape->name); + goto out_put_tape; + } + + idetape_read_position(drive); + if (!test_bit(IDETAPE_ADDRESS_VALID, &tape->flags)) + (void)idetape_rewind_tape(drive); + + if (tape->chrdev_direction != idetape_direction_read) + clear_bit(IDETAPE_PIPELINE_ERROR, &tape->flags); + + /* Read block size and write protect status from drive. */ + idetape_get_blocksize_from_block_descriptor(drive); + + /* Set write protect flag if device is opened as read-only. */ + if ((filp->f_flags & O_ACCMODE) == O_RDONLY) + tape->write_prot = 1; + else + tape->write_prot = tape->drv_write_prot; + + /* Make sure drive isn't write protected if user wants to write. */ + if (tape->write_prot) { + if ((filp->f_flags & O_ACCMODE) == O_WRONLY || + (filp->f_flags & O_ACCMODE) == O_RDWR) { + clear_bit(IDETAPE_BUSY, &tape->flags); + retval = -EROFS; + goto out_put_tape; + } + } + + /* + * Lock the tape drive door so user can't eject. + */ + if (tape->chrdev_direction == idetape_direction_none) { + if (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_restart_speed_control(drive); + tape->restart_speed_control_req = 0; + return 0; + +out_put_tape: + ide_tape_put(tape); + return retval; +} + +static void idetape_write_release (ide_drive_t *drive, unsigned int minor) +{ + idetape_tape_t *tape = drive->driver_data; + + 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_flush_tape_buffers(drive); + idetape_flush_tape_buffers(drive); +} + +/* + * Our character device release function. + */ +static int idetape_chrdev_release (struct inode *inode, struct file *filp) +{ + struct ide_tape_obj *tape = ide_tape_f(filp); + ide_drive_t *drive = tape->drive; + idetape_pc_t pc; + unsigned int minor = iminor(inode); + + lock_kernel(); + tape = drive->driver_data; +#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_write_release(drive, minor); + 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 && test_bit(IDETAPE_MEDIUM_PRESENT, &tape->flags)) + (void) idetape_rewind_tape(drive); + if (tape->chrdev_direction == idetape_direction_none) { + if (tape->door_locked == DOOR_LOCKED) { + if (idetape_create_prevent_cmd(drive, &pc, 0)) { + if (!idetape_queue_pc_tail(drive, &pc)) + tape->door_locked = DOOR_UNLOCKED; + } + } + } + clear_bit(IDETAPE_BUSY, &tape->flags); + ide_tape_put(tape); + unlock_kernel(); + 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 idetape_id_gcw gcw; + struct hd_driveid *id = drive->id; +#if IDETAPE_DEBUG_INFO + unsigned short mask,i; +#endif /* IDETAPE_DEBUG_INFO */ + + if (drive->id_read == 0) + return 1; + + *((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("ATA\n");break; + case 2: printk("ATAPI\n");break; + case 3: printk("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("Direct-access Device\n");break; + case 1: printk("Streaming Tape Device\n");break; + case 2: case 3: case 4: printk("Reserved\n");break; + case 5: printk("CD-ROM Device\n");break; + case 6: printk("Reserved\n"); + case 7: printk("Optical memory Device\n");break; + case 0x1f: printk("Unknown or no Device type\n");break; + default: printk("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("Microprocessor DRQ\n");break; + case 1: printk("Interrupt DRQ\n");break; + case 2: printk("Accelerated DRQ\n");break; + case 3: printk("Reserved\n");break; + } + printk(KERN_INFO "ide-tape: Command Packet Size: "); + switch (gcw.packet_size) { + case 0: printk("12 bytes\n");break; + case 1: printk("16 bytes\n");break; + default: printk("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("%d ",i); + if (id->dma_1word & (mask << 8)) + printk("(active) "); + } + printk("\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("%d ",i); + if (id->dma_mword & (mask << 8)) + printk("(active) "); + } + printk("\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("Not supported\n"); + else + printk("%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("Not supported\n"); + else + printk("%d ns\n",id->eide_dma_time); + + printk(KERN_INFO "ide-tape: Minimum PIO cycle without IORDY: "); + if (id->eide_pio == 0) + printk("Not supported\n"); + else + printk("%d ns\n",id->eide_pio); + + printk(KERN_INFO "ide-tape: Minimum PIO cycle with IORDY: "); + if (id->eide_pio_iordy == 0) + printk("Not supported\n"); + else + printk("%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; +} + +/* + * 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'; + 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); +} + +/* + * 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; + +#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 */ +} + +/* + * ide_get_blocksize_from_block_descriptor does a mode sense page 0 with block descriptor + * and if it succeeds sets the tape block size with the reported value + */ +static void idetape_get_blocksize_from_block_descriptor(ide_drive_t *drive) +{ + + idetape_tape_t *tape = drive->driver_data; + idetape_pc_t pc; + idetape_mode_parameter_header_t *header; + idetape_parameter_block_descriptor_t *block_descrp; + + idetape_create_mode_sense_cmd(&pc, IDETAPE_BLOCK_DESCRIPTOR); + if (idetape_queue_pc_tail(drive, &pc)) { + printk(KERN_ERR "ide-tape: Can't get block descriptor\n"); + if (tape->tape_block_size == 0) { + printk(KERN_WARNING "ide-tape: Cannot deal with zero block size, assume 32k\n"); + tape->tape_block_size = 32768; + } + return; + } + header = (idetape_mode_parameter_header_t *) pc.buffer; + block_descrp = (idetape_parameter_block_descriptor_t *) (pc.buffer + sizeof(idetape_mode_parameter_header_t)); + tape->tape_block_size =( block_descrp->length[0]<<16) + (block_descrp->length[1]<<8) + block_descrp->length[2]; + tape->drv_write_prot = (header->dsp & 0x80) >> 7; + +#if IDETAPE_DEBUG_INFO + printk(KERN_INFO "ide-tape: Adjusted block size - %d\n", tape->tape_block_size); +#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, 1, 0xffff, tape->stage_size / 1024, 1, &tape->min_pipeline, NULL); + ide_add_setting(drive, "pipeline", SETTING_RW, -1, -1, TYPE_INT, 1, 0xffff, tape->stage_size / 1024, 1, &tape->max_stages, NULL); + ide_add_setting(drive, "pipeline_max", SETTING_RW, -1, -1, TYPE_INT, 1, 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); + ide_add_setting(drive, "debug_level",SETTING_RW, -1, -1, TYPE_INT, 0, 0xffff, 1, 1, &tape->debug_level, 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; + struct sysinfo si; + + spin_lock_init(&tape->spinlock); + drive->dsc_overlap = 1; +#ifdef CONFIG_BLK_DEV_IDEPCI + if (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 */ + /* Seagate Travan drives do not support DSC overlap. */ + if (strstr(drive->id->model, "Seagate STT3401")) + drive->dsc_overlap = 0; + 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); + idetape_get_blocksize_from_block_descriptor(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; + 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 = max(tape->capabilities.speed, tape->capabilities.max_speed); + + tape->max_stages = speed * 1000 * 10 / tape->stage_size; + + /* + * Limit memory use for pipeline to 10% of physical memory + */ + si_meminfo(&si); + if (tape->max_stages * tape->stage_size > si.totalram * si.mem_unit / 10) + tape->max_stages = si.totalram * si.mem_unit / (10 * tape->stage_size); + tape->max_stages = min(tape->max_stages, IDETAPE_MAX_PIPELINE_STAGES); + tape->min_pipeline = min(tape->max_stages, IDETAPE_MIN_PIPELINE_STAGES); + tape->max_pipeline = min(tape->max_stages * 2, IDETAPE_MAX_PIPELINE_STAGES); + if (tape->max_stages == 0) + tape->max_stages = tape->min_pipeline = tape->max_pipeline = 1; + + 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 = max_t(unsigned long, min_t(unsigned long, 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; + unsigned long flags; + + spin_lock_irqsave(&ide_lock, flags); + if (test_bit(IDETAPE_BUSY, &tape->flags) || drive->usage || + tape->first_stage != NULL || tape->merge_stage_size) { + spin_unlock_irqrestore(&ide_lock, flags); + return 1; + } + + spin_unlock_irqrestore(&ide_lock, flags); + DRIVER(drive)->busy = 0; + (void) ide_unregister_subdriver(drive); + + ide_unregister_region(tape->disk); + + ide_tape_put(tape); + + return 0; +} + +static void ide_tape_release(struct kref *kref) +{ + struct ide_tape_obj *tape = to_ide_tape(kref); + ide_drive_t *drive = tape->drive; + struct gendisk *g = tape->disk; + + drive->dsc_overlap = 0; + drive->driver_data = NULL; + devfs_remove("%s/mt", drive->devfs_name); + devfs_remove("%s/mtn", drive->devfs_name); + devfs_unregister_tape(g->number); + idetape_devs[tape->minor] = NULL; + g->private_data = NULL; + put_disk(g); + kfree(tape); +} + +#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[] = { + { "capacity", S_IFREG|S_IRUGO, proc_ide_read_capacity, NULL }, + { "name", S_IFREG|S_IRUGO, proc_idetape_read_name, NULL }, + { NULL, 0, NULL, NULL } +}; + +#else + +#define idetape_proc NULL + +#endif + +static int idetape_attach(ide_drive_t *drive); + +/* + * IDE subdriver functions, registered with ide.c + */ +static ide_driver_t idetape_driver = { + .owner = THIS_MODULE, + .name = "ide-tape", + .version = IDETAPE_VERSION, + .media = ide_tape, + .busy = 1, + .supports_dsc_overlap = 1, + .cleanup = idetape_cleanup, + .do_request = idetape_do_request, + .end_request = idetape_end_request, + .error = __ide_error, + .abort = __ide_abort, + .proc = idetape_proc, + .attach = idetape_attach, + .drives = LIST_HEAD_INIT(idetape_driver.drives), +}; + +/* + * Our character device supporting functions, passed to register_chrdev. + */ +static struct file_operations idetape_fops = { + .owner = THIS_MODULE, + .read = idetape_chrdev_read, + .write = idetape_chrdev_write, + .ioctl = idetape_chrdev_ioctl, + .open = idetape_chrdev_open, + .release = idetape_chrdev_release, +}; + +static int idetape_open(struct inode *inode, struct file *filp) +{ + struct gendisk *disk = inode->i_bdev->bd_disk; + struct ide_tape_obj *tape; + ide_drive_t *drive; + + if (!(tape = ide_tape_get(disk))) + return -ENXIO; + + drive = tape->drive; + + drive->usage++; + + return 0; +} + +static int idetape_release(struct inode *inode, struct file *filp) +{ + struct gendisk *disk = inode->i_bdev->bd_disk; + struct ide_tape_obj *tape = ide_tape_g(disk); + ide_drive_t *drive = tape->drive; + + drive->usage--; + + ide_tape_put(tape); + + return 0; +} + +static int idetape_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct block_device *bdev = inode->i_bdev; + struct ide_tape_obj *tape = ide_tape_g(bdev->bd_disk); + ide_drive_t *drive = tape->drive; + int err = generic_ide_ioctl(drive, file, bdev, cmd, arg); + if (err == -EINVAL) + err = idetape_blkdev_ioctl(drive, cmd, arg); + return err; +} + +static struct block_device_operations idetape_block_ops = { + .owner = THIS_MODULE, + .open = idetape_open, + .release = idetape_release, + .ioctl = idetape_ioctl, +}; + +static int idetape_attach (ide_drive_t *drive) +{ + idetape_tape_t *tape; + struct gendisk *g; + int minor; + + if (!strstr("ide-tape", drive->driver_req)) + goto failed; + if (!drive->present) + goto failed; + if (drive->media != ide_tape) + goto failed; + if (!idetape_identify_device (drive)) { + printk(KERN_ERR "ide-tape: %s: not supported by this version of ide-tape\n", drive->name); + goto failed; + } + if (drive->scsi) { + printk("ide-tape: passing drive %s to ide-scsi emulation.\n", drive->name); + goto failed; + } + if (strstr(drive->id->model, "OnStream DI-")) { + printk(KERN_WARNING "ide-tape: Use drive %s with ide-scsi emulation and osst.\n", drive->name); + printk(KERN_WARNING "ide-tape: OnStream support will be removed soon from ide-tape!\n"); + } + 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); + goto failed; + } + + g = alloc_disk(1 << PARTN_BITS); + if (!g) + goto out_free_tape; + + ide_init_disk(g, drive); + + if (ide_register_subdriver(drive, &idetape_driver)) { + printk(KERN_ERR "ide-tape: %s: Failed to register the driver with ide.c\n", drive->name); + goto out_put_disk; + } + + memset(tape, 0, sizeof(*tape)); + + kref_init(&tape->kref); + + tape->drive = drive; + tape->driver = &idetape_driver; + tape->disk = g; + + g->private_data = &tape->driver; + + drive->driver_data = tape; + + down(&idetape_ref_sem); + for (minor = 0; idetape_devs[minor]; minor++) + ; + idetape_devs[minor] = tape; + up(&idetape_ref_sem); + + idetape_setup(drive, tape, minor); + + devfs_mk_cdev(MKDEV(HWIF(drive)->major, minor), + S_IFCHR | S_IRUGO | S_IWUGO, + "%s/mt", drive->devfs_name); + devfs_mk_cdev(MKDEV(HWIF(drive)->major, minor + 128), + S_IFCHR | S_IRUGO | S_IWUGO, + "%s/mtn", drive->devfs_name); + + g->number = devfs_register_tape(drive->devfs_name); + g->fops = &idetape_block_ops; + ide_register_region(g); + + return 0; +out_put_disk: + put_disk(g); +out_free_tape: + kfree(tape); +failed: + return 1; +} + +MODULE_DESCRIPTION("ATAPI Streaming TAPE Driver"); +MODULE_LICENSE("GPL"); + +static void __exit idetape_exit (void) +{ + ide_unregister_driver(&idetape_driver); + unregister_chrdev(IDETAPE_MAJOR, "ht"); +} + +/* + * idetape_init will register the driver for each tape. + */ +static int idetape_init (void) +{ + if (register_chrdev(IDETAPE_MAJOR, "ht", &idetape_fops)) { + printk(KERN_ERR "ide-tape: Failed to register character device interface\n"); + return -EBUSY; + } + ide_register_driver(&idetape_driver); + return 0; +} + +module_init(idetape_init); +module_exit(idetape_exit); +MODULE_ALIAS_CHARDEV_MAJOR(IDETAPE_MAJOR); diff --git a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c new file mode 100644 index 0000000..d04f62a --- /dev/null +++ b/drivers/ide/ide-taskfile.c @@ -0,0 +1,884 @@ +/* + * linux/drivers/ide/ide-taskfile.c Version 0.38 March 05, 2003 + * + * Copyright (C) 2000-2002 Michael Cornwell <cornwell@acm.org> + * Copyright (C) 2000-2002 Andre Hedrick <andre@linux-ide.org> + * Copyright (C) 2001-2002 Klaus Smolin + * IBM Storage Technology Division + * Copyright (C) 2003-2004 Bartlomiej Zolnierkiewicz + * + * The big the bad and the ugly. + * + * Problems to be fixed because of BH interface or the lack therefore. + * + * Fill me in stupid !!! + * + * HOST: + * General refers to the Controller and Driver "pair". + * DATA HANDLER: + * Under the context of Linux it generally refers to an interrupt handler. + * However, it correctly describes the 'HOST' + * DATA BLOCK: + * The amount of data needed to be transfered as predefined in the + * setup of the device. + * STORAGE ATOMIC: + * The 'DATA BLOCK' associated to the 'DATA HANDLER', and can be as + * small as a single sector or as large as the entire command block + * request. + */ + +#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/slab.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/hdreg.h> +#include <linux/ide.h> +#include <linux/bitops.h> + +#include <asm/byteorder.h> +#include <asm/irq.h> +#include <asm/uaccess.h> +#include <asm/io.h> + +#define DEBUG_TASKFILE 0 /* unset when fixed */ + +static void ata_bswap_data (void *buffer, int wcount) +{ + u16 *p = buffer; + + while (wcount--) { + *p = *p << 8 | *p >> 8; p++; + *p = *p << 8 | *p >> 8; p++; + } +} + +static void taskfile_input_data(ide_drive_t *drive, void *buffer, u32 wcount) +{ + HWIF(drive)->ata_input_data(drive, buffer, wcount); + if (drive->bswap) + ata_bswap_data(buffer, wcount); +} + +static void taskfile_output_data(ide_drive_t *drive, void *buffer, u32 wcount) +{ + if (drive->bswap) { + ata_bswap_data(buffer, wcount); + HWIF(drive)->ata_output_data(drive, buffer, wcount); + ata_bswap_data(buffer, wcount); + } else { + HWIF(drive)->ata_output_data(drive, buffer, wcount); + } +} + +int taskfile_lib_get_identify (ide_drive_t *drive, u8 *buf) +{ + ide_task_t args; + memset(&args, 0, sizeof(ide_task_t)); + args.tfRegister[IDE_NSECTOR_OFFSET] = 0x01; + if (drive->media == ide_disk) + args.tfRegister[IDE_COMMAND_OFFSET] = WIN_IDENTIFY; + else + args.tfRegister[IDE_COMMAND_OFFSET] = WIN_PIDENTIFY; + args.command_type = IDE_DRIVE_TASK_IN; + args.data_phase = TASKFILE_IN; + args.handler = &task_in_intr; + return ide_raw_taskfile(drive, &args, buf); +} + +ide_startstop_t do_rw_taskfile (ide_drive_t *drive, ide_task_t *task) +{ + ide_hwif_t *hwif = HWIF(drive); + task_struct_t *taskfile = (task_struct_t *) task->tfRegister; + hob_struct_t *hobfile = (hob_struct_t *) task->hobRegister; + u8 HIHI = (drive->addressing == 1) ? 0xE0 : 0xEF; + + /* ALL Command Block Executions SHALL clear nIEN, unless otherwise */ + if (IDE_CONTROL_REG) { + /* clear nIEN */ + hwif->OUTB(drive->ctl, IDE_CONTROL_REG); + } + SELECT_MASK(drive, 0); + + if (drive->addressing == 1) { + hwif->OUTB(hobfile->feature, IDE_FEATURE_REG); + hwif->OUTB(hobfile->sector_count, IDE_NSECTOR_REG); + hwif->OUTB(hobfile->sector_number, IDE_SECTOR_REG); + hwif->OUTB(hobfile->low_cylinder, IDE_LCYL_REG); + hwif->OUTB(hobfile->high_cylinder, IDE_HCYL_REG); + } + + hwif->OUTB(taskfile->feature, IDE_FEATURE_REG); + hwif->OUTB(taskfile->sector_count, IDE_NSECTOR_REG); + hwif->OUTB(taskfile->sector_number, IDE_SECTOR_REG); + hwif->OUTB(taskfile->low_cylinder, IDE_LCYL_REG); + hwif->OUTB(taskfile->high_cylinder, IDE_HCYL_REG); + + hwif->OUTB((taskfile->device_head & HIHI) | drive->select.all, IDE_SELECT_REG); + + if (task->handler != NULL) { + if (task->prehandler != NULL) { + hwif->OUTBSYNC(drive, taskfile->command, IDE_COMMAND_REG); + ndelay(400); /* FIXME */ + return task->prehandler(drive, task->rq); + } + ide_execute_command(drive, taskfile->command, task->handler, WAIT_WORSTCASE, NULL); + return ide_started; + } + + if (!drive->using_dma) + return ide_stopped; + + switch (taskfile->command) { + case WIN_WRITEDMA_ONCE: + case WIN_WRITEDMA: + case WIN_WRITEDMA_EXT: + case WIN_READDMA_ONCE: + case WIN_READDMA: + case WIN_READDMA_EXT: + case WIN_IDENTIFY_DMA: + if (!hwif->dma_setup(drive)) { + hwif->dma_exec_cmd(drive, taskfile->command); + hwif->dma_start(drive); + return ide_started; + } + break; + default: + if (task->handler == NULL) + return ide_stopped; + } + + return ide_stopped; +} + +EXPORT_SYMBOL(do_rw_taskfile); + +/* + * set_multmode_intr() is invoked on completion of a WIN_SETMULT cmd. + */ +ide_startstop_t set_multmode_intr (ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + u8 stat; + + if (OK_STAT(stat = hwif->INB(IDE_STATUS_REG),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. + */ +ide_startstop_t set_geometry_intr (ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + int retries = 5; + u8 stat; + + while (((stat = hwif->INB(IDE_STATUS_REG)) & BUSY_STAT) && retries--) + udelay(10); + + if (OK_STAT(stat, READY_STAT, BAD_STAT)) + return ide_stopped; + + if (stat & (ERR_STAT|DRQ_STAT)) + return ide_error(drive, "set_geometry_intr", stat); + + if (HWGROUP(drive)->handler != NULL) + BUG(); + ide_set_handler(drive, &set_geometry_intr, WAIT_WORSTCASE, NULL); + return ide_started; +} + +/* + * recal_intr() is invoked on completion of a WIN_RESTORE (recalibrate) cmd. + */ +ide_startstop_t recal_intr (ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + u8 stat; + + if (!OK_STAT(stat = hwif->INB(IDE_STATUS_REG), READY_STAT, BAD_STAT)) + return ide_error(drive, "recal_intr", stat); + return ide_stopped; +} + +/* + * Handler for commands without a data phase + */ +ide_startstop_t task_no_data_intr (ide_drive_t *drive) +{ + ide_task_t *args = HWGROUP(drive)->rq->special; + ide_hwif_t *hwif = HWIF(drive); + u8 stat; + + local_irq_enable(); + if (!OK_STAT(stat = hwif->INB(IDE_STATUS_REG),READY_STAT,BAD_STAT)) { + return ide_error(drive, "task_no_data_intr", stat); + /* calls ide_end_drive_cmd */ + } + if (args) + ide_end_drive_cmd(drive, stat, hwif->INB(IDE_ERROR_REG)); + + return ide_stopped; +} + +EXPORT_SYMBOL(task_no_data_intr); + +static u8 wait_drive_not_busy(ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + int retries = 100; + u8 stat; + + /* + * Last sector was transfered, wait until drive is ready. + * This can take up to 10 usec, but we will wait max 1 ms + * (drive_cmd_intr() waits that long). + */ + while (((stat = hwif->INB(IDE_STATUS_REG)) & BUSY_STAT) && retries--) + udelay(10); + + if (!retries) + printk(KERN_ERR "%s: drive still BUSY!\n", drive->name); + + return stat; +} + +static void ide_pio_sector(ide_drive_t *drive, unsigned int write) +{ + ide_hwif_t *hwif = drive->hwif; + struct scatterlist *sg = hwif->sg_table; + struct page *page; +#ifdef CONFIG_HIGHMEM + unsigned long flags; +#endif + unsigned int offset; + u8 *buf; + + page = sg[hwif->cursg].page; + offset = sg[hwif->cursg].offset + hwif->cursg_ofs * SECTOR_SIZE; + + /* get the current page and offset */ + page = nth_page(page, (offset >> PAGE_SHIFT)); + offset %= PAGE_SIZE; + +#ifdef CONFIG_HIGHMEM + local_irq_save(flags); +#endif + buf = kmap_atomic(page, KM_BIO_SRC_IRQ) + offset; + + hwif->nleft--; + hwif->cursg_ofs++; + + if ((hwif->cursg_ofs * SECTOR_SIZE) == sg[hwif->cursg].length) { + hwif->cursg++; + hwif->cursg_ofs = 0; + } + + /* do the actual data transfer */ + if (write) + taskfile_output_data(drive, buf, SECTOR_WORDS); + else + taskfile_input_data(drive, buf, SECTOR_WORDS); + + kunmap_atomic(buf, KM_BIO_SRC_IRQ); +#ifdef CONFIG_HIGHMEM + local_irq_restore(flags); +#endif +} + +static void ide_pio_multi(ide_drive_t *drive, unsigned int write) +{ + unsigned int nsect; + + nsect = min_t(unsigned int, drive->hwif->nleft, drive->mult_count); + while (nsect--) + ide_pio_sector(drive, write); +} + +static inline void ide_pio_datablock(ide_drive_t *drive, struct request *rq, + unsigned int write) +{ + if (rq->bio) /* fs request */ + rq->errors = 0; + + switch (drive->hwif->data_phase) { + case TASKFILE_MULTI_IN: + case TASKFILE_MULTI_OUT: + ide_pio_multi(drive, write); + break; + default: + ide_pio_sector(drive, write); + break; + } +} + +static ide_startstop_t task_error(ide_drive_t *drive, struct request *rq, + const char *s, u8 stat) +{ + if (rq->bio) { + ide_hwif_t *hwif = drive->hwif; + int sectors = hwif->nsect - hwif->nleft; + + switch (hwif->data_phase) { + case TASKFILE_IN: + if (hwif->nleft) + break; + /* fall through */ + case TASKFILE_OUT: + sectors--; + break; + case TASKFILE_MULTI_IN: + if (hwif->nleft) + break; + /* fall through */ + case TASKFILE_MULTI_OUT: + sectors -= drive->mult_count; + default: + break; + } + + if (sectors > 0) { + ide_driver_t *drv; + + drv = *(ide_driver_t **)rq->rq_disk->private_data; + drv->end_request(drive, 1, sectors); + } + } + return ide_error(drive, s, stat); +} + +static void task_end_request(ide_drive_t *drive, struct request *rq, u8 stat) +{ + if (rq->flags & REQ_DRIVE_TASKFILE) { + ide_task_t *task = rq->special; + + if (task->tf_out_flags.all) { + u8 err = drive->hwif->INB(IDE_ERROR_REG); + ide_end_drive_cmd(drive, stat, err); + return; + } + } + + ide_end_request(drive, 1, rq->hard_nr_sectors); +} + +/* + * Handler for command with PIO data-in phase (Read/Read Multiple). + */ +ide_startstop_t task_in_intr (ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + struct request *rq = HWGROUP(drive)->rq; + u8 stat = hwif->INB(IDE_STATUS_REG); + + /* new way for dealing with premature shared PCI interrupts */ + if (!OK_STAT(stat, DATA_READY, BAD_R_STAT)) { + if (stat & (ERR_STAT | DRQ_STAT)) + return task_error(drive, rq, __FUNCTION__, stat); + /* No data yet, so wait for another IRQ. */ + ide_set_handler(drive, &task_in_intr, WAIT_WORSTCASE, NULL); + return ide_started; + } + + ide_pio_datablock(drive, rq, 0); + + /* If it was the last datablock check status and finish transfer. */ + if (!hwif->nleft) { + stat = wait_drive_not_busy(drive); + if (!OK_STAT(stat, 0, BAD_R_STAT)) + return task_error(drive, rq, __FUNCTION__, stat); + task_end_request(drive, rq, stat); + return ide_stopped; + } + + /* Still data left to transfer. */ + ide_set_handler(drive, &task_in_intr, WAIT_WORSTCASE, NULL); + + return ide_started; +} +EXPORT_SYMBOL(task_in_intr); + +/* + * Handler for command with PIO data-out phase (Write/Write Multiple). + */ +static ide_startstop_t task_out_intr (ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + struct request *rq = HWGROUP(drive)->rq; + u8 stat = hwif->INB(IDE_STATUS_REG); + + if (!OK_STAT(stat, DRIVE_READY, drive->bad_wstat)) + return task_error(drive, rq, __FUNCTION__, stat); + + /* Deal with unexpected ATA data phase. */ + if (((stat & DRQ_STAT) == 0) ^ !hwif->nleft) + return task_error(drive, rq, __FUNCTION__, stat); + + if (!hwif->nleft) { + task_end_request(drive, rq, stat); + return ide_stopped; + } + + /* Still data left to transfer. */ + ide_pio_datablock(drive, rq, 1); + ide_set_handler(drive, &task_out_intr, WAIT_WORSTCASE, NULL); + + return ide_started; +} + +ide_startstop_t pre_task_out_intr (ide_drive_t *drive, struct request *rq) +{ + ide_startstop_t startstop; + + if (ide_wait_stat(&startstop, drive, DATA_READY, + drive->bad_wstat, WAIT_DRQ)) { + printk(KERN_ERR "%s: no DRQ after issuing %sWRITE%s\n", + drive->name, + drive->hwif->data_phase ? "MULT" : "", + drive->addressing ? "_EXT" : ""); + return startstop; + } + + if (!drive->unmask) + local_irq_disable(); + + ide_set_handler(drive, &task_out_intr, WAIT_WORSTCASE, NULL); + ide_pio_datablock(drive, rq, 1); + + return ide_started; +} +EXPORT_SYMBOL(pre_task_out_intr); + +static int ide_diag_taskfile(ide_drive_t *drive, ide_task_t *args, unsigned long data_size, u8 *buf) +{ + struct request rq; + + memset(&rq, 0, sizeof(rq)); + rq.flags = REQ_DRIVE_TASKFILE; + rq.buffer = buf; + + /* + * (ks) We transfer currently only whole sectors. + * This is suffient for now. But, it would be great, + * if we would find a solution to transfer any size. + * To support special commands like READ LONG. + */ + if (args->command_type != IDE_DRIVE_TASK_NO_DATA) { + if (data_size == 0) + rq.nr_sectors = (args->hobRegister[IDE_NSECTOR_OFFSET] << 8) | args->tfRegister[IDE_NSECTOR_OFFSET]; + else + rq.nr_sectors = data_size / SECTOR_SIZE; + + if (!rq.nr_sectors) { + printk(KERN_ERR "%s: in/out command without data\n", + drive->name); + return -EFAULT; + } + + rq.hard_nr_sectors = rq.nr_sectors; + rq.hard_cur_sectors = rq.current_nr_sectors = rq.nr_sectors; + + if (args->command_type == IDE_DRIVE_TASK_RAW_WRITE) + rq.flags |= REQ_RW; + } + + rq.special = args; + return ide_do_drive_cmd(drive, &rq, ide_wait); +} + +int ide_raw_taskfile (ide_drive_t *drive, ide_task_t *args, u8 *buf) +{ + return ide_diag_taskfile(drive, args, 0, buf); +} + +EXPORT_SYMBOL(ide_raw_taskfile); + +int ide_taskfile_ioctl (ide_drive_t *drive, unsigned int cmd, unsigned long arg) +{ + ide_task_request_t *req_task; + ide_task_t args; + u8 *outbuf = NULL; + u8 *inbuf = NULL; + task_ioreg_t *argsptr = args.tfRegister; + task_ioreg_t *hobsptr = args.hobRegister; + int err = 0; + int tasksize = sizeof(struct ide_task_request_s); + int taskin = 0; + int taskout = 0; + u8 io_32bit = drive->io_32bit; + char __user *buf = (char __user *)arg; + +// printk("IDE Taskfile ...\n"); + + req_task = kmalloc(tasksize, GFP_KERNEL); + if (req_task == NULL) return -ENOMEM; + memset(req_task, 0, tasksize); + if (copy_from_user(req_task, buf, tasksize)) { + kfree(req_task); + return -EFAULT; + } + + taskout = (int) req_task->out_size; + taskin = (int) req_task->in_size; + + if (taskout) { + int outtotal = tasksize; + outbuf = kmalloc(taskout, GFP_KERNEL); + if (outbuf == NULL) { + err = -ENOMEM; + goto abort; + } + memset(outbuf, 0, taskout); + if (copy_from_user(outbuf, buf + outtotal, taskout)) { + err = -EFAULT; + goto abort; + } + } + + if (taskin) { + int intotal = tasksize + taskout; + inbuf = kmalloc(taskin, GFP_KERNEL); + if (inbuf == NULL) { + err = -ENOMEM; + goto abort; + } + memset(inbuf, 0, taskin); + if (copy_from_user(inbuf, buf + intotal, taskin)) { + err = -EFAULT; + goto abort; + } + } + + memset(&args, 0, sizeof(ide_task_t)); + memcpy(argsptr, req_task->io_ports, HDIO_DRIVE_TASK_HDR_SIZE); + memcpy(hobsptr, req_task->hob_ports, HDIO_DRIVE_HOB_HDR_SIZE); + + args.tf_in_flags = req_task->in_flags; + args.tf_out_flags = req_task->out_flags; + args.data_phase = req_task->data_phase; + args.command_type = req_task->req_cmd; + + drive->io_32bit = 0; + switch(req_task->data_phase) { + case TASKFILE_OUT_DMAQ: + case TASKFILE_OUT_DMA: + err = ide_diag_taskfile(drive, &args, taskout, outbuf); + break; + case TASKFILE_IN_DMAQ: + case TASKFILE_IN_DMA: + err = ide_diag_taskfile(drive, &args, taskin, inbuf); + break; + case TASKFILE_MULTI_OUT: + if (!drive->mult_count) { + /* (hs): give up if multcount is not set */ + printk(KERN_ERR "%s: %s Multimode Write " \ + "multcount is not set\n", + drive->name, __FUNCTION__); + err = -EPERM; + goto abort; + } + /* fall through */ + case TASKFILE_OUT: + args.prehandler = &pre_task_out_intr; + args.handler = &task_out_intr; + err = ide_diag_taskfile(drive, &args, taskout, outbuf); + break; + case TASKFILE_MULTI_IN: + if (!drive->mult_count) { + /* (hs): give up if multcount is not set */ + printk(KERN_ERR "%s: %s Multimode Read failure " \ + "multcount is not set\n", + drive->name, __FUNCTION__); + err = -EPERM; + goto abort; + } + /* fall through */ + case TASKFILE_IN: + args.handler = &task_in_intr; + err = ide_diag_taskfile(drive, &args, taskin, inbuf); + break; + case TASKFILE_NO_DATA: + args.handler = &task_no_data_intr; + err = ide_diag_taskfile(drive, &args, 0, NULL); + break; + default: + err = -EFAULT; + goto abort; + } + + memcpy(req_task->io_ports, &(args.tfRegister), HDIO_DRIVE_TASK_HDR_SIZE); + memcpy(req_task->hob_ports, &(args.hobRegister), HDIO_DRIVE_HOB_HDR_SIZE); + req_task->in_flags = args.tf_in_flags; + req_task->out_flags = args.tf_out_flags; + + if (copy_to_user(buf, req_task, tasksize)) { + err = -EFAULT; + goto abort; + } + if (taskout) { + int outtotal = tasksize; + if (copy_to_user(buf + outtotal, outbuf, taskout)) { + err = -EFAULT; + goto abort; + } + } + if (taskin) { + int intotal = tasksize + taskout; + if (copy_to_user(buf + intotal, inbuf, taskin)) { + err = -EFAULT; + goto abort; + } + } +abort: + kfree(req_task); + if (outbuf != NULL) + kfree(outbuf); + if (inbuf != NULL) + kfree(inbuf); + +// printk("IDE Taskfile ioctl ended. rc = %i\n", err); + + drive->io_32bit = io_32bit; + + return err; +} + +int ide_wait_cmd (ide_drive_t *drive, u8 cmd, u8 nsect, u8 feature, u8 sectors, u8 *buf) +{ + struct request rq; + u8 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); +} + +/* + * FIXME : this needs to map into at taskfile. <andre@linux-ide.org> + */ +int ide_cmd_ioctl (ide_drive_t *drive, unsigned int cmd, unsigned long arg) +{ + int err = 0; + u8 args[4], *argbuf = args; + u8 xfer_rate = 0; + int argsize = 4; + ide_task_t tfargs; + + if (NULL == (void *) arg) { + struct request rq; + ide_init_drive_cmd(&rq); + return ide_do_drive_cmd(drive, &rq, ide_wait); + } + + if (copy_from_user(args, (void __user *)arg, 4)) + return -EFAULT; + + memset(&tfargs, 0, sizeof(ide_task_t)); + tfargs.tfRegister[IDE_FEATURE_OFFSET] = args[2]; + tfargs.tfRegister[IDE_NSECTOR_OFFSET] = args[3]; + tfargs.tfRegister[IDE_SECTOR_OFFSET] = args[1]; + tfargs.tfRegister[IDE_LCYL_OFFSET] = 0x00; + tfargs.tfRegister[IDE_HCYL_OFFSET] = 0x00; + tfargs.tfRegister[IDE_SELECT_OFFSET] = 0x00; + tfargs.tfRegister[IDE_COMMAND_OFFSET] = args[0]; + + 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 (set_transfer(drive, &tfargs)) { + xfer_rate = args[1]; + if (ide_ata66_check(drive, &tfargs)) + goto abort; + } + + err = ide_wait_cmd(drive, args[0], args[1], args[2], args[3], argbuf); + + if (!err && xfer_rate) { + /* active-retuning-calls future */ + ide_set_xfer_rate(drive, xfer_rate); + ide_driveid_update(drive); + } +abort: + if (copy_to_user((void __user *)arg, argbuf, argsize)) + err = -EFAULT; + if (argsize > 4) + kfree(argbuf); + return err; +} + +static int ide_wait_cmd_task(ide_drive_t *drive, u8 *buf) +{ + struct request rq; + + ide_init_drive_cmd(&rq); + rq.flags = REQ_DRIVE_TASK; + rq.buffer = buf; + return ide_do_drive_cmd(drive, &rq, ide_wait); +} + +/* + * FIXME : this needs to map into at taskfile. <andre@linux-ide.org> + */ +int ide_task_ioctl (ide_drive_t *drive, unsigned int cmd, unsigned long arg) +{ + void __user *p = (void __user *)arg; + int err = 0; + u8 args[7], *argbuf = args; + int argsize = 7; + + if (copy_from_user(args, p, 7)) + return -EFAULT; + err = ide_wait_cmd_task(drive, argbuf); + if (copy_to_user(p, argbuf, argsize)) + err = -EFAULT; + return err; +} + +/* + * NOTICE: This is additions from IBM to provide a discrete interface, + * for selective taskregister access operations. Nice JOB Klaus!!! + * Glad to be able to work and co-develop this with you and IBM. + */ +ide_startstop_t flagged_taskfile (ide_drive_t *drive, ide_task_t *task) +{ + ide_hwif_t *hwif = HWIF(drive); + task_struct_t *taskfile = (task_struct_t *) task->tfRegister; + hob_struct_t *hobfile = (hob_struct_t *) task->hobRegister; +#if DEBUG_TASKFILE + u8 status; +#endif + + if (task->data_phase == TASKFILE_MULTI_IN || + task->data_phase == TASKFILE_MULTI_OUT) { + if (!drive->mult_count) { + printk(KERN_ERR "%s: multimode not set!\n", drive->name); + return ide_stopped; + } + } + + /* + * (ks) Check taskfile in/out flags. + * If set, then execute as it is defined. + * If not set, then define default settings. + * The default values are: + * write and read all taskfile registers (except data) + * write and read the hob registers (sector,nsector,lcyl,hcyl) + */ + if (task->tf_out_flags.all == 0) { + task->tf_out_flags.all = IDE_TASKFILE_STD_OUT_FLAGS; + if (drive->addressing == 1) + task->tf_out_flags.all |= (IDE_HOB_STD_OUT_FLAGS << 8); + } + + if (task->tf_in_flags.all == 0) { + task->tf_in_flags.all = IDE_TASKFILE_STD_IN_FLAGS; + if (drive->addressing == 1) + task->tf_in_flags.all |= (IDE_HOB_STD_IN_FLAGS << 8); + } + + /* ALL Command Block Executions SHALL clear nIEN, unless otherwise */ + if (IDE_CONTROL_REG) + /* clear nIEN */ + hwif->OUTB(drive->ctl, IDE_CONTROL_REG); + SELECT_MASK(drive, 0); + +#if DEBUG_TASKFILE + status = hwif->INB(IDE_STATUS_REG); + if (status & 0x80) { + printk("flagged_taskfile -> Bad status. Status = %02x. wait 100 usec ...\n", status); + udelay(100); + status = hwif->INB(IDE_STATUS_REG); + printk("flagged_taskfile -> Status = %02x\n", status); + } +#endif + + if (task->tf_out_flags.b.data) { + u16 data = taskfile->data + (hobfile->data << 8); + hwif->OUTW(data, IDE_DATA_REG); + } + + /* (ks) send hob registers first */ + if (task->tf_out_flags.b.nsector_hob) + hwif->OUTB(hobfile->sector_count, IDE_NSECTOR_REG); + if (task->tf_out_flags.b.sector_hob) + hwif->OUTB(hobfile->sector_number, IDE_SECTOR_REG); + if (task->tf_out_flags.b.lcyl_hob) + hwif->OUTB(hobfile->low_cylinder, IDE_LCYL_REG); + if (task->tf_out_flags.b.hcyl_hob) + hwif->OUTB(hobfile->high_cylinder, IDE_HCYL_REG); + + /* (ks) Send now the standard registers */ + if (task->tf_out_flags.b.error_feature) + hwif->OUTB(taskfile->feature, IDE_FEATURE_REG); + /* refers to number of sectors to transfer */ + if (task->tf_out_flags.b.nsector) + hwif->OUTB(taskfile->sector_count, IDE_NSECTOR_REG); + /* refers to sector offset or start sector */ + if (task->tf_out_flags.b.sector) + hwif->OUTB(taskfile->sector_number, IDE_SECTOR_REG); + if (task->tf_out_flags.b.lcyl) + hwif->OUTB(taskfile->low_cylinder, IDE_LCYL_REG); + if (task->tf_out_flags.b.hcyl) + hwif->OUTB(taskfile->high_cylinder, IDE_HCYL_REG); + + /* + * (ks) In the flagged taskfile approch, we will use all specified + * registers and the register value will not be changed, except the + * select bit (master/slave) in the drive_head register. We must make + * sure that the desired drive is selected. + */ + hwif->OUTB(taskfile->device_head | drive->select.all, IDE_SELECT_REG); + switch(task->data_phase) { + + case TASKFILE_OUT_DMAQ: + case TASKFILE_OUT_DMA: + case TASKFILE_IN_DMAQ: + case TASKFILE_IN_DMA: + hwif->dma_setup(drive); + hwif->dma_exec_cmd(drive, taskfile->command); + hwif->dma_start(drive); + break; + + default: + if (task->handler == NULL) + return ide_stopped; + + /* Issue the command */ + if (task->prehandler) { + hwif->OUTBSYNC(drive, taskfile->command, IDE_COMMAND_REG); + ndelay(400); /* FIXME */ + return task->prehandler(drive, task->rq); + } + ide_execute_command(drive, taskfile->command, task->handler, WAIT_WORSTCASE, NULL); + } + + return ide_started; +} diff --git a/drivers/ide/ide-timing.h b/drivers/ide/ide-timing.h new file mode 100644 index 0000000..c1196ce --- /dev/null +++ b/drivers/ide/ide-timing.h @@ -0,0 +1,281 @@ +#ifndef _IDE_TIMING_H +#define _IDE_TIMING_H + +/* + * $Id: ide-timing.h,v 1.6 2001/12/23 22:47:56 vojtech Exp $ + * + * Copyright (c) 1999-2001 Vojtech Pavlik + */ + +/* + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include <linux/hdreg.h> + +#define XFER_PIO_5 0x0d +#define XFER_UDMA_SLOW 0x4f + +struct ide_timing { + short mode; + short setup; /* t1 */ + short act8b; /* t2 for 8-bit io */ + short rec8b; /* t2i for 8-bit io */ + short cyc8b; /* t0 for 8-bit io */ + short active; /* t2 or tD */ + short recover; /* t2i or tK */ + short cycle; /* t0 */ + short udma; /* t2CYCTYP/2 */ +}; + +/* + * PIO 0-5, MWDMA 0-2 and UDMA 0-6 timings (in nanoseconds). + * These were taken from ATA/ATAPI-6 standard, rev 0a, except + * for PIO 5, which is a nonstandard extension and UDMA6, which + * is currently supported only by Maxtor drives. + */ + +static struct ide_timing ide_timing[] = { + + { XFER_UDMA_6, 0, 0, 0, 0, 0, 0, 0, 15 }, + { XFER_UDMA_5, 0, 0, 0, 0, 0, 0, 0, 20 }, + { XFER_UDMA_4, 0, 0, 0, 0, 0, 0, 0, 30 }, + { XFER_UDMA_3, 0, 0, 0, 0, 0, 0, 0, 45 }, + + { XFER_UDMA_2, 0, 0, 0, 0, 0, 0, 0, 60 }, + { XFER_UDMA_1, 0, 0, 0, 0, 0, 0, 0, 80 }, + { XFER_UDMA_0, 0, 0, 0, 0, 0, 0, 0, 120 }, + + { XFER_UDMA_SLOW, 0, 0, 0, 0, 0, 0, 0, 150 }, + + { XFER_MW_DMA_2, 25, 0, 0, 0, 70, 25, 120, 0 }, + { XFER_MW_DMA_1, 45, 0, 0, 0, 80, 50, 150, 0 }, + { XFER_MW_DMA_0, 60, 0, 0, 0, 215, 215, 480, 0 }, + + { XFER_SW_DMA_2, 60, 0, 0, 0, 120, 120, 240, 0 }, + { XFER_SW_DMA_1, 90, 0, 0, 0, 240, 240, 480, 0 }, + { XFER_SW_DMA_0, 120, 0, 0, 0, 480, 480, 960, 0 }, + + { XFER_PIO_5, 20, 50, 30, 100, 50, 30, 100, 0 }, + { XFER_PIO_4, 25, 70, 25, 120, 70, 25, 120, 0 }, + { XFER_PIO_3, 30, 80, 70, 180, 80, 70, 180, 0 }, + + { XFER_PIO_2, 30, 290, 40, 330, 100, 90, 240, 0 }, + { XFER_PIO_1, 50, 290, 93, 383, 125, 100, 383, 0 }, + { XFER_PIO_0, 70, 290, 240, 600, 165, 150, 600, 0 }, + + { XFER_PIO_SLOW, 120, 290, 240, 960, 290, 240, 960, 0 }, + + { -1 } +}; + +#define IDE_TIMING_SETUP 0x01 +#define IDE_TIMING_ACT8B 0x02 +#define IDE_TIMING_REC8B 0x04 +#define IDE_TIMING_CYC8B 0x08 +#define IDE_TIMING_8BIT 0x0e +#define IDE_TIMING_ACTIVE 0x10 +#define IDE_TIMING_RECOVER 0x20 +#define IDE_TIMING_CYCLE 0x40 +#define IDE_TIMING_UDMA 0x80 +#define IDE_TIMING_ALL 0xff + +#define MIN(a,b) ((a)<(b)?(a):(b)) +#define MAX(a,b) ((a)>(b)?(a):(b)) +#define FIT(v,min,max) MAX(MIN(v,max),min) +#define ENOUGH(v,unit) (((v)-1)/(unit)+1) +#define EZ(v,unit) ((v)?ENOUGH(v,unit):0) + +#define XFER_MODE 0xf0 +#define XFER_UDMA_133 0x48 +#define XFER_UDMA_100 0x44 +#define XFER_UDMA_66 0x42 +#define XFER_UDMA 0x40 +#define XFER_MWDMA 0x20 +#define XFER_SWDMA 0x10 +#define XFER_EPIO 0x01 +#define XFER_PIO 0x00 + +static short ide_find_best_mode(ide_drive_t *drive, int map) +{ + struct hd_driveid *id = drive->id; + short best = 0; + + if (!id) + return XFER_PIO_SLOW; + + if ((map & XFER_UDMA) && (id->field_valid & 4)) { /* Want UDMA and UDMA bitmap valid */ + + if ((map & XFER_UDMA_133) == XFER_UDMA_133) + if ((best = (id->dma_ultra & 0x0040) ? XFER_UDMA_6 : 0)) return best; + + if ((map & XFER_UDMA_100) == XFER_UDMA_100) + if ((best = (id->dma_ultra & 0x0020) ? XFER_UDMA_5 : 0)) return best; + + if ((map & XFER_UDMA_66) == XFER_UDMA_66) + if ((best = (id->dma_ultra & 0x0010) ? XFER_UDMA_4 : + (id->dma_ultra & 0x0008) ? XFER_UDMA_3 : 0)) return best; + + if ((best = (id->dma_ultra & 0x0004) ? XFER_UDMA_2 : + (id->dma_ultra & 0x0002) ? XFER_UDMA_1 : + (id->dma_ultra & 0x0001) ? XFER_UDMA_0 : 0)) return best; + } + + if ((map & XFER_MWDMA) && (id->field_valid & 2)) { /* Want MWDMA and drive has EIDE fields */ + + if ((best = (id->dma_mword & 0x0004) ? XFER_MW_DMA_2 : + (id->dma_mword & 0x0002) ? XFER_MW_DMA_1 : + (id->dma_mword & 0x0001) ? XFER_MW_DMA_0 : 0)) return best; + } + + if (map & XFER_SWDMA) { /* Want SWDMA */ + + if (id->field_valid & 2) { /* EIDE SWDMA */ + + if ((best = (id->dma_1word & 0x0004) ? XFER_SW_DMA_2 : + (id->dma_1word & 0x0002) ? XFER_SW_DMA_1 : + (id->dma_1word & 0x0001) ? XFER_SW_DMA_0 : 0)) return best; + } + + if (id->capability & 1) { /* Pre-EIDE style SWDMA */ + + if ((best = (id->tDMA == 2) ? XFER_SW_DMA_2 : + (id->tDMA == 1) ? XFER_SW_DMA_1 : + (id->tDMA == 0) ? XFER_SW_DMA_0 : 0)) return best; + } + } + + + if ((map & XFER_EPIO) && (id->field_valid & 2)) { /* EIDE PIO modes */ + + if ((best = (drive->id->eide_pio_modes & 4) ? XFER_PIO_5 : + (drive->id->eide_pio_modes & 2) ? XFER_PIO_4 : + (drive->id->eide_pio_modes & 1) ? XFER_PIO_3 : 0)) return best; + } + + return (drive->id->tPIO == 2) ? XFER_PIO_2 : + (drive->id->tPIO == 1) ? XFER_PIO_1 : + (drive->id->tPIO == 0) ? XFER_PIO_0 : XFER_PIO_SLOW; +} + +static void ide_timing_quantize(struct ide_timing *t, struct ide_timing *q, int T, int UT) +{ + q->setup = EZ(t->setup * 1000, T); + q->act8b = EZ(t->act8b * 1000, T); + q->rec8b = EZ(t->rec8b * 1000, T); + q->cyc8b = EZ(t->cyc8b * 1000, T); + q->active = EZ(t->active * 1000, T); + q->recover = EZ(t->recover * 1000, T); + q->cycle = EZ(t->cycle * 1000, T); + q->udma = EZ(t->udma * 1000, UT); +} + +static void ide_timing_merge(struct ide_timing *a, struct ide_timing *b, struct ide_timing *m, unsigned int what) +{ + if (what & IDE_TIMING_SETUP ) m->setup = MAX(a->setup, b->setup); + if (what & IDE_TIMING_ACT8B ) m->act8b = MAX(a->act8b, b->act8b); + if (what & IDE_TIMING_REC8B ) m->rec8b = MAX(a->rec8b, b->rec8b); + if (what & IDE_TIMING_CYC8B ) m->cyc8b = MAX(a->cyc8b, b->cyc8b); + if (what & IDE_TIMING_ACTIVE ) m->active = MAX(a->active, b->active); + if (what & IDE_TIMING_RECOVER) m->recover = MAX(a->recover, b->recover); + if (what & IDE_TIMING_CYCLE ) m->cycle = MAX(a->cycle, b->cycle); + if (what & IDE_TIMING_UDMA ) m->udma = MAX(a->udma, b->udma); +} + +static struct ide_timing* ide_timing_find_mode(short speed) +{ + struct ide_timing *t; + + for (t = ide_timing; t->mode != speed; t++) + if (t->mode < 0) + return NULL; + return t; +} + +static int ide_timing_compute(ide_drive_t *drive, short speed, struct ide_timing *t, int T, int UT) +{ + struct hd_driveid *id = drive->id; + struct ide_timing *s, p; + +/* + * Find the mode. + */ + + if (!(s = ide_timing_find_mode(speed))) + return -EINVAL; + +/* + * If the drive is an EIDE drive, it can tell us it needs extended + * PIO/MWDMA cycle timing. + */ + + if (id && id->field_valid & 2) { /* EIDE drive */ + + memset(&p, 0, sizeof(p)); + + switch (speed & XFER_MODE) { + + case XFER_PIO: + if (speed <= XFER_PIO_2) p.cycle = p.cyc8b = id->eide_pio; + else p.cycle = p.cyc8b = id->eide_pio_iordy; + break; + + case XFER_MWDMA: + p.cycle = id->eide_dma_min; + break; + } + + ide_timing_merge(&p, t, t, IDE_TIMING_CYCLE | IDE_TIMING_CYC8B); + } + +/* + * Convert the timing to bus clock counts. + */ + + ide_timing_quantize(s, t, T, UT); + +/* + * Even in DMA/UDMA modes we still use PIO access for IDENTIFY, S.M.A.R.T + * and some other commands. We have to ensure that the DMA cycle timing is + * slower/equal than the fastest PIO timing. + */ + + if ((speed & XFER_MODE) != XFER_PIO) { + ide_timing_compute(drive, ide_find_best_mode(drive, XFER_PIO | XFER_EPIO), &p, T, UT); + ide_timing_merge(&p, t, t, IDE_TIMING_ALL); + } + +/* + * Lenghten active & recovery time so that cycle time is correct. + */ + + if (t->act8b + t->rec8b < t->cyc8b) { + t->act8b += (t->cyc8b - (t->act8b + t->rec8b)) / 2; + t->rec8b = t->cyc8b - t->act8b; + } + + if (t->active + t->recover < t->cycle) { + t->active += (t->cycle - (t->active + t->recover)) / 2; + t->recover = t->cycle - t->active; + } + + return 0; +} + +#endif diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c new file mode 100644 index 0000000..973dec7 --- /dev/null +++ b/drivers/ide/ide.c @@ -0,0 +1,2269 @@ +/* + * linux/drivers/ide/ide.c Version 7.00beta2 Mar 05 2003 + * + * Copyright (C) 1994-1998 Linus Torvalds & authors (see below) + */ + +/* + * Mostly written by Mark Lord <mlord@pobox.com> + * and Gadi Oxman <gadio@netvision.net.il> + * and Andre Hedrick <andre@linux-ide.org> + * + * 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 + * Version 6.31 Debug Share INTR's and request queue streaming + * Native ATA-100 support + * Prep for Cascades Project + * Version 7.00alpha First named revision of ide rearrange + * + * 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: 7.00alpha2" +#define VERSION "Id: ide.c 7.00a2 20020906" + +#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/slab.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/ide.h> +#include <linux/devfs_fs_kernel.h> +#include <linux/completion.h> +#include <linux/reboot.h> +#include <linux/cdrom.h> +#include <linux/seq_file.h> +#include <linux/device.h> +#include <linux/bitops.h> + +#include <asm/byteorder.h> +#include <asm/irq.h> +#include <asm/uaccess.h> +#include <asm/io.h> + + +/* default maximum number of failures */ +#define IDE_DEFAULT_MAX_FAILURES 1 + +static const u8 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; /* holds the "idebus=" parameter */ +static int system_bus_speed; /* holds what we think is VESA/PCI bus speed */ +static int initializing; /* set while initializing built-in drivers */ + +DECLARE_MUTEX(ide_cfg_sem); + __cacheline_aligned_in_smp DEFINE_SPINLOCK(ide_lock); + +#ifdef CONFIG_BLK_DEV_IDEPCI +static int ide_scan_direction; /* THIS was formerly 2.2.x pci=reverse */ +#endif + +#ifdef CONFIG_IDEDMA_AUTO +int noautodma = 0; +#else +int noautodma = 1; +#endif + +EXPORT_SYMBOL(noautodma); + +/* + * This is declared extern in ide.h, for access by other IDE modules: + */ +ide_hwif_t ide_hwifs[MAX_HWIFS]; /* master data repository */ + +EXPORT_SYMBOL(ide_hwifs); + +static struct list_head ide_drives = LIST_HEAD_INIT(ide_drives); + +/* + * Do not even *think* about calling this! + */ +static void init_hwif_data(ide_hwif_t *hwif, unsigned int index) +{ + unsigned int unit; + + /* bulk initialize hwif & drive info with zeros */ + memset(hwif, 0, sizeof(ide_hwif_t)); + + /* fill in any non-zero initial values */ + hwif->index = index; + hwif->major = ide_hwif_to_major[index]; + + hwif->name[0] = 'i'; + hwif->name[1] = 'd'; + hwif->name[2] = 'e'; + hwif->name[3] = '0' + index; + + hwif->bus_state = BUSSTATE_ON; + + hwif->atapi_dma = 0; /* disable all atapi dma */ + hwif->ultra_mask = 0x80; /* disable all ultra */ + hwif->mwdma_mask = 0x80; /* disable all mwdma */ + hwif->swdma_mask = 0x80; /* disable all swdma */ + + sema_init(&hwif->gendev_rel_sem, 0); + + default_hwif_iops(hwif); + default_hwif_transport(hwif); + 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; + drive->max_failures = IDE_DEFAULT_MAX_FAILURES; + drive->using_dma = 0; + drive->is_flash = 0; + drive->vdma = 0; + INIT_LIST_HEAD(&drive->list); + sema_init(&drive->gendev_rel_sem, 0); + } +} + +static void init_hwif_default(ide_hwif_t *hwif, unsigned int index) +{ + hw_regs_t hw; + + memset(&hw, 0, sizeof(hw_regs_t)); + + 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 +} + +extern void ide_arm_init(void); + +/* + * 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. + * + * FIXME: We should stuff the setup data into __init and copy the + * relevant hwifs/allocate them properly during boot. + */ +#define MAGIC_COOKIE 0x12345678 +static void __init init_ide_data (void) +{ + ide_hwif_t *hwif; + 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) { + hwif = &ide_hwifs[index]; + init_hwif_data(hwif, index); + init_hwif_default(hwif, index); +#if !defined(CONFIG_PPC32) || !defined(CONFIG_PCI) + hwif->irq = hwif->hw.irq = + ide_init_default_irq(hwif->io_ports[IDE_DATA_OFFSET]); +#endif + } +#ifdef CONFIG_IDE_ARM + initializing = 1; + ide_arm_init(); + initializing = 0; +#endif +} + +/** + * ide_system_bus_speed - guess bus speed + * + * 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. Drivers should only use this as a last resort. + * + * Returns a guessed speed in MHz. + */ + +static int ide_system_bus_speed(void) +{ +#ifdef CONFIG_PCI + static struct pci_device_id pci_default[] = { + { PCI_DEVICE(PCI_ANY_ID, PCI_ANY_ID) }, + { } + }; +#else +#define pci_default 0 +#endif /* CONFIG_PCI */ + + if (!system_bus_speed) { + if (idebus_parameter) { + /* user supplied value */ + system_bus_speed = idebus_parameter; + } else if (pci_dev_present(pci_default)) { + /* safe default value for PCI */ + system_bus_speed = 33; + } else { + /* safe default value for VESA and PCI */ + system_bus_speed = 50; + } + printk(KERN_INFO "ide: Assuming %dMHz system bus speed " + "for PIO modes%s\n", system_bus_speed, + idebus_parameter ? "" : "; override with idebus=xx"); + } + return system_bus_speed; +} + +/* + * drives_lock protects the list of drives, drivers_lock the + * list of drivers. Currently nobody takes both at once. + */ + +static DEFINE_SPINLOCK(drives_lock); +static DEFINE_SPINLOCK(drivers_lock); +static LIST_HEAD(drivers); + +/* Iterator for the driver list. */ + +static void *m_start(struct seq_file *m, loff_t *pos) +{ + struct list_head *p; + loff_t l = *pos; + spin_lock(&drivers_lock); + list_for_each(p, &drivers) + if (!l--) + return list_entry(p, ide_driver_t, drivers); + return NULL; +} + +static void *m_next(struct seq_file *m, void *v, loff_t *pos) +{ + struct list_head *p = ((ide_driver_t *)v)->drivers.next; + (*pos)++; + return p==&drivers ? NULL : list_entry(p, ide_driver_t, drivers); +} + +static void m_stop(struct seq_file *m, void *v) +{ + spin_unlock(&drivers_lock); +} + +static int show_driver(struct seq_file *m, void *v) +{ + ide_driver_t *driver = v; + seq_printf(m, "%s version %s\n", driver->name, driver->version); + return 0; +} + +struct seq_operations ide_drivers_op = { + .start = m_start, + .next = m_next, + .stop = m_stop, + .show = show_driver +}; + +#ifdef CONFIG_PROC_FS +struct proc_dir_entry *proc_ide_root; +#endif + +static struct resource* hwif_request_region(ide_hwif_t *hwif, + unsigned long addr, int num) +{ + struct resource *res = request_region(addr, num, hwif->name); + + if (!res) + printk(KERN_ERR "%s: I/O resource 0x%lX-0x%lX not free.\n", + hwif->name, addr, addr+num-1); + return res; +} + +/** + * ide_hwif_request_regions - request resources for IDE + * @hwif: interface to use + * + * Requests all the needed resources for an interface. + * Right now core IDE code does this work which is deeply wrong. + * MMIO leaves it to the controller driver, + * PIO will migrate this way over time. + */ + +int ide_hwif_request_regions(ide_hwif_t *hwif) +{ + unsigned long addr; + unsigned int i; + + if (hwif->mmio == 2) + return 0; + BUG_ON(hwif->mmio == 1); + addr = hwif->io_ports[IDE_CONTROL_OFFSET]; + if (addr && !hwif_request_region(hwif, addr, 1)) + goto control_region_busy; + hwif->straight8 = 0; + addr = hwif->io_ports[IDE_DATA_OFFSET]; + if ((addr | 7) == hwif->io_ports[IDE_STATUS_OFFSET]) { + if (!hwif_request_region(hwif, addr, 8)) + goto data_region_busy; + hwif->straight8 = 1; + return 0; + } + for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) { + addr = hwif->io_ports[i]; + if (!hwif_request_region(hwif, addr, 1)) { + while (--i) + release_region(addr, 1); + goto data_region_busy; + } + } + return 0; + +data_region_busy: + addr = hwif->io_ports[IDE_CONTROL_OFFSET]; + if (addr) + release_region(addr, 1); +control_region_busy: + /* If any errors are return, we drop the hwif interface. */ + return -EBUSY; +} + +/** + * ide_hwif_release_regions - free IDE resources + * + * Note that we only release the standard ports, + * and do not even try to handle any extra ports + * allocated for weird IDE interface chipsets. + * + * Note also that we don't yet handle mmio resources here. More + * importantly our caller should be doing this so we need to + * restructure this as a helper function for drivers. + */ + +void ide_hwif_release_regions(ide_hwif_t *hwif) +{ + u32 i = 0; + + if (hwif->mmio == 2) + return; + if (hwif->io_ports[IDE_CONTROL_OFFSET]) + release_region(hwif->io_ports[IDE_CONTROL_OFFSET], 1); + if (hwif->straight8) { + release_region(hwif->io_ports[IDE_DATA_OFFSET], 8); + return; + } + for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) + if (hwif->io_ports[i]) + release_region(hwif->io_ports[i], 1); +} + +/** + * ide_hwif_restore - restore hwif to template + * @hwif: hwif to update + * @tmp_hwif: template + * + * Restore hwif to a previous state by copying most settngs + * from the template. + */ + +static void ide_hwif_restore(ide_hwif_t *hwif, ide_hwif_t *tmp_hwif) +{ + hwif->hwgroup = tmp_hwif->hwgroup; + + hwif->gendev.parent = tmp_hwif->gendev.parent; + + hwif->proc = tmp_hwif->proc; + + hwif->major = tmp_hwif->major; + hwif->straight8 = tmp_hwif->straight8; + hwif->bus_state = tmp_hwif->bus_state; + + hwif->atapi_dma = tmp_hwif->atapi_dma; + hwif->ultra_mask = tmp_hwif->ultra_mask; + hwif->mwdma_mask = tmp_hwif->mwdma_mask; + hwif->swdma_mask = tmp_hwif->swdma_mask; + + hwif->chipset = tmp_hwif->chipset; + hwif->hold = tmp_hwif->hold; + +#ifdef CONFIG_BLK_DEV_IDEPCI + hwif->pci_dev = tmp_hwif->pci_dev; + hwif->cds = tmp_hwif->cds; +#endif + + hwif->tuneproc = tmp_hwif->tuneproc; + hwif->speedproc = tmp_hwif->speedproc; + hwif->selectproc = tmp_hwif->selectproc; + hwif->reset_poll = tmp_hwif->reset_poll; + hwif->pre_reset = tmp_hwif->pre_reset; + hwif->resetproc = tmp_hwif->resetproc; + hwif->intrproc = tmp_hwif->intrproc; + hwif->maskproc = tmp_hwif->maskproc; + hwif->quirkproc = tmp_hwif->quirkproc; + hwif->busproc = tmp_hwif->busproc; + + hwif->ata_input_data = tmp_hwif->ata_input_data; + hwif->ata_output_data = tmp_hwif->ata_output_data; + hwif->atapi_input_bytes = tmp_hwif->atapi_input_bytes; + hwif->atapi_output_bytes = tmp_hwif->atapi_output_bytes; + + hwif->dma_setup = tmp_hwif->dma_setup; + hwif->dma_exec_cmd = tmp_hwif->dma_exec_cmd; + hwif->dma_start = tmp_hwif->dma_start; + hwif->ide_dma_end = tmp_hwif->ide_dma_end; + hwif->ide_dma_check = tmp_hwif->ide_dma_check; + hwif->ide_dma_on = tmp_hwif->ide_dma_on; + hwif->ide_dma_off_quietly = tmp_hwif->ide_dma_off_quietly; + hwif->ide_dma_test_irq = tmp_hwif->ide_dma_test_irq; + hwif->ide_dma_host_on = tmp_hwif->ide_dma_host_on; + hwif->ide_dma_host_off = tmp_hwif->ide_dma_host_off; + hwif->ide_dma_lostirq = tmp_hwif->ide_dma_lostirq; + hwif->ide_dma_timeout = tmp_hwif->ide_dma_timeout; + + hwif->OUTB = tmp_hwif->OUTB; + hwif->OUTBSYNC = tmp_hwif->OUTBSYNC; + hwif->OUTW = tmp_hwif->OUTW; + hwif->OUTL = tmp_hwif->OUTL; + hwif->OUTSW = tmp_hwif->OUTSW; + hwif->OUTSL = tmp_hwif->OUTSL; + + hwif->INB = tmp_hwif->INB; + hwif->INW = tmp_hwif->INW; + hwif->INL = tmp_hwif->INL; + hwif->INSW = tmp_hwif->INSW; + hwif->INSL = tmp_hwif->INSL; + + hwif->sg_max_nents = tmp_hwif->sg_max_nents; + + hwif->mmio = tmp_hwif->mmio; + hwif->rqsize = tmp_hwif->rqsize; + hwif->no_lba48 = tmp_hwif->no_lba48; + +#ifndef CONFIG_BLK_DEV_IDECS + hwif->irq = tmp_hwif->irq; +#endif + + hwif->dma_base = tmp_hwif->dma_base; + hwif->dma_master = tmp_hwif->dma_master; + hwif->dma_command = tmp_hwif->dma_command; + hwif->dma_vendor1 = tmp_hwif->dma_vendor1; + hwif->dma_status = tmp_hwif->dma_status; + hwif->dma_vendor3 = tmp_hwif->dma_vendor3; + hwif->dma_prdtable = tmp_hwif->dma_prdtable; + + hwif->dma_extra = tmp_hwif->dma_extra; + hwif->config_data = tmp_hwif->config_data; + hwif->select_data = tmp_hwif->select_data; + hwif->autodma = tmp_hwif->autodma; + hwif->udma_four = tmp_hwif->udma_four; + hwif->no_dsc = tmp_hwif->no_dsc; + + hwif->hwif_data = tmp_hwif->hwif_data; +} + +/** + * ide_unregister - free an ide interface + * @index: index of interface (will change soon to a pointer) + * + * Perform the final unregister of an IDE interface. At the moment + * we don't refcount interfaces so this will also get split up. + * + * Locking: + * The caller must not hold the IDE locks + * The drive present/vanishing is not yet properly locked + * Take care with the callbacks. These have been split to avoid + * deadlocking the IDE layer. The shutdown callback is called + * before we take the lock and free resources. It is up to the + * caller to be sure there is no pending I/O here, and that + * the interfce will not be reopened (present/vanishing locking + * isnt yet done btw). After we commit to the final kill we + * call the cleanup callback with the ide locks held. + * + * Unregister restores the hwif structures to the default state. + * This is raving bonkers. + */ + +void ide_unregister(unsigned int index) +{ + ide_drive_t *drive; + ide_hwif_t *hwif, *g; + static ide_hwif_t tmp_hwif; /* protected by ide_cfg_sem */ + ide_hwgroup_t *hwgroup; + int irq_count = 0, unit, i; + + BUG_ON(index >= MAX_HWIFS); + + BUG_ON(in_interrupt()); + BUG_ON(irqs_disabled()); + down(&ide_cfg_sem); + spin_lock_irq(&ide_lock); + 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->usage || DRIVER(drive)->busy) + goto abort; + drive->dead = 1; + } + hwif->present = 0; + + spin_unlock_irq(&ide_lock); + + for (unit = 0; unit < MAX_DRIVES; ++unit) { + drive = &hwif->drives[unit]; + if (!drive->present) + continue; + DRIVER(drive)->cleanup(drive); + } + + destroy_proc_ide_interface(hwif); + + 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); + + spin_lock_irq(&ide_lock); + /* + * Note that we only release the standard ports, + * and do not even try to handle any extra ports + * allocated for weird IDE interface chipsets. + */ + ide_hwif_release_regions(hwif); + + /* + * Remove us from the hwgroup, and free + * the hwgroup if we were the only member + */ + for (i = 0; i < MAX_DRIVES; ++i) { + drive = &hwif->drives[i]; + if (drive->devfs_name[0] != '\0') { + devfs_remove(drive->devfs_name); + drive->devfs_name[0] = '\0'; + } + if (!drive->present) + continue; + if (drive == drive->next) { + /* special case: last drive from hwgroup. */ + BUG_ON(hwgroup->drive != drive); + hwgroup->drive = NULL; + } else { + ide_drive_t *walk; + + walk = hwgroup->drive; + while (walk->next != drive) + walk = walk->next; + walk->next = drive->next; + if (hwgroup->drive == drive) { + hwgroup->drive = drive->next; + hwgroup->hwif = HWIF(hwgroup->drive); + } + } + BUG_ON(hwgroup->drive == drive); + if (drive->id != NULL) { + kfree(drive->id); + drive->id = NULL; + } + drive->present = 0; + /* Messed up locking ... */ + spin_unlock_irq(&ide_lock); + blk_cleanup_queue(drive->queue); + device_unregister(&drive->gendev); + down(&drive->gendev_rel_sem); + spin_lock_irq(&ide_lock); + drive->queue = NULL; + } + if (hwif->next == hwif) { + BUG_ON(hwgroup->hwif != hwif); + kfree(hwgroup); + } else { + /* There is another interface in hwgroup. + * Unlink us, and set hwgroup->drive and ->hwif to + * something sane. + */ + g = hwgroup->hwif; + while (g->next != hwif) + g = g->next; + g->next = hwif->next; + if (hwgroup->hwif == hwif) { + /* Chose a random hwif for hwgroup->hwif. + * It's guaranteed that there are no drives + * left in the hwgroup. + */ + BUG_ON(hwgroup->drive != NULL); + hwgroup->hwif = g; + } + BUG_ON(hwgroup->hwif == hwif); + } + + /* More messed up locking ... */ + spin_unlock_irq(&ide_lock); + device_unregister(&hwif->gendev); + down(&hwif->gendev_rel_sem); + + /* + * Remove us from the kernel's knowledge + */ + blk_unregister_region(MKDEV(hwif->major, 0), MAX_DRIVES<<PARTN_BITS); + kfree(hwif->sg_table); + unregister_blkdev(hwif->major, hwif->name); + spin_lock_irq(&ide_lock); + + if (hwif->dma_base) { + (void) ide_release_dma(hwif); + + hwif->dma_base = 0; + hwif->dma_master = 0; + hwif->dma_command = 0; + hwif->dma_vendor1 = 0; + hwif->dma_status = 0; + hwif->dma_vendor3 = 0; + hwif->dma_prdtable = 0; + } + + /* copy original settings */ + tmp_hwif = *hwif; + + /* restore hwif data to pristine status */ + init_hwif_data(hwif, index); + init_hwif_default(hwif, index); + + ide_hwif_restore(hwif, &tmp_hwif); + +abort: + spin_unlock_irq(&ide_lock); + up(&ide_cfg_sem); +} + +EXPORT_SYMBOL(ide_unregister); + + +/** + * ide_setup_ports - set up IDE interface ports + * @hw: register descriptions + * @base: base register + * @offsets: table of register offsets + * @ctrl: control register + * @ack_irq: IRQ ack + * @irq: interrupt lie + * + * 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. This is basically a helper + * + */ + +void ide_setup_ports ( hw_regs_t *hw, + unsigned long base, int *offsets, + unsigned long ctrl, unsigned long intr, + ide_ack_intr_t *ack_intr, +/* + * ide_io_ops_t *iops, + */ + 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; +#if defined(CONFIG_AMIGA) || defined(CONFIG_MAC) + case IDE_IRQ_OFFSET: + hw->io_ports[i] = intr; + break; +#endif /* (CONFIG_AMIGA) || (CONFIG_MAC) */ + 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; +/* + * hw->iops = iops; + */ +} + +/** + * ide_register_hw_with_fixup - register IDE interface + * @hw: hardware registers + * @hwifp: pointer to returned hwif + * @fixup: fixup function + * + * Register an IDE interface, specifying exactly the registers etc. + * Set init=1 iff calling before probes have taken place. + * + * Returns -1 on error. + */ + +int ide_register_hw_with_fixup(hw_regs_t *hw, ide_hwif_t **hwifp, void(*fixup)(ide_hwif_t *hwif)) +{ + 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->hold) + continue; + 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); + else if (!hwif->hold) { + init_hwif_data(hwif, index); + init_hwif_default(hwif, 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; + hwif->chipset = hw->chipset; + + if (!initializing) { + probe_hwif_init_with_fixup(hwif, fixup); + create_proc_ide_interfaces(); + } + + if (hwifp) + *hwifp = hwif; + + return (initializing || hwif->present) ? index : -1; +} + +EXPORT_SYMBOL(ide_register_hw_with_fixup); + +int ide_register_hw(hw_regs_t *hw, ide_hwif_t **hwifp) +{ + return ide_register_hw_with_fixup(hw, hwifp, NULL); +} + +EXPORT_SYMBOL(ide_register_hw); + +/* + * Locks for IDE setting functionality + */ + +DECLARE_MUTEX(ide_setting_sem); + +/** + * __ide_add_setting - add an ide setting option + * @drive: drive to use + * @name: setting name + * @rw: true if the function is read write + * @read_ioctl: function to call on read + * @write_ioctl: function to call on write + * @data_type: type of data + * @min: range minimum + * @max: range maximum + * @mul_factor: multiplication scale + * @div_factor: divison scale + * @data: private data field + * @set: setting + * @auto_remove: setting auto removal flag + * + * Removes the setting named from the device if it is present. + * The function takes the settings_lock to protect against + * parallel changes. This function must not be called from IRQ + * context. Returns 0 on success or -1 on failure. + * + * BUGS: This code is seriously over-engineered. There is also + * magic about how the driver specific features are setup. If + * a driver is attached we assume the driver settings are auto + * remove. + */ + +static int __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, int auto_remove) +{ + ide_settings_t **p = (ide_settings_t **) &drive->settings, *setting = NULL; + + down(&ide_setting_sem); + 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 (auto_remove) + setting->auto_remove = 1; + *p = setting; + up(&ide_setting_sem); + return 0; +abort: + up(&ide_setting_sem); + if (setting) + kfree(setting); + return -1; +} + +int 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) +{ + return __ide_add_setting(drive, name, rw, read_ioctl, write_ioctl, data_type, min, max, mul_factor, div_factor, data, set, 1); +} + +EXPORT_SYMBOL(ide_add_setting); + +/** + * __ide_remove_setting - remove an ide setting option + * @drive: drive to use + * @name: setting name + * + * Removes the setting named from the device if it is present. + * The caller must hold the setting semaphore. + */ + +static void __ide_remove_setting (ide_drive_t *drive, char *name) +{ + ide_settings_t **p, *setting; + + p = (ide_settings_t **) &drive->settings; + + while ((*p) && strcmp((*p)->name, name)) + p = &((*p)->next); + if ((setting = (*p)) == NULL) + return; + + (*p) = setting->next; + + kfree(setting->name); + kfree(setting); +} + +/** + * ide_find_setting_by_ioctl - find a drive specific ioctl + * @drive: drive to scan + * @cmd: ioctl command to handle + * + * Scan's the device setting table for a matching entry and returns + * this or NULL if no entry is found. The caller must hold the + * setting semaphore + */ + +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_find_setting_by_name - find a drive specific setting + * @drive: drive to scan + * @name: setting name + * + * Scan's the device setting table for a matching entry and returns + * this or NULL if no entry is found. The caller must hold the + * setting semaphore + */ + +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; +} + +/** + * auto_remove_settings - remove driver specific settings + * @drive: drive + * + * Automatically remove all the driver specific settings for this + * drive. This function may sleep and must not be called from IRQ + * context. The caller must hold ide_setting_sem. + */ + +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; + } +} + +/** + * ide_read_setting - read an IDE setting + * @drive: drive to read from + * @setting: drive setting + * + * Read a drive setting and return the value. The caller + * must hold the ide_setting_sem when making this call. + * + * BUGS: the data return and error are the same return value + * so an error -EINVAL and true return of the same value cannot + * be told apart + */ + +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(&ide_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(&ide_lock, flags); + } + return val; +} + +/** + * ide_spin_wait_hwgroup - wait for group + * @drive: drive in the group + * + * Wait for an IDE device group to go non busy and then return + * holding the ide_lock which guards the hwgroup->busy status + * and right to use it. + */ + +int ide_spin_wait_hwgroup (ide_drive_t *drive) +{ + ide_hwgroup_t *hwgroup = HWGROUP(drive); + unsigned long timeout = jiffies + (3 * HZ); + + spin_lock_irq(&ide_lock); + + while (hwgroup->busy) { + unsigned long lflags; + spin_unlock_irq(&ide_lock); + local_irq_set(lflags); + if (time_after(jiffies, timeout)) { + local_irq_restore(lflags); + printk(KERN_ERR "%s: channel busy\n", drive->name); + return -EBUSY; + } + local_irq_restore(lflags); + spin_lock_irq(&ide_lock); + } + return 0; +} + +EXPORT_SYMBOL(ide_spin_wait_hwgroup); + +/** + * ide_write_setting - read an IDE setting + * @drive: drive to read from + * @setting: drive setting + * @val: value + * + * Write a drive setting if it is possible. The caller + * must hold the ide_setting_sem when making this call. + * + * BUGS: the data return and error are the same return value + * so an error -EINVAL and true return of the same value cannot + * be told apart + * + * 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 kludgy, though safe enough. + */ + +int ide_write_setting (ide_drive_t *drive, ide_settings_t *setting, int val) +{ + 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)) + 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_irq(&ide_lock); + 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) +{ +#ifdef CONFIG_BLK_DEV_IDEDMA + if (!drive->id || !(drive->id->capability & 1)) + return -EPERM; + if (HWIF(drive)->ide_dma_check == NULL) + return -EPERM; + if (arg) { + if (HWIF(drive)->ide_dma_check(drive)) return -EIO; + if (HWIF(drive)->ide_dma_on(drive)) return -EIO; + } else { + if (__ide_dma_off(drive)) + return -EIO; + } + return 0; +#else + return -EPERM; +#endif +} + +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 = (u8) arg; + drive->special.b.set_tune = 1; + (void) ide_do_drive_cmd(drive, &rq, ide_wait); + return 0; +} + +static int set_xfer_rate (ide_drive_t *drive, int arg) +{ + int err = ide_wait_cmd(drive, + WIN_SETFEATURES, (u8) arg, + SETFEATURES_XFER, 0, NULL); + + if (!err && arg) { + ide_set_xfer_rate(drive, (u8) arg); + ide_driveid_update(drive); + } + return err; +} + +/** + * ide_add_generic_settings - generic ide settings + * @drive: drive being configured + * + * Add the generic parts of the system settings to the /proc files and + * ioctls for this IDE device. The caller must not be holding the + * ide_setting_sem. + */ + +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, 0); + __ide_add_setting(drive, "keepsettings", SETTING_RW, HDIO_GET_KEEPSETTINGS, HDIO_SET_KEEPSETTINGS, TYPE_BYTE, 0, 1, 1, 1, &drive->keep_settings, NULL, 0); + __ide_add_setting(drive, "nice1", SETTING_RW, -1, -1, TYPE_BYTE, 0, 1, 1, 1, &drive->nice1, NULL, 0); + __ide_add_setting(drive, "pio_mode", SETTING_WRITE, -1, HDIO_SET_PIO_MODE, TYPE_BYTE, 0, 255, 1, 1, NULL, set_pio_mode, 0); + __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, 0); + __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, 0); + __ide_add_setting(drive, "init_speed", SETTING_RW, -1, -1, TYPE_BYTE, 0, 70, 1, 1, &drive->init_speed, NULL, 0); + __ide_add_setting(drive, "current_speed", SETTING_RW, -1, -1, TYPE_BYTE, 0, 70, 1, 1, &drive->current_speed, set_xfer_rate, 0); + __ide_add_setting(drive, "number", SETTING_RW, -1, -1, TYPE_BYTE, 0, 3, 1, 1, &drive->dn, NULL, 0); +} + +/** + * system_bus_clock - clock guess + * + * External version of the bus clock guess used by very old IDE drivers + * for things like VLB timings. Should not be used. + */ + +int system_bus_clock (void) +{ + return((int) ((!system_bus_speed) ? ide_system_bus_speed() : system_bus_speed )); +} + +EXPORT_SYMBOL(system_bus_clock); + +/* + * Locking is badly broken here - since way back. That sucker is + * root-only, but that's not an excuse... The real question is what + * exclusion rules do we want here. + */ +int ide_replace_subdriver (ide_drive_t *drive, const char *driver) +{ + if (!drive->present || drive->usage || drive->dead) + goto abort; + if (DRIVER(drive)->cleanup(drive)) + goto abort; + strlcpy(drive->driver_req, driver, sizeof(drive->driver_req)); + if (ata_attach(drive)) { + spin_lock(&drives_lock); + list_del_init(&drive->list); + spin_unlock(&drives_lock); + drive->driver_req[0] = 0; + ata_attach(drive); + } else { + drive->driver_req[0] = 0; + } + if (drive->driver && !strcmp(drive->driver->name, driver)) + return 0; +abort: + return 1; +} + +/** + * ata_attach - attach an ATA/ATAPI device + * @drive: drive to attach + * + * Takes a drive that is as yet not assigned to any midlayer IDE + * driver (or is assigned to the default driver) and figures out + * which driver would like to own it. If nobody claims the drive + * then it is automatically attached to the default driver used for + * unclaimed objects. + * + * A return of zero indicates attachment to a driver, of one + * attachment to the default driver. + * + * Takes drivers_lock. + */ + +int ata_attach(ide_drive_t *drive) +{ + struct list_head *p; + spin_lock(&drivers_lock); + list_for_each(p, &drivers) { + ide_driver_t *driver = list_entry(p, ide_driver_t, drivers); + if (!try_module_get(driver->owner)) + continue; + spin_unlock(&drivers_lock); + if (driver->attach(drive) == 0) { + module_put(driver->owner); + drive->gendev.driver = &driver->gen_driver; + return 0; + } + spin_lock(&drivers_lock); + module_put(driver->owner); + } + drive->gendev.driver = NULL; + spin_unlock(&drivers_lock); + if (ide_register_subdriver(drive, NULL)) + panic("ide: default attach failed"); + return 1; +} + +static int generic_ide_suspend(struct device *dev, pm_message_t state) +{ + ide_drive_t *drive = dev->driver_data; + struct request rq; + struct request_pm_state rqpm; + ide_task_t args; + + memset(&rq, 0, sizeof(rq)); + memset(&rqpm, 0, sizeof(rqpm)); + memset(&args, 0, sizeof(args)); + rq.flags = REQ_PM_SUSPEND; + rq.special = &args; + rq.pm = &rqpm; + rqpm.pm_step = ide_pm_state_start_suspend; + rqpm.pm_state = state; + + return ide_do_drive_cmd(drive, &rq, ide_wait); +} + +static int generic_ide_resume(struct device *dev) +{ + ide_drive_t *drive = dev->driver_data; + struct request rq; + struct request_pm_state rqpm; + ide_task_t args; + + memset(&rq, 0, sizeof(rq)); + memset(&rqpm, 0, sizeof(rqpm)); + memset(&args, 0, sizeof(args)); + rq.flags = REQ_PM_RESUME; + rq.special = &args; + rq.pm = &rqpm; + rqpm.pm_step = ide_pm_state_start_resume; + rqpm.pm_state = 0; + + return ide_do_drive_cmd(drive, &rq, ide_head_wait); +} + +int generic_ide_ioctl(ide_drive_t *drive, struct file *file, struct block_device *bdev, + unsigned int cmd, unsigned long arg) +{ + ide_settings_t *setting; + ide_driver_t *drv; + int err = 0; + void __user *p = (void __user *)arg; + + down(&ide_setting_sem); + if ((setting = ide_find_setting_by_ioctl(drive, cmd)) != NULL) { + if (cmd == setting->read_ioctl) { + err = ide_read_setting(drive, setting); + up(&ide_setting_sem); + return err >= 0 ? put_user(err, (long __user *)arg) : err; + } else { + if (bdev != bdev->bd_contains) + err = -EINVAL; + else + err = ide_write_setting(drive, setting, arg); + up(&ide_setting_sem); + return err; + } + } + up(&ide_setting_sem); + + switch (cmd) { + case HDIO_GETGEO: + { + struct hd_geometry geom; + if (!p || (drive->media != ide_disk && drive->media != ide_floppy)) return -EINVAL; + geom.heads = drive->bios_head; + geom.sectors = drive->bios_sect; + geom.cylinders = (u16)drive->bios_cyl; /* truncate */ + geom.start = get_start_sect(bdev); + if (copy_to_user(p, &geom, sizeof(struct hd_geometry))) + return -EFAULT; + return 0; + } + + case HDIO_OBSOLETE_IDENTITY: + case HDIO_GET_IDENTITY: + if (bdev != bdev->bd_contains) + return -EINVAL; + if (drive->id_read == 0) + return -ENOMSG; + if (copy_to_user(p, 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 __user *) arg); + +#ifdef CONFIG_IDE_TASK_IOCTL + case HDIO_DRIVE_TASKFILE: + if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) + return -EACCES; + switch(drive->media) { + case ide_disk: + return ide_taskfile_ioctl(drive, cmd, arg); + default: + return -ENOMSG; + } +#endif /* CONFIG_IDE_TASK_IOCTL */ + + case HDIO_DRIVE_CMD: + if (!capable(CAP_SYS_RAWIO)) + return -EACCES; + return ide_cmd_ioctl(drive, cmd, arg); + + case HDIO_DRIVE_TASK: + if (!capable(CAP_SYS_RAWIO)) + return -EACCES; + return ide_task_ioctl(drive, cmd, arg); + + case HDIO_SCAN_HWIF: + { + hw_regs_t hw; + int args[3]; + if (!capable(CAP_SYS_RAWIO)) return -EACCES; + if (copy_from_user(args, p, 3 * sizeof(int))) + return -EFAULT; + memset(&hw, 0, sizeof(hw)); + ide_init_hwif_ports(&hw, (unsigned long) args[0], + (unsigned long) args[1], NULL); + hw.irq = args[2]; + if (ide_register_hw(&hw, NULL) == -1) + return -EIO; + return 0; + } + case HDIO_UNREGISTER_HWIF: + if (!capable(CAP_SYS_RAWIO)) return -EACCES; + /* (arg > MAX_HWIFS) checked in function */ + ide_unregister(arg); + return 0; + case HDIO_SET_NICE: + if (!capable(CAP_SYS_ADMIN)) return -EACCES; + if (arg != (arg & ((1 << IDE_NICE_DSC_OVERLAP) | (1 << IDE_NICE_1)))) + return -EPERM; + drive->dsc_overlap = (arg >> IDE_NICE_DSC_OVERLAP) & 1; + drv = *(ide_driver_t **)bdev->bd_disk->private_data; + if (drive->dsc_overlap && !drv->supports_dsc_overlap) { + drive->dsc_overlap = 0; + return -EPERM; + } + drive->nice1 = (arg >> IDE_NICE_1) & 1; + return 0; + case HDIO_DRIVE_RESET: + { + unsigned long flags; + if (!capable(CAP_SYS_ADMIN)) return -EACCES; + + /* + * Abort the current command on the + * group if there is one, taking + * care not to allow anything else + * to be queued and to die on the + * spot if we miss one somehow + */ + + spin_lock_irqsave(&ide_lock, flags); + + ide_abort(drive, "drive reset"); + + if(HWGROUP(drive)->handler) + BUG(); + + /* Ensure nothing gets queued after we + drop the lock. Reset will clear the busy */ + + HWGROUP(drive)->busy = 1; + spin_unlock_irqrestore(&ide_lock, flags); + (void) ide_do_reset(drive); + + return 0; + } + + case CDROMEJECT: + case CDROMCLOSETRAY: + return scsi_cmd_ioctl(file, bdev->bd_disk, cmd, p); + + case HDIO_GET_BUSSTATE: + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + if (put_user(HWIF(drive)->bus_state, (long __user *)arg)) + return -EFAULT; + return 0; + + case HDIO_SET_BUSSTATE: + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + if (HWIF(drive)->busproc) + return HWIF(drive)->busproc(drive, (int)arg); + return -EOPNOTSUPP; + default: + return -EINVAL; + } +} + +EXPORT_SYMBOL(generic_ide_ioctl); + +/* + * 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 */ +} + +#ifdef CONFIG_BLK_DEV_ALI14XX +static int __initdata probe_ali14xx; +extern int ali14xx_init(void); +#endif +#ifdef CONFIG_BLK_DEV_UMC8672 +static int __initdata probe_umc8672; +extern int umc8672_init(void); +#endif +#ifdef CONFIG_BLK_DEV_DTC2278 +static int __initdata probe_dtc2278; +extern int dtc2278_init(void); +#endif +#ifdef CONFIG_BLK_DEV_HT6560B +static int __initdata probe_ht6560b; +extern int ht6560b_init(void); +#endif +#ifdef CONFIG_BLK_DEV_QD65XX +static int __initdata probe_qd65xx; +extern int qd65xx_init(void); +#endif + +static int __initdata is_chipset_set[MAX_HWIFS]; + +/* + * ide_setup() gets called VERY EARLY during initialization, + * to handle kernel "command line" strings beginning with "hdx=" or "ide". + * + * Remember to update Documentation/ide.txt if you change something here. + */ +static 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); + + + if (strncmp(s,"hd",2) == 0 && s[2] == '=') /* hd= is for hd.c */ + return 0; /* driver and not us */ + + if (strncmp(s,"ide",3) && strncmp(s,"idebus",6) && strncmp(s,"hd",2)) + return 0; + + printk(KERN_INFO "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 1; + } +#endif /* CONFIG_BLK_DEV_IDEDOUBLER */ + + if (!strcmp(s, "ide=nodma")) { + printk(" : Prevented DMA\n"); + noautodma = 1; + return 1; + } + +#ifdef CONFIG_BLK_DEV_IDEPCI + if (!strcmp(s, "ide=reverse")) { + ide_scan_direction = 1; + printk(" : Enabled support for IDE inverse scan order.\n"); + return 1; + } +#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", "minus8", "swapdata", "bswap", + "minus11", "remap", "remap63", "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) { + strlcpy(drive->driver_req, s + 4, sizeof(drive->driver_req)); + goto done; + } + switch (match_parm(&s[3], hd_words, vals, 3)) { + case -1: /* "none" */ + 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; + /* an ATAPI device ignores DRDY */ + drive->ready_stat = 0; + hwif->noprobe = 0; + goto done; + case -5: /* "serialize" */ + printk(" -- USE \"ide%d=serialize\" INSTEAD", hw); + goto do_serialize; + case -6: /* "autotune" */ + drive->autotune = IDE_TUNE_AUTO; + goto obsolete_option; + case -7: /* "noautotune" */ + drive->autotune = IDE_TUNE_NOAUTO; + goto obsolete_option; + case -9: /* "swapdata" */ + case -10: /* "bswap" */ + drive->bswap = 1; + goto done; + case -12: /* "remap" */ + drive->remap_0_to_1 = 1; + goto done; + case -13: /* "remap63" */ + drive->sect0 = 63; + goto done; + case -14: /* "scsi" */ + drive->scsi = 1; + goto done; + case 3: /* cyl,head,sect */ + drive->media = ide_disk; + drive->ready_stat = READY_STAT; + 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 (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 to ease the hardcoding. + */ + static const char *ide_words[] = { + "noprobe", "serialize", "autotune", "noautotune", + "reset", "dma", "ata66", "minus8", "minus9", + "minus10", "four", "qd65xx", "ht6560b", "cmd640_vlb", + "dtc2278", "umc8672", "ali14xx", 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. + * Note: we can't depend on hwif->chipset here. + */ + if ((i >= -18 && i <= -11) || (i > 0 && i <= 3)) { + /* chipset already specified */ + if (is_chipset_set[hw]) + goto bad_option; + if (i > -18 && i <= -11) { + /* these drivers are for "ide0=" only */ + if (hw != 0) + goto bad_hwif; + /* chipset already specified for 2nd port */ + if (is_chipset_set[hw+1]) + goto bad_option; + } + is_chipset_set[hw] = 1; + printk("\n"); + } + + switch (i) { +#ifdef CONFIG_BLK_DEV_ALI14XX + case -17: /* "ali14xx" */ + probe_ali14xx = 1; + goto done; +#endif +#ifdef CONFIG_BLK_DEV_UMC8672 + case -16: /* "umc8672" */ + probe_umc8672 = 1; + goto done; +#endif +#ifdef CONFIG_BLK_DEV_DTC2278 + case -15: /* "dtc2278" */ + probe_dtc2278 = 1; + goto done; +#endif +#ifdef CONFIG_BLK_DEV_CMD640 + case -14: /* "cmd640_vlb" */ + { + extern int cmd640_vlb; /* flag for cmd640.c */ + cmd640_vlb = 1; + goto done; + } +#endif +#ifdef CONFIG_BLK_DEV_HT6560B + case -13: /* "ht6560b" */ + probe_ht6560b = 1; + goto done; +#endif +#ifdef CONFIG_BLK_DEV_QD65XX + case -12: /* "qd65xx" */ + probe_qd65xx = 1; + goto done; +#endif +#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 obsolete_option; +#else + goto bad_hwif; +#endif + case -6: /* dma */ + hwif->autodma = 1; + goto obsolete_option; + case -5: /* "reset" */ + hwif->reset = 1; + goto obsolete_option; + case -4: /* "noautotune" */ + hwif->drives[0].autotune = IDE_TUNE_NOAUTO; + hwif->drives[1].autotune = IDE_TUNE_NOAUTO; + goto obsolete_option; + case -3: /* "autotune" */ + hwif->drives[0].autotune = IDE_TUNE_AUTO; + hwif->drives[1].autotune = IDE_TUNE_AUTO; + goto obsolete_option; + case -2: /* "serialize" */ + do_serialize: + hwif->mate = &ide_hwifs[hw^1]; + hwif->mate->mate = hwif; + hwif->serialized = hwif->mate->serialized = 1; + goto obsolete_option; + + 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, (unsigned long) vals[0], (unsigned long) 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_forced; + goto obsolete_option; + + case 0: goto bad_option; + default: + printk(" -- SUPPORT NOT CONFIGURED IN THIS KERNEL\n"); + return 1; + } + } +bad_option: + printk(" -- BAD OPTION\n"); + return 1; +obsolete_option: + printk(" -- OBSOLETE OPTION, WILL BE REMOVED SOON!\n"); + return 1; +bad_hwif: + printk("-- NOT SUPPORTED ON ide%d", hw); +done: + printk("\n"); + return 1; +} + +extern void pnpide_init(void); +extern void h8300_ide_init(void); + +/* + * probe_for_hwifs() finds/initializes "known" IDE interfaces + */ +static void __init probe_for_hwifs (void) +{ +#ifdef CONFIG_BLK_DEV_IDEPCI + ide_scan_pcibus(ide_scan_direction); +#endif /* CONFIG_BLK_DEV_IDEPCI */ + +#ifdef CONFIG_ETRAX_IDE + { + extern void init_e100_ide(void); + init_e100_ide(); + } +#endif /* CONFIG_ETRAX_IDE */ +#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_IDE_PMAC + { + extern void pmac_ide_probe(void); + pmac_ide_probe(); + } +#endif /* CONFIG_BLK_DEV_IDE_PMAC */ +#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_Q40IDE + { + extern void q40ide_init(void); + q40ide_init(); + } +#endif /* CONFIG_BLK_DEV_Q40IDE */ +#ifdef CONFIG_BLK_DEV_BUDDHA + { + extern void buddha_init(void); + buddha_init(); + } +#endif /* CONFIG_BLK_DEV_BUDDHA */ +#ifdef CONFIG_BLK_DEV_IDEPNP + pnpide_init(); +#endif +#ifdef CONFIG_H8300 + h8300_ide_init(); +#endif +} + +int ide_register_subdriver(ide_drive_t *drive, ide_driver_t *driver) +{ + unsigned long flags; + + spin_lock_irqsave(&ide_lock, flags); + if (!drive->present || drive->driver != NULL || + drive->usage || drive->dead) { + spin_unlock_irqrestore(&ide_lock, flags); + return 1; + } + drive->driver = driver; + spin_unlock_irqrestore(&ide_lock, flags); + spin_lock(&drives_lock); + list_add_tail(&drive->list, driver ? &driver->drives : &ide_drives); + spin_unlock(&drives_lock); +// printk(KERN_INFO "%s: attached %s driver.\n", drive->name, driver->name); +#ifdef CONFIG_PROC_FS + if (driver) + ide_add_proc_entries(drive->proc, driver->proc, drive); +#endif + return 0; +} + +EXPORT_SYMBOL(ide_register_subdriver); + +/** + * ide_unregister_subdriver - disconnect drive from driver + * @drive: drive to unplug + * + * Disconnect a drive from the driver it was attached to and then + * clean up the various proc files and other objects attached to it. + * + * Takes ide_setting_sem, ide_lock and drives_lock. + * Caller must hold none of the locks. + * + * No locking versus subdriver unload because we are moving to the + * default driver anyway. Wants double checking. + */ + +int ide_unregister_subdriver (ide_drive_t *drive) +{ + unsigned long flags; + + down(&ide_setting_sem); + spin_lock_irqsave(&ide_lock, flags); + if (drive->usage || drive->driver == NULL || DRIVER(drive)->busy) { + spin_unlock_irqrestore(&ide_lock, flags); + up(&ide_setting_sem); + return 1; + } +#ifdef CONFIG_PROC_FS + ide_remove_proc_entries(drive->proc, DRIVER(drive)->proc); +#endif + auto_remove_settings(drive); + drive->driver = NULL; + spin_unlock_irqrestore(&ide_lock, flags); + up(&ide_setting_sem); + spin_lock(&drives_lock); + list_del_init(&drive->list); + spin_unlock(&drives_lock); + /* drive will be added to &ide_drives in ata_attach() */ + return 0; +} + +EXPORT_SYMBOL(ide_unregister_subdriver); + +static int ide_drive_remove(struct device * dev) +{ + ide_drive_t * drive = container_of(dev,ide_drive_t,gendev); + DRIVER(drive)->cleanup(drive); + return 0; +} + +/** + * ide_register_driver - register IDE device driver + * @driver: the IDE device driver + * + * Register a new device driver and then scan the devices + * on the IDE bus in case any should be attached to the + * driver we have just registered. If so attach them. + * + * Takes drivers_lock and drives_lock. + */ + +int ide_register_driver(ide_driver_t *driver) +{ + struct list_head list; + struct list_head *list_loop; + struct list_head *tmp_storage; + + spin_lock(&drivers_lock); + list_add(&driver->drivers, &drivers); + spin_unlock(&drivers_lock); + + INIT_LIST_HEAD(&list); + spin_lock(&drives_lock); + list_splice_init(&ide_drives, &list); + spin_unlock(&drives_lock); + + list_for_each_safe(list_loop, tmp_storage, &list) { + ide_drive_t *drive = container_of(list_loop, ide_drive_t, list); + list_del_init(&drive->list); + if (drive->present) + ata_attach(drive); + } + driver->gen_driver.name = (char *) driver->name; + driver->gen_driver.bus = &ide_bus_type; + driver->gen_driver.remove = ide_drive_remove; + return driver_register(&driver->gen_driver); +} + +EXPORT_SYMBOL(ide_register_driver); + +/** + * ide_unregister_driver - unregister IDE device driver + * @driver: the IDE device driver + * + * Called when a driver module is being unloaded. We reattach any + * devices to whatever driver claims them next (typically the default + * driver). + * + * Takes drivers_lock and called functions will take ide_setting_sem. + */ + +void ide_unregister_driver(ide_driver_t *driver) +{ + ide_drive_t *drive; + + spin_lock(&drivers_lock); + list_del(&driver->drivers); + spin_unlock(&drivers_lock); + + driver_unregister(&driver->gen_driver); + + while(!list_empty(&driver->drives)) { + drive = list_entry(driver->drives.next, ide_drive_t, list); + if (driver->cleanup(drive)) { + printk(KERN_ERR "%s: cleanup_module() called while still busy\n", drive->name); + BUG(); + } + ata_attach(drive); + } +} + +EXPORT_SYMBOL(ide_unregister_driver); + +/* + * Probe module + */ + +EXPORT_SYMBOL(ide_lock); + +struct bus_type ide_bus_type = { + .name = "ide", + .suspend = generic_ide_suspend, + .resume = generic_ide_resume, +}; + +/* + * This is gets invoked once during initialization, to set *everything* up + */ +static int __init ide_init(void) +{ + printk(KERN_INFO "Uniform Multi-Platform E-IDE driver " REVISION "\n"); + devfs_mk_dir("ide"); + system_bus_speed = ide_system_bus_speed(); + + bus_register(&ide_bus_type); + + init_ide_data(); + +#ifdef CONFIG_PROC_FS + proc_ide_root = proc_mkdir("ide", NULL); +#endif + +#ifdef CONFIG_BLK_DEV_ALI14XX + if (probe_ali14xx) + (void)ali14xx_init(); +#endif +#ifdef CONFIG_BLK_DEV_UMC8672 + if (probe_umc8672) + (void)umc8672_init(); +#endif +#ifdef CONFIG_BLK_DEV_DTC2278 + if (probe_dtc2278) + (void)dtc2278_init(); +#endif +#ifdef CONFIG_BLK_DEV_HT6560B + if (probe_ht6560b) + (void)ht6560b_init(); +#endif +#ifdef CONFIG_BLK_DEV_QD65XX + if (probe_qd65xx) + (void)qd65xx_init(); +#endif + + initializing = 1; + /* Probe for special PCI and other "known" interface chipsets. */ + probe_for_hwifs(); + initializing = 0; + +#ifdef CONFIG_PROC_FS + proc_ide_create(); +#endif + return 0; +} + +#ifdef MODULE +static char *options = NULL; +module_param(options, charp, 0); +MODULE_LICENSE("GPL"); + +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 (!ide_setup(line)) + printk (KERN_INFO "Unknown option '%s'\n", line); + } +} + +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_remove("ide"); + + bus_unregister(&ide_bus_type); +} + +#else /* !MODULE */ + +__setup("", ide_setup); + +module_init(ide_init); + +#endif /* MODULE */ diff --git a/drivers/ide/legacy/Makefile b/drivers/ide/legacy/Makefile new file mode 100644 index 0000000..c797106 --- /dev/null +++ b/drivers/ide/legacy/Makefile @@ -0,0 +1,13 @@ + +obj-$(CONFIG_BLK_DEV_ALI14XX) += ali14xx.o +obj-$(CONFIG_BLK_DEV_DTC2278) += dtc2278.o +obj-$(CONFIG_BLK_DEV_HT6560B) += ht6560b.o +obj-$(CONFIG_BLK_DEV_QD65XX) += qd65xx.o +obj-$(CONFIG_BLK_DEV_UMC8672) += umc8672.o + +obj-$(CONFIG_BLK_DEV_IDECS) += ide-cs.o + +# Last of all +obj-$(CONFIG_BLK_DEV_HD) += hd.o + +EXTRA_CFLAGS := -Idrivers/ide diff --git a/drivers/ide/legacy/ali14xx.c b/drivers/ide/legacy/ali14xx.c new file mode 100644 index 0000000..fb88711 --- /dev/null +++ b/drivers/ide/legacy/ali14xx.c @@ -0,0 +1,253 @@ +/* + * linux/drivers/ide/legacy/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/module.h> +#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 <linux/init.h> + +#include <asm/io.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 { u8 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 { u8 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; /* base port address */ +static int regPort; /* port for register number */ +static int dataPort; /* port for register data */ +static u8 regOn; /* output to base port to access registers */ +static u8 regOff; /* output to base port to close registers */ + +/*------------------------------------------------------------------------*/ + +/* + * Read a controller register. + */ +static inline u8 inReg (u8 reg) +{ + outb_p(reg, regPort); + return inb(dataPort); +} + +/* + * Write a controller register. + */ +static void outReg (u8 data, u8 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, u8 pio) +{ + int driveNum; + int time1, time2; + u8 param1, param2, param3, param4; + unsigned long flags; + ide_pio_data_t d; + int bus_speed = system_bus_clock(); + + 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(KERN_DEBUG "%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; + spin_lock_irqsave(&ide_lock, flags); + 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); + spin_unlock_irqrestore(&ide_lock, flags); +} + +/* + * Auto-detect the IDE controller port. + */ +static int __init findPort (void) +{ + int i; + u8 t; + unsigned long flags; + + local_irq_save(flags); + 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); + local_irq_restore(flags); + if (t != 0x50) + return 0; + return 1; /* success */ + } + } + outb_p(regOff, basePort); + } + local_irq_restore(flags); + return 0; +} + +/* + * Initialize controller registers with default values. + */ +static int __init initRegisters (void) { + RegInitializer *p; + u8 t; + unsigned long flags; + + local_irq_save(flags); + 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); + local_irq_restore(flags); + return t; +} + +static int __init ali14xx_probe(void) +{ + ide_hwif_t *hwif, *mate; + + printk(KERN_DEBUG "ali14xx: base=0x%03x, regOn=0x%02x.\n", + basePort, regOn); + + /* initialize controller registers */ + if (!initRegisters()) { + printk(KERN_ERR "ali14xx: Chip initialization failed.\n"); + return 1; + } + + hwif = &ide_hwifs[0]; + mate = &ide_hwifs[1]; + + hwif->chipset = ide_ali14xx; + hwif->tuneproc = &ali14xx_tune_drive; + hwif->mate = mate; + + mate->chipset = ide_ali14xx; + mate->tuneproc = &ali14xx_tune_drive; + mate->mate = hwif; + mate->channel = 1; + + probe_hwif_init(hwif); + probe_hwif_init(mate); + + create_proc_ide_interfaces(); + + return 0; +} + +/* Can be called directly from ide.c. */ +int __init ali14xx_init(void) +{ + /* auto-detect IDE controller port */ + if (findPort()) { + if (ali14xx_probe()) + return -ENODEV; + return 0; + } + printk(KERN_ERR "ali14xx: not found.\n"); + return -ENODEV; +} + +#ifdef MODULE +module_init(ali14xx_init); +#endif + +MODULE_AUTHOR("see local file"); +MODULE_DESCRIPTION("support of ALI 14XX IDE chipsets"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/legacy/buddha.c b/drivers/ide/legacy/buddha.c new file mode 100644 index 0000000..0391a31 --- /dev/null +++ b/drivers/ide/legacy/buddha.c @@ -0,0 +1,235 @@ +/* + * linux/drivers/ide/legacy/buddha.c -- Amiga Buddha, Catweasel and X-Surf IDE Driver + * + * Copyright (C) 1997, 2001 by Geert Uytterhoeven and others + * + * This driver was written based on the specifications in README.buddha and + * the X-Surf info from Inside_XSurf.txt available at + * http://www.jschoenfeld.com + * + * 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 <linux/init.h> + +#include <asm/amigahw.h> +#include <asm/amigaints.h> + + + /* + * The Buddha has 2 IDE interfaces, the Catweasel has 3, X-Surf has 2 + */ + +#define BUDDHA_NUM_HWIFS 2 +#define CATWEASEL_NUM_HWIFS 3 +#define XSURF_NUM_HWIFS 2 + + /* + * Bases of the IDE interfaces (relative to the board address) + */ + +#define BUDDHA_BASE1 0x800 +#define BUDDHA_BASE2 0xa00 +#define BUDDHA_BASE3 0xc00 + +#define XSURF_BASE1 0xb000 /* 2.5" Interface */ +#define XSURF_BASE2 0xd000 /* 3.5" Interface */ + +static u_int buddha_bases[CATWEASEL_NUM_HWIFS] __initdata = { + BUDDHA_BASE1, BUDDHA_BASE2, BUDDHA_BASE3 +}; + +static u_int xsurf_bases[XSURF_NUM_HWIFS] __initdata = { + XSURF_BASE1, XSURF_BASE2 +}; + + + /* + * 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 +#define XSURF_CONTROL -1 /* X-Surf has no CS1* (Control/AltStat) */ + +static int buddha_offsets[IDE_NR_PORTS] __initdata = { + BUDDHA_DATA, BUDDHA_ERROR, BUDDHA_NSECTOR, BUDDHA_SECTOR, BUDDHA_LCYL, + BUDDHA_HCYL, BUDDHA_SELECT, BUDDHA_STATUS, BUDDHA_CONTROL, -1 +}; + +static int xsurf_offsets[IDE_NR_PORTS] __initdata = { + BUDDHA_DATA, BUDDHA_ERROR, BUDDHA_NSECTOR, BUDDHA_SECTOR, BUDDHA_LCYL, + BUDDHA_HCYL, BUDDHA_SELECT, BUDDHA_STATUS, XSURF_CONTROL, -1 +}; + + /* + * Other registers + */ + +#define BUDDHA_IRQ1 0xf00 /* MSB = 1, Harddisk is source of */ +#define BUDDHA_IRQ2 0xf40 /* interrupt */ +#define BUDDHA_IRQ3 0xf80 + +#define XSURF_IRQ1 0x7e +#define XSURF_IRQ2 0x7e + +static int buddha_irqports[CATWEASEL_NUM_HWIFS] __initdata = { + BUDDHA_IRQ1, BUDDHA_IRQ2, BUDDHA_IRQ3 +}; + +static int xsurf_irqports[XSURF_NUM_HWIFS] __initdata = { + XSURF_IRQ1, XSURF_IRQ2 +}; + +#define BUDDHA_IRQ_MR 0xfc0 /* master interrupt enable */ + + + /* + * Board information + */ + +typedef enum BuddhaType_Enum { + BOARD_BUDDHA, BOARD_CATWEASEL, BOARD_XSURF +} BuddhaType; + + + /* + * Check and acknowledge the interrupt status + */ + +static int buddha_ack_intr(ide_hwif_t *hwif) +{ + unsigned char ch; + + ch = z_readb(hwif->io_ports[IDE_IRQ_OFFSET]); + if (!(ch & 0x80)) + return 0; + return 1; +} + +static int xsurf_ack_intr(ide_hwif_t *hwif) +{ + unsigned char ch; + + ch = z_readb(hwif->io_ports[IDE_IRQ_OFFSET]); + /* X-Surf needs a 0 written to IRQ register to ensure ISA bit A11 stays at 0 */ + z_writeb(0, hwif->io_ports[IDE_IRQ_OFFSET]); + if (!(ch & 0x80)) + return 0; + return 1; +} + + /* + * Probe for a Buddha or Catweasel IDE interface + */ + +void __init buddha_init(void) +{ + hw_regs_t hw; + ide_hwif_t *hwif; + int i, index; + + struct zorro_dev *z = NULL; + u_long buddha_board = 0; + BuddhaType type; + int buddha_num_hwifs; + + while ((z = zorro_find_device(ZORRO_WILDCARD, z))) { + unsigned long board; + if (z->id == ZORRO_PROD_INDIVIDUAL_COMPUTERS_BUDDHA) { + buddha_num_hwifs = BUDDHA_NUM_HWIFS; + type=BOARD_BUDDHA; + } else if (z->id == ZORRO_PROD_INDIVIDUAL_COMPUTERS_CATWEASEL) { + buddha_num_hwifs = CATWEASEL_NUM_HWIFS; + type=BOARD_CATWEASEL; + } else if (z->id == ZORRO_PROD_INDIVIDUAL_COMPUTERS_X_SURF) { + buddha_num_hwifs = XSURF_NUM_HWIFS; + type=BOARD_XSURF; + } else + continue; + + board = z->resource.start; + +/* + * FIXME: we now have selectable mmio v/s iomio transports. + */ + + if(type != BOARD_XSURF) { + if (!request_mem_region(board+BUDDHA_BASE1, 0x800, "IDE")) + continue; + } else { + if (!request_mem_region(board+XSURF_BASE1, 0x1000, "IDE")) + continue; + if (!request_mem_region(board+XSURF_BASE2, 0x1000, "IDE")) + goto fail_base2; + if (!request_mem_region(board+XSURF_IRQ1, 0x8, "IDE")) { + release_mem_region(board+XSURF_BASE2, 0x1000); +fail_base2: + release_mem_region(board+XSURF_BASE1, 0x1000); + continue; + } + } + buddha_board = ZTWO_VADDR(board); + + /* write to BUDDHA_IRQ_MR to enable the board IRQ */ + /* X-Surf doesn't have this. IRQs are always on */ + if (type != BOARD_XSURF) + z_writeb(0, buddha_board+BUDDHA_IRQ_MR); + + for(i=0;i<buddha_num_hwifs;i++) { + if(type != BOARD_XSURF) { + ide_setup_ports(&hw, (buddha_board+buddha_bases[i]), + buddha_offsets, 0, + (buddha_board+buddha_irqports[i]), + buddha_ack_intr, +// budda_iops, + IRQ_AMIGA_PORTS); + } else { + ide_setup_ports(&hw, (buddha_board+xsurf_bases[i]), + xsurf_offsets, 0, + (buddha_board+xsurf_irqports[i]), + xsurf_ack_intr, +// xsurf_iops, + IRQ_AMIGA_PORTS); + } + + index = ide_register_hw(&hw, &hwif); + if (index != -1) { + hwif->mmio = 2; + printk("ide%d: ", index); + switch(type) { + case BOARD_BUDDHA: + printk("Buddha"); + break; + case BOARD_CATWEASEL: + printk("Catweasel"); + break; + case BOARD_XSURF: + printk("X-Surf"); + break; + } + printk(" IDE interface\n"); + } + } + } +} diff --git a/drivers/ide/legacy/dtc2278.c b/drivers/ide/legacy/dtc2278.c new file mode 100644 index 0000000..20eb5b8 --- /dev/null +++ b/drivers/ide/legacy/dtc2278.c @@ -0,0 +1,165 @@ +/* + * linux/drivers/ide/legacy/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/module.h> +#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 <linux/init.h> + +#include <asm/io.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, u8 pio) +{ + unsigned long flags; + + pio = ide_get_best_pio_mode(drive, pio, 4, NULL); + + if (pio >= 3) { + spin_lock_irqsave(&ide_lock, flags); + /* + * This enables PIO mode4 (3?) on the first interface + */ + sub22(1,0xc3); + sub22(0,0xa0); + spin_unlock_irqrestore(&ide_lock, flags); + } 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; +} + +static int __init probe_dtc2278(void) +{ + unsigned long flags; + ide_hwif_t *hwif, *mate; + + hwif = &ide_hwifs[0]; + mate = &ide_hwifs[1]; + + if (hwif->chipset != ide_unknown || mate->chipset != ide_unknown) + return 1; + + local_irq_save(flags); + /* + * 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 + local_irq_restore(flags); + + hwif->serialized = 1; + hwif->chipset = ide_dtc2278; + hwif->tuneproc = &tune_dtc2278; + hwif->drives[0].no_unmask = 1; + hwif->drives[1].no_unmask = 1; + hwif->mate = mate; + + mate->serialized = 1; + mate->chipset = ide_dtc2278; + mate->drives[0].no_unmask = 1; + mate->drives[1].no_unmask = 1; + mate->mate = hwif; + mate->channel = 1; + + probe_hwif_init(hwif); + probe_hwif_init(mate); + + create_proc_ide_interfaces(); + + return 0; +} + +/* Can be called directly from ide.c. */ +int __init dtc2278_init(void) +{ + if (probe_dtc2278()) { + printk(KERN_ERR "dtc2278: ide interfaces already in use!\n"); + return -EBUSY; + } + return 0; +} + +#ifdef MODULE +module_init(dtc2278_init); +#endif + +MODULE_AUTHOR("See Local File"); +MODULE_DESCRIPTION("support of DTC-2278 VLB IDE chipsets"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/legacy/falconide.c b/drivers/ide/legacy/falconide.c new file mode 100644 index 0000000..a9f2cd5 --- /dev/null +++ b/drivers/ide/legacy/falconide.c @@ -0,0 +1,78 @@ +/* + * linux/drivers/ide/legacy/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 <linux/init.h> + +#include <asm/setup.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 falconide_offsets[IDE_NR_PORTS] __initdata = { + 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 +}; + + + /* + * falconide_intr_lock is used to obtain access to the IDE interrupt, + * which is shared between several drivers. + */ + +int falconide_intr_lock; + + + /* + * 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, ATA_HD_BASE, falconide_offsets, + 0, 0, NULL, +// falconide_iops, + 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/legacy/gayle.c b/drivers/ide/legacy/gayle.c new file mode 100644 index 0000000..3fac3e9 --- /dev/null +++ b/drivers/ide/legacy/gayle.c @@ -0,0 +1,186 @@ +/* + * linux/drivers/ide/legacy/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 <linux/init.h> +#include <linux/zorro.h> + +#include <asm/setup.h> +#include <asm/amigahw.h> +#include <asm/amigaints.h> +#include <asm/amigayle.h> + + + /* + * Bases of the IDE interfaces + */ + +#define GAYLE_BASE_4000 0xdd2020 /* A4000/A4000T */ +#define GAYLE_BASE_1200 0xda0000 /* A1200/A600 and E-Matrix 530 */ + + /* + * 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 gayle_offsets[IDE_NR_PORTS] __initdata = { + 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 +#define GAYLE_IDEREG_SIZE 0x2000 +#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) +#define GAYLE_IDEREG_SIZE (ide_doubler ? 0x1000 : 0x2000) +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 = z_readb(hwif->io_ports[IDE_IRQ_OFFSET]); + if (!(ch & GAYLE_IRQ_IDE)) + return 0; + return 1; +} + +static int gayle_ack_intr_a1200(ide_hwif_t *hwif) +{ + unsigned char ch; + + ch = z_readb(hwif->io_ports[IDE_IRQ_OFFSET]); + if (!(ch & GAYLE_IRQ_IDE)) + return 0; + (void)z_readb(hwif->io_ports[IDE_STATUS_OFFSET]); + z_writeb(0x7c, 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)) + goto found; + +#ifdef CONFIG_ZORRO + if (zorro_find_device(ZORRO_PROD_MTEC_VIPER_MK_V_E_MATRIX_530_SCSI_IDE, + NULL)) + goto found; +#endif + return; + +found: + for (i = 0; i < GAYLE_NUM_PROBE_HWIFS; i++) { + unsigned long base, ctrlport, irqport; + ide_ack_intr_t *ack_intr; + hw_regs_t hw; + ide_hwif_t *hwif; + int index; + unsigned long phys_base, res_start, res_n; + + if (a4000) { + phys_base = GAYLE_BASE_4000; + irqport = (unsigned long)ZTWO_VADDR(GAYLE_IRQ_4000); + ack_intr = gayle_ack_intr_a4000; + } else { + phys_base = GAYLE_BASE_1200; + irqport = (unsigned long)ZTWO_VADDR(GAYLE_IRQ_1200); + ack_intr = gayle_ack_intr_a1200; + } +/* + * FIXME: we now have selectable modes between mmio v/s iomio + */ + + phys_base += i*GAYLE_NEXT_PORT; + + res_start = ((unsigned long)phys_base) & ~(GAYLE_NEXT_PORT-1); + res_n = GAYLE_IDEREG_SIZE; + + if (!request_mem_region(res_start, res_n, "IDE")) + continue; + + base = (unsigned long)ZTWO_VADDR(phys_base); + ctrlport = GAYLE_HAS_CONTROL_REG ? (base + GAYLE_CONTROL) : 0; + + ide_setup_ports(&hw, base, gayle_offsets, + ctrlport, irqport, ack_intr, +// &gayle_iops, + IRQ_AMIGA_PORTS); + + index = ide_register_hw(&hw, &hwif); + if (index != -1) { + hwif->mmio = 2; + 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 */ + } + } else + release_mem_region(res_start, res_n); + } +} diff --git a/drivers/ide/legacy/hd.c b/drivers/ide/legacy/hd.c new file mode 100644 index 0000000..c409055 --- /dev/null +++ b/drivers/ide/legacy/hd.c @@ -0,0 +1,864 @@ +/* + * 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. + * + * Bugfix: max_sectors must be <= 255 or the wheels tend to come + * off in a hurry once you queue things up - Paul G. 02/2001 + */ + +/* Uncomment the following if you want verbose error reports. */ +/* #define VERBOSE_ERRORS */ + +#include <linux/blkdev.h> +#include <linux/errno.h> +#include <linux/signal.h> +#include <linux/interrupt.h> +#include <linux/timer.h> +#include <linux/fs.h> +#include <linux/kernel.h> +#include <linux/genhd.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/ioport.h> +#include <linux/mc146818rtc.h> /* CMOS defines */ +#include <linux/init.h> +#include <linux/blkpg.h> +#include <linux/hdreg.h> + +#define REALLY_SLOW_IO +#include <asm/system.h> +#include <asm/io.h> +#include <asm/uaccess.h> + +#ifdef __arm__ +#undef HD_IRQ +#endif +#include <asm/irq.h> +#ifdef __arm__ +#define HD_IRQ IRQ_HARDDISK +#endif + +/* Hd controller regster ports */ + +#define HD_DATA 0x1f0 /* _CTL when writing */ +#define HD_ERROR 0x1f1 /* see err-bits */ +#define HD_NSECTOR 0x1f2 /* nr of sectors to read/write */ +#define HD_SECTOR 0x1f3 /* starting sector */ +#define HD_LCYL 0x1f4 /* starting cylinder */ +#define HD_HCYL 0x1f5 /* high byte of starting cyl */ +#define HD_CURRENT 0x1f6 /* 101dhhhh , d=drive, hhhh=head */ +#define HD_STATUS 0x1f7 /* see status-bits */ +#define HD_FEATURE HD_ERROR /* same io address, read=error, write=feature */ +#define HD_PRECOMP HD_FEATURE /* obsolete use of this port - predates IDE */ +#define HD_COMMAND HD_STATUS /* same io address, read=status, write=cmd */ + +#define HD_CMD 0x3f6 /* used for resets */ +#define HD_ALTSTATUS 0x3f6 /* same as HD_STATUS but doesn't clear irq */ + +/* Bits of HD_STATUS */ +#define ERR_STAT 0x01 +#define INDEX_STAT 0x02 +#define ECC_STAT 0x04 /* Corrected error */ +#define DRQ_STAT 0x08 +#define SEEK_STAT 0x10 +#define SERVICE_STAT SEEK_STAT +#define WRERR_STAT 0x20 +#define READY_STAT 0x40 +#define BUSY_STAT 0x80 + +/* Bits for HD_ERROR */ +#define MARK_ERR 0x01 /* Bad address mark */ +#define TRK0_ERR 0x02 /* couldn't find track 0 */ +#define ABRT_ERR 0x04 /* Command aborted */ +#define MCR_ERR 0x08 /* media change request */ +#define ID_ERR 0x10 /* ID field not found */ +#define MC_ERR 0x20 /* media changed */ +#define ECC_ERR 0x40 /* Uncorrectable ECC error */ +#define BBD_ERR 0x80 /* pre-EIDE meaning: block marked bad */ +#define ICRC_ERR 0x80 /* new meaning: CRC error during transfer */ + +static DEFINE_SPINLOCK(hd_lock); +static struct request_queue *hd_queue; + +#define MAJOR_NR HD_MAJOR +#define QUEUE (hd_queue) +#define CURRENT elv_next_request(hd_queue) + +#define TIMEOUT_VALUE (6*HZ) +#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 int reset; +static int hd_error; + +/* + * This struct defines the HD's and their types. + */ +struct hd_i_struct { + unsigned int head,sect,cyl,wpcom,lzone,ctl; + int unit; + int recalibrate; + int special_op; +}; + +#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 gendisk *hd_gendisk[MAX_HD]; + +static struct timer_list device_timer; + +#define TIMEOUT_VALUE (6*HZ) + +#define SET_TIMER \ + do { \ + mod_timer(&device_timer, jiffies + TIMEOUT_VALUE); \ + } while (0) + +static void (*do_hd)(void) = NULL; +#define SET_HANDLER(x) \ +if ((do_hd = (x)) != NULL) \ + SET_TIMER; \ +else \ + del_timer(&device_timer); + + +#if (HD_DELAY > 0) +unsigned long last_req; + +unsigned long read_timer(void) +{ + extern spinlock_t i8253_lock; + unsigned long t, flags; + int i; + + spin_lock_irqsave(&i8253_lock, flags); + t = jiffies * 11932; + outb_p(0, 0x43); + i = inb_p(0x40); + i |= inb(0x40) << 8; + spin_unlock_irqrestore(&i8253_lock, flags); + return(t - i); +} +#endif + +static 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) +{ + char *name = "hd?"; + if (CURRENT) + name = CURRENT->rq_disk->disk_name; + +#ifdef VERBOSE_ERRORS + printk("%s: %s: status=0x%02x { ", name, 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("%s: %s: error=0x%02x { ", name, 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 (CURRENT) + printk(", sector=%ld", CURRENT->sector); + } + printk("\n"); + } +#else + printk("%s: %s: status=0x%02x.\n", name, msg, stat & 0xff); + if ((stat & ERR_STAT) == 0) { + hd_error = 0; + } else { + hd_error = inb(HD_ERROR); + printk("%s: %s: error=0x%02x.\n", name, msg, hd_error & 0xff); + } +#endif +} + +static 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(struct hd_i_struct *disk, + 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(disk->unit, head)) { + reset = 1; + return; + } + SET_HANDLER(intr_addr); + outb_p(disk->ctl,HD_CMD); + port=HD_DATA; + outb_p(disk->wpcom>>2,++port); + outb_p(nsect,++port); + outb_p(sect,++port); + outb_p(cyl,++port); + outb_p(cyl>>8,++port); + outb_p(0xA0|(disk->unit<<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) { + struct hd_i_struct *disk = &hd_info[i]; + disk->special_op = disk->recalibrate = 1; + hd_out(disk,disk->sect,disk->sect,disk->head-1, + disk->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. + */ +static 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) +{ + struct request *req = CURRENT; + if (req != NULL) { + struct hd_i_struct *disk = req->rq_disk->private_data; + if (++req->errors >= MAX_ERRORS || (hd_error & BBD_ERR)) { + end_request(req, 0); + disk->special_op = disk->recalibrate = 1; + } else if (req->errors % RESET_FREQ == 0) + reset = 1; + else if ((hd_error & TRK0_ERR) || req->errors % RECAL_FREQ == 0) + disk->special_op = disk->recalibrate = 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) +{ + struct request *req; + 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: + req = CURRENT; + insw(HD_DATA,req->buffer,256); + req->sector++; + req->buffer += 512; + req->errors = 0; + i = --req->nr_sectors; + --req->current_nr_sectors; +#ifdef DEBUG + printk("%s: read: sector %ld, remaining = %ld, buffer=%p\n", + req->rq_disk->disk_name, req->sector, req->nr_sectors, + req->buffer+512)); +#endif + if (req->current_nr_sectors <= 0) + end_request(req, 1); + if (i > 0) { + SET_HANDLER(&read_intr); + return; + } + (void) inb_p(HD_STATUS); +#if (HD_DELAY > 0) + last_req = read_timer(); +#endif + if (elv_next_request(QUEUE)) + hd_request(); + return; +} + +static void write_intr(void) +{ + struct request *req = CURRENT; + int i; + int retries = 100000; + + do { + i = (unsigned) inb_p(HD_STATUS); + if (i & BUSY_STAT) + continue; + if (!OK_STATUS(i)) + break; + if ((req->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: + req->sector++; + i = --req->nr_sectors; + --req->current_nr_sectors; + req->buffer += 512; + if (!i || (req->bio && req->current_nr_sectors <= 0)) + end_request(req, 1); + if (i > 0) { + SET_HANDLER(&write_intr); + outsw(HD_DATA,req->buffer,256); + local_irq_enable(); + } 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(unsigned long dummy) +{ + char *name; + + do_hd = NULL; + + if (!CURRENT) + return; + + disable_irq(HD_IRQ); + local_irq_enable(); + reset = 1; + name = CURRENT->rq_disk->disk_name; + printk("%s: timeout\n", name); + if (++CURRENT->errors >= MAX_ERRORS) { +#ifdef DEBUG + printk("%s: too many errors\n", name); +#endif + end_request(CURRENT, 0); + } + local_irq_disable(); + hd_request(); + enable_irq(HD_IRQ); +} + +static int do_special_op(struct hd_i_struct *disk, struct request *req) +{ + if (disk->recalibrate) { + disk->recalibrate = 0; + hd_out(disk,disk->sect,0,0,0,WIN_RESTORE,&recal_intr); + return reset; + } + if (disk->head > 16) { + printk ("%s: cannot handle device with more than 16 heads - giving up\n", req->rq_disk->disk_name); + end_request(req, 0); + } + disk->special_op = 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 block, nsect, sec, track, head, cyl; + struct hd_i_struct *disk; + struct request *req; + + if (do_hd) + return; +repeat: + del_timer(&device_timer); + local_irq_enable(); + + req = CURRENT; + if (!req) { + do_hd = NULL; + return; + } + + if (reset) { + local_irq_disable(); + reset_hd(); + return; + } + disk = req->rq_disk->private_data; + block = req->sector; + nsect = req->nr_sectors; + if (block >= get_capacity(req->rq_disk) || + ((block+nsect) > get_capacity(req->rq_disk))) { + printk("%s: bad access: block=%d, count=%d\n", + req->rq_disk->disk_name, block, nsect); + end_request(req, 0); + goto repeat; + } + + if (disk->special_op) { + if (do_special_op(disk, req)) + goto repeat; + return; + } + sec = block % disk->sect + 1; + track = block / disk->sect; + head = track % disk->head; + cyl = track / disk->head; +#ifdef DEBUG + printk("%s: %sing: CHS=%d/%d/%d, sectors=%d, buffer=%p\n", + req->rq_disk->disk_name, (req->cmd == READ)?"read":"writ", + cyl, head, sec, nsect, req->buffer); +#endif + if (req->flags & REQ_CMD) { + switch (rq_data_dir(req)) { + case READ: + hd_out(disk,nsect,sec,head,cyl,WIN_READ,&read_intr); + if (reset) + goto repeat; + break; + case WRITE: + hd_out(disk,nsect,sec,head,cyl,WIN_WRITE,&write_intr); + if (reset) + goto repeat; + if (wait_DRQ()) { + bad_rw_intr(); + goto repeat; + } + outsw(HD_DATA,req->buffer,256); + break; + default: + printk("unknown hd-command\n"); + end_request(req, 0); + break; + } + } +} + +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_i_struct *disk = inode->i_bdev->bd_disk->private_data; + struct hd_geometry __user *loc = (struct hd_geometry __user *) arg; + struct hd_geometry g; + + if (cmd != HDIO_GETGEO) + return -EINVAL; + if (!loc) + return -EINVAL; + g.heads = disk->head; + g.sectors = disk->sect; + g.cylinders = disk->cyl; + g.start = get_start_sect(inode->i_bdev); + return copy_to_user(loc, &g, sizeof g) ? -EFAULT : 0; +} + +/* + * Releasing a block device means we sync() it, so that it can safely + * be forgotten about... + */ + +static irqreturn_t hd_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + void (*handler)(void) = do_hd; + + do_hd = NULL; + del_timer(&device_timer); + if (!handler) + handler = unexpected_hd_interrupt; + handler(); + local_irq_enable(); + return IRQ_HANDLED; +} + +static struct block_device_operations hd_fops = { + .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 int __init hd_init(void) +{ + int drive; + + if (register_blkdev(MAJOR_NR,"hd")) + return -1; + + hd_queue = blk_init_queue(do_hd_request, &hd_lock); + if (!hd_queue) { + unregister_blkdev(MAJOR_NR,"hd"); + return -ENOMEM; + } + + blk_queue_max_sectors(hd_queue, 255); + init_timer(&device_timer); + device_timer.function = hd_times_out; + blk_queue_hardsect_size(hd_queue, 512); + +#ifdef __i386__ + if (!NR_HD) { + extern struct drive_info drive_info; + unsigned char *BIOS = (unsigned char *) &drive_info; + unsigned long flags; + 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. + + Currently the rtc_lock is a bit academic since this + driver is non-modular, but someday... ? Paul G. + */ + + spin_lock_irqsave(&rtc_lock, flags); + cmos_disks = CMOS_READ(0x12); + spin_unlock_irqrestore(&rtc_lock, flags); + + if (cmos_disks & 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 + if (!NR_HD) + goto out; + + for (drive=0 ; drive < NR_HD ; drive++) { + struct gendisk *disk = alloc_disk(64); + struct hd_i_struct *p = &hd_info[drive]; + if (!disk) + goto Enomem; + disk->major = MAJOR_NR; + disk->first_minor = drive << 6; + disk->fops = &hd_fops; + sprintf(disk->disk_name, "hd%c", 'a'+drive); + disk->private_data = p; + set_capacity(disk, p->head * p->sect * p->cyl); + disk->queue = hd_queue; + p->unit = drive; + hd_gendisk[drive] = disk; + printk ("%s: %luMB, CHS=%d/%d/%d\n", + disk->disk_name, (unsigned long)get_capacity(disk)/2048, + p->cyl, p->head, p->sect); + } + + 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); + goto out1; + } + if (!request_region(HD_DATA, 8, "hd")) { + printk(KERN_WARNING "hd: port 0x%x busy\n", HD_DATA); + goto out2; + } + if (!request_region(HD_CMD, 1, "hd(cmd)")) { + printk(KERN_WARNING "hd: port 0x%x busy\n", HD_CMD); + goto out3; + } + + /* Let them fly */ + for(drive=0; drive < NR_HD; drive++) + add_disk(hd_gendisk[drive]); + + return 0; + +out3: + release_region(HD_DATA, 8); +out2: + free_irq(HD_IRQ, NULL); +out1: + for (drive = 0; drive < NR_HD; drive++) + put_disk(hd_gendisk[drive]); + NR_HD = 0; +out: + del_timer(&device_timer); + unregister_blkdev(MAJOR_NR,"hd"); + blk_cleanup_queue(hd_queue); + return -1; +Enomem: + while (drive--) + put_disk(hd_gendisk[drive]); + goto out; +} + +static int parse_hd_setup (char *line) { + int ints[6]; + + (void) get_options(line, ARRAY_SIZE(ints), ints); + hd_setup(NULL, ints); + + return 1; +} +__setup("hd=", parse_hd_setup); + +module_init(hd_init); diff --git a/drivers/ide/legacy/ht6560b.c b/drivers/ide/legacy/ht6560b.c new file mode 100644 index 0000000..a77fb24 --- /dev/null +++ b/drivers/ide/legacy/ht6560b.c @@ -0,0 +1,370 @@ +/* + * linux/drivers/ide/legacy/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/module.h> +#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 <linux/init.h> + +#include <asm/io.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) (u8)(((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) (u8)((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 u8 current_select = 0; + static u8 current_timing = 0; + u8 select, timing; + + local_irq_save(flags); + + 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) HWIF(drive)->INB(HT_CONFIG_PORT); + (void) HWIF(drive)->INB(HT_CONFIG_PORT); + (void) HWIF(drive)->INB(HT_CONFIG_PORT); + (void) HWIF(drive)->INB(HT_CONFIG_PORT); + HWIF(drive)->OUTB(select, HT_CONFIG_PORT); + /* + * Set timing for this drive: + */ + HWIF(drive)->OUTB(timing, IDE_SELECT_REG); + (void) HWIF(drive)->INB(IDE_STATUS_REG); +#ifdef DEBUG + printk("ht6560b: %s: select=%#x timing=%#x\n", + drive->name, select, timing); +#endif + } + local_irq_restore(flags); +} + +/* + * Autodetection and initialization of ht6560b + */ +static int __init try_to_init_ht6560b(void) +{ + u8 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 u8 ht_pio2timings(ide_drive_t *drive, u8 pio) +{ + int active_time, recovery_time; + int active_cycles, recovery_cycles; + ide_pio_data_t d; + int bus_speed = system_bus_clock(); + + 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. + */ + 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 (u8)((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, u8 state) +{ + unsigned long flags; + int t = HT_PREFETCH_MODE << 8; + + spin_lock_irqsave(&ide_lock, flags); + + /* + * 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; + } + + spin_unlock_irqrestore(&ide_lock, flags); + +#ifdef DEBUG + printk("ht6560b: drive %s prefetch mode %sabled\n", drive->name, (state ? "en" : "dis")); +#endif +} + +static void tune_ht6560b (ide_drive_t *drive, u8 pio) +{ + unsigned long flags; + u8 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); + + spin_lock_irqsave(&ide_lock, flags); + + drive->drive_data &= 0xff00; + drive->drive_data |= timing; + + spin_unlock_irqrestore(&ide_lock, flags); + +#ifdef DEBUG + printk("ht6560b: drive %s tuned to pio mode %#x timing=%#x\n", drive->name, pio, timing); +#endif +} + +/* Can be called directly from ide.c. */ +int __init ht6560b_init(void) +{ + ide_hwif_t *hwif, *mate; + int t; + + hwif = &ide_hwifs[0]; + mate = &ide_hwifs[1]; + + if (!request_region(HT_CONFIG_PORT, 1, hwif->name)) { + printk(KERN_NOTICE "%s: HT_CONFIG_PORT not found\n", + __FUNCTION__); + return -ENODEV; + } + + if (!try_to_init_ht6560b()) { + printk(KERN_NOTICE "%s: HBA not found\n", __FUNCTION__); + goto release_region; + } + + hwif->chipset = ide_ht6560b; + hwif->selectproc = &ht6560b_selectproc; + hwif->tuneproc = &tune_ht6560b; + hwif->serialized = 1; /* is this needed? */ + hwif->mate = mate; + + mate->chipset = ide_ht6560b; + mate->selectproc = &ht6560b_selectproc; + mate->tuneproc = &tune_ht6560b; + mate->serialized = 1; /* is this needed? */ + mate->mate = hwif; + mate->channel = 1; + + /* + * Setting default configurations for drives + */ + t = (HT_CONFIG_DEFAULT << 8); + t |= HT_TIMING_DEFAULT; + hwif->drives[0].drive_data = t; + hwif->drives[1].drive_data = t; + + t |= (HT_SECONDARY_IF << 8); + mate->drives[0].drive_data = t; + mate->drives[1].drive_data = t; + + probe_hwif_init(hwif); + probe_hwif_init(mate); + + create_proc_ide_interfaces(); + + return 0; + +release_region: + release_region(HT_CONFIG_PORT, 1); + return -ENODEV; +} + +#ifdef MODULE +module_init(ht6560b_init); +#endif + +MODULE_AUTHOR("See Local File"); +MODULE_DESCRIPTION("HT-6560B EIDE-controller support"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/legacy/ide-cs.c b/drivers/ide/legacy/ide-cs.c new file mode 100644 index 0000000..e20327e --- /dev/null +++ b/drivers/ide/legacy/ide-cs.c @@ -0,0 +1,481 @@ +/*====================================================================== + + A driver for PCMCIA IDE/ATA disk cards + + ide-cs.c 1.3 2002/10/26 05:45:31 + + 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 + <dahinds@users.sourceforge.net>. 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 General 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/slab.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/ioport.h> +#include <linux/ide.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> +#include <pcmcia/ciscode.h> + +/*====================================================================*/ + +/* Module parameters */ + +MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>"); +MODULE_DESCRIPTION("PCMCIA ATA/IDE card driver"); +MODULE_LICENSE("Dual MPL/GPL"); + +#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0) + +#ifdef PCMCIA_DEBUG +INT_MODULE_PARM(pc_debug, PCMCIA_DEBUG); +#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args) +static char *version = +"ide-cs.c 1.3 2002/10/26 05:45:31 (David Hinds)"; +#else +#define DEBUG(n, args...) +#endif + +/*====================================================================*/ + +static const char ide_major[] = { + IDE0_MAJOR, IDE1_MAJOR, IDE2_MAJOR, IDE3_MAJOR, + IDE4_MAJOR, IDE5_MAJOR +}; + +typedef struct ide_info_t { + dev_link_t link; + int ndev; + dev_node_t node; + int hd; +} ide_info_t; + +static void ide_release(dev_link_t *); +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; + +/*====================================================================== + + 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 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->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_LEVEL_ID; + 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.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 = pcmcia_register_client(&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; + 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; + + if (link->state & DEV_CONFIG) + ide_release(link); + + if (link->handle) { + ret = pcmcia_deregister_client(link->handle); + if (ret != CS_SUCCESS) + cs_error(link->handle, DeregisterClient, ret); + } + + /* Unlink, free device structure */ + *linkp = link->next; + kfree(link->priv); + +} /* ide_detach */ + +static int idecs_register(unsigned long io, unsigned long ctl, unsigned long irq) +{ + hw_regs_t hw; + memset(&hw, 0, sizeof(hw)); + ide_init_hwif_ports(&hw, io, ctl, NULL); + hw.irq = irq; + hw.chipset = ide_pci; + return ide_register_hw_with_fixup(&hw, NULL, ide_undecoded_slave); +} + +/*====================================================================== + + 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, ret) \ +do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) + +static void ide_config(dev_link_t *link) +{ + client_handle_t handle = link->handle; + ide_info_t *info = link->priv; + tuple_t tuple; + struct { + u_short buf[128]; + cisparse_t parse; + config_info_t conf; + cistpl_cftable_entry_t dflt; + } *stk = NULL; + cistpl_cftable_entry_t *cfg; + int i, pass, last_ret = 0, last_fn = 0, hd, is_kme = 0; + unsigned long io_base, ctl_base; + + DEBUG(0, "ide_config(0x%p)\n", link); + + stk = kmalloc(sizeof(*stk), GFP_KERNEL); + if (!stk) goto err_mem; + memset(stk, 0, sizeof(*stk)); + cfg = &stk->parse.cftable_entry; + + tuple.TupleData = (cisdata_t *)&stk->buf; + tuple.TupleOffset = 0; + tuple.TupleDataMax = 255; + tuple.Attributes = 0; + tuple.DesiredTuple = CISTPL_CONFIG; + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); + CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); + CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &stk->parse)); + link->conf.ConfigBase = stk->parse.config.base; + link->conf.Present = stk->parse.config.rmask[0]; + + tuple.DesiredTuple = CISTPL_MANFID; + if (!pcmcia_get_first_tuple(handle, &tuple) && + !pcmcia_get_tuple_data(handle, &tuple) && + !pcmcia_parse_tuple(handle, &tuple, &stk->parse)) + is_kme = ((stk->parse.manfid.manf == MANFID_KME) && + ((stk->parse.manfid.card == PRODID_KME_KXLC005_A) || + (stk->parse.manfid.card == PRODID_KME_KXLC005_B))); + + /* Configure card */ + link->state |= DEV_CONFIG; + + /* Not sure if this is right... look up the current Vcc */ + CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(handle, &stk->conf)); + link->conf.Vcc = stk->conf.Vcc; + + pass = io_base = ctl_base = 0; + tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; + tuple.Attributes = 0; + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); + while (1) { + if (pcmcia_get_tuple_data(handle, &tuple) != 0) goto next_entry; + if (pcmcia_parse_tuple(handle, &tuple, &stk->parse) != 0) goto next_entry; + + /* Check for matching Vcc, unless we're desperate */ + if (!pass) { + if (cfg->vcc.present & (1 << CISTPL_POWER_VNOM)) { + if (stk->conf.Vcc != cfg->vcc.param[CISTPL_POWER_VNOM] / 10000) + goto next_entry; + } else if (stk->dflt.vcc.present & (1 << CISTPL_POWER_VNOM)) { + if (stk->conf.Vcc != stk->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 (stk->dflt.vpp1.present & (1 << CISTPL_POWER_VNOM)) + link->conf.Vpp1 = link->conf.Vpp2 = + stk->dflt.vpp1.param[CISTPL_POWER_VNOM] / 10000; + + if ((cfg->io.nwin > 0) || (stk->dflt.io.nwin > 0)) { + cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &stk->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 = (is_kme) ? 2 : 1; + if (pcmcia_request_io(link->handle, &link->io) != 0) + goto next_entry; + 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; + if (pcmcia_request_io(link->handle, &link->io) != 0) + goto next_entry; + 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) + memcpy(&stk->dflt, cfg, sizeof(stk->dflt)); + if (pass) { + CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple)); + } else if (pcmcia_get_next_tuple(handle, &tuple) != 0) { + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); + memset(&stk->dflt, 0, sizeof(stk->dflt)); + pass++; + } + } + + CS_CHECK(RequestIRQ, pcmcia_request_irq(handle, &link->irq)); + CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf)); + + /* disable drive interrupts during IDE probe */ + outb(0x02, ctl_base); + + /* special setup for KXLC005 card */ + if (is_kme) + outb(0x81, ctl_base+1); + + /* retry registration in case device is still spinning up */ + for (hd = -1, i = 0; i < 10; i++) { + hd = idecs_register(io_base, ctl_base, link->irq.AssignedIRQ); + if (hd >= 0) break; + if (link->io.NumPorts1 == 0x20) { + outb(0x02, ctl_base + 0x10); + hd = idecs_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%3lx & 0x%3lx" + ", irq %u failed\n", io_base, ctl_base, + link->irq.AssignedIRQ); + goto failed; + } + + 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; + kfree(stk); + return; + +err_mem: + printk(KERN_NOTICE "ide-cs: ide_config failed memory allocation\n"); + goto failed; + +cs_failed: + cs_error(link->handle, last_fn, last_ret); +failed: + kfree(stk); + ide_release(link); + link->state &= ~DEV_CONFIG_PENDING; +} /* 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(dev_link_t *link) +{ + ide_info_t *info = link->priv; + + DEBUG(0, "ide_release(0x%p)\n", link); + + if (info->ndev) { + /* FIXME: if this fails we need to queue the cleanup somehow + -- need to investigate the required PCMCIA magic */ + ide_unregister(info->hd); + } + info->ndev = 0; + link->dev = NULL; + + pcmcia_release_configuration(link->handle); + pcmcia_release_io(link->handle, &link->io); + pcmcia_release_irq(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) + ide_release(link); + 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) + pcmcia_release_configuration(link->handle); + break; + case CS_EVENT_PM_RESUME: + link->state &= ~DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_CARD_RESET: + if (DEV_OK(link)) + pcmcia_request_configuration(link->handle, &link->conf); + break; + } + return 0; +} /* ide_event */ + +static struct pcmcia_driver ide_cs_driver = { + .owner = THIS_MODULE, + .drv = { + .name = "ide-cs", + }, + .attach = ide_attach, + .detach = ide_detach, +}; + +static int __init init_ide_cs(void) +{ + return pcmcia_register_driver(&ide_cs_driver); +} + +static void __exit exit_ide_cs(void) +{ + pcmcia_unregister_driver(&ide_cs_driver); + BUG_ON(dev_list != NULL); +} + +module_init(init_ide_cs); +module_exit(exit_ide_cs); diff --git a/drivers/ide/legacy/macide.c b/drivers/ide/legacy/macide.c new file mode 100644 index 0000000..90cac609 --- /dev/null +++ b/drivers/ide/legacy/macide.c @@ -0,0 +1,155 @@ +/* + * linux/drivers/ide/legacy/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/config.h> +#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> +#include <asm/mac_baboon.h> + +#define IDE_BASE 0x50F1A000 /* Base address of IDE controller */ + +/* + * Generic IDE registers as offsets from the base + * These match MkLinux so they should be correct. + */ + +#define IDE_DATA 0x00 +#define IDE_ERROR 0x04 /* see err-bits */ +#define IDE_NSECTOR 0x08 /* nr of sectors to read/write */ +#define IDE_SECTOR 0x0c /* starting sector */ +#define IDE_LCYL 0x10 /* starting cylinder */ +#define IDE_HCYL 0x14 /* high byte of starting cyl */ +#define IDE_SELECT 0x18 /* 101dhhhh , d=drive, hhhh=head */ +#define IDE_STATUS 0x1c /* see status-bits */ +#define IDE_CONTROL 0x38 /* control/altstatus */ + +/* + * Mac-specific registers + */ + +/* + * this register is odd; it doesn't seem to do much and it's + * not word-aligned like virtually every other hardware register + * on the Mac... + */ + +#define IDE_IFR 0x101 /* (0x101) IDE interrupt flags on Quadra: + * + * 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 + */ + +volatile unsigned char *ide_ifr = (unsigned char *) (IDE_BASE + IDE_IFR); + +static int macide_offsets[IDE_NR_PORTS] = { + IDE_DATA, IDE_ERROR, IDE_NSECTOR, IDE_SECTOR, IDE_LCYL, + IDE_HCYL, IDE_SELECT, IDE_STATUS, IDE_CONTROL +}; + +int macide_ack_intr(ide_hwif_t* hwif) +{ + if (*ide_ifr & 0x20) { + *ide_ifr &= ~0x20; + return 1; + } + return 0; +} + +#ifdef CONFIG_BLK_DEV_MAC_MEDIABAY +static void macide_mediabay_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + int state = baboon->mb_status & 0x04; + + printk(KERN_INFO "macide: media bay %s detected\n", state? "removal":"insertion"); +} +#endif + +/* + * Probe for a Macintosh IDE interface + */ + +void macide_init(void) +{ + hw_regs_t hw; + ide_hwif_t *hwif; + int index = -1; + + switch (macintosh_config->ide_type) { + case MAC_IDE_QUADRA: + ide_setup_ports(&hw, IDE_BASE, macide_offsets, + 0, 0, macide_ack_intr, +// quadra_ide_iops, + IRQ_NUBUS_F); + index = ide_register_hw(&hw, &hwif); + break; + case MAC_IDE_PB: + ide_setup_ports(&hw, IDE_BASE, macide_offsets, + 0, 0, macide_ack_intr, +// macide_pb_iops, + IRQ_NUBUS_C); + index = ide_register_hw(&hw, &hwif); + break; + case MAC_IDE_BABOON: + ide_setup_ports(&hw, BABOON_BASE, macide_offsets, + 0, 0, NULL, +// macide_baboon_iops, + IRQ_BABOON_1); + index = ide_register_hw(&hw, &hwif); + if (index == -1) break; + if (macintosh_config->ident == MAC_MODEL_PB190) { + + /* Fix breakage in ide-disk.c: drive capacity */ + /* is not initialized for drives without a */ + /* hardware ID, and we can't get that without */ + /* probing the drive which freezes a 190. */ + + ide_drive_t *drive = &ide_hwifs[index].drives[0]; + drive->capacity64 = drive->cyl*drive->head*drive->sect; + +#ifdef CONFIG_BLK_DEV_MAC_MEDIABAY + request_irq(IRQ_BABOON_2, macide_mediabay_interrupt, + IRQ_FLG_FAST, "mediabay", + macide_mediabay_interrupt); +#endif + } + break; + + default: + return; + } + + if (index != -1) { + hwif->mmio = 2; + if (macintosh_config->ide_type == MAC_IDE_QUADRA) + printk(KERN_INFO "ide%d: Macintosh Quadra IDE interface\n", index); + else if (macintosh_config->ide_type == MAC_IDE_PB) + printk(KERN_INFO "ide%d: Macintosh Powerbook IDE interface\n", index); + else if (macintosh_config->ide_type == MAC_IDE_BABOON) + printk(KERN_INFO "ide%d: Macintosh Powerbook Baboon IDE interface\n", index); + else + printk(KERN_INFO "ide%d: Unknown Macintosh IDE interface\n", index); + } +} diff --git a/drivers/ide/legacy/q40ide.c b/drivers/ide/legacy/q40ide.c new file mode 100644 index 0000000..2a78b79 --- /dev/null +++ b/drivers/ide/legacy/q40ide.c @@ -0,0 +1,150 @@ +/* + * linux/drivers/ide/legacy/q40ide.c -- Q40 I/O port IDE Driver + * + * (c) Richard Zidlicky + * + * 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> + + /* + * Bases of the IDE interfaces + */ + +#define Q40IDE_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 unsigned long pcide_bases[Q40IDE_NUM_HWIFS] = { + PCIDE_BASE1, PCIDE_BASE2, /* PCIDE_BASE3, PCIDE_BASE4 , PCIDE_BASE5, + PCIDE_BASE6 */ +}; + + + /* + * Offsets from one of the above bases + */ + +/* used to do addr translation here but it is easier to do in setup ports */ +/*#define IDE_OFF_B(x) ((unsigned long)Q40_ISA_IO_B((IDE_##x##_OFFSET)))*/ + +#define IDE_OFF_B(x) ((unsigned long)((IDE_##x##_OFFSET))) +#define IDE_OFF_W(x) ((unsigned long)((IDE_##x##_OFFSET))) + +static const int pcide_offsets[IDE_NR_PORTS] = { + IDE_OFF_W(DATA), IDE_OFF_B(ERROR), IDE_OFF_B(NSECTOR), IDE_OFF_B(SECTOR), + IDE_OFF_B(LCYL), IDE_OFF_B(HCYL), 6 /*IDE_OFF_B(CURRENT)*/, IDE_OFF_B(STATUS), + 518/*IDE_OFF(CMD)*/ +}; + +static int q40ide_default_irq(unsigned long base) +{ + switch (base) { + case 0x1f0: return 14; + case 0x170: return 15; + case 0x1e8: return 11; + default: + return 0; + } +} + + +/* + * This is very similar to ide_setup_ports except that addresses + * are pretranslated for q40 ISA access + */ +void q40_ide_setup_ports ( hw_regs_t *hw, + unsigned long base, int *offsets, + unsigned long ctrl, unsigned long intr, + ide_ack_intr_t *ack_intr, +/* + * ide_io_ops_t *iops, + */ + int irq) +{ + int i; + + for (i = 0; i < IDE_NR_PORTS; i++) { + /* BIG FAT WARNING: + assumption: only DATA port is ever used in 16 bit mode */ + if ( i==0 ) + hw->io_ports[i] = Q40_ISA_IO_W(base + offsets[i]); + else + hw->io_ports[i] = Q40_ISA_IO_B(base + offsets[i]); + } + + hw->irq = irq; + hw->dma = NO_DMA; + hw->ack_intr = ack_intr; +/* + * hw->iops = iops; + */ +} + + + +/* + * the static array is needed to have the name reported in /proc/ioports, + * hwif->name unfortunately isn´t available yet + */ +static const char *q40_ide_names[Q40IDE_NUM_HWIFS]={ + "ide0", "ide1" +}; + +/* + * Probe for Q40 IDE interfaces + */ + +void q40ide_init(void) +{ + int i; + ide_hwif_t *hwif; + int index; + const char *name; + + if (!MACH_IS_Q40) + return ; + + for (i = 0; i < Q40IDE_NUM_HWIFS; i++) { + hw_regs_t hw; + + name = q40_ide_names[i]; + if (!request_region(pcide_bases[i], 8, name)) { + printk("could not reserve ports %lx-%lx for %s\n", + pcide_bases[i],pcide_bases[i]+8,name); + continue; + } + if (!request_region(pcide_bases[i]+0x206, 1, name)) { + printk("could not reserve port %lx for %s\n", + pcide_bases[i]+0x206,name); + release_region(pcide_bases[i], 8); + continue; + } + q40_ide_setup_ports(&hw,(unsigned long) pcide_bases[i], (int *)pcide_offsets, + pcide_bases[i]+0x206, + 0, NULL, +// m68kide_iops, + q40ide_default_irq(pcide_bases[i])); + index = ide_register_hw(&hw, &hwif); + // **FIXME** + if (index != -1) + hwif->mmio = 2; + } +} + diff --git a/drivers/ide/legacy/qd65xx.c b/drivers/ide/legacy/qd65xx.c new file mode 100644 index 0000000..563fab0 --- /dev/null +++ b/drivers/ide/legacy/qd65xx.c @@ -0,0 +1,511 @@ +/* + * linux/drivers/ide/legacy/qd65xx.c Version 0.07 Sep 30, 2001 + * + * Copyright (C) 1996-2001 Linus Torvalds & author (see below) + */ + +/* + * Version 0.03 Cleaned auto-tune, added probe + * Version 0.04 Added second channel tuning + * Version 0.05 Enhanced tuning ; added qd6500 support + * Version 0.06 Added dos driver's list + * Version 0.07 Second channel bug fix + * + * QDI QD6500/QD6580 EIDE controller fast support + * + * Please set local bus speed using kernel parameter idebus + * for example, "idebus=33" stands for 33Mhz VLbus + * To activate controller support, use "ide0=qd65xx" + * To enable tuning, use "ide0=autotune" + * To enable second channel tuning (qd6580 only), use "ide1=autotune" + */ + +/* + * Rewritten from the work of Colten Edwards <pje120@cs.usask.ca> by + * Samuel Thibault <samuel.thibault@fnac.net> + */ + +#undef REALLY_SLOW_IO /* most systems can safely undef this */ + +#include <linux/module.h> +#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 <linux/init.h> +#include <asm/system.h> +#include <asm/io.h> + +#include "qd65xx.h" + +/* + * I/O ports are 0x30-0x31 (and 0x32-0x33 for qd6580) + * or 0xb0-0xb1 (and 0xb2-0xb3 for qd6580) + * -- qd6500 is a single IDE interface + * -- qd6580 is a dual IDE interface + * + * More research on qd6580 being done by willmore@cig.mot.com (David) + * More Information given by Petr Soucek (petr@ryston.cz) + * http://www.ryston.cz/petr/vlb + */ + +/* + * base: Timer1 + * + * + * base+0x01: Config (R/O) + * + * bit 0: ide baseport: 1 = 0x1f0 ; 0 = 0x170 (only useful for qd6500) + * bit 1: qd65xx baseport: 1 = 0xb0 ; 0 = 0x30 + * bit 2: ID3: bus speed: 1 = <=33MHz ; 0 = >33MHz + * bit 3: qd6500: 1 = disabled, 0 = enabled + * qd6580: 1 + * upper nibble: + * qd6500: 1100 + * qd6580: either 1010 or 0101 + * + * + * base+0x02: Timer2 (qd6580 only) + * + * + * base+0x03: Control (qd6580 only) + * + * bits 0-3 must always be set 1 + * bit 4 must be set 1, but is set 0 by dos driver while measuring vlb clock + * bit 0 : 1 = Only primary port enabled : channel 0 for hda, channel 1 for hdb + * 0 = Primary and Secondary ports enabled : channel 0 for hda & hdb + * channel 1 for hdc & hdd + * bit 1 : 1 = only disks on primary port + * 0 = disks & ATAPI devices on primary port + * bit 2-4 : always 0 + * bit 5 : status, but of what ? + * bit 6 : always set 1 by dos driver + * bit 7 : set 1 for non-ATAPI devices on primary port + * (maybe read-ahead and post-write buffer ?) + */ + +static int timings[4]={-1,-1,-1,-1}; /* stores current timing for each timer */ + +static void qd_write_reg (u8 content, unsigned long reg) +{ + unsigned long flags; + + spin_lock_irqsave(&ide_lock, flags); + outb(content,reg); + spin_unlock_irqrestore(&ide_lock, flags); +} + +static u8 __init qd_read_reg (unsigned long reg) +{ + unsigned long flags; + u8 read; + + spin_lock_irqsave(&ide_lock, flags); + read = inb(reg); + spin_unlock_irqrestore(&ide_lock, flags); + return read; +} + +/* + * qd_select: + * + * This routine is invoked from ide.c to prepare for access to a given drive. + */ + +static void qd_select (ide_drive_t *drive) +{ + u8 index = (( (QD_TIMREG(drive)) & 0x80 ) >> 7) | + (QD_TIMREG(drive) & 0x02); + + if (timings[index] != QD_TIMING(drive)) + qd_write_reg(timings[index] = QD_TIMING(drive), QD_TIMREG(drive)); +} + +/* + * qd6500_compute_timing + * + * computes the timing value where + * lower nibble represents active time, in count of VLB clocks + * upper nibble represents recovery time, in count of VLB clocks + */ + +static u8 qd6500_compute_timing (ide_hwif_t *hwif, int active_time, int recovery_time) +{ + u8 active_cycle,recovery_cycle; + + if (system_bus_clock()<=33) { + active_cycle = 9 - IDE_IN(active_time * system_bus_clock() / 1000 + 1, 2, 9); + recovery_cycle = 15 - IDE_IN(recovery_time * system_bus_clock() / 1000 + 1, 0, 15); + } else { + active_cycle = 8 - IDE_IN(active_time * system_bus_clock() / 1000 + 1, 1, 8); + recovery_cycle = 18 - IDE_IN(recovery_time * system_bus_clock() / 1000 + 1, 3, 18); + } + + return((recovery_cycle<<4) | 0x08 | active_cycle); +} + +/* + * qd6580_compute_timing + * + * idem for qd6580 + */ + +static u8 qd6580_compute_timing (int active_time, int recovery_time) +{ + u8 active_cycle = 17 - IDE_IN(active_time * system_bus_clock() / 1000 + 1, 2, 17); + u8 recovery_cycle = 15 - IDE_IN(recovery_time * system_bus_clock() / 1000 + 1, 2, 15); + + return((recovery_cycle<<4) | active_cycle); +} + +/* + * qd_find_disk_type + * + * tries to find timing from dos driver's table + */ + +static int qd_find_disk_type (ide_drive_t *drive, + int *active_time, int *recovery_time) +{ + struct qd65xx_timing_s *p; + char model[40]; + + if (!*drive->id->model) return 0; + + strncpy(model,drive->id->model,40); + ide_fixstring(model,40,1); /* byte-swap */ + + for (p = qd65xx_timing ; p->offset != -1 ; p++) { + if (!strncmp(p->model, model+p->offset, 4)) { + printk(KERN_DEBUG "%s: listed !\n", drive->name); + *active_time = p->active; + *recovery_time = p->recovery; + return 1; + } + } + return 0; +} + +/* + * qd_timing_ok: + * + * check whether timings don't conflict + */ + +static int qd_timing_ok (ide_drive_t drives[]) +{ + return (IDE_IMPLY(drives[0].present && drives[1].present, + IDE_IMPLY(QD_TIMREG(drives) == QD_TIMREG(drives+1), + QD_TIMING(drives) == QD_TIMING(drives+1)))); + /* if same timing register, must be same timing */ +} + +/* + * qd_set_timing: + * + * records the timing, and enables selectproc as needed + */ + +static void qd_set_timing (ide_drive_t *drive, u8 timing) +{ + ide_hwif_t *hwif = HWIF(drive); + + drive->drive_data &= 0xff00; + drive->drive_data |= timing; + if (qd_timing_ok(hwif->drives)) { + qd_select(drive); /* selects once */ + hwif->selectproc = NULL; + } else + hwif->selectproc = &qd_select; + + printk(KERN_DEBUG "%s: %#x\n", drive->name, timing); +} + +/* + * qd6500_tune_drive + */ + +static void qd6500_tune_drive (ide_drive_t *drive, u8 pio) +{ + int active_time = 175; + int recovery_time = 415; /* worst case values from the dos driver */ + + if (drive->id && !qd_find_disk_type(drive, &active_time, &recovery_time) + && drive->id->tPIO && (drive->id->field_valid & 0x02) + && drive->id->eide_pio >= 240) { + + printk(KERN_INFO "%s: PIO mode%d\n", drive->name, + drive->id->tPIO); + active_time = 110; + recovery_time = drive->id->eide_pio - 120; + } + + qd_set_timing(drive, qd6500_compute_timing(HWIF(drive), active_time, recovery_time)); +} + +/* + * qd6580_tune_drive + */ + +static void qd6580_tune_drive (ide_drive_t *drive, u8 pio) +{ + ide_pio_data_t d; + int base = HWIF(drive)->select_data; + int active_time = 175; + int recovery_time = 415; /* worst case values from the dos driver */ + + if (drive->id && !qd_find_disk_type(drive, &active_time, &recovery_time)) { + pio = ide_get_best_pio_mode(drive, pio, 255, &d); + pio = min_t(u8, pio, 4); + + switch (pio) { + case 0: break; + case 3: + if (d.cycle_time >= 110) { + active_time = 86; + recovery_time = d.cycle_time - 102; + } else + printk(KERN_WARNING "%s: Strange recovery time !\n",drive->name); + break; + case 4: + if (d.cycle_time >= 69) { + active_time = 70; + recovery_time = d.cycle_time - 61; + } else + printk(KERN_WARNING "%s: Strange recovery time !\n",drive->name); + break; + default: + if (d.cycle_time >= 180) { + active_time = 110; + recovery_time = d.cycle_time - 120; + } else { + active_time = ide_pio_timings[pio].active_time; + recovery_time = d.cycle_time + -active_time; + } + } + printk(KERN_INFO "%s: PIO mode%d\n", drive->name,pio); + } + + if (!HWIF(drive)->channel && drive->media != ide_disk) { + qd_write_reg(0x5f, QD_CONTROL_PORT); + printk(KERN_WARNING "%s: ATAPI: disabled read-ahead FIFO " + "and post-write buffer on %s.\n", + drive->name, HWIF(drive)->name); + } + + qd_set_timing(drive, qd6580_compute_timing(active_time, recovery_time)); +} + +/* + * qd_testreg + * + * tests if the given port is a register + */ + +static int __init qd_testreg(int port) +{ + u8 savereg; + u8 readreg; + unsigned long flags; + + spin_lock_irqsave(&ide_lock, flags); + savereg = inb_p(port); + outb_p(QD_TESTVAL, port); /* safe value */ + readreg = inb_p(port); + outb(savereg, port); + spin_unlock_irqrestore(&ide_lock, flags); + + if (savereg == QD_TESTVAL) { + printk(KERN_ERR "Outch ! the probe for qd65xx isn't reliable !\n"); + printk(KERN_ERR "Please contact maintainers to tell about your hardware\n"); + printk(KERN_ERR "Assuming qd65xx is not present.\n"); + return 1; + } + + return (readreg != QD_TESTVAL); +} + +/* + * qd_setup: + * + * called to setup an ata channel : adjusts attributes & links for tuning + */ + +static void __init qd_setup(ide_hwif_t *hwif, int base, int config, + unsigned int data0, unsigned int data1, + void (*tuneproc) (ide_drive_t *, u8 pio)) +{ + hwif->chipset = ide_qd65xx; + hwif->channel = hwif->index; + hwif->select_data = base; + hwif->config_data = config; + hwif->drives[0].drive_data = data0; + hwif->drives[1].drive_data = data1; + hwif->drives[0].io_32bit = + hwif->drives[1].io_32bit = 1; + hwif->tuneproc = tuneproc; + probe_hwif_init(hwif); +} + +/* + * qd_unsetup: + * + * called to unsetup an ata channel : back to default values, unlinks tuning + */ +/* +static void __exit qd_unsetup(ide_hwif_t *hwif) +{ + u8 config = hwif->config_data; + int base = hwif->select_data; + void *tuneproc = (void *) hwif->tuneproc; + + if (hwif->chipset != ide_qd65xx) + return; + + printk(KERN_NOTICE "%s: back to defaults\n", hwif->name); + + hwif->selectproc = NULL; + hwif->tuneproc = NULL; + + if (tuneproc == (void *) qd6500_tune_drive) { + // will do it for both + qd_write_reg(QD6500_DEF_DATA, QD_TIMREG(&hwif->drives[0])); + } else if (tuneproc == (void *) qd6580_tune_drive) { + if (QD_CONTROL(hwif) & QD_CONTR_SEC_DISABLED) { + qd_write_reg(QD6580_DEF_DATA, QD_TIMREG(&hwif->drives[0])); + qd_write_reg(QD6580_DEF_DATA2, QD_TIMREG(&hwif->drives[1])); + } else { + qd_write_reg(hwif->channel ? QD6580_DEF_DATA2 : QD6580_DEF_DATA, QD_TIMREG(&hwif->drives[0])); + } + } else { + printk(KERN_WARNING "Unknown qd65xx tuning fonction !\n"); + printk(KERN_WARNING "keeping settings !\n"); + } +} +*/ + +/* + * qd_probe: + * + * looks at the specified baseport, and if qd found, registers & initialises it + * return 1 if another qd may be probed + */ + +static int __init qd_probe(int base) +{ + ide_hwif_t *hwif; + u8 config; + u8 unit; + + config = qd_read_reg(QD_CONFIG_PORT); + + if (! ((config & QD_CONFIG_BASEPORT) >> 1 == (base == 0xb0)) ) + return 1; + + unit = ! (config & QD_CONFIG_IDE_BASEPORT); + + if ((config & 0xf0) == QD_CONFIG_QD6500) { + + if (qd_testreg(base)) return 1; /* bad register */ + + /* qd6500 found */ + + hwif = &ide_hwifs[unit]; + printk(KERN_NOTICE "%s: qd6500 at %#x\n", hwif->name, base); + printk(KERN_DEBUG "qd6500: config=%#x, ID3=%u\n", + config, QD_ID3); + + if (config & QD_CONFIG_DISABLED) { + printk(KERN_WARNING "qd6500 is disabled !\n"); + return 1; + } + + qd_setup(hwif, base, config, QD6500_DEF_DATA, QD6500_DEF_DATA, + &qd6500_tune_drive); + + create_proc_ide_interfaces(); + + return 1; + } + + if (((config & 0xf0) == QD_CONFIG_QD6580_A) || + ((config & 0xf0) == QD_CONFIG_QD6580_B)) { + + u8 control; + + if (qd_testreg(base) || qd_testreg(base+0x02)) return 1; + /* bad registers */ + + /* qd6580 found */ + + control = qd_read_reg(QD_CONTROL_PORT); + + printk(KERN_NOTICE "qd6580 at %#x\n", base); + printk(KERN_DEBUG "qd6580: config=%#x, control=%#x, ID3=%u\n", + config, control, QD_ID3); + + if (control & QD_CONTR_SEC_DISABLED) { + /* secondary disabled */ + + hwif = &ide_hwifs[unit]; + printk(KERN_INFO "%s: qd6580: single IDE board\n", + hwif->name); + qd_setup(hwif, base, config | (control << 8), + QD6580_DEF_DATA, QD6580_DEF_DATA2, + &qd6580_tune_drive); + qd_write_reg(QD_DEF_CONTR,QD_CONTROL_PORT); + + create_proc_ide_interfaces(); + + return 1; + } else { + ide_hwif_t *mate; + + hwif = &ide_hwifs[0]; + mate = &ide_hwifs[1]; + /* secondary enabled */ + printk(KERN_INFO "%s&%s: qd6580: dual IDE board\n", + hwif->name, mate->name); + + qd_setup(hwif, base, config | (control << 8), + QD6580_DEF_DATA, QD6580_DEF_DATA, + &qd6580_tune_drive); + qd_setup(mate, base, config | (control << 8), + QD6580_DEF_DATA2, QD6580_DEF_DATA2, + &qd6580_tune_drive); + qd_write_reg(QD_DEF_CONTR,QD_CONTROL_PORT); + + create_proc_ide_interfaces(); + + return 0; /* no other qd65xx possible */ + } + } + /* no qd65xx found */ + return 1; +} + +/* Can be called directly from ide.c. */ +int __init qd65xx_init(void) +{ + if (qd_probe(0x30)) + qd_probe(0xb0); + if (ide_hwifs[0].chipset != ide_qd65xx && + ide_hwifs[1].chipset != ide_qd65xx) + return -ENODEV; + return 0; +} + +#ifdef MODULE +module_init(qd65xx_init); +#endif + +MODULE_AUTHOR("Samuel Thibault"); +MODULE_DESCRIPTION("support of qd65xx vlb ide chipset"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/legacy/qd65xx.h b/drivers/ide/legacy/qd65xx.h new file mode 100644 index 0000000..633a424 --- /dev/null +++ b/drivers/ide/legacy/qd65xx.h @@ -0,0 +1,140 @@ +/* + * linux/drivers/ide/legacy/qd65xx.h + * + * Copyright (c) 2000 Linus Torvalds & authors + */ + +/* + * Authors: Petr Soucek <petr@ryston.cz> + * Samuel Thibault <samuel.thibault@fnac.net> + */ + +/* truncates a in [b,c] */ +#define IDE_IN(a,b,c) ( ((a)<(b)) ? (b) : ( (a)>(c) ? (c) : (a)) ) + +#define IDE_IMPLY(a,b) ((!(a)) || (b)) + +#define QD_TIM1_PORT (base) +#define QD_CONFIG_PORT (base+0x01) +#define QD_TIM2_PORT (base+0x02) +#define QD_CONTROL_PORT (base+0x03) + +#define QD_CONFIG_IDE_BASEPORT 0x01 +#define QD_CONFIG_BASEPORT 0x02 +#define QD_CONFIG_ID3 0x04 +#define QD_CONFIG_DISABLED 0x08 +#define QD_CONFIG_QD6500 0xc0 +#define QD_CONFIG_QD6580_A 0xa0 +#define QD_CONFIG_QD6580_B 0x50 + +#define QD_CONTR_SEC_DISABLED 0x01 + +#define QD_ID3 ((config & QD_CONFIG_ID3)!=0) + +#define QD_CONFIG(hwif) ((hwif)->config_data & 0x00ff) +#define QD_CONTROL(hwif) (((hwif)->config_data & 0xff00) >> 8) + +#define QD_TIMING(drive) (byte)(((drive)->drive_data) & 0x00ff) +#define QD_TIMREG(drive) (byte)((((drive)->drive_data) & 0xff00) >> 8) + +#define QD6500_DEF_DATA ((QD_TIM1_PORT<<8) | (QD_ID3 ? 0x0c : 0x08)) +#define QD6580_DEF_DATA ((QD_TIM1_PORT<<8) | (QD_ID3 ? 0x0a : 0x00)) +#define QD6580_DEF_DATA2 ((QD_TIM2_PORT<<8) | (QD_ID3 ? 0x0a : 0x00)) +#define QD_DEF_CONTR (0x40 | ((control & 0x02) ? 0x9f : 0x1f)) + +#define QD_TESTVAL 0x19 /* safe value */ + +/* Drive specific timing taken from DOS driver v3.7 */ + +static struct qd65xx_timing_s { + s8 offset; /* ofset from the beginning of Model Number" */ + char model[4]; /* 4 chars from Model number, no conversion */ + s16 active; /* active time */ + s16 recovery; /* recovery time */ +} qd65xx_timing [] = { + { 30, "2040", 110, 225 }, /* Conner CP30204 */ + { 30, "2045", 135, 225 }, /* Conner CP30254 */ + { 30, "1040", 155, 325 }, /* Conner CP30104 */ + { 30, "1047", 135, 265 }, /* Conner CP30174 */ + { 30, "5344", 135, 225 }, /* Conner CP3544 */ + { 30, "01 4", 175, 405 }, /* Conner CP-3104 */ + { 27, "C030", 175, 375 }, /* Conner CP3000 */ + { 8, "PL42", 110, 295 }, /* Quantum LP240 */ + { 8, "PL21", 110, 315 }, /* Quantum LP120 */ + { 8, "PL25", 175, 385 }, /* Quantum LP52 */ + { 4, "PA24", 110, 285 }, /* WD Piranha SP4200 */ + { 6, "2200", 110, 260 }, /* WD Caviar AC2200 */ + { 6, "3204", 110, 235 }, /* WD Caviar AC2340 */ + { 6, "1202", 110, 265 }, /* WD Caviar AC2120 */ + { 0, "DS3-", 135, 315 }, /* Teac SD340 */ + { 8, "KM32", 175, 355 }, /* Toshiba MK234 */ + { 2, "53A1", 175, 355 }, /* Seagate ST351A */ + { 2, "4108", 175, 295 }, /* Seagate ST1480A */ + { 2, "1344", 175, 335 }, /* Seagate ST3144A */ + { 6, "7 12", 110, 225 }, /* Maxtor 7213A */ + { 30, "02F4", 145, 295 }, /* Conner 3204F */ + { 2, "1302", 175, 335 }, /* Seagate ST3120A */ + { 2, "2334", 145, 265 }, /* Seagate ST3243A */ + { 2, "2338", 145, 275 }, /* Seagate ST3283A */ + { 2, "3309", 145, 275 }, /* Seagate ST3390A */ + { 2, "5305", 145, 275 }, /* Seagate ST3550A */ + { 2, "4100", 175, 295 }, /* Seagate ST1400A */ + { 2, "4110", 175, 295 }, /* Seagate ST1401A */ + { 2, "6300", 135, 265 }, /* Seagate ST3600A */ + { 2, "5300", 135, 265 }, /* Seagate ST3500A */ + { 6, "7 31", 135, 225 }, /* Maxtor 7131 AT */ + { 6, "7 43", 115, 265 }, /* Maxtor 7345 AT */ + { 6, "7 42", 110, 255 }, /* Maxtor 7245 AT */ + { 6, "3 04", 135, 265 }, /* Maxtor 340 AT */ + { 6, "61 0", 135, 285 }, /* WD AC160 */ + { 6, "1107", 135, 235 }, /* WD AC1170 */ + { 6, "2101", 110, 220 }, /* WD AC1210 */ + { 6, "4202", 135, 245 }, /* WD AC2420 */ + { 6, "41 0", 175, 355 }, /* WD Caviar 140 */ + { 6, "82 0", 175, 355 }, /* WD Caviar 280 */ + { 8, "PL01", 175, 375 }, /* Quantum LP105 */ + { 8, "PL25", 110, 295 }, /* Quantum LP525 */ + { 10, "4S 2", 175, 385 }, /* Quantum ELS42 */ + { 10, "8S 5", 175, 385 }, /* Quantum ELS85 */ + { 10, "1S72", 175, 385 }, /* Quantum ELS127 */ + { 10, "1S07", 175, 385 }, /* Quantum ELS170 */ + { 8, "ZE42", 135, 295 }, /* Quantum EZ240 */ + { 8, "ZE21", 175, 385 }, /* Quantum EZ127 */ + { 8, "ZE58", 175, 385 }, /* Quantum EZ85 */ + { 8, "ZE24", 175, 385 }, /* Quantum EZ42 */ + { 27, "C036", 155, 325 }, /* Conner CP30064 */ + { 27, "C038", 155, 325 }, /* Conner CP30084 */ + { 6, "2205", 110, 255 }, /* WDC AC2250 */ + { 2, " CHA", 140, 415 }, /* WDC AH series; WDC AH260, WDC */ + { 2, " CLA", 140, 415 }, /* WDC AL series: WDC AL2120, 2170, */ + { 4, "UC41", 140, 415 }, /* WDC CU140 */ + { 6, "1207", 130, 275 }, /* WDC AC2170 */ + { 6, "2107", 130, 275 }, /* WDC AC1270 */ + { 6, "5204", 130, 275 }, /* WDC AC2540 */ + { 30, "3004", 110, 235 }, /* Conner CP30340 */ + { 30, "0345", 135, 255 }, /* Conner CP30544 */ + { 12, "12A3", 175, 320 }, /* MAXTOR LXT-213A */ + { 12, "43A0", 145, 240 }, /* MAXTOR LXT-340A */ + { 6, "7 21", 180, 290 }, /* Maxtor 7120 AT */ + { 6, "7 71", 135, 240 }, /* Maxtor 7170 AT */ + { 12, "45\0000", 110, 205 }, /* MAXTOR MXT-540 */ + { 8, "PL11", 180, 290 }, /* QUANTUM LP110A */ + { 8, "OG21", 150, 275 }, /* QUANTUM GO120 */ + { 12, "42A5", 175, 320 }, /* MAXTOR LXT-245A */ + { 2, "2309", 175, 295 }, /* ST3290A */ + { 2, "3358", 180, 310 }, /* ST3385A */ + { 2, "6355", 180, 310 }, /* ST3655A */ + { 2, "1900", 175, 270 }, /* ST9100A */ + { 2, "1954", 175, 270 }, /* ST9145A */ + { 2, "1909", 175, 270 }, /* ST9190AG */ + { 2, "2953", 175, 270 }, /* ST9235A */ + { 2, "1359", 175, 270 }, /* ST3195A */ + { 24, "3R11", 175, 290 }, /* ALPS ELECTRIC Co.,LTD, DR311C */ + { 0, "2M26", 175, 215 }, /* M262XT-0Ah */ + { 4, "2253", 175, 300 }, /* HP C2235A */ + { 4, "-32A", 145, 245 }, /* H3133-A2 */ + { 30, "0326", 150, 270 }, /* Samsung Electronics 120MB */ + { 30, "3044", 110, 195 }, /* Conner CFA340A */ + { 30, "43A0", 110, 195 }, /* Conner CFA340A */ + { -1, " ", 175, 415 } /* unknown disk name */ +}; diff --git a/drivers/ide/legacy/umc8672.c b/drivers/ide/legacy/umc8672.c new file mode 100644 index 0000000..cdbdb2f --- /dev/null +++ b/drivers/ide/legacy/umc8672.c @@ -0,0 +1,183 @@ +/* + * linux/drivers/ide/legacy/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/module.h> +#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 <linux/init.h> + +#include <asm/io.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 u8 current_speeds[4] = {UMC_DRIVE0, UMC_DRIVE1, UMC_DRIVE2, UMC_DRIVE3}; +static const u8 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 u8 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 u8 in_umc (char port) +{ + outb_p(port,0x108); + return inb_p(0x109); +} + +static void umc_set_speeds (u8 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, u8 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]); + spin_lock_irqsave(&ide_lock, flags); + if (hwgroup && hwgroup->handler != NULL) { + printk(KERN_ERR "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); + } + spin_unlock_irqrestore(&ide_lock, flags); +} + +static int __init umc8672_probe(void) +{ + unsigned long flags; + ide_hwif_t *hwif, *mate; + + if (!request_region(0x108, 2, "umc8672")) { + printk(KERN_ERR "umc8672: ports 0x108-0x109 already in use.\n"); + return 1; + } + local_irq_save(flags); + outb_p(0x5A,0x108); /* enable umc */ + if (in_umc (0xd5) != 0xa0) { + local_irq_restore(flags); + printk(KERN_ERR "umc8672: not found\n"); + release_region(0x108, 2); + return 1; + } + outb_p(0xa5,0x108); /* disable umc */ + + umc_set_speeds (current_speeds); + local_irq_restore(flags); + + hwif = &ide_hwifs[0]; + mate = &ide_hwifs[1]; + + hwif->chipset = ide_umc8672; + hwif->tuneproc = &tune_umc; + hwif->mate = mate; + + mate->chipset = ide_umc8672; + mate->tuneproc = &tune_umc; + mate->mate = hwif; + mate->channel = 1; + + probe_hwif_init(hwif); + probe_hwif_init(mate); + + create_proc_ide_interfaces(); + + return 0; +} + +/* Can be called directly from ide.c. */ +int __init umc8672_init(void) +{ + if (umc8672_probe()) + return -ENODEV; + return 0; +} + +#ifdef MODULE +module_init(umc8672_init); +#endif + +MODULE_AUTHOR("Wolfram Podien"); +MODULE_DESCRIPTION("Support for UMC 8672 IDE chipset"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/pci/Makefile b/drivers/ide/pci/Makefile new file mode 100644 index 0000000..55e6e55 --- /dev/null +++ b/drivers/ide/pci/Makefile @@ -0,0 +1,34 @@ + +obj-$(CONFIG_BLK_DEV_AEC62XX) += aec62xx.o +obj-$(CONFIG_BLK_DEV_ALI15X3) += alim15x3.o +obj-$(CONFIG_BLK_DEV_AMD74XX) += amd74xx.o +obj-$(CONFIG_BLK_DEV_ATIIXP) += atiixp.o +obj-$(CONFIG_BLK_DEV_CMD64X) += cmd64x.o +obj-$(CONFIG_BLK_DEV_CS5520) += cs5520.o +obj-$(CONFIG_BLK_DEV_CS5530) += cs5530.o +obj-$(CONFIG_BLK_DEV_SC1200) += sc1200.o +obj-$(CONFIG_BLK_DEV_CY82C693) += cy82c693.o +obj-$(CONFIG_BLK_DEV_HPT34X) += hpt34x.o +obj-$(CONFIG_BLK_DEV_HPT366) += hpt366.o +#obj-$(CONFIG_BLK_DEV_HPT37X) += hpt37x.o +obj-$(CONFIG_BLK_DEV_IT8172) += it8172.o +obj-$(CONFIG_BLK_DEV_NS87415) += ns87415.o +obj-$(CONFIG_BLK_DEV_OPTI621) += opti621.o +obj-$(CONFIG_BLK_DEV_PDC202XX_OLD) += pdc202xx_old.o +obj-$(CONFIG_BLK_DEV_PDC202XX_NEW) += pdc202xx_new.o +obj-$(CONFIG_BLK_DEV_PIIX) += piix.o +obj-$(CONFIG_BLK_DEV_RZ1000) += rz1000.o +obj-$(CONFIG_BLK_DEV_SVWKS) += serverworks.o +obj-$(CONFIG_BLK_DEV_SGIIOC4) += sgiioc4.o +obj-$(CONFIG_BLK_DEV_SIIMAGE) += siimage.o +obj-$(CONFIG_BLK_DEV_SIS5513) += sis5513.o +obj-$(CONFIG_BLK_DEV_SL82C105) += sl82c105.o +obj-$(CONFIG_BLK_DEV_SLC90E66) += slc90e66.o +obj-$(CONFIG_BLK_DEV_TRIFLEX) += triflex.o +obj-$(CONFIG_BLK_DEV_TRM290) += trm290.o +obj-$(CONFIG_BLK_DEV_VIA82CXXX) += via82cxxx.o + +# Must appear at the end of the block +obj-$(CONFIG_BLK_DEV_GENERIC) += generic.o + +EXTRA_CFLAGS := -Idrivers/ide diff --git a/drivers/ide/pci/aec62xx.c b/drivers/ide/pci/aec62xx.c new file mode 100644 index 0000000..52cadc0 --- /dev/null +++ b/drivers/ide/pci/aec62xx.c @@ -0,0 +1,485 @@ +/* + * linux/drivers/ide/pci/aec62xx.c Version 0.11 March 27, 2002 + * + * Copyright (C) 1999-2002 Andre Hedrick <andre@linux-ide.org> + * + */ + +#include <linux/module.h> +#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 <linux/init.h> + +#include <asm/io.h> + +struct chipset_bus_clock_list_entry { + u8 xfer_speed; + u8 chipset_settings; + u8 ultra_settings; +}; + +static struct chipset_bus_clock_list_entry aec6xxx_33_base [] = { + { XFER_UDMA_6, 0x31, 0x07 }, + { XFER_UDMA_5, 0x31, 0x06 }, + { XFER_UDMA_4, 0x31, 0x05 }, + { XFER_UDMA_3, 0x31, 0x04 }, + { XFER_UDMA_2, 0x31, 0x03 }, + { XFER_UDMA_1, 0x31, 0x02 }, + { XFER_UDMA_0, 0x31, 0x01 }, + + { XFER_MW_DMA_2, 0x31, 0x00 }, + { XFER_MW_DMA_1, 0x31, 0x00 }, + { XFER_MW_DMA_0, 0x0a, 0x00 }, + { XFER_PIO_4, 0x31, 0x00 }, + { XFER_PIO_3, 0x33, 0x00 }, + { XFER_PIO_2, 0x08, 0x00 }, + { XFER_PIO_1, 0x0a, 0x00 }, + { XFER_PIO_0, 0x00, 0x00 }, + { 0, 0x00, 0x00 } +}; + +static struct chipset_bus_clock_list_entry aec6xxx_34_base [] = { + { XFER_UDMA_6, 0x41, 0x06 }, + { XFER_UDMA_5, 0x41, 0x05 }, + { XFER_UDMA_4, 0x41, 0x04 }, + { XFER_UDMA_3, 0x41, 0x03 }, + { XFER_UDMA_2, 0x41, 0x02 }, + { XFER_UDMA_1, 0x41, 0x01 }, + { XFER_UDMA_0, 0x41, 0x01 }, + + { XFER_MW_DMA_2, 0x41, 0x00 }, + { XFER_MW_DMA_1, 0x42, 0x00 }, + { XFER_MW_DMA_0, 0x7a, 0x00 }, + { XFER_PIO_4, 0x41, 0x00 }, + { XFER_PIO_3, 0x43, 0x00 }, + { XFER_PIO_2, 0x78, 0x00 }, + { XFER_PIO_1, 0x7a, 0x00 }, + { XFER_PIO_0, 0x70, 0x00 }, + { 0, 0x00, 0x00 } +}; + +#define BUSCLOCK(D) \ + ((struct chipset_bus_clock_list_entry *) pci_get_drvdata((D))) + +#if 0 + if (dev->device == PCI_DEVICE_ID_ARTOP_ATP850UF) { + (void) pci_read_config_byte(dev, 0x54, &art); + p += sprintf(p, "DMA Mode: %s(%s)", + (c0&0x20)?((art&0x03)?"UDMA":" DMA"):" PIO", + (art&0x02)?"2":(art&0x01)?"1":"0"); + p += sprintf(p, " %s(%s)", + (c0&0x40)?((art&0x0c)?"UDMA":" DMA"):" PIO", + (art&0x08)?"2":(art&0x04)?"1":"0"); + p += sprintf(p, " %s(%s)", + (c1&0x20)?((art&0x30)?"UDMA":" DMA"):" PIO", + (art&0x20)?"2":(art&0x10)?"1":"0"); + p += sprintf(p, " %s(%s)\n", + (c1&0x40)?((art&0xc0)?"UDMA":" DMA"):" PIO", + (art&0x80)?"2":(art&0x40)?"1":"0"); + } else { +#endif + +/* + * TO DO: active tuning and correction of cards without a bios. + */ +static u8 pci_bus_clock_list (u8 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 chipset_table->chipset_settings; +} + +static u8 pci_bus_clock_list_ultra (u8 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 chipset_table->ultra_settings; +} + +static u8 aec62xx_ratemask (ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + u8 mode; + + switch(hwif->pci_dev->device) { + case PCI_DEVICE_ID_ARTOP_ATP865: + case PCI_DEVICE_ID_ARTOP_ATP865R: +#if 0 + mode = (hwif->INB(hwif->dma_master) & 0x10) ? 4 : 3; +#else + mode = (hwif->INB(((hwif->channel) ? + hwif->mate->dma_status : + hwif->dma_status)) & 0x10) ? 4 : 3; +#endif + break; + case PCI_DEVICE_ID_ARTOP_ATP860: + case PCI_DEVICE_ID_ARTOP_ATP860R: + mode = 2; + break; + case PCI_DEVICE_ID_ARTOP_ATP850UF: + default: + return 1; + } + + if (!eighty_ninty_three(drive)) + mode = min(mode, (u8)1); + return mode; +} + +static int aec6210_tune_chipset (ide_drive_t *drive, u8 xferspeed) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + u16 d_conf = 0; + u8 speed = ide_rate_filter(aec62xx_ratemask(drive), xferspeed); + u8 ultra = 0, ultra_conf = 0; + u8 tmp0 = 0, tmp1 = 0, tmp2 = 0; + unsigned long flags; + + local_irq_save(flags); + /* 0x40|(2*drive->dn): Active, 0x41|(2*drive->dn): Recovery */ + pci_read_config_word(dev, 0x40|(2*drive->dn), &d_conf); + tmp0 = pci_bus_clock_list(speed, BUSCLOCK(dev)); + d_conf = ((tmp0 & 0xf0) << 4) | (tmp0 & 0xf); + pci_write_config_word(dev, 0x40|(2*drive->dn), d_conf); + + tmp1 = 0x00; + tmp2 = 0x00; + pci_read_config_byte(dev, 0x54, &ultra); + tmp1 = ((0x00 << (2*drive->dn)) | (ultra & ~(3 << (2*drive->dn)))); + ultra_conf = pci_bus_clock_list_ultra(speed, BUSCLOCK(dev)); + tmp2 = ((ultra_conf << (2*drive->dn)) | (tmp1 & ~(3 << (2*drive->dn)))); + pci_write_config_byte(dev, 0x54, tmp2); + local_irq_restore(flags); + return(ide_config_drive_speed(drive, speed)); +} + +static int aec6260_tune_chipset (ide_drive_t *drive, u8 xferspeed) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + u8 speed = ide_rate_filter(aec62xx_ratemask(drive), xferspeed); + u8 unit = (drive->select.b.unit & 0x01); + u8 tmp1 = 0, tmp2 = 0; + u8 ultra = 0, drive_conf = 0, ultra_conf = 0; + unsigned long flags; + + local_irq_save(flags); + /* high 4-bits: Active, low 4-bits: Recovery */ + pci_read_config_byte(dev, 0x40|drive->dn, &drive_conf); + drive_conf = pci_bus_clock_list(speed, BUSCLOCK(dev)); + pci_write_config_byte(dev, 0x40|drive->dn, drive_conf); + + pci_read_config_byte(dev, (0x44|hwif->channel), &ultra); + tmp1 = ((0x00 << (4*unit)) | (ultra & ~(7 << (4*unit)))); + ultra_conf = pci_bus_clock_list_ultra(speed, BUSCLOCK(dev)); + tmp2 = ((ultra_conf << (4*unit)) | (tmp1 & ~(7 << (4*unit)))); + pci_write_config_byte(dev, (0x44|hwif->channel), tmp2); + local_irq_restore(flags); + return(ide_config_drive_speed(drive, speed)); +} + +static int aec62xx_tune_chipset (ide_drive_t *drive, u8 speed) +{ + switch (HWIF(drive)->pci_dev->device) { + case PCI_DEVICE_ID_ARTOP_ATP865: + case PCI_DEVICE_ID_ARTOP_ATP865R: + case PCI_DEVICE_ID_ARTOP_ATP860: + case PCI_DEVICE_ID_ARTOP_ATP860R: + return ((int) aec6260_tune_chipset(drive, speed)); + case PCI_DEVICE_ID_ARTOP_ATP850UF: + return ((int) aec6210_tune_chipset(drive, speed)); + default: + return -1; + } +} + +static int config_chipset_for_dma (ide_drive_t *drive) +{ + u8 speed = ide_dma_speed(drive, aec62xx_ratemask(drive)); + + if (!(speed)) + return 0; + + (void) aec62xx_tune_chipset(drive, speed); + return ide_dma_enable(drive); +} + +static void aec62xx_tune_drive (ide_drive_t *drive, u8 pio) +{ + u8 speed = 0; + u8 new_pio = XFER_PIO_0 + ide_get_best_pio_mode(drive, 255, 5, NULL); + + switch(pio) { + case 5: speed = new_pio; break; + 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) aec62xx_tune_chipset(drive, speed); +} + +static int aec62xx_config_drive_xfer_rate (ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + struct hd_driveid *id = drive->id; + + if ((id->capability & 1) && drive->autodma) { + + if (ide_use_dma(drive)) { + if (config_chipset_for_dma(drive)) + return hwif->ide_dma_on(drive); + } + + goto fast_ata_pio; + + } else if ((id->capability & 8) || (id->field_valid & 2)) { +fast_ata_pio: + aec62xx_tune_drive(drive, 5); + return hwif->ide_dma_off_quietly(drive); + } + /* IORDY not supported */ + return 0; +} + +static int aec62xx_irq_timeout (ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + + switch(dev->device) { + case PCI_DEVICE_ID_ARTOP_ATP860: + case PCI_DEVICE_ID_ARTOP_ATP860R: + case PCI_DEVICE_ID_ARTOP_ATP865: + case PCI_DEVICE_ID_ARTOP_ATP865R: + printk(" AEC62XX time out "); +#if 0 + { + int i = 0; + u8 reg49h = 0; + pci_read_config_byte(HWIF(drive)->pci_dev, 0x49, ®49h); + for (i=0;i<256;i++) + pci_write_config_byte(HWIF(drive)->pci_dev, 0x49, reg49h|0x10); + pci_write_config_byte(HWIF(drive)->pci_dev, 0x49, reg49h & ~0x10); + } + return 0; +#endif + default: + break; + } +#if 0 + { + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + u8 tmp1 = 0, tmp2 = 0, mode6 = 0; + + pci_read_config_byte(dev, 0x44, &tmp1); + pci_read_config_byte(dev, 0x45, &tmp2); + printk(" AEC6280 r44=%x r45=%x ",tmp1,tmp2); + mode6 = HWIF(drive)->INB(((hwif->channel) ? + hwif->mate->dma_status : + hwif->dma_status)); + printk(" AEC6280 133=%x ", (mode6 & 0x10)); + } +#endif + return 0; +} + +static unsigned int __devinit init_chipset_aec62xx(struct pci_dev *dev, const char *name) +{ + int bus_speed = system_bus_clock(); + + 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(KERN_INFO "%s: ROM enabled at 0x%08lx\n", name, dev->resource[PCI_ROM_RESOURCE].start); + } + + if (bus_speed <= 33) + pci_set_drvdata(dev, (void *) aec6xxx_33_base); + else + pci_set_drvdata(dev, (void *) aec6xxx_34_base); + + return dev->irq; +} + +static void __devinit init_hwif_aec62xx(ide_hwif_t *hwif) +{ + hwif->autodma = 0; + hwif->tuneproc = &aec62xx_tune_drive; + hwif->speedproc = &aec62xx_tune_chipset; + + if (hwif->pci_dev->device == PCI_DEVICE_ID_ARTOP_ATP850UF) { + hwif->serialized = hwif->channel; + hwif->no_dsc = 1; + } + + if (hwif->mate) + hwif->mate->serialized = hwif->serialized; + + if (!hwif->dma_base) { + hwif->drives[0].autotune = 1; + hwif->drives[1].autotune = 1; + return; + } + + hwif->ultra_mask = 0x7f; + hwif->mwdma_mask = 0x07; + hwif->swdma_mask = 0x07; + + hwif->ide_dma_check = &aec62xx_config_drive_xfer_rate; + hwif->ide_dma_lostirq = &aec62xx_irq_timeout; + hwif->ide_dma_timeout = &aec62xx_irq_timeout; + if (!noautodma) + hwif->autodma = 1; + hwif->drives[0].autodma = hwif->autodma; + hwif->drives[1].autodma = hwif->autodma; +} + +static void __devinit init_dma_aec62xx(ide_hwif_t *hwif, unsigned long dmabase) +{ + struct pci_dev *dev = hwif->pci_dev; + + if (dev->device == PCI_DEVICE_ID_ARTOP_ATP850UF) { + u8 reg54h = 0; + unsigned long flags; + + spin_lock_irqsave(&ide_lock, flags); + pci_read_config_byte(dev, 0x54, ®54h); + pci_write_config_byte(dev, 0x54, reg54h & ~(hwif->channel ? 0xF0 : 0x0F)); + spin_unlock_irqrestore(&ide_lock, flags); + } else { + u8 ata66 = 0; + pci_read_config_byte(hwif->pci_dev, 0x49, &ata66); + if (!(hwif->udma_four)) + hwif->udma_four = (ata66&(hwif->channel?0x02:0x01))?0:1; + } + + ide_setup_dma(hwif, dmabase, 8); +} + +static int __devinit init_setup_aec62xx(struct pci_dev *dev, ide_pci_device_t *d) +{ + return ide_setup_pci_device(dev, d); +} + +static int __devinit init_setup_aec6x80(struct pci_dev *dev, ide_pci_device_t *d) +{ + unsigned long bar4reg = pci_resource_start(dev, 4); + + if (inb(bar4reg+2) & 0x10) { + strcpy(d->name, "AEC6880"); + if (dev->device == PCI_DEVICE_ID_ARTOP_ATP865R) + strcpy(d->name, "AEC6880R"); + } else { + strcpy(d->name, "AEC6280"); + if (dev->device == PCI_DEVICE_ID_ARTOP_ATP865R) + strcpy(d->name, "AEC6280R"); + } + + return ide_setup_pci_device(dev, d); +} + +static ide_pci_device_t aec62xx_chipsets[] __devinitdata = { + { /* 0 */ + .name = "AEC6210", + .init_setup = init_setup_aec62xx, + .init_chipset = init_chipset_aec62xx, + .init_hwif = init_hwif_aec62xx, + .init_dma = init_dma_aec62xx, + .channels = 2, + .autodma = AUTODMA, + .enablebits = {{0x4a,0x02,0x02}, {0x4a,0x04,0x04}}, + .bootable = OFF_BOARD, + },{ /* 1 */ + .name = "AEC6260", + .init_setup = init_setup_aec62xx, + .init_chipset = init_chipset_aec62xx, + .init_hwif = init_hwif_aec62xx, + .init_dma = init_dma_aec62xx, + .channels = 2, + .autodma = NOAUTODMA, + .bootable = OFF_BOARD, + },{ /* 2 */ + .name = "AEC6260R", + .init_setup = init_setup_aec62xx, + .init_chipset = init_chipset_aec62xx, + .init_hwif = init_hwif_aec62xx, + .init_dma = init_dma_aec62xx, + .channels = 2, + .autodma = AUTODMA, + .enablebits = {{0x4a,0x02,0x02}, {0x4a,0x04,0x04}}, + .bootable = NEVER_BOARD, + },{ /* 3 */ + .name = "AEC6X80", + .init_setup = init_setup_aec6x80, + .init_chipset = init_chipset_aec62xx, + .init_hwif = init_hwif_aec62xx, + .init_dma = init_dma_aec62xx, + .channels = 2, + .autodma = AUTODMA, + .bootable = OFF_BOARD, + },{ /* 4 */ + .name = "AEC6X80R", + .init_setup = init_setup_aec6x80, + .init_chipset = init_chipset_aec62xx, + .init_hwif = init_hwif_aec62xx, + .init_dma = init_dma_aec62xx, + .channels = 2, + .autodma = AUTODMA, + .enablebits = {{0x4a,0x02,0x02}, {0x4a,0x04,0x04}}, + .bootable = OFF_BOARD, + } +}; + +/** + * aec62xx_init_one - called when a AEC is found + * @dev: the aec62xx device + * @id: the matching pci id + * + * Called when the PCI registration layer (or the IDE initialization) + * finds a device matching our IDE device tables. + */ + +static int __devinit aec62xx_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + ide_pci_device_t *d = &aec62xx_chipsets[id->driver_data]; + + return d->init_setup(dev, d); +} + +static struct pci_device_id aec62xx_pci_tbl[] = { + { PCI_VENDOR_ID_ARTOP, PCI_DEVICE_ID_ARTOP_ATP850UF, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + { PCI_VENDOR_ID_ARTOP, PCI_DEVICE_ID_ARTOP_ATP860, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 }, + { PCI_VENDOR_ID_ARTOP, PCI_DEVICE_ID_ARTOP_ATP860R, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2 }, + { PCI_VENDOR_ID_ARTOP, PCI_DEVICE_ID_ARTOP_ATP865, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 3 }, + { PCI_VENDOR_ID_ARTOP, PCI_DEVICE_ID_ARTOP_ATP865R, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4 }, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, aec62xx_pci_tbl); + +static struct pci_driver driver = { + .name = "AEC62xx_IDE", + .id_table = aec62xx_pci_tbl, + .probe = aec62xx_init_one, +}; + +static int aec62xx_ide_init(void) +{ + return ide_pci_register_driver(&driver); +} + +module_init(aec62xx_ide_init); + +MODULE_AUTHOR("Andre Hedrick"); +MODULE_DESCRIPTION("PCI driver module for ARTOP AEC62xx IDE"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/pci/alim15x3.c b/drivers/ide/pci/alim15x3.c new file mode 100644 index 0000000..67efb38 --- /dev/null +++ b/drivers/ide/pci/alim15x3.c @@ -0,0 +1,913 @@ +/* + * linux/drivers/ide/pci/alim15x3.c Version 0.17 2003/01/02 + * + * Copyright (C) 1998-2000 Michel Aubry, Maintainer + * Copyright (C) 1998-2000 Andrzej Krzysztofowicz, Maintainer + * Copyright (C) 1999-2000 CJ, cjtsai@ali.com.tw, Maintainer + * + * Copyright (C) 1998-2000 Andre Hedrick (andre@linux-ide.org) + * May be copied or modified under the terms of the GNU General Public License + * Copyright (C) 2002 Alan Cox <alan@redhat.com> + * ALi (now ULi M5228) support by Clear Zhang <Clear.Zhang@ali.com.tw> + * + * (U)DMA capable version of ali 1533/1543(C), 1535(D) + * + ********************************************************************** + * 9/7/99 --Parts from the above author are included and need to be + * converted into standard interface, once I finish the thought. + * + * Recent changes + * Don't use LBA48 mode on ALi <= 0xC4 + * Don't poke 0x79 with a non ALi northbridge + * Don't flip undefined bits on newer chipsets (fix Fujitsu laptop hang) + * Allow UDMA6 on revisions > 0xC4 + * + * Documentation + * Chipset documentation available under NDA only + * + */ + +#include <linux/config.h> +#include <linux/module.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 <linux/init.h> + +#include <asm/io.h> + +#define DISPLAY_ALI_TIMINGS + +/* + * ALi devices are not plug in. Otherwise these static values would + * need to go. They ought to go away anyway + */ + +static u8 m5229_revision; +static u8 chip_is_1543c_e; +static struct pci_dev *isa_dev; + +#if defined(DISPLAY_ALI_TIMINGS) && defined(CONFIG_PROC_FS) +#include <linux/stat.h> +#include <linux/proc_fs.h> + +static u8 ali_proc = 0; + +static struct pci_dev *bmide_dev; + +static char *fifo[4] = { + "FIFO Off", + "FIFO On ", + "DMA mode", + "PIO mode" }; + +static char *udmaT[8] = { + "1.5T", + " 2T", + "2.5T", + " 3T", + "3.5T", + " 4T", + " 6T", + " 8T" +}; + +static char *channel_status[8] = { + "OK ", + "busy ", + "DRQ ", + "DRQ busy ", + "error ", + "error busy ", + "error DRQ ", + "error DRQ busy" +}; + +/** + * ali_get_info - generate proc file for ALi IDE + * @buffer: buffer to fill + * @addr: address of user start in buffer + * @offset: offset into 'file' + * @count: buffer count + * + * Walks the Ali devices and outputs summary data on the tuning and + * anything else that will help with debugging + */ + +static int ali_get_info (char *buffer, char **addr, off_t offset, int count) +{ + unsigned long bibma; + u8 reg53h, reg5xh, reg5yh, reg5xh1, reg5yh1, c0, c1, rev, tmp; + char *q, *p = buffer; + + /* fetch rev. */ + pci_read_config_byte(bmide_dev, 0x08, &rev); + if (rev >= 0xc1) /* M1543C or newer */ + udmaT[7] = " ???"; + else + fifo[3] = " ??? "; + + /* first fetch bibma: */ + + bibma = pci_resource_start(bmide_dev, 4); + + /* + * at that point bibma+0x2 et bibma+0xa are byte + * registers to investigate: + */ + c0 = inb(bibma + 0x02); + c1 = inb(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 { + int t1 = (tmp = (reg5xh & 0x03)) ? (tmp << 3) : 4; + int t2 = (tmp = ((reg5xh & 0x30)>>4)) ? (tmp << 3) : 4; + int t3 = (tmp = (reg5yh & 0x03)) ? (tmp << 3) : 4; + int t4 = (tmp = ((reg5yh & 0x30)>>4)) ? (tmp << 3) : 4; + p += sprintf(p, q, t1, t2, t3, t4); + } + +#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) */ + +/** + * ali15x3_tune_drive - set up a drive + * @drive: drive to tune + * @pio: unused + * + * Select the best PIO timing for the drive in question. Then + * program the controller for this drive set up + */ + +static void ali15x3_tune_drive (ide_drive_t *drive, u8 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; + u8 s_clc, a_clc, r_clc; + unsigned long flags; + int bus_speed = system_bus_clock(); + int port = hwif->channel ? 0x5c : 0x58; + int portFIFO = hwif->channel ? 0x55 : 0x54; + u8 cd_dma_fifo = 0; + int unit = drive->select.b.unit & 1; + + 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; + } + local_irq_save(flags); + + /* + * PIO mode => ATA FIFO on, ATAPI FIFO off + */ + pci_read_config_byte(dev, portFIFO, &cd_dma_fifo); + if (drive->media==ide_disk) { + if (unit) { + 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 (unit) { + 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); + local_irq_restore(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) + */ + +} + +/** + * ali15x3_can_ultra - check for ultra DMA support + * @drive: drive to do the check + * + * Check the drive and controller revisions. Return 0 if UDMA is + * not available, or 1 if UDMA can be used. The actual rules for + * the ALi are + * No UDMA on revisions <= 0x20 + * Disk only for revisions < 0xC2 + * Not WDC drives for revisions < 0xC2 + * + * FIXME: WDC ifdef needs to die + */ + +static u8 ali15x3_can_ultra (ide_drive_t *drive) +{ +#ifndef CONFIG_WDC_ALI15X3 + struct hd_driveid *id = drive->id; +#endif /* CONFIG_WDC_ALI15X3 */ + + if (m5229_revision <= 0x20) { + return 0; + } else if ((m5229_revision < 0xC2) && +#ifndef CONFIG_WDC_ALI15X3 + ((chip_is_1543c_e && strstr(id->model, "WDC ")) || + (drive->media!=ide_disk))) { +#else /* CONFIG_WDC_ALI15X3 */ + (drive->media!=ide_disk)) { +#endif /* CONFIG_WDC_ALI15X3 */ + return 0; + } else { + return 1; + } +} + +/** + * ali15x3_ratemask - generate DMA mode list + * @drive: drive to compute against + * + * Generate a list of the available DMA modes for the drive. + * FIXME: this function contains lots of bogus masking we can dump + * + * Return the highest available mode (UDMA33, UDMA66, UDMA100,..) + */ + +static u8 ali15x3_ratemask (ide_drive_t *drive) +{ + u8 mode = 0, can_ultra = ali15x3_can_ultra(drive); + + if (m5229_revision > 0xC4 && can_ultra) { + mode = 4; + } else if (m5229_revision == 0xC4 && can_ultra) { + mode = 3; + } else if (m5229_revision >= 0xC2 && can_ultra) { + mode = 2; + } else if (can_ultra) { + return 1; + } else { + return 0; + } + + /* + * If the drive sees no suitable cable then UDMA 33 + * is the highest permitted mode + */ + + if (!eighty_ninty_three(drive)) + mode = min(mode, (u8)1); + return mode; +} + +/** + * ali15x3_tune_chipset - set up chiset for new speed + * @drive: drive to configure for + * @xferspeed: desired speed + * + * Configure the hardware for the desired IDE transfer mode. + * We also do the needed drive configuration through helpers + */ + +static int ali15x3_tune_chipset (ide_drive_t *drive, u8 xferspeed) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + u8 speed = ide_rate_filter(ali15x3_ratemask(drive), xferspeed); + u8 speed1 = speed; + u8 unit = (drive->select.b.unit & 0x01); + u8 tmpbyte = 0x00; + int m5229_udma = (hwif->channel) ? 0x57 : 0x56; + + if (speed == XFER_UDMA_6) + speed1 = 0x47; + + if (speed < XFER_UDMA_0) { + u8 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); + + if (speed < XFER_SW_DMA_0) + ali15x3_tune_drive(drive, speed); + } else { + pci_read_config_byte(dev, m5229_udma, &tmpbyte); + tmpbyte &= (0x0f << ((1-unit) << 2)); + /* + * enable ultra dma and set timing + */ + tmpbyte |= ((0x08 | ((4-speed1)&0x07)) << (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 (ide_config_drive_speed(drive, speed)); +} + + +/** + * config_chipset_for_dma - set up DMA mode + * @drive: drive to configure for + * + * Place a drive into DMA mode and tune the chipset for + * the selected speed. + * + * Returns true if DMA mode can be used + */ + +static int config_chipset_for_dma (ide_drive_t *drive) +{ + u8 speed = ide_dma_speed(drive, ali15x3_ratemask(drive)); + + if (!(speed)) + return 0; + + (void) ali15x3_tune_chipset(drive, speed); + return ide_dma_enable(drive); +} + +/** + * ali15x3_config_drive_for_dma - configure for DMA + * @drive: drive to configure + * + * Configure a drive for DMA operation. If DMA is not possible we + * drop the drive into PIO mode instead. + * + * FIXME: exactly what are we trying to return here + */ + +static int ali15x3_config_drive_for_dma(ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + struct hd_driveid *id = drive->id; + + if ((m5229_revision<=0x20) && (drive->media!=ide_disk)) + return hwif->ide_dma_off_quietly(drive); + + drive->init_speed = 0; + + if ((id != NULL) && ((id->capability & 1) != 0) && drive->autodma) { + /* Consult the list of known "bad" drives */ + if (__ide_dma_bad_drive(drive)) + goto ata_pio; + if ((id->field_valid & 4) && (m5229_revision >= 0xC2)) { + if (id->dma_ultra & hwif->ultra_mask) { + /* Force if Capable UltraDMA */ + int dma = config_chipset_for_dma(drive); + if ((id->field_valid & 2) && !dma) + goto try_dma_modes; + } + } else if (id->field_valid & 2) { +try_dma_modes: + if ((id->dma_mword & hwif->mwdma_mask) || + (id->dma_1word & hwif->swdma_mask)) { + /* Force if Capable regular DMA modes */ + if (!config_chipset_for_dma(drive)) + goto no_dma_set; + } + } else if (__ide_dma_good_drive(drive) && + (id->eide_dma_time < 150)) { + /* Consult the list of known "good" drives */ + if (!config_chipset_for_dma(drive)) + goto no_dma_set; + } else { + goto ata_pio; + } + } else { +ata_pio: + hwif->tuneproc(drive, 255); +no_dma_set: + return hwif->ide_dma_off_quietly(drive); + } + return hwif->ide_dma_on(drive); +} + +/** + * ali15x3_dma_setup - begin a DMA phase + * @drive: target device + * + * Returns 1 if the DMA cannot be performed, zero on success. + */ + +static int ali15x3_dma_setup(ide_drive_t *drive) +{ + if (m5229_revision < 0xC2 && drive->media != ide_disk) { + if (rq_data_dir(drive->hwif->hwgroup->rq)) + return 1; /* try PIO instead of DMA */ + } + return ide_dma_setup(drive); +} + +/** + * init_chipset_ali15x3 - Initialise an ALi IDE controller + * @dev: PCI device + * @name: Name of the controller + * + * This function initializes the ALI IDE controller and where + * appropriate also sets up the 1533 southbridge. + */ + +static unsigned int __init init_chipset_ali15x3 (struct pci_dev *dev, const char *name) +{ + unsigned long flags; + u8 tmpbyte; + struct pci_dev *north = pci_find_slot(0, PCI_DEVFN(0,0)); + + 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 defined(DISPLAY_ALI_TIMINGS) && defined(CONFIG_PROC_FS) + if (!ali_proc) { + ali_proc = 1; + bmide_dev = dev; + ide_pci_create_host_proc("ali", ali_get_info); + } +#endif /* defined(DISPLAY_ALI_TIMINGS) && defined(CONFIG_PROC_FS) */ + + local_irq_save(flags); + + if (m5229_revision < 0xC2) { + /* + * 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); + local_irq_restore(flags); + return 0; + } + + /* + * 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); + + /* + * We should only tune the 1533 enable if we are using an ALi + * North bridge. We might have no north found on some zany + * box without a device at 0:0.0. The ALi bridge will be at + * 0:0.0 so if we didn't find one we know what is cooking. + */ + if (north && north->vendor != PCI_VENDOR_ID_AL) { + local_irq_restore(flags); + return 0; + } + + if (m5229_revision < 0xC5 && isa_dev) + { + /* + * 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); + } + } + local_irq_restore(flags); + return 0; +} + +/** + * ata66_ali15x3 - check for UDMA 66 support + * @hwif: IDE interface + * + * This checks if the controller and the cable are capable + * of UDMA66 transfers. It doesn't check the drives. + * But see note 2 below! + * + * FIXME: frobs bits that are not defined on newer ALi devicea + */ + +static unsigned int __init ata66_ali15x3 (ide_hwif_t *hwif) +{ + struct pci_dev *dev = hwif->pci_dev; + unsigned int ata66 = 0; + u8 cable_80_pin[2] = { 0, 0 }; + + unsigned long flags; + u8 tmpbyte; + + local_irq_save(flags); + + if (m5229_revision >= 0xC2) { + /* + * 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; + /* + * Allow ata66 if cable of current channel has 80 pins + */ + ata66 = (hwif->channel)?cable_80_pin[1]:cable_80_pin[0]; + } else { + /* + * 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 + * (Not on later devices it seems) + * + * 0x53 changes meaning on later revs - we must no touch + * bit 1 on them. Need to check if 0x20 is the right break + */ + + pci_read_config_byte(dev, 0x53, &tmpbyte); + + if(m5229_revision <= 0x20) + tmpbyte = (tmpbyte & (~0x02)) | 0x01; + else + tmpbyte |= 0x01; + + pci_write_config_byte(dev, 0x53, tmpbyte); + + local_irq_restore(flags); + + return(ata66); +} + +/** + * init_hwif_common_ali15x3 - Set up ALI IDE hardware + * @hwif: IDE interface + * + * Initialize the IDE structure side of the ALi 15x3 driver. + */ + +static void __init init_hwif_common_ali15x3 (ide_hwif_t *hwif) +{ + hwif->autodma = 0; + hwif->tuneproc = &ali15x3_tune_drive; + hwif->speedproc = &ali15x3_tune_chipset; + + /* don't use LBA48 DMA on ALi devices before rev 0xC5 */ + hwif->no_lba48_dma = (m5229_revision <= 0xC4) ? 1 : 0; + + if (!hwif->dma_base) { + hwif->drives[0].autotune = 1; + hwif->drives[1].autotune = 1; + return; + } + + hwif->atapi_dma = 1; + + if (m5229_revision > 0x20) + hwif->ultra_mask = 0x7f; + hwif->mwdma_mask = 0x07; + hwif->swdma_mask = 0x07; + + if (m5229_revision >= 0x20) { + /* + * M1543C or newer for DMAing + */ + hwif->ide_dma_check = &ali15x3_config_drive_for_dma; + hwif->dma_setup = &ali15x3_dma_setup; + if (!noautodma) + hwif->autodma = 1; + if (!(hwif->udma_four)) + hwif->udma_four = ata66_ali15x3(hwif); + } + hwif->drives[0].autodma = hwif->autodma; + hwif->drives[1].autodma = hwif->autodma; +} + +/** + * init_hwif_ali15x3 - Initialize the ALI IDE x86 stuff + * @hwif: interface to configure + * + * Obtain the IRQ tables for an ALi based IDE solution on the PC + * class platforms. This part of the code isn't applicable to the + * Sparc systems + */ + +static void __init init_hwif_ali15x3 (ide_hwif_t *hwif) +{ + u8 ideic, inmir; + s8 irq_routing_table[] = { -1, 9, 3, 10, 4, 5, 7, 6, + 1, 11, 0, 12, 0, 14, 0, 15 }; + int irq = -1; + + if (hwif->pci_dev->device == PCI_DEVICE_ID_AL_M5229) + 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; + 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; + irq = irq_routing_table[inmir]; + } + if(irq >= 0) + hwif->irq = irq; + } + + init_hwif_common_ali15x3(hwif); +} + +/** + * init_dma_ali15x3 - set up DMA on ALi15x3 + * @hwif: IDE interface + * @dmabase: DMA interface base PCI address + * + * Set up the DMA functionality on the ALi 15x3. For the ALi + * controllers this is generic so we can let the generic code do + * the actual work. + */ + +static void __init init_dma_ali15x3 (ide_hwif_t *hwif, unsigned long dmabase) +{ + if (m5229_revision < 0x20) + return; + if (!(hwif->channel)) + hwif->OUTB(hwif->INB(dmabase+2) & 0x60, dmabase+2); + ide_setup_dma(hwif, dmabase, 8); +} + +static ide_pci_device_t ali15x3_chipset __devinitdata = { + .name = "ALI15X3", + .init_chipset = init_chipset_ali15x3, + .init_hwif = init_hwif_ali15x3, + .init_dma = init_dma_ali15x3, + .channels = 2, + .autodma = AUTODMA, + .bootable = ON_BOARD, +}; + +/** + * alim15x3_init_one - set up an ALi15x3 IDE controller + * @dev: PCI device to set up + * + * Perform the actual set up for an ALi15x3 that has been found by the + * hot plug layer. + */ + +static int __devinit alim15x3_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + ide_pci_device_t *d = &ali15x3_chipset; + + if(pci_find_device(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RS100, NULL)) + printk(KERN_ERR "Warning: ATI Radeon IGP Northbridge is not yet fully tested.\n"); + +#if defined(CONFIG_SPARC64) + d->init_hwif = init_hwif_common_ali15x3; +#endif /* CONFIG_SPARC64 */ + return ide_setup_pci_device(dev, d); +} + + +static struct pci_device_id alim15x3_pci_tbl[] = { + { PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M5229, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + { PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M5228, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, alim15x3_pci_tbl); + +static struct pci_driver driver = { + .name = "ALI15x3_IDE", + .id_table = alim15x3_pci_tbl, + .probe = alim15x3_init_one, +}; + +static int ali15x3_ide_init(void) +{ + return ide_pci_register_driver(&driver); +} + +module_init(ali15x3_ide_init); + +MODULE_AUTHOR("Michael Aubry, Andrzej Krzysztofowicz, CJ, Andre Hedrick, Alan Cox"); +MODULE_DESCRIPTION("PCI driver module for ALi 15x3 IDE"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/pci/amd74xx.c b/drivers/ide/pci/amd74xx.c new file mode 100644 index 0000000..47225e3 --- /dev/null +++ b/drivers/ide/pci/amd74xx.c @@ -0,0 +1,543 @@ +/* + * Version 2.13 + * + * AMD 755/756/766/8111 and nVidia nForce/2/2s/3/3s/CK804/MCP04 + * IDE driver for Linux. + * + * Copyright (c) 2000-2002 Vojtech Pavlik + * + * Based on the work of: + * Andre Hedrick + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/ioport.h> +#include <linux/blkdev.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/ide.h> +#include <asm/io.h> + +#include "ide-timing.h" + +#define DISPLAY_AMD_TIMINGS + +#define AMD_IDE_ENABLE (0x00 + amd_config->base) +#define AMD_IDE_CONFIG (0x01 + amd_config->base) +#define AMD_CABLE_DETECT (0x02 + amd_config->base) +#define AMD_DRIVE_TIMING (0x08 + amd_config->base) +#define AMD_8BIT_TIMING (0x0e + amd_config->base) +#define AMD_ADDRESS_SETUP (0x0c + amd_config->base) +#define AMD_UDMA_TIMING (0x10 + amd_config->base) + +#define AMD_UDMA 0x07 +#define AMD_UDMA_33 0x01 +#define AMD_UDMA_66 0x02 +#define AMD_UDMA_100 0x03 +#define AMD_UDMA_133 0x04 +#define AMD_CHECK_SWDMA 0x08 +#define AMD_BAD_SWDMA 0x10 +#define AMD_BAD_FIFO 0x20 +#define AMD_CHECK_SERENADE 0x40 + +/* + * AMD SouthBridge chips. + */ + +static struct amd_ide_chip { + unsigned short id; + unsigned long base; + unsigned char flags; +} amd_ide_chips[] = { + { PCI_DEVICE_ID_AMD_COBRA_7401, 0x40, AMD_UDMA_33 | AMD_BAD_SWDMA }, + { PCI_DEVICE_ID_AMD_VIPER_7409, 0x40, AMD_UDMA_66 | AMD_CHECK_SWDMA }, + { PCI_DEVICE_ID_AMD_VIPER_7411, 0x40, AMD_UDMA_100 | AMD_BAD_FIFO }, + { PCI_DEVICE_ID_AMD_OPUS_7441, 0x40, AMD_UDMA_100 }, + { PCI_DEVICE_ID_AMD_8111_IDE, 0x40, AMD_UDMA_133 | AMD_CHECK_SERENADE }, + { PCI_DEVICE_ID_NVIDIA_NFORCE_IDE, 0x50, AMD_UDMA_100 }, + { PCI_DEVICE_ID_NVIDIA_NFORCE2_IDE, 0x50, AMD_UDMA_133 }, + { PCI_DEVICE_ID_NVIDIA_NFORCE2S_IDE, 0x50, AMD_UDMA_133 }, + { PCI_DEVICE_ID_NVIDIA_NFORCE2S_SATA, 0x50, AMD_UDMA_133 }, + { PCI_DEVICE_ID_NVIDIA_NFORCE3_IDE, 0x50, AMD_UDMA_133 }, + { PCI_DEVICE_ID_NVIDIA_NFORCE3S_IDE, 0x50, AMD_UDMA_133 }, + { PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA, 0x50, AMD_UDMA_133 }, + { PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA2, 0x50, AMD_UDMA_133 }, + { PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_IDE, 0x50, AMD_UDMA_133 }, + { PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_IDE, 0x50, AMD_UDMA_133 }, + { 0 } +}; + +static struct amd_ide_chip *amd_config; +static ide_pci_device_t *amd_chipset; +static unsigned int amd_80w; +static unsigned int amd_clock; + +static char *amd_dma[] = { "MWDMA16", "UDMA33", "UDMA66", "UDMA100", "UDMA133" }; +static unsigned char amd_cyc2udma[] = { 6, 6, 5, 4, 0, 1, 1, 2, 2, 3, 3, 3, 3, 3, 3, 7 }; + +/* + * AMD /proc entry. + */ + +#ifdef CONFIG_PROC_FS + +#include <linux/stat.h> +#include <linux/proc_fs.h> + +static u8 amd74xx_proc; + +static unsigned char amd_udma2cyc[] = { 4, 6, 8, 10, 3, 2, 1, 15 }; +static unsigned long amd_base; +static struct pci_dev *bmide_dev; +extern int (*amd74xx_display_info)(char *, char **, off_t, int); /* ide-proc.c */ + +#define amd_print(format, arg...) p += sprintf(p, format "\n" , ## arg) +#define amd_print_drive(name, format, arg...)\ + p += sprintf(p, name); for (i = 0; i < 4; i++) p += sprintf(p, format, ## arg); p += sprintf(p, "\n"); + +static int amd74xx_get_info(char *buffer, char **addr, off_t offset, int count) +{ + int speed[4], cycle[4], setup[4], active[4], recover[4], den[4], + uen[4], udma[4], active8b[4], recover8b[4]; + struct pci_dev *dev = bmide_dev; + unsigned int v, u, i; + unsigned short c, w; + unsigned char t; + int len; + char *p = buffer; + + amd_print("----------AMD BusMastering IDE Configuration----------------"); + + amd_print("Driver Version: 2.13"); + amd_print("South Bridge: %s", pci_name(bmide_dev)); + + pci_read_config_byte(dev, PCI_REVISION_ID, &t); + amd_print("Revision: IDE %#x", t); + amd_print("Highest DMA rate: %s", amd_dma[amd_config->flags & AMD_UDMA]); + + amd_print("BM-DMA base: %#lx", amd_base); + amd_print("PCI clock: %d.%dMHz", amd_clock / 1000, amd_clock / 100 % 10); + + amd_print("-----------------------Primary IDE-------Secondary IDE------"); + + pci_read_config_byte(dev, AMD_IDE_CONFIG, &t); + amd_print("Prefetch Buffer: %10s%20s", (t & 0x80) ? "yes" : "no", (t & 0x20) ? "yes" : "no"); + amd_print("Post Write Buffer: %10s%20s", (t & 0x40) ? "yes" : "no", (t & 0x10) ? "yes" : "no"); + + pci_read_config_byte(dev, AMD_IDE_ENABLE, &t); + amd_print("Enabled: %10s%20s", (t & 0x02) ? "yes" : "no", (t & 0x01) ? "yes" : "no"); + + c = inb(amd_base + 0x02) | (inb(amd_base + 0x0a) << 8); + amd_print("Simplex only: %10s%20s", (c & 0x80) ? "yes" : "no", (c & 0x8000) ? "yes" : "no"); + + amd_print("Cable Type: %10s%20s", (amd_80w & 1) ? "80w" : "40w", (amd_80w & 2) ? "80w" : "40w"); + + if (!amd_clock) + return p - buffer; + + amd_print("-------------------drive0----drive1----drive2----drive3-----"); + + pci_read_config_byte(dev, AMD_ADDRESS_SETUP, &t); + pci_read_config_dword(dev, AMD_DRIVE_TIMING, &v); + pci_read_config_word(dev, AMD_8BIT_TIMING, &w); + pci_read_config_dword(dev, AMD_UDMA_TIMING, &u); + + for (i = 0; i < 4; i++) { + setup[i] = ((t >> ((3 - i) << 1)) & 0x3) + 1; + recover8b[i] = ((w >> ((1 - (i >> 1)) << 3)) & 0xf) + 1; + active8b[i] = ((w >> (((1 - (i >> 1)) << 3) + 4)) & 0xf) + 1; + active[i] = ((v >> (((3 - i) << 3) + 4)) & 0xf) + 1; + recover[i] = ((v >> ((3 - i) << 3)) & 0xf) + 1; + + udma[i] = amd_udma2cyc[((u >> ((3 - i) << 3)) & 0x7)]; + uen[i] = ((u >> ((3 - i) << 3)) & 0x40) ? 1 : 0; + den[i] = (c & ((i & 1) ? 0x40 : 0x20) << ((i & 2) << 2)); + + if (den[i] && uen[i] && udma[i] == 1) { + speed[i] = amd_clock * 3; + cycle[i] = 666666 / amd_clock; + continue; + } + + if (den[i] && uen[i] && udma[i] == 15) { + speed[i] = amd_clock * 4; + cycle[i] = 500000 / amd_clock; + continue; + } + + speed[i] = 4 * amd_clock / ((den[i] && uen[i]) ? udma[i] : (active[i] + recover[i]) * 2); + cycle[i] = 1000000 * ((den[i] && uen[i]) ? udma[i] : (active[i] + recover[i]) * 2) / amd_clock / 2; + } + + amd_print_drive("Transfer Mode: ", "%10s", den[i] ? (uen[i] ? "UDMA" : "DMA") : "PIO"); + + amd_print_drive("Address Setup: ", "%8dns", 1000000 * setup[i] / amd_clock); + amd_print_drive("Cmd Active: ", "%8dns", 1000000 * active8b[i] / amd_clock); + amd_print_drive("Cmd Recovery: ", "%8dns", 1000000 * recover8b[i] / amd_clock); + amd_print_drive("Data Active: ", "%8dns", 1000000 * active[i] / amd_clock); + amd_print_drive("Data Recovery: ", "%8dns", 1000000 * recover[i] / amd_clock); + amd_print_drive("Cycle Time: ", "%8dns", cycle[i]); + amd_print_drive("Transfer Rate: ", "%4d.%dMB/s", speed[i] / 1000, speed[i] / 100 % 10); + + /* hoping p - buffer is less than 4K... */ + len = (p - buffer) - offset; + *addr = buffer + offset; + + return len > count ? count : len; +} + +#endif + +/* + * amd_set_speed() writes timing values to the chipset registers + */ + +static void amd_set_speed(struct pci_dev *dev, unsigned char dn, struct ide_timing *timing) +{ + unsigned char t; + + pci_read_config_byte(dev, AMD_ADDRESS_SETUP, &t); + t = (t & ~(3 << ((3 - dn) << 1))) | ((FIT(timing->setup, 1, 4) - 1) << ((3 - dn) << 1)); + pci_write_config_byte(dev, AMD_ADDRESS_SETUP, t); + + pci_write_config_byte(dev, AMD_8BIT_TIMING + (1 - (dn >> 1)), + ((FIT(timing->act8b, 1, 16) - 1) << 4) | (FIT(timing->rec8b, 1, 16) - 1)); + + pci_write_config_byte(dev, AMD_DRIVE_TIMING + (3 - dn), + ((FIT(timing->active, 1, 16) - 1) << 4) | (FIT(timing->recover, 1, 16) - 1)); + + switch (amd_config->flags & AMD_UDMA) { + case AMD_UDMA_33: t = timing->udma ? (0xc0 | (FIT(timing->udma, 2, 5) - 2)) : 0x03; break; + case AMD_UDMA_66: t = timing->udma ? (0xc0 | amd_cyc2udma[FIT(timing->udma, 2, 10)]) : 0x03; break; + case AMD_UDMA_100: t = timing->udma ? (0xc0 | amd_cyc2udma[FIT(timing->udma, 1, 10)]) : 0x03; break; + case AMD_UDMA_133: t = timing->udma ? (0xc0 | amd_cyc2udma[FIT(timing->udma, 1, 15)]) : 0x03; break; + default: return; + } + + pci_write_config_byte(dev, AMD_UDMA_TIMING + (3 - dn), t); +} + +/* + * amd_set_drive() computes timing values configures the drive and + * the chipset to a desired transfer mode. It also can be called + * by upper layers. + */ + +static int amd_set_drive(ide_drive_t *drive, u8 speed) +{ + ide_drive_t *peer = HWIF(drive)->drives + (~drive->dn & 1); + struct ide_timing t, p; + int T, UT; + + if (speed != XFER_PIO_SLOW && speed != drive->current_speed) + if (ide_config_drive_speed(drive, speed)) + printk(KERN_WARNING "ide%d: Drive %d didn't accept speed setting. Oh, well.\n", + drive->dn >> 1, drive->dn & 1); + + T = 1000000000 / amd_clock; + UT = T / min_t(int, max_t(int, amd_config->flags & AMD_UDMA, 1), 2); + + ide_timing_compute(drive, speed, &t, T, UT); + + if (peer->present) { + ide_timing_compute(peer, peer->current_speed, &p, T, UT); + ide_timing_merge(&p, &t, &t, IDE_TIMING_8BIT); + } + + if (speed == XFER_UDMA_5 && amd_clock <= 33333) t.udma = 1; + if (speed == XFER_UDMA_6 && amd_clock <= 33333) t.udma = 15; + + amd_set_speed(HWIF(drive)->pci_dev, drive->dn, &t); + + if (!drive->init_speed) + drive->init_speed = speed; + drive->current_speed = speed; + + return 0; +} + +/* + * amd74xx_tune_drive() is a callback from upper layers for + * PIO-only tuning. + */ + +static void amd74xx_tune_drive(ide_drive_t *drive, u8 pio) +{ + if (pio == 255) { + amd_set_drive(drive, ide_find_best_mode(drive, XFER_PIO | XFER_EPIO)); + return; + } + + amd_set_drive(drive, XFER_PIO_0 + min_t(byte, pio, 5)); +} + +/* + * amd74xx_dmaproc() is a callback from upper layers that can do + * a lot, but we use it for DMA/PIO tuning only, delegating everything + * else to the default ide_dmaproc(). + */ + +static int amd74xx_ide_dma_check(ide_drive_t *drive) +{ + int w80 = HWIF(drive)->udma_four; + + u8 speed = ide_find_best_mode(drive, + XFER_PIO | XFER_EPIO | XFER_MWDMA | XFER_UDMA | + ((amd_config->flags & AMD_BAD_SWDMA) ? 0 : XFER_SWDMA) | + (w80 && (amd_config->flags & AMD_UDMA) >= AMD_UDMA_66 ? XFER_UDMA_66 : 0) | + (w80 && (amd_config->flags & AMD_UDMA) >= AMD_UDMA_100 ? XFER_UDMA_100 : 0) | + (w80 && (amd_config->flags & AMD_UDMA) >= AMD_UDMA_133 ? XFER_UDMA_133 : 0)); + + amd_set_drive(drive, speed); + + if (drive->autodma && (speed & XFER_MODE) != XFER_PIO) + return HWIF(drive)->ide_dma_on(drive); + return HWIF(drive)->ide_dma_off_quietly(drive); +} + +/* + * The initialization callback. Here we determine the IDE chip type + * and initialize its drive independent registers. + */ + +static unsigned int __init init_chipset_amd74xx(struct pci_dev *dev, const char *name) +{ + unsigned char t; + unsigned int u; + int i; + +/* + * Check for bad SWDMA. + */ + + if (amd_config->flags & AMD_CHECK_SWDMA) { + pci_read_config_byte(dev, PCI_REVISION_ID, &t); + if (t <= 7) + amd_config->flags |= AMD_BAD_SWDMA; + } + +/* + * Check 80-wire cable presence. + */ + + switch (amd_config->flags & AMD_UDMA) { + + case AMD_UDMA_133: + case AMD_UDMA_100: + pci_read_config_byte(dev, AMD_CABLE_DETECT, &t); + pci_read_config_dword(dev, AMD_UDMA_TIMING, &u); + amd_80w = ((t & 0x3) ? 1 : 0) | ((t & 0xc) ? 2 : 0); + for (i = 24; i >= 0; i -= 8) + if (((u >> i) & 4) && !(amd_80w & (1 << (1 - (i >> 4))))) { + printk(KERN_WARNING "%s: BIOS didn't set cable bits correctly. Enabling workaround.\n", + amd_chipset->name); + amd_80w |= (1 << (1 - (i >> 4))); + } + break; + + case AMD_UDMA_66: + pci_read_config_dword(dev, AMD_UDMA_TIMING, &u); + for (i = 24; i >= 0; i -= 8) + if ((u >> i) & 4) + amd_80w |= (1 << (1 - (i >> 4))); + break; + } + +/* + * Take care of prefetch & postwrite. + */ + + pci_read_config_byte(dev, AMD_IDE_CONFIG, &t); + pci_write_config_byte(dev, AMD_IDE_CONFIG, + (amd_config->flags & AMD_BAD_FIFO) ? (t & 0x0f) : (t | 0xf0)); + +/* + * Take care of incorrectly wired Serenade mainboards. + */ + + if ((amd_config->flags & AMD_CHECK_SERENADE) && + dev->subsystem_vendor == PCI_VENDOR_ID_AMD && + dev->subsystem_device == PCI_DEVICE_ID_AMD_SERENADE) + amd_config->flags = AMD_UDMA_100; + +/* + * Determine the system bus clock. + */ + + amd_clock = system_bus_clock() * 1000; + + switch (amd_clock) { + case 33000: amd_clock = 33333; break; + case 37000: amd_clock = 37500; break; + case 41000: amd_clock = 41666; break; + } + + if (amd_clock < 20000 || amd_clock > 50000) { + printk(KERN_WARNING "%s: User given PCI clock speed impossible (%d), using 33 MHz instead.\n", + amd_chipset->name, amd_clock); + printk(KERN_WARNING "%s: Use ide0=ata66 if you want to assume 80-wire cable\n", + amd_chipset->name); + amd_clock = 33333; + } + +/* + * Print the boot message. + */ + + pci_read_config_byte(dev, PCI_REVISION_ID, &t); + printk(KERN_INFO "%s: %s (rev %02x) %s controller\n", + amd_chipset->name, pci_name(dev), t, amd_dma[amd_config->flags & AMD_UDMA]); + +/* + * Register /proc/ide/amd74xx entry + */ + +#if defined(DISPLAY_AMD_TIMINGS) && defined(CONFIG_PROC_FS) + if (!amd74xx_proc) { + amd_base = pci_resource_start(dev, 4); + bmide_dev = dev; + ide_pci_create_host_proc("amd74xx", amd74xx_get_info); + amd74xx_proc = 1; + } +#endif /* DISPLAY_AMD_TIMINGS && CONFIG_PROC_FS */ + + return dev->irq; +} + +static void __init init_hwif_amd74xx(ide_hwif_t *hwif) +{ + int i; + + if (hwif->irq == 0) /* 0 is bogus but will do for now */ + hwif->irq = pci_get_legacy_ide_irq(hwif->pci_dev, hwif->channel); + + hwif->autodma = 0; + + hwif->tuneproc = &amd74xx_tune_drive; + hwif->speedproc = &amd_set_drive; + + for (i = 0; i < 2; i++) { + hwif->drives[i].io_32bit = 1; + hwif->drives[i].unmask = 1; + hwif->drives[i].autotune = 1; + hwif->drives[i].dn = hwif->channel * 2 + i; + } + + if (!hwif->dma_base) + return; + + hwif->atapi_dma = 1; + hwif->ultra_mask = 0x7f; + hwif->mwdma_mask = 0x07; + hwif->swdma_mask = 0x07; + + if (!hwif->udma_four) + hwif->udma_four = (amd_80w >> hwif->channel) & 1; + hwif->ide_dma_check = &amd74xx_ide_dma_check; + if (!noautodma) + hwif->autodma = 1; + hwif->drives[0].autodma = hwif->autodma; + hwif->drives[1].autodma = hwif->autodma; +} + +#define DECLARE_AMD_DEV(name_str) \ + { \ + .name = name_str, \ + .init_chipset = init_chipset_amd74xx, \ + .init_hwif = init_hwif_amd74xx, \ + .channels = 2, \ + .autodma = AUTODMA, \ + .enablebits = {{0x40,0x02,0x02}, {0x40,0x01,0x01}}, \ + .bootable = ON_BOARD, \ + } + +#define DECLARE_NV_DEV(name_str) \ + { \ + .name = name_str, \ + .init_chipset = init_chipset_amd74xx, \ + .init_hwif = init_hwif_amd74xx, \ + .channels = 2, \ + .autodma = AUTODMA, \ + .enablebits = {{0x50,0x02,0x02}, {0x50,0x01,0x01}}, \ + .bootable = ON_BOARD, \ + } + +static ide_pci_device_t amd74xx_chipsets[] __devinitdata = { + /* 0 */ DECLARE_AMD_DEV("AMD7401"), + /* 1 */ DECLARE_AMD_DEV("AMD7409"), + /* 2 */ DECLARE_AMD_DEV("AMD7411"), + /* 3 */ DECLARE_AMD_DEV("AMD7441"), + /* 4 */ DECLARE_AMD_DEV("AMD8111"), + + /* 5 */ DECLARE_NV_DEV("NFORCE"), + /* 6 */ DECLARE_NV_DEV("NFORCE2"), + /* 7 */ DECLARE_NV_DEV("NFORCE2-U400R"), + /* 8 */ DECLARE_NV_DEV("NFORCE2-U400R-SATA"), + /* 9 */ DECLARE_NV_DEV("NFORCE3-150"), + /* 10 */ DECLARE_NV_DEV("NFORCE3-250"), + /* 11 */ DECLARE_NV_DEV("NFORCE3-250-SATA"), + /* 12 */ DECLARE_NV_DEV("NFORCE3-250-SATA2"), + /* 13 */ DECLARE_NV_DEV("NFORCE-CK804"), + /* 14 */ DECLARE_NV_DEV("NFORCE-MCP04"), +}; + +static int __devinit amd74xx_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + amd_chipset = amd74xx_chipsets + id->driver_data; + amd_config = amd_ide_chips + id->driver_data; + if (dev->device != amd_config->id) { + printk(KERN_ERR "%s: assertion 0x%02x == 0x%02x failed !\n", + pci_name(dev), dev->device, amd_config->id); + return -ENODEV; + } + return ide_setup_pci_device(dev, amd_chipset); +} + +static struct pci_device_id amd74xx_pci_tbl[] = { + { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_COBRA_7401, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7409, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 }, + { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7411, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2 }, + { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_OPUS_7441, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 3 }, + { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4 }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 5 }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 6 }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2S_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 7 }, +#ifdef CONFIG_BLK_DEV_IDE_SATA + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2S_SATA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 8 }, +#endif + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 9 }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 10 }, +#ifdef CONFIG_BLK_DEV_IDE_SATA + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 11 }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 12 }, +#endif + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 13 }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 14 }, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, amd74xx_pci_tbl); + +static struct pci_driver driver = { + .name = "AMD_IDE", + .id_table = amd74xx_pci_tbl, + .probe = amd74xx_probe, +}; + +static int amd74xx_ide_init(void) +{ + return ide_pci_register_driver(&driver); +} + +module_init(amd74xx_ide_init); + +MODULE_AUTHOR("Vojtech Pavlik"); +MODULE_DESCRIPTION("AMD PCI IDE driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/pci/atiixp.c b/drivers/ide/pci/atiixp.c new file mode 100644 index 0000000..df9ee9a --- /dev/null +++ b/drivers/ide/pci/atiixp.c @@ -0,0 +1,370 @@ +/* + * linux/drivers/ide/pci/atiixp.c Version 0.01-bart2 Feb. 26, 2004 + * + * Copyright (C) 2003 ATI Inc. <hyu@ati.com> + * Copyright (C) 2004 Bartlomiej Zolnierkiewicz + * + */ + +#include <linux/config.h> +#include <linux/types.h> +#include <linux/module.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 <linux/init.h> + +#include <asm/io.h> + +#define ATIIXP_IDE_PIO_TIMING 0x40 +#define ATIIXP_IDE_MDMA_TIMING 0x44 +#define ATIIXP_IDE_PIO_CONTROL 0x48 +#define ATIIXP_IDE_PIO_MODE 0x4a +#define ATIIXP_IDE_UDMA_CONTROL 0x54 +#define ATIIXP_IDE_UDMA_MODE 0x56 + +typedef struct { + u8 command_width; + u8 recover_width; +} atiixp_ide_timing; + +static atiixp_ide_timing pio_timing[] = { + { 0x05, 0x0d }, + { 0x04, 0x07 }, + { 0x03, 0x04 }, + { 0x02, 0x02 }, + { 0x02, 0x00 }, +}; + +static atiixp_ide_timing mdma_timing[] = { + { 0x07, 0x07 }, + { 0x02, 0x01 }, + { 0x02, 0x00 }, +}; + +static int save_mdma_mode[4]; + +/** + * atiixp_ratemask - compute rate mask for ATIIXP IDE + * @drive: IDE drive to compute for + * + * Returns the available modes for the ATIIXP IDE controller. + */ + +static u8 atiixp_ratemask(ide_drive_t *drive) +{ + u8 mode = 3; + + if (!eighty_ninty_three(drive)) + mode = min(mode, (u8)1); + return mode; +} + +/** + * atiixp_dma_2_pio - return the PIO mode matching DMA + * @xfer_rate: transfer speed + * + * Returns the nearest equivalent PIO timing for the PIO or DMA + * mode requested by the controller. + */ + +static u8 atiixp_dma_2_pio(u8 xfer_rate) { + switch(xfer_rate) { + case XFER_UDMA_6: + case XFER_UDMA_5: + 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; + } +} + +static int atiixp_ide_dma_host_on(ide_drive_t *drive) +{ + struct pci_dev *dev = drive->hwif->pci_dev; + unsigned long flags; + u16 tmp16; + + spin_lock_irqsave(&ide_lock, flags); + + pci_read_config_word(dev, ATIIXP_IDE_UDMA_CONTROL, &tmp16); + if (save_mdma_mode[drive->dn]) + tmp16 &= ~(1 << drive->dn); + else + tmp16 |= (1 << drive->dn); + pci_write_config_word(dev, ATIIXP_IDE_UDMA_CONTROL, tmp16); + + spin_unlock_irqrestore(&ide_lock, flags); + + return __ide_dma_host_on(drive); +} + +static int atiixp_ide_dma_host_off(ide_drive_t *drive) +{ + struct pci_dev *dev = drive->hwif->pci_dev; + unsigned long flags; + u16 tmp16; + + spin_lock_irqsave(&ide_lock, flags); + + pci_read_config_word(dev, ATIIXP_IDE_UDMA_CONTROL, &tmp16); + tmp16 &= ~(1 << drive->dn); + pci_write_config_word(dev, ATIIXP_IDE_UDMA_CONTROL, tmp16); + + spin_unlock_irqrestore(&ide_lock, flags); + + return __ide_dma_host_off(drive); +} + +/** + * atiixp_tune_drive - tune a drive attached to a ATIIXP + * @drive: drive to tune + * @pio: desired PIO mode + * + * Set the interface PIO mode. + */ + +static void atiixp_tuneproc(ide_drive_t *drive, u8 pio) +{ + struct pci_dev *dev = drive->hwif->pci_dev; + unsigned long flags; + int timing_shift = (drive->dn & 2) ? 16 : 0 + (drive->dn & 1) ? 0 : 8; + u32 pio_timing_data; + u16 pio_mode_data; + + spin_lock_irqsave(&ide_lock, flags); + + pci_read_config_word(dev, ATIIXP_IDE_PIO_MODE, &pio_mode_data); + pio_mode_data &= ~(0x07 << (drive->dn * 4)); + pio_mode_data |= (pio << (drive->dn * 4)); + pci_write_config_word(dev, ATIIXP_IDE_PIO_MODE, pio_mode_data); + + pci_read_config_dword(dev, ATIIXP_IDE_PIO_TIMING, &pio_timing_data); + pio_timing_data &= ~(0xff << timing_shift); + pio_timing_data |= (pio_timing[pio].recover_width << timing_shift) | + (pio_timing[pio].command_width << (timing_shift + 4)); + pci_write_config_dword(dev, ATIIXP_IDE_PIO_TIMING, pio_timing_data); + + spin_unlock_irqrestore(&ide_lock, flags); +} + +/** + * atiixp_tune_chipset - tune a ATIIXP interface + * @drive: IDE drive to tune + * @xferspeed: speed to configure + * + * Set a ATIIXP interface channel to the desired speeds. This involves + * requires the right timing data into the ATIIXP configuration space + * then setting the drive parameters appropriately + */ + +static int atiixp_speedproc(ide_drive_t *drive, u8 xferspeed) +{ + struct pci_dev *dev = drive->hwif->pci_dev; + unsigned long flags; + int timing_shift = (drive->dn & 2) ? 16 : 0 + (drive->dn & 1) ? 0 : 8; + u32 tmp32; + u16 tmp16; + u8 speed, pio; + + speed = ide_rate_filter(atiixp_ratemask(drive), xferspeed); + + spin_lock_irqsave(&ide_lock, flags); + + save_mdma_mode[drive->dn] = 0; + if (speed >= XFER_UDMA_0) { + pci_read_config_word(dev, ATIIXP_IDE_UDMA_MODE, &tmp16); + tmp16 &= ~(0x07 << (drive->dn * 4)); + tmp16 |= ((speed & 0x07) << (drive->dn * 4)); + pci_write_config_word(dev, ATIIXP_IDE_UDMA_MODE, tmp16); + } else { + if ((speed >= XFER_MW_DMA_0) && (speed <= XFER_MW_DMA_2)) { + save_mdma_mode[drive->dn] = speed; + pci_read_config_dword(dev, ATIIXP_IDE_MDMA_TIMING, &tmp32); + tmp32 &= ~(0xff << timing_shift); + tmp32 |= (mdma_timing[speed & 0x03].recover_width << timing_shift) | + (mdma_timing[speed & 0x03].command_width << (timing_shift + 4)); + pci_write_config_dword(dev, ATIIXP_IDE_MDMA_TIMING, tmp32); + } + } + + spin_unlock_irqrestore(&ide_lock, flags); + + if (speed >= XFER_SW_DMA_0) + pio = atiixp_dma_2_pio(speed); + else + pio = speed - XFER_PIO_0; + + atiixp_tuneproc(drive, pio); + + return ide_config_drive_speed(drive, speed); +} + +/** + * atiixp_config_drive_for_dma - configure drive for DMA + * @drive: IDE drive to configure + * + * Set up a ATIIXP interface channel for the best available speed. + * We prefer UDMA if it is available and then MWDMA. If DMA is + * not available we switch to PIO and return 0. + */ + +static int atiixp_config_drive_for_dma(ide_drive_t *drive) +{ + u8 speed = ide_dma_speed(drive, atiixp_ratemask(drive)); + + /* If no DMA speed was available then disable DMA and use PIO. */ + if (!speed) { + u8 tspeed = ide_get_best_pio_mode(drive, 255, 5, NULL); + speed = atiixp_dma_2_pio(XFER_PIO_0 + tspeed) + XFER_PIO_0; + } + + (void) atiixp_speedproc(drive, speed); + return ide_dma_enable(drive); +} + +/** + * atiixp_dma_check - set up an IDE device + * @drive: IDE drive to configure + * + * Set up the ATIIXP interface for the best available speed on this + * interface, preferring DMA to PIO. + */ + +static int atiixp_dma_check(ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + struct hd_driveid *id = drive->id; + u8 tspeed, speed; + + drive->init_speed = 0; + + if ((id->capability & 1) && drive->autodma) { + + if (ide_use_dma(drive)) { + if (atiixp_config_drive_for_dma(drive)) + return hwif->ide_dma_on(drive); + } + + goto fast_ata_pio; + + } else if ((id->capability & 8) || (id->field_valid & 2)) { +fast_ata_pio: + tspeed = ide_get_best_pio_mode(drive, 255, 5, NULL); + speed = atiixp_dma_2_pio(XFER_PIO_0 + tspeed) + XFER_PIO_0; + hwif->speedproc(drive, speed); + return hwif->ide_dma_off_quietly(drive); + } + /* IORDY not supported */ + return 0; +} + +/** + * init_hwif_atiixp - fill in the hwif for the ATIIXP + * @hwif: IDE interface + * + * Set up the ide_hwif_t for the ATIIXP interface according to the + * capabilities of the hardware. + */ + +static void __devinit init_hwif_atiixp(ide_hwif_t *hwif) +{ + if (!hwif->irq) + hwif->irq = hwif->channel ? 15 : 14; + + hwif->autodma = 0; + hwif->tuneproc = &atiixp_tuneproc; + hwif->speedproc = &atiixp_speedproc; + hwif->drives[0].autotune = 1; + hwif->drives[1].autotune = 1; + + if (!hwif->dma_base) + return; + + hwif->atapi_dma = 1; + hwif->ultra_mask = 0x3f; + hwif->mwdma_mask = 0x06; + hwif->swdma_mask = 0x04; + + /* FIXME: proper cable detection needed */ + hwif->udma_four = 1; + hwif->ide_dma_host_on = &atiixp_ide_dma_host_on; + hwif->ide_dma_host_off = &atiixp_ide_dma_host_off; + hwif->ide_dma_check = &atiixp_dma_check; + if (!noautodma) + hwif->autodma = 1; + + hwif->drives[1].autodma = hwif->autodma; + hwif->drives[0].autodma = hwif->autodma; +} + +static ide_pci_device_t atiixp_pci_info[] __devinitdata = { + { /* 0 */ + .name = "ATIIXP", + .init_hwif = init_hwif_atiixp, + .channels = 2, + .autodma = AUTODMA, + .enablebits = {{0x48,0x01,0x00}, {0x48,0x08,0x00}}, + .bootable = ON_BOARD, + } +}; + +/** + * atiixp_init_one - called when a ATIIXP is found + * @dev: the atiixp device + * @id: the matching pci id + * + * Called when the PCI registration layer (or the IDE initialization) + * finds a device matching our IDE device tables. + */ + +static int __devinit atiixp_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + return ide_setup_pci_device(dev, &atiixp_pci_info[id->driver_data]); +} + +static struct pci_device_id atiixp_pci_tbl[] = { + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP200_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP300_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP400_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, atiixp_pci_tbl); + +static struct pci_driver driver = { + .name = "ATIIXP_IDE", + .id_table = atiixp_pci_tbl, + .probe = atiixp_init_one, +}; + +static int atiixp_ide_init(void) +{ + return ide_pci_register_driver(&driver); +} + +module_init(atiixp_ide_init); + +MODULE_AUTHOR("HUI YU"); +MODULE_DESCRIPTION("PCI driver module for ATI IXP IDE"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/pci/cmd640.c b/drivers/ide/pci/cmd640.c new file mode 100644 index 0000000..92a2b7c --- /dev/null +++ b/drivers/ide/pci/cmd640.c @@ -0,0 +1,879 @@ +/* + * linux/drivers/ide/pci/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 weird 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 + +//#define CMD640_DUMP_REGS + +#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 <linux/init.h> + +#include <asm/io.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 u8 prefetch_regs[4] = {CNTRL, CNTRL, ARTTIM23, ARTTIM23}; +static u8 prefetch_masks[4] = {CNTRL_DIS_RA0, CNTRL_DIS_RA1, ARTTIM23_DIS_RA2, ARTTIM23_DIS_RA3}; + +#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED + +static u8 arttim_regs[4] = {ARTTIM0, ARTTIM1, ARTTIM23, ARTTIM23}; +static u8 drwtim_regs[4] = {DRWTIM0, DRWTIM1, DRWTIM23, DRWTIM23}; + +/* + * Current cmd640 timing values for each drive. + * The defaults for each are the slowest possible timings. + */ +static u8 setup_counts[4] = {4, 4, 4, 4}; /* Address setup count (in clocks) */ +static u8 active_counts[4] = {16, 16, 16, 16}; /* Active count (encoded) */ +static u8 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)(u16 reg, u8 val); +static u8 (*__get_cmd640_reg)(u16 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 (u16 reg, u8 val) +{ + outl_p((reg & 0xfc) | cmd640_key, 0xcf8); + outb_p(val, (reg & 3) | 0xcfc); +} + +static u8 get_cmd640_reg_pci1 (u16 reg) +{ + outl_p((reg & 0xfc) | cmd640_key, 0xcf8); + return inb_p((reg & 3) | 0xcfc); +} + +/* PCI method 2 access (from CMD datasheet) */ + +static void put_cmd640_reg_pci2 (u16 reg, u8 val) +{ + outb_p(0x10, 0xcf8); + outb_p(val, cmd640_key + reg); + outb_p(0, 0xcf8); +} + +static u8 get_cmd640_reg_pci2 (u16 reg) +{ + u8 b; + + outb_p(0x10, 0xcf8); + b = inb_p(cmd640_key + reg); + outb_p(0, 0xcf8); + return b; +} + +/* VLB access */ + +static void put_cmd640_reg_vlb (u16 reg, u8 val) +{ + outb_p(reg, cmd640_key); + outb_p(val, cmd640_key + 4); +} + +static u8 get_cmd640_reg_vlb (u16 reg) +{ + outb_p(reg, cmd640_key); + return inb_p(cmd640_key + 4); +} + +static u8 get_cmd640_reg(u16 reg) +{ + u8 b; + unsigned long flags; + + spin_lock_irqsave(&ide_lock, flags); + b = __get_cmd640_reg(reg); + spin_unlock_irqrestore(&ide_lock, flags); + return b; +} + +static void put_cmd640_reg(u16 reg, u8 val) +{ + unsigned long flags; + + spin_lock_irqsave(&ide_lock, flags); + __put_cmd640_reg(reg,val); + spin_unlock_irqrestore(&ide_lock, flags); +} + +static int __init match_pci_cmd640_device (void) +{ + const u8 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) +{ + u8 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; + + spin_lock_irqsave(&ide_lock, flags); + + 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) { + spin_unlock_irqrestore(&ide_lock, flags); + return 0; /* nothing responded */ + } + } + spin_unlock_irqrestore(&ide_lock, flags); + return 1; /* success */ +} + +#ifdef CMD640_DUMP_REGS +/* + * Dump out all cmd640 registers. May be called from ide.c + */ +static 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]; + u8 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_forced) { + 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]; + u8 b; + unsigned long flags; + + spin_lock_irqsave(&ide_lock, flags); + 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); + spin_unlock_irqrestore(&ide_lock, flags); +} + +/* + * Dump out current drive clocks settings + */ +static void display_clocks (unsigned int index) +{ + u8 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 u8 pack_nibbles (u8 upper, u8 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) +{ + u8 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; + u8 setup_count = setup_counts[index]; + u8 active_count = active_counts[index]; + u8 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 + */ + spin_lock_irqsave(&ide_lock, flags); + /* + * 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)); + spin_unlock_irqrestore(&ide_lock, flags); +} + +/* + * Set a specific pio_mode for a drive + */ +static void cmd640_set_mode (unsigned int index, u8 pio_mode, unsigned int cycle_time) +{ + int setup_time, active_time, recovery_time, clock_time; + u8 setup_count, active_count, recovery_count, recovery_count2, cycle_count; + int bus_speed = system_bus_clock(); + + 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, u8 mode_wanted) +{ + u8 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 */ + +static int pci_conf1(void) +{ + u32 tmp; + unsigned long flags; + + spin_lock_irqsave(&ide_lock, flags); + outb(0x01, 0xCFB); + tmp = inl(0xCF8); + outl(0x80000000, 0xCF8); + if (inl(0xCF8) == 0x80000000) { + outl(tmp, 0xCF8); + spin_unlock_irqrestore(&ide_lock, flags); + return 1; + } + outl(tmp, 0xCF8); + spin_unlock_irqrestore(&ide_lock, flags); + return 0; +} + +static int pci_conf2(void) +{ + unsigned long flags; + + spin_lock_irqsave(&ide_lock, flags); + outb(0x00, 0xCFB); + outb(0x00, 0xCF8); + outb(0x00, 0xCFA); + if (inb(0xCF8) == 0x00 && inb(0xCF8) == 0x00) { + spin_unlock_irqrestore(&ide_lock, flags); + return 1; + } + spin_unlock_irqrestore(&ide_lock, flags); + return 0; +} + +/* + * 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; + u8 b, cfr; + + if (cmd640_vlb && probe_for_cmd640_vlb()) { + bus_type = "VLB"; + } else { + cmd640_vlb = 0; + /* Find out what kind of PCI probing is supported otherwise + Justin Gibbs will sulk.. */ + if (pci_conf1() && probe_for_cmd640_pci1()) + bus_type = "PCI (type1)"; + else if (pci_conf2() && 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(KERN_ERR "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(KERN_INFO "%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/pci/cmd64x.c b/drivers/ide/pci/cmd64x.c new file mode 100644 index 0000000..3de9ab8 --- /dev/null +++ b/drivers/ide/pci/cmd64x.c @@ -0,0 +1,821 @@ +/* $Id: cmd64x.c,v 1.21 2000/01/30 23:23:16 + * + * linux/drivers/ide/pci/cmd64x.c Version 1.30 Sept 10, 2002 + * + * 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-2002 Andre Hedrick <andre@linux-ide.org> + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/hdreg.h> +#include <linux/ide.h> +#include <linux/init.h> + +#include <asm/io.h> + +#define DISPLAY_CMD64X_TIMINGS + +#define CMD_DEBUG 0 + +#if CMD_DEBUG +#define cmdprintk(x...) printk(x) +#else +#define cmdprintk(x...) +#endif + +/* + * CMD64x specific registers definition. + */ +#define CFR 0x50 +#define CFR_INTR_CH0 0x02 +#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 ARTTIM23_INTR_CH1 0x10 +#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 MRDMODE_INTR_CH0 0x04 +#define MRDMODE_INTR_CH1 0x08 +#define MRDMODE_BLK_CH0 0x10 +#define MRDMODE_BLK_CH1 0x20 +#define BMIDESR0 0x72 +#define UDIDETCR0 0x73 +#define DTPR0 0x74 +#define BMIDECR1 0x78 +#define BMIDECSR 0x79 +#define BMIDESR1 0x7A +#define UDIDETCR1 0x7B +#define DTPR1 0x7C + +#if defined(DISPLAY_CMD64X_TIMINGS) && defined(CONFIG_PROC_FS) +#include <linux/stat.h> +#include <linux/proc_fs.h> + +static u8 cmd64x_proc = 0; + +#define CMD_MAX_DEVS 5 + +static struct pci_dev *cmd_devs[CMD_MAX_DEVS]; +static int n_cmd_devs; + +static char * print_cmd64x_get_info (char *buf, struct pci_dev *dev, int index) +{ + char *p = buf; + + 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 reg50 = 0, reg71 = 0; /* extra */ + + p += sprintf(p, "\nController: %d\n", index); + p += sprintf(p, "CMD%x Chipset.\n", dev->device); + (void) pci_read_config_byte(dev, CFR, ®50); + (void) pci_read_config_byte(dev, ARTTIM0, ®53); + (void) pci_read_config_byte(dev, DRWTIM0, ®54); + (void) pci_read_config_byte(dev, ARTTIM1, ®55); + (void) pci_read_config_byte(dev, DRWTIM1, ®56); + (void) pci_read_config_byte(dev, ARTTIM2, ®57); + (void) pci_read_config_byte(dev, DRWTIM2, ®58); + (void) pci_read_config_byte(dev, DRWTIM3, ®5b); + (void) pci_read_config_byte(dev, MRDMODE, ®71); + (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); + + 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, "DMA Mode: %s(%s) %s(%s)", + (reg72&0x20)?((reg73&0x01)?"UDMA":" DMA"):" PIO", + (reg72&0x20)?( + ((reg73&0x30)==0x30)?(((reg73&0x35)==0x35)?"3":"0"): + ((reg73&0x20)==0x20)?(((reg73&0x25)==0x25)?"3":"1"): + ((reg73&0x10)==0x10)?(((reg73&0x15)==0x15)?"4":"2"): + ((reg73&0x00)==0x00)?(((reg73&0x05)==0x05)?"5":"2"): + "X"):"?", + (reg72&0x40)?((reg73&0x02)?"UDMA":" DMA"):" PIO", + (reg72&0x40)?( + ((reg73&0xC0)==0xC0)?(((reg73&0xC5)==0xC5)?"3":"0"): + ((reg73&0x80)==0x80)?(((reg73&0x85)==0x85)?"3":"1"): + ((reg73&0x40)==0x40)?(((reg73&0x4A)==0x4A)?"4":"2"): + ((reg73&0x00)==0x00)?(((reg73&0x0A)==0x0A)?"5":"2"): + "X"):"?"); + p += sprintf(p, " %s(%s) %s(%s)\n", + (reg7a&0x20)?((reg7b&0x01)?"UDMA":" DMA"):" PIO", + (reg7a&0x20)?( + ((reg7b&0x30)==0x30)?(((reg7b&0x35)==0x35)?"3":"0"): + ((reg7b&0x20)==0x20)?(((reg7b&0x25)==0x25)?"3":"1"): + ((reg7b&0x10)==0x10)?(((reg7b&0x15)==0x15)?"4":"2"): + ((reg7b&0x00)==0x00)?(((reg7b&0x05)==0x05)?"5":"2"): + "X"):"?", + (reg7a&0x40)?((reg7b&0x02)?"UDMA":" DMA"):" PIO", + (reg7a&0x40)?( + ((reg7b&0xC0)==0xC0)?(((reg7b&0xC5)==0xC5)?"3":"0"): + ((reg7b&0x80)==0x80)?(((reg7b&0x85)==0x85)?"3":"1"): + ((reg7b&0x40)==0x40)?(((reg7b&0x4A)==0x4A)?"4":"2"): + ((reg7b&0x00)==0x00)?(((reg7b&0x0A)==0x0A)?"5":"2"): + "X"):"?" ); + p += sprintf(p, "PIO Mode: %s %s" + " %s %s\n", + "?", "?", "?", "?"); + p += sprintf(p, " %s %s\n", + (reg50 & CFR_INTR_CH0) ? "interrupting" : "polling ", + (reg57 & ARTTIM23_INTR_CH1) ? "interrupting" : "polling"); + p += sprintf(p, " %s %s\n", + (reg71 & MRDMODE_INTR_CH0) ? "pending" : "clear ", + (reg71 & MRDMODE_INTR_CH1) ? "pending" : "clear"); + p += sprintf(p, " %s %s\n", + (reg71 & MRDMODE_BLK_CH0) ? "blocked" : "enabled", + (reg71 & MRDMODE_BLK_CH1) ? "blocked" : "enabled"); + + return (char *)p; +} + +static int cmd64x_get_info (char *buffer, char **addr, off_t offset, int count) +{ + char *p = buffer; + int i; + + p += sprintf(p, "\n"); + for (i = 0; i < n_cmd_devs; i++) { + struct pci_dev *dev = cmd_devs[i]; + p = print_cmd64x_get_info(p, dev, i); + } + return p-buffer; /* => must be less than 4k! */ +} + +#endif /* defined(DISPLAY_CMD64X_TIMINGS) && defined(CONFIG_PROC_FS) */ + +/* + * Registers and masks for easy access by drive index: + */ +#if 0 +static u8 prefetch_regs[4] = {CNTRL, CNTRL, ARTTIM23, ARTTIM23}; +static u8 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; + struct pci_dev *dev = HWIF(drive)->pci_dev; + ide_drive_t *drives = HWIF(drive)->drives; + u8 temp_b; + static const u8 setup_counts[] = {0x40, 0x40, 0x40, 0x80, 0, 0xc0}; + static const u8 recovery_counts[] = + {15, 15, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 0}; + static const u8 arttim_regs[2][2] = { + { ARTTIM0, ARTTIM1 }, + { ARTTIM23, ARTTIM23 } + }; + static const u8 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 = 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 + */ + local_irq_save(flags); + /* + * Program the address_setup clocks into ARTTIM reg, + * and then the active/recovery counts into the DRWTIM reg + */ + (void) pci_read_config_byte(dev, arttim_regs[channel][slave], &temp_b); + (void) pci_write_config_byte(dev, arttim_regs[channel][slave], + ((u8) setup_count) | (temp_b & 0x3f)); + (void) pci_write_config_byte(dev, drwtim_regs[channel][slave], + (u8) ((active_count << 4) | recovery_count)); + cmdprintk ("Write %x to %x\n", + ((u8) setup_count) | (temp_b & 0x3f), + arttim_regs[channel][slave]); + cmdprintk ("Write %x to %x\n", + (u8) ((active_count << 4) | recovery_count), + drwtim_regs[channel][slave]); + local_irq_restore(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, u8 mode_wanted) +{ + int setup_time, active_time, recovery_time; + int clock_time, pio_mode, cycle_time; + u8 recovery_count2, cycle_count; + int setup_count, active_count, recovery_count; + int bus_speed = system_bus_clock(); + /*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 u8 cmd64x_ratemask (ide_drive_t *drive) +{ + struct pci_dev *dev = HWIF(drive)->pci_dev; + u8 mode = 0; + + switch(dev->device) { + case PCI_DEVICE_ID_CMD_649: + mode = 3; + break; + case PCI_DEVICE_ID_CMD_648: + mode = 2; + break; + case PCI_DEVICE_ID_CMD_643: + return 0; + + case PCI_DEVICE_ID_CMD_646: + { + unsigned int class_rev = 0; + pci_read_config_dword(dev, + PCI_CLASS_REVISION, &class_rev); + class_rev &= 0xff; + /* + * 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. + */ + switch(class_rev) { + case 0x07: + case 0x05: + return 1; + case 0x03: + case 0x01: + default: + return 0; + } + } + } + if (!eighty_ninty_three(drive)) + mode = min(mode, (u8)1); + return mode; +} + +static void config_cmd64x_chipset_for_pio (ide_drive_t *drive, u8 set_speed) +{ + u8 speed = 0x00; + u8 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 void config_chipset_for_pio (ide_drive_t *drive, u8 set_speed) +{ + config_cmd64x_chipset_for_pio(drive, set_speed); +} + +static int cmd64x_tune_chipset (ide_drive_t *drive, u8 xferspeed) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + + u8 unit = (drive->select.b.unit & 0x01); + u8 regU = 0, pciU = (hwif->channel) ? UDIDETCR1 : UDIDETCR0; + u8 regD = 0, pciD = (hwif->channel) ? BMIDESR1 : BMIDESR0; + + u8 speed = ide_rate_filter(cmd64x_ratemask(drive), xferspeed); + + if (speed > XFER_PIO_4) { + (void) pci_read_config_byte(dev, pciD, ®D); + (void) pci_read_config_byte(dev, pciU, ®U); + regD &= ~(unit ? 0x40 : 0x20); + regU &= ~(unit ? 0xCA : 0x35); + (void) pci_write_config_byte(dev, pciD, regD); + (void) pci_write_config_byte(dev, pciU, regU); + (void) pci_read_config_byte(dev, pciD, ®D); + (void) pci_read_config_byte(dev, pciU, ®U); + } + + switch(speed) { + case XFER_UDMA_5: regU |= (unit ? 0x0A : 0x05); break; + case XFER_UDMA_4: regU |= (unit ? 0x4A : 0x15); break; + case XFER_UDMA_3: regU |= (unit ? 0x8A : 0x25); break; + case XFER_UDMA_2: regU |= (unit ? 0x42 : 0x11); break; + case XFER_UDMA_1: regU |= (unit ? 0x82 : 0x21); break; + case XFER_UDMA_0: regU |= (unit ? 0xC2 : 0x31); break; + case XFER_MW_DMA_2: regD |= (unit ? 0x40 : 0x10); break; + case XFER_MW_DMA_1: regD |= (unit ? 0x80 : 0x20); break; + case XFER_MW_DMA_0: regD |= (unit ? 0xC0 : 0x30); break; + case XFER_SW_DMA_2: regD |= (unit ? 0x40 : 0x10); break; + case XFER_SW_DMA_1: regD |= (unit ? 0x80 : 0x20); break; + case XFER_SW_DMA_0: regD |= (unit ? 0xC0 : 0x30); break; + case XFER_PIO_4: cmd64x_tuneproc(drive, 4); break; + case XFER_PIO_3: cmd64x_tuneproc(drive, 3); break; + case XFER_PIO_2: cmd64x_tuneproc(drive, 2); break; + case XFER_PIO_1: cmd64x_tuneproc(drive, 1); break; + case XFER_PIO_0: cmd64x_tuneproc(drive, 0); break; + + default: + return 1; + } + + if (speed > XFER_PIO_4) { + (void) pci_write_config_byte(dev, pciU, regU); + regD |= (unit ? 0x40 : 0x20); + (void) pci_write_config_byte(dev, pciD, regD); + } + + return (ide_config_drive_speed(drive, speed)); +} + +static int config_chipset_for_dma (ide_drive_t *drive) +{ + u8 speed = ide_dma_speed(drive, cmd64x_ratemask(drive)); + + config_chipset_for_pio(drive, !speed); + + if (!speed) + return 0; + + if(ide_set_xfer_rate(drive, speed)) + return 0; + + if (!drive->init_speed) + drive->init_speed = speed; + + return ide_dma_enable(drive); +} + +static int cmd64x_config_drive_for_dma (ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + struct hd_driveid *id = drive->id; + + if ((id != NULL) && ((id->capability & 1) != 0) && drive->autodma) { + + if (ide_use_dma(drive)) { + if (config_chipset_for_dma(drive)) + return hwif->ide_dma_on(drive); + } + + goto fast_ata_pio; + + } else if ((id->capability & 8) || (id->field_valid & 2)) { +fast_ata_pio: + config_chipset_for_pio(drive, 1); + return hwif->ide_dma_off_quietly(drive); + } + /* IORDY not supported */ + return 0; +} + +static int cmd64x_alt_dma_status (struct pci_dev *dev) +{ + switch(dev->device) { + case PCI_DEVICE_ID_CMD_648: + case PCI_DEVICE_ID_CMD_649: + return 1; + default: + break; + } + return 0; +} + +static int cmd64x_ide_dma_end (ide_drive_t *drive) +{ + u8 dma_stat = 0, dma_cmd = 0; + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + + drive->waiting_for_dma = 0; + /* read DMA command state */ + dma_cmd = hwif->INB(hwif->dma_command); + /* stop DMA */ + hwif->OUTB((dma_cmd & ~1), hwif->dma_command); + /* get DMA status */ + dma_stat = hwif->INB(hwif->dma_status); + /* clear the INTR & ERROR bits */ + hwif->OUTB(dma_stat|6, hwif->dma_status); + if (cmd64x_alt_dma_status(dev)) { + u8 dma_intr = 0; + u8 dma_mask = (hwif->channel) ? ARTTIM23_INTR_CH1 : + CFR_INTR_CH0; + u8 dma_reg = (hwif->channel) ? ARTTIM2 : CFR; + (void) pci_read_config_byte(dev, dma_reg, &dma_intr); + /* clear the INTR bit */ + (void) pci_write_config_byte(dev, dma_reg, dma_intr|dma_mask); + } + /* purge DMA mappings */ + ide_destroy_dmatable(drive); + /* verify good DMA status */ + return (dma_stat & 7) != 4; +} + +static int cmd64x_ide_dma_test_irq (ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + u8 dma_alt_stat = 0, mask = (hwif->channel) ? MRDMODE_INTR_CH1 : + MRDMODE_INTR_CH0; + u8 dma_stat = hwif->INB(hwif->dma_status); + + (void) pci_read_config_byte(dev, MRDMODE, &dma_alt_stat); +#ifdef DEBUG + printk("%s: dma_stat: 0x%02x dma_alt_stat: " + "0x%02x mask: 0x%02x\n", drive->name, + dma_stat, dma_alt_stat, mask); +#endif + if (!(dma_alt_stat & mask)) + return 0; + + /* return 1 if INTR asserted */ + if ((dma_stat & 4) == 4) + return 1; + + return 0; +} + +/* + * ASUS P55T2P4D with CMD646 chipset revision 0x01 requires the old + * event order for DMA transfers. + */ + +static int cmd646_1_ide_dma_end (ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + u8 dma_stat = 0, dma_cmd = 0; + + drive->waiting_for_dma = 0; + /* get DMA status */ + dma_stat = hwif->INB(hwif->dma_status); + /* read DMA command state */ + dma_cmd = hwif->INB(hwif->dma_command); + /* stop DMA */ + hwif->OUTB((dma_cmd & ~1), hwif->dma_command); + /* clear the INTR & ERROR bits */ + hwif->OUTB(dma_stat|6, hwif->dma_status); + /* and free any DMA resources */ + ide_destroy_dmatable(drive); + /* verify good DMA status */ + return (dma_stat & 7) != 4; +} + +static unsigned int __devinit init_chipset_cmd64x(struct pci_dev *dev, const char *name) +{ + u32 class_rev = 0; + u8 mrdmode = 0; + + pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev); + class_rev &= 0xff; + +#ifdef __i386__ + 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 "%s: ROM enabled at 0x%08lx\n", name, dev->resource[PCI_ROM_RESOURCE].start); + } +#endif + + switch(dev->device) { + case PCI_DEVICE_ID_CMD_643: + break; + case PCI_DEVICE_ID_CMD_646: + printk(KERN_INFO "%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: + case PCI_DEVICE_ID_CMD_649: + break; + default: + break; + } + + /* Set a good latency timer and cache line size value. */ + (void) pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64); + /* FIXME: pci_set_master() to ensure a good latency timer value */ + + /* 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); +#ifdef CONFIG_PPC + (void) pci_write_config_byte(dev, UDIDETCR0, 0xf0); +#endif /* CONFIG_PPC */ + +#if defined(DISPLAY_CMD64X_TIMINGS) && defined(CONFIG_PROC_FS) + + cmd_devs[n_cmd_devs++] = dev; + + if (!cmd64x_proc) { + cmd64x_proc = 1; + ide_pci_create_host_proc("cmd64x", cmd64x_get_info); + } +#endif /* DISPLAY_CMD64X_TIMINGS && CONFIG_PROC_FS */ + + return 0; +} + +static unsigned int __devinit ata66_cmd64x(ide_hwif_t *hwif) +{ + u8 ata66 = 0, mask = (hwif->channel) ? 0x02 : 0x01; + + switch(hwif->pci_dev->device) { + case PCI_DEVICE_ID_CMD_643: + case PCI_DEVICE_ID_CMD_646: + return ata66; + default: + break; + } + pci_read_config_byte(hwif->pci_dev, BMIDECSR, &ata66); + return (ata66 & mask) ? 1 : 0; +} + +static void __devinit init_hwif_cmd64x(ide_hwif_t *hwif) +{ + struct pci_dev *dev = hwif->pci_dev; + unsigned int class_rev; + + hwif->autodma = 0; + pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev); + class_rev &= 0xff; + + hwif->tuneproc = &cmd64x_tuneproc; + hwif->speedproc = &cmd64x_tune_chipset; + + if (!hwif->dma_base) { + hwif->drives[0].autotune = 1; + hwif->drives[1].autotune = 1; + return; + } + + hwif->atapi_dma = 1; + + hwif->ultra_mask = 0x3f; + hwif->mwdma_mask = 0x07; + hwif->swdma_mask = 0x07; + + if (dev->device == PCI_DEVICE_ID_CMD_643) + hwif->ultra_mask = 0x80; + if (dev->device == PCI_DEVICE_ID_CMD_646) + hwif->ultra_mask = (class_rev > 0x04) ? 0x07 : 0x80; + if (dev->device == PCI_DEVICE_ID_CMD_648) + hwif->ultra_mask = 0x1f; + + hwif->ide_dma_check = &cmd64x_config_drive_for_dma; + if (!(hwif->udma_four)) + hwif->udma_four = ata66_cmd64x(hwif); + + if (dev->device == PCI_DEVICE_ID_CMD_646) { + hwif->chipset = ide_cmd646; + if (class_rev == 0x01) { + hwif->ide_dma_end = &cmd646_1_ide_dma_end; + } else { + hwif->ide_dma_end = &cmd64x_ide_dma_end; + hwif->ide_dma_test_irq = &cmd64x_ide_dma_test_irq; + } + } else { + hwif->ide_dma_end = &cmd64x_ide_dma_end; + hwif->ide_dma_test_irq = &cmd64x_ide_dma_test_irq; + } + + + if (!noautodma) + hwif->autodma = 1; + hwif->drives[0].autodma = hwif->autodma; + hwif->drives[1].autodma = hwif->autodma; +} + +static ide_pci_device_t cmd64x_chipsets[] __devinitdata = { + { /* 0 */ + .name = "CMD643", + .init_chipset = init_chipset_cmd64x, + .init_hwif = init_hwif_cmd64x, + .channels = 2, + .autodma = AUTODMA, + .bootable = ON_BOARD, + },{ /* 1 */ + .name = "CMD646", + .init_chipset = init_chipset_cmd64x, + .init_hwif = init_hwif_cmd64x, + .channels = 2, + .autodma = AUTODMA, + .enablebits = {{0x00,0x00,0x00}, {0x51,0x80,0x80}}, + .bootable = ON_BOARD, + },{ /* 2 */ + .name = "CMD648", + .init_chipset = init_chipset_cmd64x, + .init_hwif = init_hwif_cmd64x, + .channels = 2, + .autodma = AUTODMA, + .bootable = ON_BOARD, + },{ /* 3 */ + .name = "CMD649", + .init_chipset = init_chipset_cmd64x, + .init_hwif = init_hwif_cmd64x, + .channels = 2, + .autodma = AUTODMA, + .bootable = ON_BOARD, + } +}; + +static int __devinit cmd64x_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + return ide_setup_pci_device(dev, &cmd64x_chipsets[id->driver_data]); +} + +static struct pci_device_id cmd64x_pci_tbl[] = { + { PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_643, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_646, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1}, + { PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_648, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2}, + { PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_649, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 3}, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, cmd64x_pci_tbl); + +static struct pci_driver driver = { + .name = "CMD64x_IDE", + .id_table = cmd64x_pci_tbl, + .probe = cmd64x_init_one, +}; + +static int cmd64x_ide_init(void) +{ + return ide_pci_register_driver(&driver); +} + +module_init(cmd64x_ide_init); + +MODULE_AUTHOR("Eddie Dost, David Miller, Andre Hedrick"); +MODULE_DESCRIPTION("PCI driver module for CMD64x IDE"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/pci/cs5520.c b/drivers/ide/pci/cs5520.c new file mode 100644 index 0000000..7dc2468 --- /dev/null +++ b/drivers/ide/pci/cs5520.c @@ -0,0 +1,274 @@ +/* + * IDE tuning and bus mastering support for the CS5510/CS5520 + * chipsets + * + * The CS5510/CS5520 are slightly unusual devices. Unlike the + * typical IDE controllers they do bus mastering with the drive in + * PIO mode and smarter silicon. + * + * The practical upshot of this is that we must always tune the + * drive for the right PIO mode. We must also ignore all the blacklists + * and the drive bus mastering DMA information. + * + * *** This driver is strictly experimental *** + * + * (c) Copyright Red Hat Inc 2002 + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * For the avoidance of doubt the "preferred form" of this code is one which + * is in an open non patent encumbered format. Where cryptographic key signing + * forms part of the process of creating an executable the information + * including keys needed to generate an equivalently functional executable + * are deemed to be part of the source code. + * + */ + +#include <linux/config.h> +#include <linux/module.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/init.h> +#include <linux/pci.h> +#include <linux/ide.h> +#include <linux/dma-mapping.h> + +#include <asm/io.h> +#include <asm/irq.h> + +struct pio_clocks +{ + int address; + int assert; + int recovery; +}; + +static struct pio_clocks cs5520_pio_clocks[]={ + {3, 6, 11}, + {2, 5, 6}, + {1, 4, 3}, + {1, 3, 2}, + {1, 2, 1} +}; + +static int cs5520_tune_chipset(ide_drive_t *drive, u8 xferspeed) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *pdev = hwif->pci_dev; + u8 speed = min((u8)XFER_PIO_4, xferspeed); + int pio = speed; + u8 reg; + int controller = drive->dn > 1 ? 1 : 0; + int error; + + switch(speed) + { + case XFER_PIO_4: + case XFER_PIO_3: + case XFER_PIO_2: + case XFER_PIO_1: + case XFER_PIO_0: + pio -= XFER_PIO_0; + break; + default: + pio = 0; + printk(KERN_ERR "cs55x0: bad ide timing.\n"); + } + + printk("PIO clocking = %d\n", pio); + + /* FIXME: if DMA = 1 do we need to set the DMA bit here ? */ + + /* 8bit CAT/CRT - 8bit command timing for channel */ + pci_write_config_byte(pdev, 0x62 + controller, + (cs5520_pio_clocks[pio].recovery << 4) | + (cs5520_pio_clocks[pio].assert)); + + /* 0x64 - 16bit Primary, 0x68 - 16bit Secondary */ + + /* FIXME: should these use address ? */ + /* Data read timing */ + pci_write_config_byte(pdev, 0x64 + 4*controller + (drive->dn&1), + (cs5520_pio_clocks[pio].recovery << 4) | + (cs5520_pio_clocks[pio].assert)); + /* Write command timing */ + pci_write_config_byte(pdev, 0x66 + 4*controller + (drive->dn&1), + (cs5520_pio_clocks[pio].recovery << 4) | + (cs5520_pio_clocks[pio].assert)); + + /* Set the DMA enable/disable flag */ + reg = inb(hwif->dma_base + 0x02 + 8*controller); + reg |= 1<<((drive->dn&1)+5); + outb(reg, hwif->dma_base + 0x02 + 8*controller); + + error = ide_config_drive_speed(drive, speed); + /* ATAPI is harder so leave it for now */ + if(!error && drive->media == ide_disk) + error = hwif->ide_dma_on(drive); + + return error; +} + +static void cs5520_tune_drive(ide_drive_t *drive, u8 pio) +{ + pio = ide_get_best_pio_mode(drive, pio, 4, NULL); + cs5520_tune_chipset(drive, (XFER_PIO_0 + pio)); +} + +static int cs5520_config_drive_xfer_rate(ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + + /* Tune the drive for PIO modes up to PIO 4 */ + cs5520_tune_drive(drive, 4); + /* Then tell the core to use DMA operations */ + return hwif->ide_dma_on(drive); +} + +/* + * We provide a callback for our nonstandard DMA location + */ + +static void __devinit cs5520_init_setup_dma(struct pci_dev *dev, ide_pci_device_t *d, ide_hwif_t *hwif) +{ + unsigned long bmide = pci_resource_start(dev, 2); /* Not the usual 4 */ + if(hwif->mate && hwif->mate->dma_base) /* Second channel at primary + 8 */ + bmide += 8; + ide_setup_dma(hwif, bmide, 8); +} + +/* + * We wrap the DMA activate to set the vdma flag. This is needed + * so that the IDE DMA layer issues PIO not DMA commands over the + * DMA channel + */ + +static int cs5520_dma_on(ide_drive_t *drive) +{ + drive->vdma = 1; + return 0; +} + +static void __devinit init_hwif_cs5520(ide_hwif_t *hwif) +{ + hwif->tuneproc = &cs5520_tune_drive; + hwif->speedproc = &cs5520_tune_chipset; + hwif->ide_dma_check = &cs5520_config_drive_xfer_rate; + hwif->ide_dma_on = &cs5520_dma_on; + + if(!noautodma) + hwif->autodma = 1; + + if(!hwif->dma_base) + { + hwif->drives[0].autotune = 1; + hwif->drives[1].autotune = 1; + return; + } + + hwif->atapi_dma = 0; + hwif->ultra_mask = 0; + hwif->swdma_mask = 0; + hwif->mwdma_mask = 0; + + hwif->drives[0].autodma = hwif->autodma; + hwif->drives[1].autodma = hwif->autodma; +} + +#define DECLARE_CS_DEV(name_str) \ + { \ + .name = name_str, \ + .init_setup_dma = cs5520_init_setup_dma, \ + .init_hwif = init_hwif_cs5520, \ + .channels = 2, \ + .autodma = AUTODMA, \ + .bootable = ON_BOARD, \ + .flags = IDEPCI_FLAG_ISA_PORTS, \ + } + +static ide_pci_device_t cyrix_chipsets[] __devinitdata = { + /* 0 */ DECLARE_CS_DEV("Cyrix 5510"), + /* 1 */ DECLARE_CS_DEV("Cyrix 5520") +}; + +/* + * The 5510/5520 are a bit weird. They don't quite set up the way + * the PCI helper layer expects so we must do much of the set up + * work longhand. + */ + +static int __devinit cs5520_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + ata_index_t index; + ide_pci_device_t *d = &cyrix_chipsets[id->driver_data]; + + ide_setup_pci_noise(dev, d); + + /* We must not grab the entire device, it has 'ISA' space in its + BARS too and we will freak out other bits of the kernel */ + if(pci_enable_device_bars(dev, 1<<2)) + { + printk(KERN_WARNING "%s: Unable to enable 55x0.\n", d->name); + return 1; + } + pci_set_master(dev); + if (pci_set_dma_mask(dev, DMA_32BIT_MASK)) { + printk(KERN_WARNING "cs5520: No suitable DMA available.\n"); + return -ENODEV; + } + + index.all = 0xf0f0; + + /* + * Now the chipset is configured we can let the core + * do all the device setup for us + */ + + ide_pci_setup_ports(dev, d, 14, &index); + + if((index.b.low & 0xf0) != 0xf0) + probe_hwif_init(&ide_hwifs[index.b.low]); + if((index.b.high & 0xf0) != 0xf0) + probe_hwif_init(&ide_hwifs[index.b.high]); + return 0; +} + +static struct pci_device_id cs5520_pci_tbl[] = { + { PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5510, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5520, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1}, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, cs5520_pci_tbl); + +static struct pci_driver driver = { + .name = "Cyrix_IDE", + .id_table = cs5520_pci_tbl, + .probe = cs5520_init_one, +}; + +static int cs5520_ide_init(void) +{ + return ide_pci_register_driver(&driver); +} + +module_init(cs5520_ide_init); + +MODULE_AUTHOR("Alan Cox"); +MODULE_DESCRIPTION("PCI driver module for Cyrix 5510/5520 IDE"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/pci/cs5530.c b/drivers/ide/pci/cs5530.c new file mode 100644 index 0000000..0381961 --- /dev/null +++ b/drivers/ide/pci/cs5530.c @@ -0,0 +1,384 @@ +/* + * linux/drivers/ide/pci/cs5530.c Version 0.7 Sept 10, 2002 + * + * Copyright (C) 2000 Andre Hedrick <andre@linux-ide.org> + * Ditto of GNU General Public License. + * + * 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. + * + * Documentation: + * CS5530 documentation available from National Semiconductor. + */ + +#include <linux/config.h> +#include <linux/module.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> + +/** + * cs5530_xfer_set_mode - set a new transfer mode at the drive + * @drive: drive to tune + * @mode: new mode + * + * Logging wrapper to the IDE driver speed configuration. This can + * probably go away now. + */ + +static int cs5530_set_xfer_mode (ide_drive_t *drive, u8 mode) +{ + printk(KERN_DEBUG "%s: cs5530_set_xfer_mode(%s)\n", + drive->name, ide_xfer_verbose(mode)); + return (ide_config_drive_speed(drive, mode)); +} + +/* + * 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 - select/set PIO modes + * + * 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, u8 pio) /* pio=255 means "autotune" */ +{ + ide_hwif_t *hwif = HWIF(drive); + unsigned int format; + unsigned long basereg = CS5530_BASEREG(hwif); + static u8 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 = (hwif->INL(basereg+4) >> 31) & 1; + hwif->OUTL(cs5530_pio_timings[format][pio], + basereg+(drive->select.b.unit<<3)); + } +} + +/** + * cs5530_config_dma - select/set DMA and UDMA modes + * @drive: drive to tune + * + * cs5530_config_dma() handles selection/setting of DMA/UDMA modes + * for both the chipset and drive. The CS5530 has limitations about + * mixing DMA/UDMA on the same cable. + */ + +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 reg, timings; + unsigned long basereg; + + /* + * Default to DMA-off in case we run into trouble here. + */ + hwif->ide_dma_off_quietly(drive); + /* turn off DMA while we fiddle */ + hwif->ide_dma_host_off(drive); + /* 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. + * + * Note: This relies on the fact we never fail from UDMA to MWDMA_2 + * but instead drop to PIO + */ + if (mate->present) { + struct hd_driveid *mateid = mate->id; + if (mateid && (mateid->capability & 1) && + !__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) && drive->autodma && + !__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(KERN_ERR "%s: cs5530_config_dma: huh? mode=%02x\n", + drive->name, mode); + return 1; /* failure */ + } + basereg = CS5530_BASEREG(hwif); + reg = hwif->INL(basereg+4); /* get drive0 config register */ + timings |= reg & 0x80000000; /* preserve PIO format bit */ + if (unit == 0) { /* are we configuring drive0? */ + hwif->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 */ + hwif->OUTL(reg, basereg+4); /* write drive0 config register */ + hwif->OUTL(timings, basereg+12); /* write drive1 config register */ + } + (void) hwif->ide_dma_host_on(drive); + /* set DMA_capable bit */ + + /* + * Finally, turn DMA on in software, and exit. + */ + return hwif->ide_dma_on(drive); /* success */ +} + +/** + * init_chipset_5530 - set up 5530 bridge + * @dev: PCI device + * @name: device name + * + * Initialize the cs5530 bridge for reliable IDE DMA operation. + */ + +static unsigned int __init init_chipset_cs5530 (struct pci_dev *dev, const char *name) +{ + struct pci_dev *master_0 = NULL, *cs5530_0 = NULL; + unsigned long flags; + + dev = NULL; + while ((dev = pci_find_device(PCI_VENDOR_ID_CYRIX, PCI_ANY_ID, dev)) != NULL) { + 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(KERN_ERR "%s: unable to locate PCI MASTER function\n", name); + return 0; + } + if (!cs5530_0) { + printk(KERN_ERR "%s: unable to locate CS5530 LEGACY function\n", name); + return 0; + } + + spin_lock_irqsave(&ide_lock, flags); + /* 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_set_master(cs5530_0); + pci_set_mwi(cs5530_0); + + /* + * 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); + + spin_unlock_irqrestore(&ide_lock, flags); + + return 0; +} + +/** + * init_hwif_cs5530 - initialise an IDE channel + * @hwif: IDE to initialize + * + * This gets invoked by the IDE driver once for each channel. It + * performs channel-specific pre-initialization before drive probing. + */ + +static void __init init_hwif_cs5530 (ide_hwif_t *hwif) +{ + unsigned long basereg; + u32 d0_timings; + hwif->autodma = 0; + + if (hwif->mate) + hwif->serialized = hwif->mate->serialized = 1; + + hwif->tuneproc = &cs5530_tuneproc; + basereg = CS5530_BASEREG(hwif); + d0_timings = hwif->INL(basereg+0); + if (CS5530_BAD_PIO(d0_timings)) { + /* PIO timings not initialized? */ + hwif->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(hwif->INL(basereg+8))) { + /* PIO timings not initialized? */ + hwif->OUTL(cs5530_pio_timings[(d0_timings>>31)&1][0], basereg+8); + if (!hwif->drives[1].autotune) + hwif->drives[1].autotune = 1; + /* needs autotuning later */ + } + + hwif->atapi_dma = 1; + hwif->ultra_mask = 0x07; + hwif->mwdma_mask = 0x07; + + hwif->ide_dma_check = &cs5530_config_dma; + if (!noautodma) + hwif->autodma = 1; + hwif->drives[0].autodma = hwif->autodma; + hwif->drives[1].autodma = hwif->autodma; +} + +static ide_pci_device_t cs5530_chipset __devinitdata = { + .name = "CS5530", + .init_chipset = init_chipset_cs5530, + .init_hwif = init_hwif_cs5530, + .channels = 2, + .autodma = AUTODMA, + .bootable = ON_BOARD, +}; + +static int __devinit cs5530_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + return ide_setup_pci_device(dev, &cs5530_chipset); +} + +static struct pci_device_id cs5530_pci_tbl[] = { + { PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, cs5530_pci_tbl); + +static struct pci_driver driver = { + .name = "CS5530 IDE", + .id_table = cs5530_pci_tbl, + .probe = cs5530_init_one, +}; + +static int cs5530_ide_init(void) +{ + return ide_pci_register_driver(&driver); +} + +module_init(cs5530_ide_init); + +MODULE_AUTHOR("Mark Lord"); +MODULE_DESCRIPTION("PCI driver module for Cyrix/NS 5530 IDE"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/pci/cy82c693.c b/drivers/ide/pci/cy82c693.c new file mode 100644 index 0000000..80d67e9 --- /dev/null +++ b/drivers/ide/pci/cy82c693.c @@ -0,0 +1,531 @@ +/* + * linux/drivers/ide/pci/cy82c693.c Version 0.40 Sep. 10, 2002 + * + * Copyright (C) 1998-2000 Andreas S. Krebs (akrebs@altavista.net), Maintainer + * Copyright (C) 1998-2002 Andre Hedrick <andre@linux-ide.org>, Integrator + * + * CYPRESS CY82C693 chipset IDE controller + * + * The CY82C693 chipset is used on Digital's PC-Alpha 164SX boards. + * Writing 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 + * + * Ancient 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 drives. + * 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/config.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/ide.h> +#include <linux/init.h> + +#include <asm/io.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! By using a to low value will cause DMA timeouts + * and drop IDE performance, and by 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. + */ + +/* twice the value written in cy82c693ub datasheet */ +#define BUSMASTER_TIMEOUT 0x50 +/* + * 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 { + u8 address_time; /* Address setup (clocks) */ + u8 time_16r; /* clocks for 16bit IOR (0xF0=Active/data, 0x0F=Recovery) */ + u8 time_16w; /* clocks for 16bit IOW (0xF0=Active/data, 0x0F=Recovery) */ + u8 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 (u8 pio, pio_clocks_t *p_pclk) +{ + int clk1, clk2; + int bus_speed = system_bus_clock(); /* 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 = (u8)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-lib.c + */ + + p_pclk->time_16r = (u8)clk1; + p_pclk->time_16w = (u8)clk1; + + /* what are good values for 8bit ?? */ + p_pclk->time_8 = (u8)clk1; +} + +/* + * set DMA mode a specific channel for CY82C693 + */ + +static void cy82c693_dma_enable (ide_drive_t *drive, int mode, int single) +{ + u8 index = 0, data = 0; + + 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 */ + + HWIF(drive)->OUTB(index, CY82_INDEX_PORT); + data = HWIF(drive)->INB(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 = (u8)mode|(u8)(single<<2); + + HWIF(drive)->OUTB(index, CY82_INDEX_PORT); + HWIF(drive)->OUTB(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; + HWIF(drive)->OUTB(CY82_INDEX_TIMEOUT, CY82_INDEX_PORT); + HWIF(drive)->OUTB(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_ide_dma_on (ide_drive_t *drive) +{ + 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) { + /* enable multi */ + cy82c693_dma_enable(drive, (mmode >> 1), 0); + } else if (smode != 0) { + /* enable single */ + cy82c693_dma_enable(drive, (smode >> 1), 1); + } + } + } + return __ide_dma_on(drive); +} + +/* + * tune ide drive - set PIO mode + */ +static void cy82c693_tune_drive (ide_drive_t *drive, u8 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 */ + + /* let's calc the values for this PIO mode */ + compute_clocks(pio, &pclk); + + /* 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 + */ +static unsigned int __init init_chipset_cy82c693(struct pci_dev *dev, const char *name) +{ + if (PCI_FUNC(dev->devfn) != 1) + return 0; + +#ifdef CY82C693_SETDMA_CLOCK + u8 data = 0; +#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 */ + + outb(CY82_INDEX_CTRLREG1, CY82_INDEX_PORT); + data = inb(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; + + outb(CY82_INDEX_CTRLREG1, CY82_INDEX_PORT); + outb(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 + */ +static void __init init_hwif_cy82c693(ide_hwif_t *hwif) +{ + hwif->autodma = 0; + + hwif->chipset = ide_cy82c693; + hwif->tuneproc = &cy82c693_tune_drive; + + if (!hwif->dma_base) { + hwif->drives[0].autotune = 1; + hwif->drives[1].autotune = 1; + return; + } + + hwif->atapi_dma = 1; + hwif->mwdma_mask = 0x04; + hwif->swdma_mask = 0x04; + + hwif->ide_dma_on = &cy82c693_ide_dma_on; + if (!noautodma) + hwif->autodma = 1; + hwif->drives[0].autodma = hwif->autodma; + hwif->drives[1].autodma = hwif->autodma; +} + +static __initdata ide_hwif_t *primary; + +void __init init_iops_cy82c693(ide_hwif_t *hwif) +{ + if (PCI_FUNC(hwif->pci_dev->devfn) == 1) + primary = hwif; + else { + hwif->mate = primary; + hwif->channel = 1; + } +} + +static ide_pci_device_t cy82c693_chipsets[] __devinitdata = { + { /* 0 */ + .name = "CY82C693", + .init_chipset = init_chipset_cy82c693, + .init_iops = init_iops_cy82c693, + .init_hwif = init_hwif_cy82c693, + .channels = 1, + .autodma = AUTODMA, + .bootable = ON_BOARD, + } +}; + +static int __devinit cy82c693_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + ide_pci_device_t *d = &cy82c693_chipsets[id->driver_data]; + struct pci_dev *dev2; + int ret = -ENODEV; + + /* CY82C693 is more than only a IDE controller. + Function 1 is primary IDE channel, function 2 - secondary. */ + if ((dev->class >> 8) == PCI_CLASS_STORAGE_IDE && + PCI_FUNC(dev->devfn) == 1) { + dev2 = pci_find_slot(dev->bus->number, dev->devfn + 1); + ret = ide_setup_pci_devices(dev, dev2, d); + } + return ret; +} + +static struct pci_device_id cy82c693_pci_tbl[] = { + { PCI_VENDOR_ID_CONTAQ, PCI_DEVICE_ID_CONTAQ_82C693, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, cy82c693_pci_tbl); + +static struct pci_driver driver = { + .name = "Cypress_IDE", + .id_table = cy82c693_pci_tbl, + .probe = cy82c693_init_one, +}; + +static int cy82c693_ide_init(void) +{ + return ide_pci_register_driver(&driver); +} + +module_init(cy82c693_ide_init); + +MODULE_AUTHOR("Andreas Krebs, Andre Hedrick"); +MODULE_DESCRIPTION("PCI driver module for the Cypress CY82C693 IDE"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/pci/generic.c b/drivers/ide/pci/generic.c new file mode 100644 index 0000000..4565cc3 --- /dev/null +++ b/drivers/ide/pci/generic.c @@ -0,0 +1,232 @@ +/* + * linux/drivers/ide/pci/generic.c Version 0.11 December 30, 2002 + * + * Copyright (C) 2001-2002 Andre Hedrick <andre@linux-ide.org> + * Portions (C) Copyright 2002 Red Hat Inc <alan@redhat.com> + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * For the avoidance of doubt the "preferred form" of this code is one which + * is in an open non patent encumbered format. Where cryptographic key signing + * forms part of the process of creating an executable the information + * including keys needed to generate an equivalently functional executable + * are deemed to be part of the source code. + */ + +#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/module.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 <linux/init.h> + +#include <asm/io.h> + +static void __devinit init_hwif_generic (ide_hwif_t *hwif) +{ + switch(hwif->pci_dev->device) { + case PCI_DEVICE_ID_UMC_UM8673F: + case PCI_DEVICE_ID_UMC_UM8886A: + case PCI_DEVICE_ID_UMC_UM8886BF: + hwif->irq = hwif->channel ? 15 : 14; + break; + default: + break; + } + + if (!(hwif->dma_base)) + return; + + hwif->atapi_dma = 1; + hwif->ultra_mask = 0x7f; + hwif->mwdma_mask = 0x07; + hwif->swdma_mask = 0x07; + + if (!noautodma) + hwif->autodma = 1; + hwif->drives[0].autodma = hwif->autodma; + hwif->drives[1].autodma = hwif->autodma; +} + +#if 0 + /* Logic to add back later on */ + + if ((dev->class >> 8) == PCI_CLASS_STORAGE_IDE) { + ide_pci_device_t *unknown = unknown_chipset; + init_setup_unknown(dev, unknown); + return 1; + } + return 0; +#endif + +static ide_pci_device_t generic_chipsets[] __devinitdata = { + { /* 0 */ + .name = "NS87410", + .init_hwif = init_hwif_generic, + .channels = 2, + .autodma = AUTODMA, + .enablebits = {{0x43,0x08,0x08}, {0x47,0x08,0x08}}, + .bootable = ON_BOARD, + },{ /* 1 */ + .name = "SAMURAI", + .init_hwif = init_hwif_generic, + .channels = 2, + .autodma = AUTODMA, + .bootable = ON_BOARD, + },{ /* 2 */ + .name = "HT6565", + .init_hwif = init_hwif_generic, + .channels = 2, + .autodma = AUTODMA, + .bootable = ON_BOARD, + },{ /* 3 */ + .name = "UM8673F", + .init_hwif = init_hwif_generic, + .channels = 2, + .autodma = NODMA, + .bootable = ON_BOARD, + },{ /* 4 */ + .name = "UM8886A", + .init_hwif = init_hwif_generic, + .channels = 2, + .autodma = NODMA, + .bootable = ON_BOARD, + },{ /* 5 */ + .name = "UM8886BF", + .init_hwif = init_hwif_generic, + .channels = 2, + .autodma = NODMA, + .bootable = ON_BOARD, + },{ /* 6 */ + .name = "HINT_IDE", + .init_hwif = init_hwif_generic, + .channels = 2, + .autodma = AUTODMA, + .bootable = ON_BOARD, + },{ /* 7 */ + .name = "VIA_IDE", + .init_hwif = init_hwif_generic, + .channels = 2, + .autodma = NOAUTODMA, + .bootable = ON_BOARD, + },{ /* 8 */ + .name = "OPTI621V", + .init_hwif = init_hwif_generic, + .channels = 2, + .autodma = NOAUTODMA, + .bootable = ON_BOARD, + },{ /* 9 */ + .name = "VIA8237SATA", + .init_hwif = init_hwif_generic, + .channels = 2, + .autodma = AUTODMA, + .bootable = OFF_BOARD, + },{ /* 10 */ + .name = "Piccolo0102", + .init_hwif = init_hwif_generic, + .channels = 2, + .autodma = NOAUTODMA, + .bootable = ON_BOARD, + },{ /* 11 */ + .name = "Piccolo0103", + .init_hwif = init_hwif_generic, + .channels = 2, + .autodma = NOAUTODMA, + .bootable = ON_BOARD, + },{ /* 12 */ + .name = "Piccolo0105", + .init_hwif = init_hwif_generic, + .channels = 2, + .autodma = NOAUTODMA, + .bootable = ON_BOARD, + } +}; + +/** + * generic_init_one - called when a PIIX is found + * @dev: the generic device + * @id: the matching pci id + * + * Called when the PCI registration layer (or the IDE initialization) + * finds a device matching our IDE device tables. + */ + +static int __devinit generic_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + ide_pci_device_t *d = &generic_chipsets[id->driver_data]; + u16 command; + int ret = -ENODEV; + + if (dev->vendor == PCI_VENDOR_ID_UMC && + dev->device == PCI_DEVICE_ID_UMC_UM8886A && + (!(PCI_FUNC(dev->devfn) & 1))) + goto out; /* UM8886A/BF pair */ + + if (dev->vendor == PCI_VENDOR_ID_OPTI && + dev->device == PCI_DEVICE_ID_OPTI_82C558 && + (!(PCI_FUNC(dev->devfn) & 1))) + goto out; + + pci_read_config_word(dev, PCI_COMMAND, &command); + if (!(command & PCI_COMMAND_IO)) { + printk(KERN_INFO "Skipping disabled %s IDE controller.\n", d->name); + goto out; + } + ret = ide_setup_pci_device(dev, d); +out: + return ret; +} + +static struct pci_device_id generic_pci_tbl[] = { + { PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87410, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_SAMURAI_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1}, + { PCI_VENDOR_ID_HOLTEK, PCI_DEVICE_ID_HOLTEK_6565, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2}, + { PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8673F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 3}, + { PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8886A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4}, + { PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8886BF, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 5}, + { PCI_VENDOR_ID_HINT, PCI_DEVICE_ID_HINT_VXPROII_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 6}, + { PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C561, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 7}, + { PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C558, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 8}, +#ifdef CONFIG_BLK_DEV_IDE_SATA + { PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237_SATA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 9}, +#endif + { PCI_VENDOR_ID_TOSHIBA,PCI_DEVICE_ID_TOSHIBA_PICCOLO, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 10}, + { PCI_VENDOR_ID_TOSHIBA,PCI_DEVICE_ID_TOSHIBA_PICCOLO_1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 11}, + { PCI_VENDOR_ID_TOSHIBA,PCI_DEVICE_ID_TOSHIBA_PICCOLO_2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 12}, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, generic_pci_tbl); + +static struct pci_driver driver = { + .name = "PCI_IDE", + .id_table = generic_pci_tbl, + .probe = generic_init_one, +}; + +static int generic_ide_init(void) +{ + return ide_pci_register_driver(&driver); +} + +module_init(generic_ide_init); + +MODULE_AUTHOR("Andre Hedrick"); +MODULE_DESCRIPTION("PCI driver module for generic PCI IDE"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/pci/hpt34x.c b/drivers/ide/pci/hpt34x.c new file mode 100644 index 0000000..bbde462 --- /dev/null +++ b/drivers/ide/pci/hpt34x.c @@ -0,0 +1,278 @@ +/* + * linux/drivers/ide/pci/hpt34x.c Version 0.40 Sept 10, 2002 + * + * Copyright (C) 1998-2000 Andre Hedrick <andre@linux-ide.org> + * 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/module.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> + +#define HPT343_DEBUG_DRIVE_INFO 0 + +static u8 hpt34x_ratemask (ide_drive_t *drive) +{ + return 1; +} + +static void hpt34x_clear_chipset (ide_drive_t *drive) +{ + struct pci_dev *dev = HWIF(drive)->pci_dev; + u32 reg1 = 0, tmp1 = 0, reg2 = 0, tmp2 = 0; + + pci_read_config_dword(dev, 0x44, ®1); + pci_read_config_dword(dev, 0x48, ®2); + tmp1 = ((0x00 << (3*drive->dn)) | (reg1 & ~(7 << (3*drive->dn)))); + tmp2 = (reg2 & ~(0x11 << drive->dn)); + pci_write_config_dword(dev, 0x44, tmp1); + pci_write_config_dword(dev, 0x48, tmp2); +} + +static int hpt34x_tune_chipset (ide_drive_t *drive, u8 xferspeed) +{ + struct pci_dev *dev = HWIF(drive)->pci_dev; + u8 speed = ide_rate_filter(hpt34x_ratemask(drive), xferspeed); + u32 reg1= 0, tmp1 = 0, reg2 = 0, tmp2 = 0; + u8 hi_speed, lo_speed; + + hi_speed = speed >> 4; + lo_speed = speed & 0x0f; + + if (hi_speed & 7) { + hi_speed = (hi_speed & 4) ? 0x01 : 0x10; + } else { + lo_speed <<= 5; + lo_speed >>= 5; + } + + pci_read_config_dword(dev, 0x44, ®1); + pci_read_config_dword(dev, 0x48, ®2); + tmp1 = ((lo_speed << (3*drive->dn)) | (reg1 & ~(7 << (3*drive->dn)))); + tmp2 = ((hi_speed << drive->dn) | reg2); + pci_write_config_dword(dev, 0x44, tmp1); + pci_write_config_dword(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)\n", + drive->name, ide_xfer_verbose(speed), + drive->dn, reg1, tmp1, reg2, tmp2, + hi_speed, lo_speed); +#endif /* HPT343_DEBUG_DRIVE_INFO */ + + return(ide_config_drive_speed(drive, speed)); +} + +static void hpt34x_tune_drive (ide_drive_t *drive, u8 pio) +{ + pio = ide_get_best_pio_mode(drive, pio, 5, NULL); + hpt34x_clear_chipset(drive); + (void) hpt34x_tune_chipset(drive, (XFER_PIO_0 + pio)); +} + +/* + * 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. Initially for designed for + * HPT343 UDMA chipset by HighPoint|Triones Technologies, Inc. + */ + +static int config_chipset_for_dma (ide_drive_t *drive) +{ + u8 speed = ide_dma_speed(drive, hpt34x_ratemask(drive)); + + if (!(speed)) + return 0; + + hpt34x_clear_chipset(drive); + (void) hpt34x_tune_chipset(drive, speed); + return ide_dma_enable(drive); +} + +static int hpt34x_config_drive_xfer_rate (ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + struct hd_driveid *id = drive->id; + + drive->init_speed = 0; + + if (id && (id->capability & 1) && drive->autodma) { + + if (ide_use_dma(drive)) { + if (config_chipset_for_dma(drive)) +#ifndef CONFIG_HPT34X_AUTODMA + return hwif->ide_dma_off_quietly(drive); +#else + return hwif->ide_dma_on(drive); +#endif + } + + goto fast_ata_pio; + + } else if ((id->capability & 8) || (id->field_valid & 2)) { +fast_ata_pio: + hpt34x_tune_drive(drive, 255); + return hwif->ide_dma_off_quietly(drive); + } + /* IORDY not supported */ + return 0; +} + +/* + * If the BIOS does not set the IO base addaress to XX00, 343 will fail. + */ +#define HPT34X_PCI_INIT_REG 0x80 + +static unsigned int __devinit init_chipset_hpt34x(struct pci_dev *dev, const char *name) +{ + int i = 0; + unsigned long hpt34xIoBase = pci_resource_start(dev, 4); + unsigned long hpt_addr[4] = { 0x20, 0x34, 0x28, 0x3c }; + unsigned long hpt_addr_len[4] = { 7, 3, 7, 3 }; + u16 cmd; + unsigned long flags; + + local_irq_save(flags); + + pci_write_config_byte(dev, HPT34X_PCI_INIT_REG, 0x00); + pci_read_config_word(dev, PCI_COMMAND, &cmd); + + if (cmd & PCI_COMMAND_MEMORY) { + if (pci_resource_start(dev, PCI_ROM_RESOURCE)) { + 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); + } + + /* + * Since 20-23 can be assigned and are R/W, we correct them. + */ + pci_write_config_word(dev, PCI_COMMAND, cmd & ~PCI_COMMAND_IO); + for(i=0; i<4; i++) { + dev->resource[i].start = (hpt34xIoBase + hpt_addr[i]); + dev->resource[i].end = dev->resource[i].start + hpt_addr_len[i]; + dev->resource[i].flags = IORESOURCE_IO; + pci_write_config_dword(dev, + (PCI_BASE_ADDRESS_0 + (i * 4)), + dev->resource[i].start); + } + pci_write_config_word(dev, PCI_COMMAND, cmd); + + local_irq_restore(flags); + + return dev->irq; +} + +static void __devinit init_hwif_hpt34x(ide_hwif_t *hwif) +{ + u16 pcicmd = 0; + + hwif->autodma = 0; + + hwif->tuneproc = &hpt34x_tune_drive; + hwif->speedproc = &hpt34x_tune_chipset; + hwif->no_dsc = 1; + hwif->drives[0].autotune = 1; + hwif->drives[1].autotune = 1; + + pci_read_config_word(hwif->pci_dev, PCI_COMMAND, &pcicmd); + + if (!hwif->dma_base) + return; + + hwif->ultra_mask = 0x07; + hwif->mwdma_mask = 0x07; + hwif->swdma_mask = 0x07; + + hwif->ide_dma_check = &hpt34x_config_drive_xfer_rate; + if (!noautodma) + hwif->autodma = (pcicmd & PCI_COMMAND_MEMORY) ? 1 : 0; + hwif->drives[0].autodma = hwif->autodma; + hwif->drives[1].autodma = hwif->autodma; +} + +static ide_pci_device_t hpt34x_chipset __devinitdata = { + .name = "HPT34X", + .init_chipset = init_chipset_hpt34x, + .init_hwif = init_hwif_hpt34x, + .channels = 2, + .autodma = NOAUTODMA, + .bootable = NEVER_BOARD, + .extra = 16 +}; + +static int __devinit hpt34x_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + ide_pci_device_t *d = &hpt34x_chipset; + static char *chipset_names[] = {"HPT343", "HPT345"}; + u16 pcicmd = 0; + + pci_read_config_word(dev, PCI_COMMAND, &pcicmd); + + d->name = chipset_names[(pcicmd & PCI_COMMAND_MEMORY) ? 1 : 0]; + d->bootable = (pcicmd & PCI_COMMAND_MEMORY) ? OFF_BOARD : NEVER_BOARD; + + return ide_setup_pci_device(dev, d); +} + +static struct pci_device_id hpt34x_pci_tbl[] = { + { PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT343, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, hpt34x_pci_tbl); + +static struct pci_driver driver = { + .name = "HPT34x_IDE", + .id_table = hpt34x_pci_tbl, + .probe = hpt34x_init_one, +}; + +static int hpt34x_ide_init(void) +{ + return ide_pci_register_driver(&driver); +} + +module_init(hpt34x_ide_init); + +MODULE_AUTHOR("Andre Hedrick"); +MODULE_DESCRIPTION("PCI driver module for Highpoint 34x IDE"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/pci/hpt366.c b/drivers/ide/pci/hpt366.c new file mode 100644 index 0000000..c8ee0b8 --- /dev/null +++ b/drivers/ide/pci/hpt366.c @@ -0,0 +1,1745 @@ +/* + * linux/drivers/ide/pci/hpt366.c Version 0.36 April 25, 2003 + * + * Copyright (C) 1999-2003 Andre Hedrick <andre@linux-ide.org> + * Portions Copyright (C) 2001 Sun Microsystems, Inc. + * Portions Copyright (C) 2003 Red Hat Inc + * + * 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. + * + * Note that final HPT370 support was done by force extraction of GPL. + * + * - add function for getting/setting power status of drive + * - the HPT370's state machine can get confused. reset it before each dma + * xfer to prevent that from happening. + * - reset state engine whenever we get an error. + * - check for busmaster state at end of dma. + * - use new highpoint timings. + * - detect bus speed using highpoint register. + * - use pll if we don't have a clock table. added a 66MHz table that's + * just 2x the 33MHz table. + * - removed turnaround. NOTE: we never want to switch between pll and + * pci clocks as the chip can glitch in those cases. the highpoint + * approved workaround slows everything down too much to be useful. in + * addition, we would have to serialize access to each chip. + * Adrian Sun <a.sun@sun.com> + * + * add drive timings for 66MHz PCI bus, + * fix ATA Cable signal detection, fix incorrect /proc info + * add /proc display for per-drive PIO/DMA/UDMA mode and + * per-channel ATA-33/66 Cable detect. + * Duncan Laurie <void@sun.com> + * + * fixup /proc output for multiple controllers + * Tim Hockin <thockin@sun.com> + * + * On hpt366: + * Reset the hpt366 on error, reset on dma + * Fix disabling Fast Interrupt hpt366. + * Mike Waychison <crlf@sun.com> + * + * Added support for 372N clocking and clock switching. The 372N needs + * different clocks on read/write. This requires overloading rw_disk and + * other deeply crazy things. Thanks to <http://www.hoerstreich.de> for + * keeping me sane. + * Alan Cox <alan@redhat.com> + * + */ + + +#include <linux/config.h> +#include <linux/types.h> +#include <linux/module.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/uaccess.h> +#include <asm/io.h> +#include <asm/irq.h> + +/* various tuning parameters */ +#define HPT_RESET_STATE_ENGINE +#undef HPT_DELAY_INTERRUPT +#undef HPT_SERIALIZE_IO + +static const char *quirk_drives[] = { + "QUANTUM FIREBALLlct08 08", + "QUANTUM FIREBALLP KA6.4", + "QUANTUM FIREBALLP LM20.4", + "QUANTUM FIREBALLP LM20.5", + NULL +}; + +static const char *bad_ata100_5[] = { + "IBM-DTLA-307075", + "IBM-DTLA-307060", + "IBM-DTLA-307045", + "IBM-DTLA-307030", + "IBM-DTLA-307020", + "IBM-DTLA-307015", + "IBM-DTLA-305040", + "IBM-DTLA-305030", + "IBM-DTLA-305020", + "IC35L010AVER07-0", + "IC35L020AVER07-0", + "IC35L030AVER07-0", + "IC35L040AVER07-0", + "IC35L060AVER07-0", + "WDC AC310200R", + NULL +}; + +static const char *bad_ata66_4[] = { + "IBM-DTLA-307075", + "IBM-DTLA-307060", + "IBM-DTLA-307045", + "IBM-DTLA-307030", + "IBM-DTLA-307020", + "IBM-DTLA-307015", + "IBM-DTLA-305040", + "IBM-DTLA-305030", + "IBM-DTLA-305020", + "IC35L010AVER07-0", + "IC35L020AVER07-0", + "IC35L030AVER07-0", + "IC35L040AVER07-0", + "IC35L060AVER07-0", + "WDC AC310200R", + NULL +}; + +static const char *bad_ata66_3[] = { + "WDC AC310200R", + NULL +}; + +static 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 { + u8 xfer_speed; + unsigned int chipset_settings; +}; + +/* key for bus clock timings + * bit + * 0:3 data_high_time. inactive time of DIOW_/DIOR_ for PIO and MW + * DMA. cycles = value + 1 + * 4:8 data_low_time. active time of DIOW_/DIOR_ for PIO and MW + * DMA. cycles = value + 1 + * 9:12 cmd_high_time. inactive time of DIOW_/DIOR_ during task file + * register access. + * 13:17 cmd_low_time. active time of DIOW_/DIOR_ during task file + * register access. + * 18:21 udma_cycle_time. clock freq and clock cycles for UDMA xfer. + * during task file register access. + * 22:24 pre_high_time. time to initialize 1st cycle for PIO and MW DMA + * xfer. + * 25:27 cmd_pre_high_time. time to initialize 1st PIO cycle for task + * register access. + * 28 UDMA enable + * 29 DMA enable + * 30 PIO_MST enable. if set, the chip is in bus master mode during + * PIO. + * 31 FIFO enable. + */ +static struct chipset_bus_clock_list_entry forty_base_hpt366[] = { + { 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 } +}; + +static struct chipset_bus_clock_list_entry thirty_three_base_hpt366[] = { + { 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 } +}; + +static struct chipset_bus_clock_list_entry twenty_five_base_hpt366[] = { + { 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 } +}; + +/* from highpoint documentation. these are old values */ +static struct chipset_bus_clock_list_entry thirty_three_base_hpt370[] = { +/* { XFER_UDMA_5, 0x1A85F442, 0x16454e31 }, */ + { XFER_UDMA_5, 0x16454e31 }, + { XFER_UDMA_4, 0x16454e31 }, + { XFER_UDMA_3, 0x166d4e31 }, + { XFER_UDMA_2, 0x16494e31 }, + { XFER_UDMA_1, 0x164d4e31 }, + { XFER_UDMA_0, 0x16514e31 }, + + { XFER_MW_DMA_2, 0x26514e21 }, + { XFER_MW_DMA_1, 0x26514e33 }, + { XFER_MW_DMA_0, 0x26514e97 }, + + { XFER_PIO_4, 0x06514e21 }, + { XFER_PIO_3, 0x06514e22 }, + { XFER_PIO_2, 0x06514e33 }, + { XFER_PIO_1, 0x06914e43 }, + { XFER_PIO_0, 0x06914e57 }, + { 0, 0x06514e57 } +}; + +static struct chipset_bus_clock_list_entry sixty_six_base_hpt370[] = { + { XFER_UDMA_5, 0x14846231 }, + { XFER_UDMA_4, 0x14886231 }, + { XFER_UDMA_3, 0x148c6231 }, + { XFER_UDMA_2, 0x148c6231 }, + { XFER_UDMA_1, 0x14906231 }, + { XFER_UDMA_0, 0x14986231 }, + + { XFER_MW_DMA_2, 0x26514e21 }, + { XFER_MW_DMA_1, 0x26514e33 }, + { XFER_MW_DMA_0, 0x26514e97 }, + + { XFER_PIO_4, 0x06514e21 }, + { XFER_PIO_3, 0x06514e22 }, + { XFER_PIO_2, 0x06514e33 }, + { XFER_PIO_1, 0x06914e43 }, + { XFER_PIO_0, 0x06914e57 }, + { 0, 0x06514e57 } +}; + +/* these are the current (4 sep 2001) timings from highpoint */ +static struct chipset_bus_clock_list_entry thirty_three_base_hpt370a[] = { + { XFER_UDMA_5, 0x12446231 }, + { XFER_UDMA_4, 0x12446231 }, + { XFER_UDMA_3, 0x126c6231 }, + { XFER_UDMA_2, 0x12486231 }, + { XFER_UDMA_1, 0x124c6233 }, + { XFER_UDMA_0, 0x12506297 }, + + { XFER_MW_DMA_2, 0x22406c31 }, + { XFER_MW_DMA_1, 0x22406c33 }, + { XFER_MW_DMA_0, 0x22406c97 }, + + { XFER_PIO_4, 0x06414e31 }, + { XFER_PIO_3, 0x06414e42 }, + { XFER_PIO_2, 0x06414e53 }, + { XFER_PIO_1, 0x06814e93 }, + { XFER_PIO_0, 0x06814ea7 }, + { 0, 0x06814ea7 } +}; + +/* 2x 33MHz timings */ +static struct chipset_bus_clock_list_entry sixty_six_base_hpt370a[] = { + { XFER_UDMA_5, 0x1488e673 }, + { XFER_UDMA_4, 0x1488e673 }, + { XFER_UDMA_3, 0x1498e673 }, + { XFER_UDMA_2, 0x1490e673 }, + { XFER_UDMA_1, 0x1498e677 }, + { XFER_UDMA_0, 0x14a0e73f }, + + { XFER_MW_DMA_2, 0x2480fa73 }, + { XFER_MW_DMA_1, 0x2480fa77 }, + { XFER_MW_DMA_0, 0x2480fb3f }, + + { XFER_PIO_4, 0x0c82be73 }, + { XFER_PIO_3, 0x0c82be95 }, + { XFER_PIO_2, 0x0c82beb7 }, + { XFER_PIO_1, 0x0d02bf37 }, + { XFER_PIO_0, 0x0d02bf5f }, + { 0, 0x0d02bf5f } +}; + +static struct chipset_bus_clock_list_entry fifty_base_hpt370a[] = { + { XFER_UDMA_5, 0x12848242 }, + { XFER_UDMA_4, 0x12ac8242 }, + { XFER_UDMA_3, 0x128c8242 }, + { XFER_UDMA_2, 0x120c8242 }, + { XFER_UDMA_1, 0x12148254 }, + { XFER_UDMA_0, 0x121882ea }, + + { XFER_MW_DMA_2, 0x22808242 }, + { XFER_MW_DMA_1, 0x22808254 }, + { XFER_MW_DMA_0, 0x228082ea }, + + { XFER_PIO_4, 0x0a81f442 }, + { XFER_PIO_3, 0x0a81f443 }, + { XFER_PIO_2, 0x0a81f454 }, + { XFER_PIO_1, 0x0ac1f465 }, + { XFER_PIO_0, 0x0ac1f48a }, + { 0, 0x0ac1f48a } +}; + +static struct chipset_bus_clock_list_entry thirty_three_base_hpt372[] = { + { XFER_UDMA_6, 0x1c81dc62 }, + { XFER_UDMA_5, 0x1c6ddc62 }, + { XFER_UDMA_4, 0x1c8ddc62 }, + { XFER_UDMA_3, 0x1c8edc62 }, /* checkme */ + { XFER_UDMA_2, 0x1c91dc62 }, + { XFER_UDMA_1, 0x1c9adc62 }, /* checkme */ + { XFER_UDMA_0, 0x1c82dc62 }, /* checkme */ + + { XFER_MW_DMA_2, 0x2c829262 }, + { XFER_MW_DMA_1, 0x2c829266 }, /* checkme */ + { XFER_MW_DMA_0, 0x2c82922e }, /* checkme */ + + { XFER_PIO_4, 0x0c829c62 }, + { XFER_PIO_3, 0x0c829c84 }, + { XFER_PIO_2, 0x0c829ca6 }, + { XFER_PIO_1, 0x0d029d26 }, + { XFER_PIO_0, 0x0d029d5e }, + { 0, 0x0d029d5e } +}; + +static struct chipset_bus_clock_list_entry fifty_base_hpt372[] = { + { XFER_UDMA_5, 0x12848242 }, + { XFER_UDMA_4, 0x12ac8242 }, + { XFER_UDMA_3, 0x128c8242 }, + { XFER_UDMA_2, 0x120c8242 }, + { XFER_UDMA_1, 0x12148254 }, + { XFER_UDMA_0, 0x121882ea }, + + { XFER_MW_DMA_2, 0x22808242 }, + { XFER_MW_DMA_1, 0x22808254 }, + { XFER_MW_DMA_0, 0x228082ea }, + + { XFER_PIO_4, 0x0a81f442 }, + { XFER_PIO_3, 0x0a81f443 }, + { XFER_PIO_2, 0x0a81f454 }, + { XFER_PIO_1, 0x0ac1f465 }, + { XFER_PIO_0, 0x0ac1f48a }, + { 0, 0x0a81f443 } +}; + +static struct chipset_bus_clock_list_entry sixty_six_base_hpt372[] = { + { XFER_UDMA_6, 0x1c869c62 }, + { XFER_UDMA_5, 0x1cae9c62 }, + { XFER_UDMA_4, 0x1c8a9c62 }, + { XFER_UDMA_3, 0x1c8e9c62 }, + { XFER_UDMA_2, 0x1c929c62 }, + { XFER_UDMA_1, 0x1c9a9c62 }, + { XFER_UDMA_0, 0x1c829c62 }, + + { XFER_MW_DMA_2, 0x2c829c62 }, + { XFER_MW_DMA_1, 0x2c829c66 }, + { XFER_MW_DMA_0, 0x2c829d2e }, + + { XFER_PIO_4, 0x0c829c62 }, + { XFER_PIO_3, 0x0c829c84 }, + { XFER_PIO_2, 0x0c829ca6 }, + { XFER_PIO_1, 0x0d029d26 }, + { XFER_PIO_0, 0x0d029d5e }, + { 0, 0x0d029d26 } +}; + +static struct chipset_bus_clock_list_entry thirty_three_base_hpt374[] = { + { XFER_UDMA_6, 0x12808242 }, + { XFER_UDMA_5, 0x12848242 }, + { XFER_UDMA_4, 0x12ac8242 }, + { XFER_UDMA_3, 0x128c8242 }, + { XFER_UDMA_2, 0x120c8242 }, + { XFER_UDMA_1, 0x12148254 }, + { XFER_UDMA_0, 0x121882ea }, + + { XFER_MW_DMA_2, 0x22808242 }, + { XFER_MW_DMA_1, 0x22808254 }, + { XFER_MW_DMA_0, 0x228082ea }, + + { XFER_PIO_4, 0x0a81f442 }, + { XFER_PIO_3, 0x0a81f443 }, + { XFER_PIO_2, 0x0a81f454 }, + { XFER_PIO_1, 0x0ac1f465 }, + { XFER_PIO_0, 0x0ac1f48a }, + { 0, 0x06814e93 } +}; + +/* FIXME: 50MHz timings for HPT374 */ + +#if 0 +static struct chipset_bus_clock_list_entry sixty_six_base_hpt374[] = { + { XFER_UDMA_6, 0x12406231 }, /* checkme */ + { XFER_UDMA_5, 0x12446231 }, /* 0x14846231 */ + { XFER_UDMA_4, 0x16814ea7 }, /* 0x14886231 */ + { XFER_UDMA_3, 0x16814ea7 }, /* 0x148c6231 */ + { XFER_UDMA_2, 0x16814ea7 }, /* 0x148c6231 */ + { XFER_UDMA_1, 0x16814ea7 }, /* 0x14906231 */ + { XFER_UDMA_0, 0x16814ea7 }, /* 0x14986231 */ + { XFER_MW_DMA_2, 0x16814ea7 }, /* 0x26514e21 */ + { XFER_MW_DMA_1, 0x16814ea7 }, /* 0x26514e97 */ + { XFER_MW_DMA_0, 0x16814ea7 }, /* 0x26514e97 */ + { XFER_PIO_4, 0x06814ea7 }, /* 0x06514e21 */ + { XFER_PIO_3, 0x06814ea7 }, /* 0x06514e22 */ + { XFER_PIO_2, 0x06814ea7 }, /* 0x06514e33 */ + { XFER_PIO_1, 0x06814ea7 }, /* 0x06914e43 */ + { XFER_PIO_0, 0x06814ea7 }, /* 0x06914e57 */ + { 0, 0x06814ea7 } +}; +#endif + +#define HPT366_DEBUG_DRIVE_INFO 0 +#define HPT374_ALLOW_ATA133_6 0 +#define HPT371_ALLOW_ATA133_6 0 +#define HPT302_ALLOW_ATA133_6 0 +#define HPT372_ALLOW_ATA133_6 1 +#define HPT370_ALLOW_ATA100_5 1 +#define HPT366_ALLOW_ATA66_4 1 +#define HPT366_ALLOW_ATA66_3 1 +#define HPT366_MAX_DEVS 8 + +#define F_LOW_PCI_33 0x23 +#define F_LOW_PCI_40 0x29 +#define F_LOW_PCI_50 0x2d +#define F_LOW_PCI_66 0x42 + +/* FIXME: compare with driver's code before removing */ +#if 0 + if (hpt_minimum_revision(dev, 3)) { + u8 cbl; + cbl = inb(iobase + 0x7b); + outb(cbl | 1, iobase + 0x7b); + outb(cbl & ~1, iobase + 0x7b); + cbl = inb(iobase + 0x7a); + p += sprintf(p, "Cable: ATA-%d" + " ATA-%d\n", + (cbl & 0x02) ? 33 : 66, + (cbl & 0x01) ? 33 : 66); + p += sprintf(p, "\n"); + } + { + u8 c2, c3; + /* older revs don't have these registers mapped + * into io space */ + pci_read_config_byte(dev, 0x43, &c0); + pci_read_config_byte(dev, 0x47, &c1); + pci_read_config_byte(dev, 0x4b, &c2); + pci_read_config_byte(dev, 0x4f, &c3); + + p += sprintf(p, "Mode: %s %s" + " %s %s\n", + (c0 & 0x10) ? "UDMA" : (c0 & 0x20) ? "DMA " : + (c0 & 0x80) ? "PIO " : "off ", + (c1 & 0x10) ? "UDMA" : (c1 & 0x20) ? "DMA " : + (c1 & 0x80) ? "PIO " : "off ", + (c2 & 0x10) ? "UDMA" : (c2 & 0x20) ? "DMA " : + (c2 & 0x80) ? "PIO " : "off ", + (c3 & 0x10) ? "UDMA" : (c3 & 0x20) ? "DMA " : + (c3 & 0x80) ? "PIO " : "off "); + } + } +#endif + +static u32 hpt_revision (struct pci_dev *dev) +{ + u32 class_rev; + pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev); + class_rev &= 0xff; + + switch(dev->device) { + /* Remap new 372N onto 372 */ + case PCI_DEVICE_ID_TTI_HPT372N: + class_rev = PCI_DEVICE_ID_TTI_HPT372; break; + case PCI_DEVICE_ID_TTI_HPT374: + class_rev = PCI_DEVICE_ID_TTI_HPT374; break; + case PCI_DEVICE_ID_TTI_HPT371: + class_rev = PCI_DEVICE_ID_TTI_HPT371; break; + case PCI_DEVICE_ID_TTI_HPT302: + class_rev = PCI_DEVICE_ID_TTI_HPT302; break; + case PCI_DEVICE_ID_TTI_HPT372: + class_rev = PCI_DEVICE_ID_TTI_HPT372; break; + default: + break; + } + return class_rev; +} + +static u32 hpt_minimum_revision (struct pci_dev *dev, int revision) +{ + unsigned int class_rev = hpt_revision(dev); + revision--; + return ((int) (class_rev > revision) ? 1 : 0); +} + +static int check_in_drive_lists(ide_drive_t *drive, const char **list); + +static u8 hpt3xx_ratemask (ide_drive_t *drive) +{ + struct pci_dev *dev = HWIF(drive)->pci_dev; + u8 mode = 0; + + if (hpt_minimum_revision(dev, 8)) { /* HPT374 */ + mode = (HPT374_ALLOW_ATA133_6) ? 4 : 3; + } else if (hpt_minimum_revision(dev, 7)) { /* HPT371 */ + mode = (HPT371_ALLOW_ATA133_6) ? 4 : 3; + } else if (hpt_minimum_revision(dev, 6)) { /* HPT302 */ + mode = (HPT302_ALLOW_ATA133_6) ? 4 : 3; + } else if (hpt_minimum_revision(dev, 5)) { /* HPT372 */ + mode = (HPT372_ALLOW_ATA133_6) ? 4 : 3; + } else if (hpt_minimum_revision(dev, 4)) { /* HPT370A */ + mode = (HPT370_ALLOW_ATA100_5) ? 3 : 2; + } else if (hpt_minimum_revision(dev, 3)) { /* HPT370 */ + mode = (HPT370_ALLOW_ATA100_5) ? 3 : 2; + mode = (check_in_drive_lists(drive, bad_ata33)) ? 0 : mode; + } else { /* HPT366 and HPT368 */ + mode = (check_in_drive_lists(drive, bad_ata33)) ? 0 : 2; + } + if (!eighty_ninty_three(drive) && (mode)) + mode = min(mode, (u8)1); + return mode; +} + +/* + * Note for the future; the SATA hpt37x we must set + * either PIO or UDMA modes 0,4,5 + */ + +static u8 hpt3xx_ratefilter (ide_drive_t *drive, u8 speed) +{ + struct pci_dev *dev = HWIF(drive)->pci_dev; + u8 mode = hpt3xx_ratemask(drive); + + if (drive->media != ide_disk) + return min(speed, (u8)XFER_PIO_4); + + switch(mode) { + case 0x04: + speed = min(speed, (u8)XFER_UDMA_6); + break; + case 0x03: + speed = min(speed, (u8)XFER_UDMA_5); + if (hpt_minimum_revision(dev, 5)) + break; + if (check_in_drive_lists(drive, bad_ata100_5)) + speed = min(speed, (u8)XFER_UDMA_4); + break; + case 0x02: + speed = min(speed, (u8)XFER_UDMA_4); + /* + * CHECK ME, Does this need to be set to 5 ?? + */ + if (hpt_minimum_revision(dev, 3)) + break; + if ((check_in_drive_lists(drive, bad_ata66_4)) || + (!(HPT366_ALLOW_ATA66_4))) + speed = min(speed, (u8)XFER_UDMA_3); + if ((check_in_drive_lists(drive, bad_ata66_3)) || + (!(HPT366_ALLOW_ATA66_3))) + speed = min(speed, (u8)XFER_UDMA_2); + break; + case 0x01: + speed = min(speed, (u8)XFER_UDMA_2); + /* + * CHECK ME, Does this need to be set to 5 ?? + */ + if (hpt_minimum_revision(dev, 3)) + break; + if (check_in_drive_lists(drive, bad_ata33)) + speed = min(speed, (u8)XFER_MW_DMA_2); + break; + case 0x00: + default: + speed = min(speed, (u8)XFER_MW_DMA_2); + break; + } + return speed; +} + +static int check_in_drive_lists (ide_drive_t *drive, const char **list) +{ + struct hd_driveid *id = drive->id; + + if (quirk_drives == list) { + while (*list) + if (strstr(id->model, *list++)) + return 1; + } else { + while (*list) + if (!strcmp(*list++,id->model)) + return 1; + } + return 0; +} + +static unsigned int pci_bus_clock_list (u8 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 chipset_table->chipset_settings; +} + +static int hpt36x_tune_chipset(ide_drive_t *drive, u8 xferspeed) +{ + struct pci_dev *dev = HWIF(drive)->pci_dev; + u8 speed = hpt3xx_ratefilter(drive, xferspeed); +// u8 speed = ide_rate_filter(hpt3xx_ratemask(drive), xferspeed); + u8 regtime = (drive->select.b.unit & 0x01) ? 0x44 : 0x40; + u8 regfast = (HWIF(drive)->channel) ? 0x55 : 0x51; + u8 drive_fast = 0; + u32 reg1 = 0, reg2 = 0; + + /* + * Disable the "fast interrupt" prediction. + */ + pci_read_config_byte(dev, regfast, &drive_fast); +#if 0 + if (drive_fast & 0x02) + pci_write_config_byte(dev, regfast, drive_fast & ~0x20); +#else + if (drive_fast & 0x80) + pci_write_config_byte(dev, regfast, drive_fast & ~0x80); +#endif + + reg2 = pci_bus_clock_list(speed, + (struct chipset_bus_clock_list_entry *) pci_get_drvdata(dev)); + /* + * Disable on-chip PIO FIFO/buffer + * (to avoid problems handling I/O errors later) + */ + pci_read_config_dword(dev, regtime, ®1); + if (speed >= XFER_MW_DMA_0) { + reg2 = (reg2 & ~0xc0000000) | (reg1 & 0xc0000000); + } else { + reg2 = (reg2 & ~0x30070000) | (reg1 & 0x30070000); + } + reg2 &= ~0x80000000; + + pci_write_config_dword(dev, regtime, reg2); + + return ide_config_drive_speed(drive, speed); +} + +static int hpt370_tune_chipset(ide_drive_t *drive, u8 xferspeed) +{ + struct pci_dev *dev = HWIF(drive)->pci_dev; + u8 speed = hpt3xx_ratefilter(drive, xferspeed); +// u8 speed = ide_rate_filter(hpt3xx_ratemask(drive), xferspeed); + u8 regfast = (HWIF(drive)->channel) ? 0x55 : 0x51; + u8 drive_pci = 0x40 + (drive->dn * 4); + u8 new_fast = 0, drive_fast = 0; + u32 list_conf = 0, drive_conf = 0; + u32 conf_mask = (speed >= XFER_MW_DMA_0) ? 0xc0000000 : 0x30070000; + + /* + * Disable the "fast interrupt" prediction. + * don't holdoff on interrupts. (== 0x01 despite what the docs say) + */ + pci_read_config_byte(dev, regfast, &drive_fast); + new_fast = drive_fast; + if (new_fast & 0x02) + new_fast &= ~0x02; + +#ifdef HPT_DELAY_INTERRUPT + if (new_fast & 0x01) + new_fast &= ~0x01; +#else + if ((new_fast & 0x01) == 0) + new_fast |= 0x01; +#endif + if (new_fast != drive_fast) + pci_write_config_byte(dev, regfast, new_fast); + + list_conf = pci_bus_clock_list(speed, + (struct chipset_bus_clock_list_entry *) + pci_get_drvdata(dev)); + + pci_read_config_dword(dev, drive_pci, &drive_conf); + list_conf = (list_conf & ~conf_mask) | (drive_conf & conf_mask); + + if (speed < XFER_MW_DMA_0) { + list_conf &= ~0x80000000; /* Disable on-chip PIO FIFO/buffer */ + } + + pci_write_config_dword(dev, drive_pci, list_conf); + + return ide_config_drive_speed(drive, speed); +} + +static int hpt372_tune_chipset(ide_drive_t *drive, u8 xferspeed) +{ + struct pci_dev *dev = HWIF(drive)->pci_dev; + u8 speed = hpt3xx_ratefilter(drive, xferspeed); +// u8 speed = ide_rate_filter(hpt3xx_ratemask(drive), xferspeed); + u8 regfast = (HWIF(drive)->channel) ? 0x55 : 0x51; + u8 drive_fast = 0, drive_pci = 0x40 + (drive->dn * 4); + u32 list_conf = 0, drive_conf = 0; + u32 conf_mask = (speed >= XFER_MW_DMA_0) ? 0xc0000000 : 0x30070000; + + /* + * Disable the "fast interrupt" prediction. + * don't holdoff on interrupts. (== 0x01 despite what the docs say) + */ + pci_read_config_byte(dev, regfast, &drive_fast); + drive_fast &= ~0x07; + pci_write_config_byte(dev, regfast, drive_fast); + + list_conf = pci_bus_clock_list(speed, + (struct chipset_bus_clock_list_entry *) + pci_get_drvdata(dev)); + pci_read_config_dword(dev, drive_pci, &drive_conf); + list_conf = (list_conf & ~conf_mask) | (drive_conf & conf_mask); + if (speed < XFER_MW_DMA_0) + list_conf &= ~0x80000000; /* Disable on-chip PIO FIFO/buffer */ + pci_write_config_dword(dev, drive_pci, list_conf); + + return ide_config_drive_speed(drive, speed); +} + +static int hpt3xx_tune_chipset (ide_drive_t *drive, u8 speed) +{ + struct pci_dev *dev = HWIF(drive)->pci_dev; + + if (hpt_minimum_revision(dev, 8)) + return hpt372_tune_chipset(drive, speed); /* not a typo */ +#if 0 + else if (hpt_minimum_revision(dev, 7)) + hpt371_tune_chipset(drive, speed); + else if (hpt_minimum_revision(dev, 6)) + hpt302_tune_chipset(drive, speed); +#endif + else if (hpt_minimum_revision(dev, 5)) + return hpt372_tune_chipset(drive, speed); + else if (hpt_minimum_revision(dev, 3)) + return hpt370_tune_chipset(drive, speed); + else /* hpt368: hpt_minimum_revision(dev, 2) */ + return hpt36x_tune_chipset(drive, speed); +} + +static void hpt3xx_tune_drive (ide_drive_t *drive, u8 pio) +{ + pio = ide_get_best_pio_mode(drive, 255, pio, NULL); + (void) hpt3xx_tune_chipset(drive, (XFER_PIO_0 + pio)); +} + +/* + * 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. Initially 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) +{ + u8 speed = ide_dma_speed(drive, hpt3xx_ratemask(drive)); + + if (!(speed)) + return 0; + + (void) hpt3xx_tune_chipset(drive, speed); + return ide_dma_enable(drive); +} + +static int hpt3xx_quirkproc (ide_drive_t *drive) +{ + return ((int) check_in_drive_lists(drive, quirk_drives)); +} + +static void hpt3xx_intrproc (ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + + if (drive->quirk_list) + return; + /* drives in the quirk_list may not like intr setups/cleanups */ + hwif->OUTB(drive->ctl|2, IDE_CONTROL_REG); +} + +static void hpt3xx_maskproc (ide_drive_t *drive, int mask) +{ + struct pci_dev *dev = HWIF(drive)->pci_dev; + + if (drive->quirk_list) { + if (hpt_minimum_revision(dev,3)) { + u8 reg5a = 0; + pci_read_config_byte(dev, 0x5a, ®5a); + if (((reg5a & 0x10) >> 4) != mask) + pci_write_config_byte(dev, 0x5a, mask ? (reg5a | 0x10) : (reg5a & ~0x10)); + } else { + if (mask) { + disable_irq(HWIF(drive)->irq); + } else { + enable_irq(HWIF(drive)->irq); + } + } + } else { + if (IDE_CONTROL_REG) + HWIF(drive)->OUTB(mask ? (drive->ctl | 2) : + (drive->ctl & ~2), + IDE_CONTROL_REG); + } +} + +static int hpt366_config_drive_xfer_rate (ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + struct hd_driveid *id = drive->id; + + drive->init_speed = 0; + + if (id && (id->capability & 1) && drive->autodma) { + + if (ide_use_dma(drive)) { + if (config_chipset_for_dma(drive)) + return hwif->ide_dma_on(drive); + } + + goto fast_ata_pio; + + } else if ((id->capability & 8) || (id->field_valid & 2)) { +fast_ata_pio: + hpt3xx_tune_drive(drive, 5); + return hwif->ide_dma_off_quietly(drive); + } + /* IORDY not supported */ + return 0; +} + +/* + * This is specific to the HPT366 UDMA bios chipset + * by HighPoint|Triones Technologies, Inc. + */ +static int hpt366_ide_dma_lostirq (ide_drive_t *drive) +{ + struct pci_dev *dev = HWIF(drive)->pci_dev; + u8 reg50h = 0, reg52h = 0, reg5ah = 0; + + pci_read_config_byte(dev, 0x50, ®50h); + pci_read_config_byte(dev, 0x52, ®52h); + pci_read_config_byte(dev, 0x5a, ®5ah); + printk("%s: (%s) reg50h=0x%02x, reg52h=0x%02x, reg5ah=0x%02x\n", + drive->name, __FUNCTION__, reg50h, reg52h, reg5ah); + if (reg5ah & 0x10) + pci_write_config_byte(dev, 0x5a, reg5ah & ~0x10); +#if 0 + /* how about we flush and reset, mmmkay? */ + pci_write_config_byte(dev, 0x51, 0x1F); + /* fall through to a reset */ + case dma_start: + case ide_dma_end: + /* reset the chips state over and over.. */ + pci_write_config_byte(dev, 0x51, 0x13); +#endif + return __ide_dma_lostirq(drive); +} + +static void hpt370_clear_engine (ide_drive_t *drive) +{ + u8 regstate = HWIF(drive)->channel ? 0x54 : 0x50; + pci_write_config_byte(HWIF(drive)->pci_dev, regstate, 0x37); + udelay(10); +} + +static void hpt370_ide_dma_start(ide_drive_t *drive) +{ +#ifdef HPT_RESET_STATE_ENGINE + hpt370_clear_engine(drive); +#endif + ide_dma_start(drive); +} + +static int hpt370_ide_dma_end (ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + u8 dma_stat = hwif->INB(hwif->dma_status); + + if (dma_stat & 0x01) { + /* wait a little */ + udelay(20); + dma_stat = hwif->INB(hwif->dma_status); + } + if ((dma_stat & 0x01) != 0) + /* fallthrough */ + (void) HWIF(drive)->ide_dma_timeout(drive); + + return __ide_dma_end(drive); +} + +static void hpt370_lostirq_timeout (ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + u8 bfifo = 0, reginfo = hwif->channel ? 0x56 : 0x52; + u8 dma_stat = 0, dma_cmd = 0; + + pci_read_config_byte(HWIF(drive)->pci_dev, reginfo, &bfifo); + printk("%s: %d bytes in FIFO\n", drive->name, bfifo); + hpt370_clear_engine(drive); + /* get dma command mode */ + dma_cmd = hwif->INB(hwif->dma_command); + /* stop dma */ + hwif->OUTB(dma_cmd & ~0x1, hwif->dma_command); + dma_stat = hwif->INB(hwif->dma_status); + /* clear errors */ + hwif->OUTB(dma_stat | 0x6, hwif->dma_status); +} + +static int hpt370_ide_dma_timeout (ide_drive_t *drive) +{ + hpt370_lostirq_timeout(drive); + hpt370_clear_engine(drive); + return __ide_dma_timeout(drive); +} + +static int hpt370_ide_dma_lostirq (ide_drive_t *drive) +{ + hpt370_lostirq_timeout(drive); + hpt370_clear_engine(drive); + return __ide_dma_lostirq(drive); +} + +/* returns 1 if DMA IRQ issued, 0 otherwise */ +static int hpt374_ide_dma_test_irq(ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + u16 bfifo = 0; + u8 reginfo = hwif->channel ? 0x56 : 0x52; + u8 dma_stat; + + pci_read_config_word(hwif->pci_dev, reginfo, &bfifo); + if (bfifo & 0x1FF) { +// printk("%s: %d bytes in FIFO\n", drive->name, bfifo); + return 0; + } + + dma_stat = hwif->INB(hwif->dma_status); + /* return 1 if INTR asserted */ + if ((dma_stat & 4) == 4) + return 1; + + if (!drive->waiting_for_dma) + printk(KERN_WARNING "%s: (%s) called while not waiting\n", + drive->name, __FUNCTION__); + return 0; +} + +static int hpt374_ide_dma_end (ide_drive_t *drive) +{ + struct pci_dev *dev = HWIF(drive)->pci_dev; + ide_hwif_t *hwif = HWIF(drive); + u8 msc_stat = 0, mscreg = hwif->channel ? 0x54 : 0x50; + u8 bwsr_stat = 0, bwsr_mask = hwif->channel ? 0x02 : 0x01; + + pci_read_config_byte(dev, 0x6a, &bwsr_stat); + pci_read_config_byte(dev, mscreg, &msc_stat); + if ((bwsr_stat & bwsr_mask) == bwsr_mask) + pci_write_config_byte(dev, mscreg, msc_stat|0x30); + return __ide_dma_end(drive); +} + +/** + * hpt372n_set_clock - perform clock switching dance + * @drive: Drive to switch + * @mode: Switching mode (0x21 for write, 0x23 otherwise) + * + * Switch the DPLL clock on the HPT372N devices. This is a + * right mess. + */ + +static void hpt372n_set_clock(ide_drive_t *drive, int mode) +{ + ide_hwif_t *hwif = HWIF(drive); + + /* FIXME: should we check for DMA active and BUG() */ + /* Tristate the bus */ + outb(0x80, hwif->dma_base+0x73); + outb(0x80, hwif->dma_base+0x77); + + /* Switch clock and reset channels */ + outb(mode, hwif->dma_base+0x7B); + outb(0xC0, hwif->dma_base+0x79); + + /* Reset state machines */ + outb(0x37, hwif->dma_base+0x70); + outb(0x37, hwif->dma_base+0x74); + + /* Complete reset */ + outb(0x00, hwif->dma_base+0x79); + + /* Reconnect channels to bus */ + outb(0x00, hwif->dma_base+0x73); + outb(0x00, hwif->dma_base+0x77); +} + +/** + * hpt372n_rw_disk - prepare for I/O + * @drive: drive for command + * @rq: block request structure + * + * This is called when a disk I/O is issued to the 372N. + * We need it because of the clock switching. + */ + +static void hpt372n_rw_disk(ide_drive_t *drive, struct request *rq) +{ + ide_hwif_t *hwif = drive->hwif; + int wantclock; + + wantclock = rq_data_dir(rq) ? 0x23 : 0x21; + + if (hwif->config_data != wantclock) { + hpt372n_set_clock(drive, wantclock); + hwif->config_data = wantclock; + } +} + +/* + * Since SUN Cobalt is attempting to do this operation, I should disclose + * this has been a long time ago Thu Jul 27 16:40:57 2000 was the patch date + * HOTSWAP ATA Infrastructure. + */ + +static void hpt3xx_reset (ide_drive_t *drive) +{ +#if 0 + unsigned long high_16 = pci_resource_start(HWIF(drive)->pci_dev, 4); + u8 reset = (HWIF(drive)->channel) ? 0x80 : 0x40; + u8 reg59h = 0; + + pci_read_config_byte(HWIF(drive)->pci_dev, 0x59, ®59h); + pci_write_config_byte(HWIF(drive)->pci_dev, 0x59, reg59h|reset); + pci_write_config_byte(HWIF(drive)->pci_dev, 0x59, reg59h); +#endif +} + +static int hpt3xx_tristate (ide_drive_t * drive, int state) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + u8 reg59h = 0, reset = (hwif->channel) ? 0x80 : 0x40; + u8 regXXh = 0, state_reg= (hwif->channel) ? 0x57 : 0x53; + +// hwif->bus_state = state; + + pci_read_config_byte(dev, 0x59, ®59h); + pci_read_config_byte(dev, state_reg, ®XXh); + + if (state) { + (void) ide_do_reset(drive); + pci_write_config_byte(dev, state_reg, regXXh|0x80); + pci_write_config_byte(dev, 0x59, reg59h|reset); + } else { + pci_write_config_byte(dev, 0x59, reg59h & ~(reset)); + pci_write_config_byte(dev, state_reg, regXXh & ~(0x80)); + (void) ide_do_reset(drive); + } + return 0; +} + +/* + * set/get power state for a drive. + * turning the power off does the following things: + * 1) soft-reset the drive + * 2) tri-states the ide bus + * + * when we turn things back on, we need to re-initialize things. + */ +#define TRISTATE_BIT 0x8000 +static int hpt370_busproc(ide_drive_t * drive, int state) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + u8 tristate = 0, resetmask = 0, bus_reg = 0; + u16 tri_reg; + + hwif->bus_state = state; + + if (hwif->channel) { + /* secondary channel */ + tristate = 0x56; + resetmask = 0x80; + } else { + /* primary channel */ + tristate = 0x52; + resetmask = 0x40; + } + + /* grab status */ + pci_read_config_word(dev, tristate, &tri_reg); + pci_read_config_byte(dev, 0x59, &bus_reg); + + /* set the state. we don't set it if we don't need to do so. + * make sure that the drive knows that it has failed if it's off */ + switch (state) { + case BUSSTATE_ON: + hwif->drives[0].failures = 0; + hwif->drives[1].failures = 0; + if ((bus_reg & resetmask) == 0) + return 0; + tri_reg &= ~TRISTATE_BIT; + bus_reg &= ~resetmask; + break; + case BUSSTATE_OFF: + hwif->drives[0].failures = hwif->drives[0].max_failures + 1; + hwif->drives[1].failures = hwif->drives[1].max_failures + 1; + if ((tri_reg & TRISTATE_BIT) == 0 && (bus_reg & resetmask)) + return 0; + tri_reg &= ~TRISTATE_BIT; + bus_reg |= resetmask; + break; + case BUSSTATE_TRISTATE: + hwif->drives[0].failures = hwif->drives[0].max_failures + 1; + hwif->drives[1].failures = hwif->drives[1].max_failures + 1; + if ((tri_reg & TRISTATE_BIT) && (bus_reg & resetmask)) + return 0; + tri_reg |= TRISTATE_BIT; + bus_reg |= resetmask; + break; + } + pci_write_config_byte(dev, 0x59, bus_reg); + pci_write_config_word(dev, tristate, tri_reg); + + return 0; +} + +static int __devinit init_hpt37x(struct pci_dev *dev) +{ + int adjust, i; + u16 freq; + u32 pll; + u8 reg5bh; + u8 reg5ah = 0; + unsigned long dmabase = pci_resource_start(dev, 4); + u8 did, rid; + int is_372n = 0; + + pci_read_config_byte(dev, 0x5a, ®5ah); + /* interrupt force enable */ + pci_write_config_byte(dev, 0x5a, (reg5ah & ~0x10)); + + if(dmabase) + { + did = inb(dmabase + 0x22); + rid = inb(dmabase + 0x28); + + if((did == 4 && rid == 6) || (did == 5 && rid > 1)) + is_372n = 1; + } + + /* + * default to pci clock. make sure MA15/16 are set to output + * to prevent drives having problems with 40-pin cables. + */ + pci_write_config_byte(dev, 0x5b, 0x23); + + /* + * set up the PLL. we need to adjust it so that it's stable. + * freq = Tpll * 192 / Tpci + * + * Todo. For non x86 should probably check the dword is + * set to 0xABCDExxx indicating the BIOS saved f_CNT + */ + pci_read_config_word(dev, 0x78, &freq); + freq &= 0x1FF; + + /* + * The 372N uses different PCI clock information and has + * some other complications + * On PCI33 timing we must clock switch + * On PCI66 timing we must NOT use the PCI clock + * + * Currently we always set up the PLL for the 372N + */ + + pci_set_drvdata(dev, NULL); + + if(is_372n) + { + printk(KERN_INFO "hpt: HPT372N detected, using 372N timing.\n"); + if(freq < 0x55) + pll = F_LOW_PCI_33; + else if(freq < 0x70) + pll = F_LOW_PCI_40; + else if(freq < 0x7F) + pll = F_LOW_PCI_50; + else + pll = F_LOW_PCI_66; + + printk(KERN_INFO "FREQ: %d PLL: %d\n", freq, pll); + + /* We always use the pll not the PCI clock on 372N */ + } + else + { + if(freq < 0x9C) + pll = F_LOW_PCI_33; + else if(freq < 0xb0) + pll = F_LOW_PCI_40; + else if(freq <0xc8) + pll = F_LOW_PCI_50; + else + pll = F_LOW_PCI_66; + + if (pll == F_LOW_PCI_33) { + if (hpt_minimum_revision(dev,8)) + pci_set_drvdata(dev, (void *) thirty_three_base_hpt374); + else if (hpt_minimum_revision(dev,5)) + pci_set_drvdata(dev, (void *) thirty_three_base_hpt372); + else if (hpt_minimum_revision(dev,4)) + pci_set_drvdata(dev, (void *) thirty_three_base_hpt370a); + else + pci_set_drvdata(dev, (void *) thirty_three_base_hpt370); + printk("HPT37X: using 33MHz PCI clock\n"); + } else if (pll == F_LOW_PCI_40) { + /* Unsupported */ + } else if (pll == F_LOW_PCI_50) { + if (hpt_minimum_revision(dev,8)) + pci_set_drvdata(dev, (void *) fifty_base_hpt370a); + else if (hpt_minimum_revision(dev,5)) + pci_set_drvdata(dev, (void *) fifty_base_hpt372); + else if (hpt_minimum_revision(dev,4)) + pci_set_drvdata(dev, (void *) fifty_base_hpt370a); + else + pci_set_drvdata(dev, (void *) fifty_base_hpt370a); + printk("HPT37X: using 50MHz PCI clock\n"); + } else { + if (hpt_minimum_revision(dev,8)) + { + printk(KERN_ERR "HPT37x: 66MHz timings are not supported.\n"); + } + else if (hpt_minimum_revision(dev,5)) + pci_set_drvdata(dev, (void *) sixty_six_base_hpt372); + else if (hpt_minimum_revision(dev,4)) + pci_set_drvdata(dev, (void *) sixty_six_base_hpt370a); + else + pci_set_drvdata(dev, (void *) sixty_six_base_hpt370); + printk("HPT37X: using 66MHz PCI clock\n"); + } + } + + /* + * only try the pll if we don't have a table for the clock + * speed that we're running at. NOTE: the internal PLL will + * result in slow reads when using a 33MHz PCI clock. we also + * don't like to use the PLL because it will cause glitches + * on PRST/SRST when the HPT state engine gets reset. + */ + if (pci_get_drvdata(dev)) + goto init_hpt37X_done; + + /* + * adjust PLL based upon PCI clock, enable it, and wait for + * stabilization. + */ + adjust = 0; + freq = (pll < F_LOW_PCI_50) ? 2 : 4; + while (adjust++ < 6) { + pci_write_config_dword(dev, 0x5c, (freq + pll) << 16 | + pll | 0x100); + + /* wait for clock stabilization */ + for (i = 0; i < 0x50000; i++) { + pci_read_config_byte(dev, 0x5b, ®5bh); + if (reg5bh & 0x80) { + /* spin looking for the clock to destabilize */ + for (i = 0; i < 0x1000; ++i) { + pci_read_config_byte(dev, 0x5b, + ®5bh); + if ((reg5bh & 0x80) == 0) + goto pll_recal; + } + pci_read_config_dword(dev, 0x5c, &pll); + pci_write_config_dword(dev, 0x5c, + pll & ~0x100); + pci_write_config_byte(dev, 0x5b, 0x21); + if (hpt_minimum_revision(dev,8)) + pci_set_drvdata(dev, (void *) fifty_base_hpt370a); + else if (hpt_minimum_revision(dev,5)) + pci_set_drvdata(dev, (void *) fifty_base_hpt372); + else if (hpt_minimum_revision(dev,4)) + pci_set_drvdata(dev, (void *) fifty_base_hpt370a); + else + pci_set_drvdata(dev, (void *) fifty_base_hpt370a); + printk("HPT37X: using 50MHz internal PLL\n"); + goto init_hpt37X_done; + } + } +pll_recal: + if (adjust & 1) + pll -= (adjust >> 1); + else + pll += (adjust >> 1); + } + +init_hpt37X_done: + /* reset state engine */ + pci_write_config_byte(dev, 0x50, 0x37); + pci_write_config_byte(dev, 0x54, 0x37); + udelay(100); + return 0; +} + +static int __devinit init_hpt366(struct pci_dev *dev) +{ + u32 reg1 = 0; + u8 drive_fast = 0; + + /* + * Disable the "fast interrupt" prediction. + */ + pci_read_config_byte(dev, 0x51, &drive_fast); + if (drive_fast & 0x80) + pci_write_config_byte(dev, 0x51, drive_fast & ~0x80); + pci_read_config_dword(dev, 0x40, ®1); + + /* detect bus speed by looking at control reg timing: */ + switch((reg1 >> 8) & 7) { + case 5: + pci_set_drvdata(dev, (void *) forty_base_hpt366); + break; + case 9: + pci_set_drvdata(dev, (void *) twenty_five_base_hpt366); + break; + case 7: + default: + pci_set_drvdata(dev, (void *) thirty_three_base_hpt366); + break; + } + + if (!pci_get_drvdata(dev)) + { + printk(KERN_ERR "hpt366: unknown bus timing.\n"); + pci_set_drvdata(dev, NULL); + } + return 0; +} + +static unsigned int __devinit init_chipset_hpt366(struct pci_dev *dev, const char *name) +{ + int ret = 0; + u8 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 != (L1_CACHE_BYTES / 4)) + pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, + (L1_CACHE_BYTES / 4)); + + 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 (hpt_minimum_revision(dev, 3)) { + ret = init_hpt37x(dev); + } else { + ret =init_hpt366(dev); + } + if (ret) + return ret; + + return dev->irq; +} + +static void __devinit init_hwif_hpt366(ide_hwif_t *hwif) +{ + struct pci_dev *dev = hwif->pci_dev; + u8 ata66 = 0, regmask = (hwif->channel) ? 0x01 : 0x02; + u8 did, rid; + unsigned long dmabase = hwif->dma_base; + int is_372n = 0; + + if(dmabase) + { + did = inb(dmabase + 0x22); + rid = inb(dmabase + 0x28); + + if((did == 4 && rid == 6) || (did == 5 && rid > 1)) + is_372n = 1; + } + + hwif->tuneproc = &hpt3xx_tune_drive; + hwif->speedproc = &hpt3xx_tune_chipset; + hwif->quirkproc = &hpt3xx_quirkproc; + hwif->intrproc = &hpt3xx_intrproc; + hwif->maskproc = &hpt3xx_maskproc; + + if(is_372n) + hwif->rw_disk = &hpt372n_rw_disk; + + /* + * The HPT37x uses the CBLID pins as outputs for MA15/MA16 + * address lines to access an external eeprom. To read valid + * cable detect state the pins must be enabled as inputs. + */ + if (hpt_minimum_revision(dev, 8) && PCI_FUNC(dev->devfn) & 1) { + /* + * HPT374 PCI function 1 + * - set bit 15 of reg 0x52 to enable TCBLID as input + * - set bit 15 of reg 0x56 to enable FCBLID as input + */ + u16 mcr3, mcr6; + pci_read_config_word(dev, 0x52, &mcr3); + pci_read_config_word(dev, 0x56, &mcr6); + pci_write_config_word(dev, 0x52, mcr3 | 0x8000); + pci_write_config_word(dev, 0x56, mcr6 | 0x8000); + /* now read cable id register */ + pci_read_config_byte(dev, 0x5a, &ata66); + pci_write_config_word(dev, 0x52, mcr3); + pci_write_config_word(dev, 0x56, mcr6); + } else if (hpt_minimum_revision(dev, 3)) { + /* + * HPT370/372 and 374 pcifn 0 + * - clear bit 0 of 0x5b to enable P/SCBLID as inputs + */ + u8 scr2; + pci_read_config_byte(dev, 0x5b, &scr2); + pci_write_config_byte(dev, 0x5b, scr2 & ~1); + /* now read cable id register */ + pci_read_config_byte(dev, 0x5a, &ata66); + pci_write_config_byte(dev, 0x5b, scr2); + } else { + pci_read_config_byte(dev, 0x5a, &ata66); + } + +#ifdef DEBUG + printk("HPT366: reg5ah=0x%02x ATA-%s Cable Port%d\n", + ata66, (ata66 & regmask) ? "33" : "66", + PCI_FUNC(hwif->pci_dev->devfn)); +#endif /* DEBUG */ + +#ifdef HPT_SERIALIZE_IO + /* serialize access to this device */ + if (hwif->mate) + hwif->serialized = hwif->mate->serialized = 1; +#endif + + if (hpt_minimum_revision(dev,3)) { + u8 reg5ah = 0; + pci_write_config_byte(dev, 0x5a, reg5ah & ~0x10); + /* + * set up ioctl for power status. + * note: power affects both + * drives on each channel + */ + hwif->resetproc = &hpt3xx_reset; + hwif->busproc = &hpt370_busproc; +// hwif->drives[0].autotune = hwif->drives[1].autotune = 1; + } else if (hpt_minimum_revision(dev,2)) { + hwif->resetproc = &hpt3xx_reset; + hwif->busproc = &hpt3xx_tristate; + } else { + hwif->resetproc = &hpt3xx_reset; + hwif->busproc = &hpt3xx_tristate; + } + + if (!hwif->dma_base) { + hwif->drives[0].autotune = 1; + hwif->drives[1].autotune = 1; + return; + } + + hwif->ultra_mask = 0x7f; + hwif->mwdma_mask = 0x07; + + if (!(hwif->udma_four)) + hwif->udma_four = ((ata66 & regmask) ? 0 : 1); + hwif->ide_dma_check = &hpt366_config_drive_xfer_rate; + + if (hpt_minimum_revision(dev,8)) { + hwif->ide_dma_test_irq = &hpt374_ide_dma_test_irq; + hwif->ide_dma_end = &hpt374_ide_dma_end; + } else if (hpt_minimum_revision(dev,5)) { + hwif->ide_dma_test_irq = &hpt374_ide_dma_test_irq; + hwif->ide_dma_end = &hpt374_ide_dma_end; + } else if (hpt_minimum_revision(dev,3)) { + hwif->dma_start = &hpt370_ide_dma_start; + hwif->ide_dma_end = &hpt370_ide_dma_end; + hwif->ide_dma_timeout = &hpt370_ide_dma_timeout; + hwif->ide_dma_lostirq = &hpt370_ide_dma_lostirq; + } else if (hpt_minimum_revision(dev,2)) + hwif->ide_dma_lostirq = &hpt366_ide_dma_lostirq; + else + hwif->ide_dma_lostirq = &hpt366_ide_dma_lostirq; + + if (!noautodma) + hwif->autodma = 1; + hwif->drives[0].autodma = hwif->autodma; + hwif->drives[1].autodma = hwif->autodma; +} + +static void __devinit init_dma_hpt366(ide_hwif_t *hwif, unsigned long dmabase) +{ + u8 masterdma = 0, slavedma = 0; + u8 dma_new = 0, dma_old = 0; + u8 primary = hwif->channel ? 0x4b : 0x43; + u8 secondary = hwif->channel ? 0x4f : 0x47; + unsigned long flags; + + if (!dmabase) + return; + + if(pci_get_drvdata(hwif->pci_dev) == NULL) + { + printk(KERN_WARNING "hpt: no known IDE timings, disabling DMA.\n"); + return; + } + + dma_old = hwif->INB(dmabase+2); + + local_irq_save(flags); + + dma_new = dma_old; + pci_read_config_byte(hwif->pci_dev, primary, &masterdma); + pci_read_config_byte(hwif->pci_dev, secondary, &slavedma); + + if (masterdma & 0x30) dma_new |= 0x20; + if (slavedma & 0x30) dma_new |= 0x40; + if (dma_new != dma_old) + hwif->OUTB(dma_new, dmabase+2); + + local_irq_restore(flags); + + ide_setup_dma(hwif, dmabase, 8); +} + +static int __devinit init_setup_hpt374(struct pci_dev *dev, ide_pci_device_t *d) +{ + struct pci_dev *findev = NULL; + + if (PCI_FUNC(dev->devfn) & 1) + return -ENODEV; + + while ((findev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, findev)) != NULL) { + if ((findev->vendor == dev->vendor) && + (findev->device == dev->device) && + ((findev->devfn - dev->devfn) == 1) && + (PCI_FUNC(findev->devfn) & 1)) { + if (findev->irq != dev->irq) { + /* FIXME: we need a core pci_set_interrupt() */ + findev->irq = dev->irq; + printk(KERN_WARNING "%s: pci-config space interrupt " + "fixed.\n", d->name); + } + return ide_setup_pci_devices(dev, findev, d); + } + } + return ide_setup_pci_device(dev, d); +} + +static int __devinit init_setup_hpt37x(struct pci_dev *dev, ide_pci_device_t *d) +{ + return ide_setup_pci_device(dev, d); +} + +static int __devinit init_setup_hpt366(struct pci_dev *dev, ide_pci_device_t *d) +{ + struct pci_dev *findev = NULL; + u8 pin1 = 0, pin2 = 0; + unsigned int class_rev; + char *chipset_names[] = {"HPT366", "HPT366", "HPT368", + "HPT370", "HPT370A", "HPT372", + "HPT372N" }; + + if (PCI_FUNC(dev->devfn) & 1) + return -ENODEV; + + pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev); + class_rev &= 0xff; + + if(dev->device == PCI_DEVICE_ID_TTI_HPT372N) + class_rev = 6; + + if(class_rev <= 6) + d->name = chipset_names[class_rev]; + + switch(class_rev) { + case 6: + case 5: + case 4: + case 3: + goto init_single; + default: + break; + } + + d->channels = 1; + + pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin1); + while ((findev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, findev)) != NULL) { + if ((findev->vendor == dev->vendor) && + (findev->device == dev->device) && + ((findev->devfn - dev->devfn) == 1) && + (PCI_FUNC(findev->devfn) & 1)) { + pci_read_config_byte(findev, PCI_INTERRUPT_PIN, &pin2); + if ((pin1 != pin2) && (dev->irq == findev->irq)) { + d->bootable = ON_BOARD; + printk("%s: onboard version of chipset, " + "pin1=%d pin2=%d\n", d->name, + pin1, pin2); + } + return ide_setup_pci_devices(dev, findev, d); + } + } +init_single: + return ide_setup_pci_device(dev, d); +} + +static ide_pci_device_t hpt366_chipsets[] __devinitdata = { + { /* 0 */ + .name = "HPT366", + .init_setup = init_setup_hpt366, + .init_chipset = init_chipset_hpt366, + .init_hwif = init_hwif_hpt366, + .init_dma = init_dma_hpt366, + .channels = 2, + .autodma = AUTODMA, + .bootable = OFF_BOARD, + .extra = 240 + },{ /* 1 */ + .name = "HPT372A", + .init_setup = init_setup_hpt37x, + .init_chipset = init_chipset_hpt366, + .init_hwif = init_hwif_hpt366, + .init_dma = init_dma_hpt366, + .channels = 2, + .autodma = AUTODMA, + .bootable = OFF_BOARD, + },{ /* 2 */ + .name = "HPT302", + .init_setup = init_setup_hpt37x, + .init_chipset = init_chipset_hpt366, + .init_hwif = init_hwif_hpt366, + .init_dma = init_dma_hpt366, + .channels = 2, + .autodma = AUTODMA, + .bootable = OFF_BOARD, + },{ /* 3 */ + .name = "HPT371", + .init_setup = init_setup_hpt37x, + .init_chipset = init_chipset_hpt366, + .init_hwif = init_hwif_hpt366, + .init_dma = init_dma_hpt366, + .channels = 2, + .autodma = AUTODMA, + .bootable = OFF_BOARD, + },{ /* 4 */ + .name = "HPT374", + .init_setup = init_setup_hpt374, + .init_chipset = init_chipset_hpt366, + .init_hwif = init_hwif_hpt366, + .init_dma = init_dma_hpt366, + .channels = 2, /* 4 */ + .autodma = AUTODMA, + .bootable = OFF_BOARD, + },{ /* 5 */ + .name = "HPT372N", + .init_setup = init_setup_hpt37x, + .init_chipset = init_chipset_hpt366, + .init_hwif = init_hwif_hpt366, + .init_dma = init_dma_hpt366, + .channels = 2, /* 4 */ + .autodma = AUTODMA, + .bootable = OFF_BOARD, + } +}; + +/** + * hpt366_init_one - called when an HPT366 is found + * @dev: the hpt366 device + * @id: the matching pci id + * + * Called when the PCI registration layer (or the IDE initialization) + * finds a device matching our IDE device tables. + */ + +static int __devinit hpt366_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + ide_pci_device_t *d = &hpt366_chipsets[id->driver_data]; + + return d->init_setup(dev, d); +} + +static struct pci_device_id hpt366_pci_tbl[] = { + { PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT366, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT372, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1}, + { PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT302, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2}, + { PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT371, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 3}, + { PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT374, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4}, + { PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT372N, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 5}, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, hpt366_pci_tbl); + +static struct pci_driver driver = { + .name = "HPT366_IDE", + .id_table = hpt366_pci_tbl, + .probe = hpt366_init_one, +}; + +static int hpt366_ide_init(void) +{ + return ide_pci_register_driver(&driver); +} + +module_init(hpt366_ide_init); + +MODULE_AUTHOR("Andre Hedrick"); +MODULE_DESCRIPTION("PCI driver module for Highpoint HPT366 IDE"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/pci/it8172.c b/drivers/ide/pci/it8172.c new file mode 100644 index 0000000..631927c --- /dev/null +++ b/drivers/ide/pci/it8172.c @@ -0,0 +1,308 @@ +/* + * + * BRIEF MODULE DESCRIPTION + * IT8172 IDE controller support + * + * Copyright 2000 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * stevel@mvista.com or source@mvista.com + * + * 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. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/config.h> +#include <linux/module.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 <linux/init.h> + +#include <asm/io.h> +#include <asm/it8172/it8172_int.h> + +/* + * Prototypes + */ +static u8 it8172_ratemask (ide_drive_t *drive) +{ + return 1; +} + +static void it8172_tune_drive (ide_drive_t *drive, u8 pio) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + int is_slave = (&hwif->drives[1] == drive); + unsigned long flags; + u16 drive_enables; + u32 drive_timing; + + pio = ide_get_best_pio_mode(drive, pio, 4, NULL); + spin_lock_irqsave(&ide_lock, flags); + pci_read_config_word(dev, 0x40, &drive_enables); + pci_read_config_dword(dev, 0x44, &drive_timing); + + /* + * FIX! The DIOR/DIOW pulse width and recovery times in port 0x44 + * are being left at the default values of 8 PCI clocks (242 nsec + * for a 33 MHz clock). These can be safely shortened at higher + * PIO modes. The DIOR/DIOW pulse width and recovery times only + * apply to PIO modes, not to the DMA modes. + */ + + /* + * Enable port 0x44. The IT8172G spec is confused; it calls + * this register the "Slave IDE Timing Register", but in fact, + * it controls timing for both master and slave drives. + */ + drive_enables |= 0x4000; + + if (is_slave) { + drive_enables &= 0xc006; + if (pio > 1) + /* enable prefetch and IORDY sample-point */ + drive_enables |= 0x0060; + } else { + drive_enables &= 0xc060; + if (pio > 1) + /* enable prefetch and IORDY sample-point */ + drive_enables |= 0x0006; + } + + pci_write_config_word(dev, 0x40, drive_enables); + spin_unlock_irqrestore(&ide_lock, flags); +} + +static u8 it8172_dma_2_pio (u8 xfer_rate) +{ + switch(xfer_rate) { + case XFER_UDMA_5: + 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; + } +} + +static int it8172_tune_chipset (ide_drive_t *drive, u8 xferspeed) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + u8 speed = ide_rate_filter(it8172_ratemask(drive), xferspeed); + int a_speed = 3 << (drive->dn * 4); + int u_flag = 1 << drive->dn; + int u_speed = 0; + u8 reg48, reg4a; + + pci_read_config_byte(dev, 0x48, ®48); + pci_read_config_byte(dev, 0x4a, ®4a); + + /* + * Setting the DMA cycle time to 2 or 3 PCI clocks (60 and 91 nsec + * at 33 MHz PCI clock) seems to cause BadCRC errors during DMA + * transfers on some drives, even though both numbers meet the minimum + * ATAPI-4 spec of 73 and 54 nsec for UDMA 1 and 2 respectively. + * So the faster times are just commented out here. The good news is + * that the slower cycle time has very little affect on transfer + * performance. + */ + + switch(speed) { + case XFER_UDMA_4: + case XFER_UDMA_2: //u_speed = 2 << (drive->dn * 4); break; + case XFER_UDMA_5: + case XFER_UDMA_3: + case XFER_UDMA_1: //u_speed = 1 << (drive->dn * 4); break; + case XFER_UDMA_0: u_speed = 0 << (drive->dn * 4); break; + case XFER_MW_DMA_2: + case XFER_MW_DMA_1: + case XFER_MW_DMA_0: + case XFER_SW_DMA_2: break; + case XFER_PIO_4: + case XFER_PIO_3: + case XFER_PIO_2: + case XFER_PIO_0: break; + default: return -1; + } + + if (speed >= XFER_UDMA_0) { + pci_write_config_byte(dev, 0x48, reg48 | u_flag); + reg4a &= ~a_speed; + pci_write_config_byte(dev, 0x4a, reg4a | u_speed); + } else { + pci_write_config_byte(dev, 0x48, reg48 & ~u_flag); + pci_write_config_byte(dev, 0x4a, reg4a & ~a_speed); + } + + it8172_tune_drive(drive, it8172_dma_2_pio(speed)); + return (ide_config_drive_speed(drive, speed)); +} + +static int it8172_config_chipset_for_dma (ide_drive_t *drive) +{ + u8 speed = ide_dma_speed(drive, it8172_ratemask(drive)); + + if (!(speed)) { + u8 tspeed = ide_get_best_pio_mode(drive, 255, 4, NULL); + speed = it8172_dma_2_pio(XFER_PIO_0 + tspeed); + } + + (void) it8172_tune_chipset(drive, speed); + return ide_dma_enable(drive); +} + +static int it8172_config_drive_xfer_rate (ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + struct hd_driveid *id = drive->id; + + drive->init_speed = 0; + + if (id && (id->capability & 1) && drive->autodma) { + + if (ide_use_dma(drive)) { + if (it8172_config_chipset_for_dma(drive)) + return hwif->ide_dma_on(drive); + } + + goto fast_ata_pio; + + } else if ((id->capability & 8) || (id->field_valid & 2)) { +fast_ata_pio: + it8172_tune_drive(drive, 5); + return hwif->ide_dma_off_quietly(drive); + } + /* IORDY not supported */ + return 0; +} + +static unsigned int __init init_chipset_it8172 (struct pci_dev *dev, const char *name) +{ + unsigned char progif; + + /* + * Place both IDE interfaces into PCI "native" mode + */ + pci_read_config_byte(dev, PCI_CLASS_PROG, &progif); + pci_write_config_byte(dev, PCI_CLASS_PROG, progif | 0x05); + + return IT8172_IDE_IRQ; +} + + +static void __init init_hwif_it8172 (ide_hwif_t *hwif) +{ + struct pci_dev* dev = hwif->pci_dev; + unsigned long cmdBase, ctrlBase; + + hwif->autodma = 0; + hwif->tuneproc = &it8172_tune_drive; + hwif->speedproc = &it8172_tune_chipset; + + cmdBase = dev->resource[0].start; + ctrlBase = dev->resource[1].start; + + ide_init_hwif_ports(&hwif->hw, cmdBase, ctrlBase | 2, NULL); + memcpy(hwif->io_ports, hwif->hw.io_ports, sizeof(hwif->io_ports)); + hwif->noprobe = 0; + + if (!hwif->dma_base) { + hwif->drives[0].autotune = 1; + hwif->drives[1].autotune = 1; + return; + } + + hwif->atapi_dma = 1; + hwif->ultra_mask = 0x07; + hwif->mwdma_mask = 0x06; + hwif->swdma_mask = 0x04; + + hwif->ide_dma_check = &it8172_config_drive_xfer_rate; + if (!noautodma) + hwif->autodma = 1; + hwif->drives[0].autodma = hwif->autodma; + hwif->drives[1].autodma = hwif->autodma; +} + +static ide_pci_device_t it8172_chipsets[] __devinitdata = { + { /* 0 */ + .name = "IT8172G", + .init_chipset = init_chipset_it8172, + .init_hwif = init_hwif_it8172, + .channels = 2, + .autodma = AUTODMA, + .enablebits = {{0x00,0x00,0x00}, {0x40,0x00,0x01}}, + .bootable = ON_BOARD, + } +}; + +static int __devinit it8172_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + if ((!(PCI_FUNC(dev->devfn) & 1) || + (!((dev->class >> 8) == PCI_CLASS_STORAGE_IDE)))) + return -ENODEV; /* IT8172 is more than an IDE controller */ + return ide_setup_pci_device(dev, &it8172_chipsets[id->driver_data]); +} + +static struct pci_device_id it8172_pci_tbl[] = { + { PCI_VENDOR_ID_ITE, PCI_DEVICE_ID_ITE_IT8172G, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, it8172_pci_tbl); + +static struct pci_driver driver = { + .name = "IT8172_IDE", + .id_table = it8172_pci_tbl, + .probe = it8172_init_one, +}; + +static int it8172_ide_init(void) +{ + return ide_pci_register_driver(&driver); +} + +module_init(it8172_ide_init); + +MODULE_AUTHOR("SteveL@mvista.com"); +MODULE_DESCRIPTION("PCI driver module for ITE 8172 IDE"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/pci/ns87415.c b/drivers/ide/pci/ns87415.c new file mode 100644 index 0000000..205a32f --- /dev/null +++ b/drivers/ide/pci/ns87415.c @@ -0,0 +1,315 @@ +/* + * linux/drivers/ide/pci/ns87415.c Version 2.00 Sep. 10, 2002 + * + * Copyright (C) 1997-1998 Mark Lord <mlord@pobox.com> + * Copyright (C) 1998 Eddie C. Dost <ecd@skynet.be> + * Copyright (C) 1999-2000 Andre Hedrick <andre@linux-ide.org> + * Copyright (C) 2004 Grant Grundler <grundler at parisc-linux.org> + * + * Inspired by an earlier effort from David S. Miller <davem@redhat.com> + */ + +#include <linux/config.h> +#include <linux/module.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/delay.h> +#include <linux/ide.h> +#include <linux/init.h> + +#include <asm/io.h> + +#ifdef CONFIG_SUPERIO +/* SUPERIO 87560 is a PoS chip that NatSem denies exists. + * Unfortunately, it's built-in on all Astro-based PA-RISC workstations + * which use the integrated NS87514 cell for CD-ROM support. + * i.e we have to support for CD-ROM installs. + * See drivers/parisc/superio.c for more gory details. + */ +#include <asm/superio.h> + +static unsigned long superio_ide_status[2]; +static unsigned long superio_ide_select[2]; +static unsigned long superio_ide_dma_status[2]; + +#define SUPERIO_IDE_MAX_RETRIES 25 + +/* Because of a defect in Super I/O, all reads of the PCI DMA status + * registers, IDE status register and the IDE select register need to be + * retried + */ +static u8 superio_ide_inb (unsigned long port) +{ + if (port == superio_ide_status[0] || + port == superio_ide_status[1] || + port == superio_ide_select[0] || + port == superio_ide_select[1] || + port == superio_ide_dma_status[0] || + port == superio_ide_dma_status[1]) { + u8 tmp; + int retries = SUPERIO_IDE_MAX_RETRIES; + + /* printk(" [ reading port 0x%x with retry ] ", port); */ + + do { + tmp = inb(port); + if (tmp == 0) + udelay(50); + } while (tmp == 0 && retries-- > 0); + + return tmp; + } + + return inb(port); +} + +static void __devinit superio_ide_init_iops (struct hwif_s *hwif) +{ + u32 base, dmabase; + u8 tmp; + struct pci_dev *pdev = hwif->pci_dev; + u8 port = hwif->channel; + + base = pci_resource_start(pdev, port * 2) & ~3; + dmabase = pci_resource_start(pdev, 4) & ~3; + + superio_ide_status[port] = base + IDE_STATUS_OFFSET; + superio_ide_select[port] = base + IDE_SELECT_OFFSET; + superio_ide_dma_status[port] = dmabase + (!port ? 2 : 0xa); + + /* Clear error/interrupt, enable dma */ + tmp = superio_ide_inb(superio_ide_dma_status[port]); + outb(tmp | 0x66, superio_ide_dma_status[port]); + + /* We need to override inb to workaround a SuperIO errata */ + hwif->INB = superio_ide_inb; +} + +static void __devinit init_iops_ns87415(ide_hwif_t *hwif) +{ + if (PCI_SLOT(hwif->pci_dev->devfn) == 0xE) { + /* Built-in - assume it's under superio. */ + superio_ide_init_iops(hwif); + } +} +#endif + +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; + + local_irq_save(flags); + 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); + } + + local_irq_restore(flags); +} + +static void ns87415_selectproc (ide_drive_t *drive) +{ + ns87415_prepare_drive (drive, drive->using_dma); +} + +static int ns87415_ide_dma_end (ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + u8 dma_stat = 0, dma_cmd = 0; + + drive->waiting_for_dma = 0; + dma_stat = hwif->INB(hwif->dma_status); + /* get dma command mode */ + dma_cmd = hwif->INB(hwif->dma_command); + /* stop DMA */ + hwif->OUTB(dma_cmd & ~1, hwif->dma_command); + /* from ERRATA: clear the INTR & ERROR bits */ + dma_cmd = hwif->INB(hwif->dma_command); + hwif->OUTB(dma_cmd|6, hwif->dma_command); + /* and free any DMA resources */ + ide_destroy_dmatable(drive); + /* verify good DMA status */ + return (dma_stat & 7) != 4; +} + +static int ns87415_ide_dma_setup(ide_drive_t *drive) +{ + /* select DMA xfer */ + ns87415_prepare_drive(drive, 1); + if (!ide_dma_setup(drive)) + return 0; + /* DMA failed: select PIO xfer */ + ns87415_prepare_drive(drive, 0); + return 1; +} + +static int ns87415_ide_dma_check (ide_drive_t *drive) +{ + if (drive->media != ide_disk) + return HWIF(drive)->ide_dma_off_quietly(drive); + return __ide_dma_check(drive); +} + +static void __init init_hwif_ns87415 (ide_hwif_t *hwif) +{ + struct pci_dev *dev = hwif->pci_dev; + unsigned int ctrl, using_inta; + u8 progif; +#ifdef __sparc_v9__ + int timeout; + u8 stat; +#endif + + hwif->autodma = 0; + hwif->selectproc = &ns87415_selectproc; + + /* + * 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; + hwif->OUTB(12, hwif->io_ports[IDE_CONTROL_OFFSET]); + udelay(10); + hwif->OUTB(8, hwif->io_ports[IDE_CONTROL_OFFSET]); + do { + udelay(50); + stat = hwif->INB(hwif->io_ports[IDE_STATUS_OFFSET]); + if (stat == 0xff) + break; + } while ((stat & BUSY_STAT) && --timeout); +#endif + } + + if (!using_inta) + hwif->irq = ide_default_irq(hwif->io_ports[IDE_DATA_OFFSET]); + else if (!hwif->irq && hwif->mate && hwif->mate->irq) + hwif->irq = hwif->mate->irq; /* share IRQ with mate */ + + if (!hwif->dma_base) + return; + + hwif->OUTB(0x60, hwif->dma_status); + hwif->dma_setup = &ns87415_ide_dma_setup; + hwif->ide_dma_check = &ns87415_ide_dma_check; + hwif->ide_dma_end = &ns87415_ide_dma_end; + + if (!noautodma) + hwif->autodma = 1; + hwif->drives[0].autodma = hwif->autodma; + hwif->drives[1].autodma = hwif->autodma; +} + +static ide_pci_device_t ns87415_chipset __devinitdata = { + .name = "NS87415", +#ifdef CONFIG_SUPERIO + .init_iops = init_iops_ns87415, +#endif + .init_hwif = init_hwif_ns87415, + .channels = 2, + .autodma = AUTODMA, + .bootable = ON_BOARD, +}; + +static int __devinit ns87415_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + return ide_setup_pci_device(dev, &ns87415_chipset); +} + +static struct pci_device_id ns87415_pci_tbl[] = { + { PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87415, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, ns87415_pci_tbl); + +static struct pci_driver driver = { + .name = "NS87415_IDE", + .id_table = ns87415_pci_tbl, + .probe = ns87415_init_one, +}; + +static int ns87415_ide_init(void) +{ + return ide_pci_register_driver(&driver); +} + +module_init(ns87415_ide_init); + +MODULE_AUTHOR("Mark Lord, Eddie Dost, Andre Hedrick"); +MODULE_DESCRIPTION("PCI driver module for NS87415 IDE"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/pci/opti621.c b/drivers/ide/pci/opti621.c new file mode 100644 index 0000000..cf4fd91 --- /dev/null +++ b/drivers/ide/pci/opti621.c @@ -0,0 +1,394 @@ +/* + * linux/drivers/ide/pci/opti621.c Version 0.7 Sept 10, 2002 + * + * 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/module.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/pci.h> +#include <linux/hdreg.h> +#include <linux/ide.h> + +#include <asm/io.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 */ +/* Uncomment 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 inaccessible. + * 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 */ + +static 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, u8 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; + } + } +} + +static 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(ide_hwif_t *hwif, u8 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. + */ +{ + hwif->INW(reg_base+1); + hwif->INW(reg_base+1); + hwif->OUTB(3, reg_base+2); + hwif->OUTB(value, reg_base+reg); + hwif->OUTB(0x83, reg_base+2); +} + +static u8 read_reg(ide_hwif_t *hwif, 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. + */ +{ + u8 ret = 0; + + hwif->INW(reg_base+1); + hwif->INW(reg_base+1); + hwif->OUTB(3, reg_base+2); + ret = hwif->INB(reg_base+reg); + hwif->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; + int bus_speed = system_bus_clock(); + + 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, u8 pio) +{ + /* primary and secondary drives share some registers, + * so we have to program both drives + */ + unsigned long flags; + u8 pio1 = 0, pio2 = 0; + pio_clocks_t first, second; + int ax, drdy; + u8 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 + + spin_lock_irqsave(&ide_lock, flags); + + reg_base = hwif->io_ports[IDE_DATA_OFFSET]; + + /* allow Register-B */ + hwif->OUTB(0xc0, reg_base+CNTRL_REG); + /* hmm, setupvic.exe does this ;-) */ + hwif->OUTB(0xff, reg_base+5); + /* if reads 0xff, adapter not exist? */ + (void) hwif->INB(reg_base+CNTRL_REG); + /* if reads 0xc0, no interface exist? */ + read_reg(hwif, CNTRL_REG); + /* read version, probably 0 */ + read_reg(hwif, STRAP_REG); + + /* program primary drive */ + /* select Index-0 for Register-A */ + write_reg(hwif, 0, MISC_REG); + /* set read cycle timings */ + write_reg(hwif, cycle1, READ_REG); + /* set write cycle timings */ + write_reg(hwif, cycle1, WRITE_REG); + + /* program secondary drive */ + /* select Index-1 for Register-B */ + write_reg(hwif, 1, MISC_REG); + /* set read cycle timings */ + write_reg(hwif, cycle2, READ_REG); + /* set write cycle timings */ + write_reg(hwif, cycle2, WRITE_REG); + + /* use Register-A for drive 0 */ + /* use Register-B for drive 1 */ + write_reg(hwif, 0x85, CNTRL_REG); + + /* set address setup, DRDY timings, */ + /* and read prefetch for both drives */ + write_reg(hwif, misc, MISC_REG); + + spin_unlock_irqrestore(&ide_lock, flags); +} + +/* + * init_hwif_opti621() is called once for each hwif found at boot. + */ +static void __init init_hwif_opti621 (ide_hwif_t *hwif) +{ + hwif->autodma = 0; + hwif->drives[0].drive_data = PIO_DONT_KNOW; + hwif->drives[1].drive_data = PIO_DONT_KNOW; + hwif->tuneproc = &opti621_tune_drive; + + if (!(hwif->dma_base)) + return; + + hwif->atapi_dma = 1; + hwif->mwdma_mask = 0x07; + hwif->swdma_mask = 0x07; + + if (!noautodma) + hwif->autodma = 1; + hwif->drives[0].autodma = hwif->autodma; + hwif->drives[1].autodma = hwif->autodma; +} + +static ide_pci_device_t opti621_chipsets[] __devinitdata = { + { /* 0 */ + .name = "OPTI621", + .init_hwif = init_hwif_opti621, + .channels = 2, + .autodma = AUTODMA, + .enablebits = {{0x45,0x80,0x00}, {0x40,0x08,0x00}}, + .bootable = ON_BOARD, + },{ /* 1 */ + .name = "OPTI621X", + .init_hwif = init_hwif_opti621, + .channels = 2, + .autodma = AUTODMA, + .enablebits = {{0x45,0x80,0x00}, {0x40,0x08,0x00}}, + .bootable = ON_BOARD, + } +}; + +static int __devinit opti621_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + return ide_setup_pci_device(dev, &opti621_chipsets[id->driver_data]); +} + +static struct pci_device_id opti621_pci_tbl[] = { + { PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C621, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C825, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1}, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, opti621_pci_tbl); + +static struct pci_driver driver = { + .name = "Opti621_IDE", + .id_table = opti621_pci_tbl, + .probe = opti621_init_one, +}; + +static int opti621_ide_init(void) +{ + return ide_pci_register_driver(&driver); +} + +module_init(opti621_ide_init); + +MODULE_AUTHOR("Jaromir Koutek, Jan Harkes, Mark Lord"); +MODULE_DESCRIPTION("PCI driver module for Opti621 IDE"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/pci/pdc202xx_new.c b/drivers/ide/pci/pdc202xx_new.c new file mode 100644 index 0000000..211641a --- /dev/null +++ b/drivers/ide/pci/pdc202xx_new.c @@ -0,0 +1,508 @@ +/* + * Promise TX2/TX4/TX2000/133 IDE driver + * + * 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. + * + * Split from: + * linux/drivers/ide/pdc202xx.c Version 0.35 Mar. 30, 2002 + * Copyright (C) 1998-2002 Andre Hedrick <andre@linux-ide.org> + * 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/module.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> + +#ifdef CONFIG_PPC_PMAC +#include <asm/prom.h> +#include <asm/pci-bridge.h> +#endif + +#define PDC202_DEBUG_CABLE 0 + +const static char *pdc_quirk_drives[] = { + "QUANTUM FIREBALLlct08 08", + "QUANTUM FIREBALLP KA6.4", + "QUANTUM FIREBALLP KA9.1", + "QUANTUM FIREBALLP LM20.4", + "QUANTUM FIREBALLP KX13.6", + "QUANTUM FIREBALLP KX20.5", + "QUANTUM FIREBALLP KX27.3", + "QUANTUM FIREBALLP LM20.5", + NULL +}; + +#define set_2regs(a, b) \ + do { \ + hwif->OUTB((a + adj), indexreg); \ + hwif->OUTB(b, datareg); \ + } while(0) + +#define set_ultra(a, b, c) \ + do { \ + set_2regs(0x10,(a)); \ + set_2regs(0x11,(b)); \ + set_2regs(0x12,(c)); \ + } while(0) + +#define set_ata2(a, b) \ + do { \ + set_2regs(0x0e,(a)); \ + set_2regs(0x0f,(b)); \ + } while(0) + +#define set_pio(a, b, c) \ + do { \ + set_2regs(0x0c,(a)); \ + set_2regs(0x0d,(b)); \ + set_2regs(0x13,(c)); \ + } while(0) + +static u8 pdcnew_ratemask (ide_drive_t *drive) +{ + u8 mode; + + switch(HWIF(drive)->pci_dev->device) { + case PCI_DEVICE_ID_PROMISE_20277: + case PCI_DEVICE_ID_PROMISE_20276: + case PCI_DEVICE_ID_PROMISE_20275: + case PCI_DEVICE_ID_PROMISE_20271: + case PCI_DEVICE_ID_PROMISE_20269: + mode = 4; + break; + case PCI_DEVICE_ID_PROMISE_20270: + case PCI_DEVICE_ID_PROMISE_20268: + mode = 3; + break; + default: + return 0; + } + if (!eighty_ninty_three(drive)) + mode = min(mode, (u8)1); + return mode; +} + +static int check_in_drive_lists (ide_drive_t *drive, const char **list) +{ + struct hd_driveid *id = drive->id; + + if (pdc_quirk_drives == list) { + while (*list) { + if (strstr(id->model, *list++)) { + return 2; + } + } + } else { + while (*list) { + if (!strcmp(*list++,id->model)) { + return 1; + } + } + } + return 0; +} + +static int pdcnew_new_tune_chipset (ide_drive_t *drive, u8 xferspeed) +{ + ide_hwif_t *hwif = HWIF(drive); + unsigned long indexreg = hwif->dma_vendor1; + unsigned long datareg = hwif->dma_vendor3; + u8 thold = 0x10; + u8 adj = (drive->dn%2) ? 0x08 : 0x00; + u8 speed = ide_rate_filter(pdcnew_ratemask(drive), xferspeed); + + if (speed == XFER_UDMA_2) { + hwif->OUTB((thold + adj), indexreg); + hwif->OUTB((hwif->INB(datareg) & 0x7f), datareg); + } + + switch (speed) { + case XFER_UDMA_7: + speed = XFER_UDMA_6; + case XFER_UDMA_6: set_ultra(0x1a, 0x01, 0xcb); break; + case XFER_UDMA_5: set_ultra(0x1a, 0x02, 0xcb); break; + case XFER_UDMA_4: set_ultra(0x1a, 0x03, 0xcd); break; + case XFER_UDMA_3: set_ultra(0x1a, 0x05, 0xcd); break; + case XFER_UDMA_2: set_ultra(0x2a, 0x07, 0xcd); break; + case XFER_UDMA_1: set_ultra(0x3a, 0x0a, 0xd0); break; + case XFER_UDMA_0: set_ultra(0x4a, 0x0f, 0xd5); break; + case XFER_MW_DMA_2: set_ata2(0x69, 0x25); break; + case XFER_MW_DMA_1: set_ata2(0x6b, 0x27); break; + case XFER_MW_DMA_0: set_ata2(0xdf, 0x5f); break; + case XFER_PIO_4: set_pio(0x23, 0x09, 0x25); break; + case XFER_PIO_3: set_pio(0x27, 0x0d, 0x35); break; + case XFER_PIO_2: set_pio(0x23, 0x26, 0x64); break; + case XFER_PIO_1: set_pio(0x46, 0x29, 0xa4); break; + case XFER_PIO_0: set_pio(0xfb, 0x2b, 0xac); break; + default: + ; + } + + return (ide_config_drive_speed(drive, speed)); +} + +/* 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 void pdcnew_tune_drive(ide_drive_t *drive, u8 pio) +{ + u8 speed; + + if (pio == 5) pio = 4; + speed = XFER_PIO_0 + ide_get_best_pio_mode(drive, 255, pio, NULL); + + (void)pdcnew_new_tune_chipset(drive, speed); +} + +static u8 pdcnew_new_cable_detect (ide_hwif_t *hwif) +{ + hwif->OUTB(0x0b, hwif->dma_vendor1); + return ((u8)((hwif->INB(hwif->dma_vendor3) & 0x04))); +} +static int config_chipset_for_dma (ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + ide_hwif_t *hwif = HWIF(drive); + u8 speed = -1; + u8 cable; + + u8 ultra_66 = ((id->dma_ultra & 0x0010) || + (id->dma_ultra & 0x0008)) ? 1 : 0; + + cable = pdcnew_new_cable_detect(hwif); + + if (ultra_66 && cable) { + printk(KERN_WARNING "Warning: %s channel requires an 80-pin cable for operation.\n", hwif->channel ? "Secondary":"Primary"); + printk(KERN_WARNING "%s reduced to Ultra33 mode.\n", drive->name); + } + + if (drive->media != ide_disk) + return 0; + if (id->capability & 4) { /* IORDY_EN & PREFETCH_EN */ + hwif->OUTB((0x13 + ((drive->dn%2) ? 0x08 : 0x00)), hwif->dma_vendor1); + hwif->OUTB((hwif->INB(hwif->dma_vendor3)|0x03), hwif->dma_vendor3); + } + + speed = ide_dma_speed(drive, pdcnew_ratemask(drive)); + + if (!(speed)) { + hwif->tuneproc(drive, 5); + return 0; + } + + (void) hwif->speedproc(drive, speed); + return ide_dma_enable(drive); +} + +static int pdcnew_config_drive_xfer_rate (ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + struct hd_driveid *id = drive->id; + + drive->init_speed = 0; + + if (id && (id->capability & 1) && drive->autodma) { + + if (ide_use_dma(drive)) { + if (config_chipset_for_dma(drive)) + return hwif->ide_dma_on(drive); + } + + goto fast_ata_pio; + + } else if ((id->capability & 8) || (id->field_valid & 2)) { +fast_ata_pio: + hwif->tuneproc(drive, 5); + return hwif->ide_dma_off_quietly(drive); + } + /* IORDY not supported */ + return 0; +} + +static int pdcnew_quirkproc (ide_drive_t *drive) +{ + return ((int) check_in_drive_lists(drive, pdc_quirk_drives)); +} + +static int pdcnew_ide_dma_lostirq(ide_drive_t *drive) +{ + if (HWIF(drive)->resetproc != NULL) + HWIF(drive)->resetproc(drive); + return __ide_dma_lostirq(drive); +} + +static int pdcnew_ide_dma_timeout(ide_drive_t *drive) +{ + if (HWIF(drive)->resetproc != NULL) + HWIF(drive)->resetproc(drive); + return __ide_dma_timeout(drive); +} + +static void pdcnew_new_reset (ide_drive_t *drive) +{ + /* + * Deleted this because it is redundant from the caller. + */ + printk(KERN_WARNING "PDC202XX: %s channel reset.\n", + HWIF(drive)->channel ? "Secondary" : "Primary"); +} + +#ifdef CONFIG_PPC_PMAC +static void __devinit apple_kiwi_init(struct pci_dev *pdev) +{ + struct device_node *np = pci_device_to_OF_node(pdev); + unsigned int class_rev = 0; + void __iomem *mmio; + u8 conf; + + if (np == NULL || !device_is_compatible(np, "kiwi-root")) + return; + + pci_read_config_dword(pdev, PCI_CLASS_REVISION, &class_rev); + class_rev &= 0xff; + + if (class_rev >= 0x03) { + /* Setup chip magic config stuff (from darwin) */ + pci_read_config_byte(pdev, 0x40, &conf); + pci_write_config_byte(pdev, 0x40, conf | 0x01); + } + mmio = ioremap(pci_resource_start(pdev, 5), + pci_resource_len(pdev, 5)); + + /* Setup some PLL stuffs */ + switch (pdev->device) { + case PCI_DEVICE_ID_PROMISE_20270: + writew(0x0d2b, mmio + 0x1202); + mdelay(30); + break; + case PCI_DEVICE_ID_PROMISE_20271: + writew(0x0826, mmio + 0x1202); + mdelay(30); + break; + } + + iounmap(mmio); +} +#endif /* CONFIG_PPC_PMAC */ + +static unsigned int __devinit init_chipset_pdcnew(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(KERN_INFO "%s: ROM enabled at 0x%08lx\n", + name, dev->resource[PCI_ROM_RESOURCE].start); + } + +#ifdef CONFIG_PPC_PMAC + apple_kiwi_init(dev); +#endif + + return dev->irq; +} + +static void __devinit init_hwif_pdc202new(ide_hwif_t *hwif) +{ + hwif->autodma = 0; + + hwif->tuneproc = &pdcnew_tune_drive; + hwif->quirkproc = &pdcnew_quirkproc; + hwif->speedproc = &pdcnew_new_tune_chipset; + hwif->resetproc = &pdcnew_new_reset; + + hwif->drives[0].autotune = hwif->drives[1].autotune = 1; + + hwif->ultra_mask = 0x7f; + hwif->mwdma_mask = 0x07; + + hwif->ide_dma_check = &pdcnew_config_drive_xfer_rate; + hwif->ide_dma_lostirq = &pdcnew_ide_dma_lostirq; + hwif->ide_dma_timeout = &pdcnew_ide_dma_timeout; + if (!(hwif->udma_four)) + hwif->udma_four = (pdcnew_new_cable_detect(hwif)) ? 0 : 1; + if (!noautodma) + hwif->autodma = 1; + hwif->drives[0].autodma = hwif->drives[1].autodma = hwif->autodma; +#if PDC202_DEBUG_CABLE + printk(KERN_DEBUG "%s: %s-pin cable\n", + hwif->name, hwif->udma_four ? "80" : "40"); +#endif /* PDC202_DEBUG_CABLE */ +} + +static int __devinit init_setup_pdcnew(struct pci_dev *dev, ide_pci_device_t *d) +{ + return ide_setup_pci_device(dev, d); +} + +static int __devinit init_setup_pdc20270(struct pci_dev *dev, + ide_pci_device_t *d) +{ + struct pci_dev *findev = NULL; + + if ((dev->bus->self && + dev->bus->self->vendor == PCI_VENDOR_ID_DEC) && + (dev->bus->self->device == PCI_DEVICE_ID_DEC_21150)) { + if (PCI_SLOT(dev->devfn) & 2) + return -ENODEV; + d->extra = 0; + while ((findev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, findev)) != NULL) { + if ((findev->vendor == dev->vendor) && + (findev->device == dev->device) && + (PCI_SLOT(findev->devfn) & 2)) { + if (findev->irq != dev->irq) { + findev->irq = dev->irq; + } + return ide_setup_pci_devices(dev, findev, d); + } + } + } + return ide_setup_pci_device(dev, d); +} + +static int __devinit init_setup_pdc20276(struct pci_dev *dev, + ide_pci_device_t *d) +{ + if ((dev->bus->self) && + (dev->bus->self->vendor == PCI_VENDOR_ID_INTEL) && + ((dev->bus->self->device == PCI_DEVICE_ID_INTEL_I960) || + (dev->bus->self->device == PCI_DEVICE_ID_INTEL_I960RM))) { + printk(KERN_INFO "ide: Skipping Promise PDC20276 " + "attached to I2O RAID controller.\n"); + return -ENODEV; + } + return ide_setup_pci_device(dev, d); +} + +static ide_pci_device_t pdcnew_chipsets[] __devinitdata = { + { /* 0 */ + .name = "PDC20268", + .init_setup = init_setup_pdcnew, + .init_chipset = init_chipset_pdcnew, + .init_hwif = init_hwif_pdc202new, + .channels = 2, + .autodma = AUTODMA, + .bootable = OFF_BOARD, + },{ /* 1 */ + .name = "PDC20269", + .init_setup = init_setup_pdcnew, + .init_chipset = init_chipset_pdcnew, + .init_hwif = init_hwif_pdc202new, + .channels = 2, + .autodma = AUTODMA, + .bootable = OFF_BOARD, + },{ /* 2 */ + .name = "PDC20270", + .init_setup = init_setup_pdc20270, + .init_chipset = init_chipset_pdcnew, + .init_hwif = init_hwif_pdc202new, + .channels = 2, + .autodma = AUTODMA, +#ifndef CONFIG_PDC202XX_FORCE + .enablebits = {{0x50,0x02,0x02}, {0x50,0x04,0x04}}, +#endif + .bootable = OFF_BOARD, + },{ /* 3 */ + .name = "PDC20271", + .init_setup = init_setup_pdcnew, + .init_chipset = init_chipset_pdcnew, + .init_hwif = init_hwif_pdc202new, + .channels = 2, + .autodma = AUTODMA, + .bootable = OFF_BOARD, + },{ /* 4 */ + .name = "PDC20275", + .init_setup = init_setup_pdcnew, + .init_chipset = init_chipset_pdcnew, + .init_hwif = init_hwif_pdc202new, + .channels = 2, + .autodma = AUTODMA, + .bootable = OFF_BOARD, + },{ /* 5 */ + .name = "PDC20276", + .init_setup = init_setup_pdc20276, + .init_chipset = init_chipset_pdcnew, + .init_hwif = init_hwif_pdc202new, + .channels = 2, + .autodma = AUTODMA, +#ifndef CONFIG_PDC202XX_FORCE + .enablebits = {{0x50,0x02,0x02}, {0x50,0x04,0x04}}, +#endif + .bootable = OFF_BOARD, + },{ /* 6 */ + .name = "PDC20277", + .init_setup = init_setup_pdcnew, + .init_chipset = init_chipset_pdcnew, + .init_hwif = init_hwif_pdc202new, + .channels = 2, + .autodma = AUTODMA, + .bootable = OFF_BOARD, + } +}; + +/** + * pdc202new_init_one - called when a pdc202xx is found + * @dev: the pdc202new device + * @id: the matching pci id + * + * Called when the PCI registration layer (or the IDE initialization) + * finds a device matching our IDE device tables. + */ + +static int __devinit pdc202new_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + ide_pci_device_t *d = &pdcnew_chipsets[id->driver_data]; + + return d->init_setup(dev, d); +} + +static struct pci_device_id pdc202new_pci_tbl[] = { + { PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20268, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20269, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1}, + { PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20270, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2}, + { PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20271, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 3}, + { PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20275, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4}, + { PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20276, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 5}, + { PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20277, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 6}, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, pdc202new_pci_tbl); + +static struct pci_driver driver = { + .name = "Promise_IDE", + .id_table = pdc202new_pci_tbl, + .probe = pdc202new_init_one, +}; + +static int pdc202new_ide_init(void) +{ + return ide_pci_register_driver(&driver); +} + +module_init(pdc202new_ide_init); + +MODULE_AUTHOR("Andre Hedrick, Frank Tiernan"); +MODULE_DESCRIPTION("PCI driver module for Promise PDC20268 and higher"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/pci/pdc202xx_old.c b/drivers/ide/pci/pdc202xx_old.c new file mode 100644 index 0000000..ad9d9581 --- /dev/null +++ b/drivers/ide/pci/pdc202xx_old.c @@ -0,0 +1,892 @@ +/* + * linux/drivers/ide/pci/pdc202xx_old.c Version 0.36 Sept 11, 2002 + * + * Copyright (C) 1998-2002 Andre Hedrick <andre@linux-ide.org> + * + * 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 + * + * Promise Ultra66 cards with BIOS v1.11 this + * compiled into the kernel if you have more than one card installed. + * + * Promise Ultra100 cards. + * + * 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" + * + */ + +/* + * 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/module.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> + +#define PDC202_DEBUG_CABLE 0 +#define PDC202XX_DEBUG_DRIVE_INFO 0 + +static const char *pdc_quirk_drives[] = { + "QUANTUM FIREBALLlct08 08", + "QUANTUM FIREBALLP KA6.4", + "QUANTUM FIREBALLP KA9.1", + "QUANTUM FIREBALLP LM20.4", + "QUANTUM FIREBALLP KX13.6", + "QUANTUM FIREBALLP KX20.5", + "QUANTUM FIREBALLP KX27.3", + "QUANTUM FIREBALLP LM20.5", + NULL +}; + +/* 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 0 + unsigned long bibma = pci_resource_start(dev, 4); + u8 hi = 0, lo = 0; + + u8 sc1c = inb_p((u16)bibma + 0x1c); + u8 sc1e = inb_p((u16)bibma + 0x1e); + u8 sc1f = inb_p((u16)bibma + 0x1f); + + p += sprintf(p, "Host Mode : %s\n", + (sc1f & 0x08) ? "Tri-Stated" : "Normal"); + p += sprintf(p, "Bus Clocking : %s\n", + ((sc1f & 0xC0) == 0xC0) ? "100 External" : + ((sc1f & 0x80) == 0x80) ? "66 External" : + ((sc1f & 0x40) == 0x40) ? "33 External" : "33 PCI Internal"); + p += sprintf(p, "IO pad select : %s mA\n", + ((sc1c & 0x03) == 0x03) ? "10" : + ((sc1c & 0x02) == 0x02) ? "8" : + ((sc1c & 0x01) == 0x01) ? "6" : + ((sc1c & 0x00) == 0x00) ? "4" : "??"); + hi = sc1e >> 4; + lo = sc1e & 0xf; + p += sprintf(p, "Status Polling Period : %d\n", hi); + p += sprintf(p, "Interrupt Check Status Polling Delay : %d\n", lo); +#endif + +static u8 pdc202xx_ratemask (ide_drive_t *drive) +{ + u8 mode; + + switch(HWIF(drive)->pci_dev->device) { + case PCI_DEVICE_ID_PROMISE_20267: + case PCI_DEVICE_ID_PROMISE_20265: + mode = 3; + break; + case PCI_DEVICE_ID_PROMISE_20263: + case PCI_DEVICE_ID_PROMISE_20262: + mode = 2; + break; + case PCI_DEVICE_ID_PROMISE_20246: + return 1; + default: + return 0; + } + if (!eighty_ninty_three(drive)) + mode = min(mode, (u8)1); + return mode; +} + +static int check_in_drive_lists (ide_drive_t *drive, const char **list) +{ + struct hd_driveid *id = drive->id; + + if (pdc_quirk_drives == list) { + while (*list) { + if (strstr(id->model, *list++)) { + return 2; + } + } + } else { + while (*list) { + if (!strcmp(*list++,id->model)) { + return 1; + } + } + } + return 0; +} + +static int pdc202xx_tune_chipset (ide_drive_t *drive, u8 xferspeed) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + u8 drive_pci = 0x60 + (drive->dn << 2); + u8 speed = ide_rate_filter(pdc202xx_ratemask(drive), xferspeed); + + u32 drive_conf; + u8 AP, BP, CP, DP; + u8 TA = 0, TB = 0, TC = 0; + + if ((drive->media != ide_disk) && (speed < XFER_SW_DMA_0)) + return -1; + + pci_read_config_dword(dev, drive_pci, &drive_conf); + 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 (speed < XFER_SW_DMA_0) { + 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); + } + } else { + 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); + + switch(speed) { + case XFER_UDMA_6: speed = XFER_UDMA_5; + case XFER_UDMA_5: + case XFER_UDMA_4: TB = 0x20; TC = 0x01; break; + case XFER_UDMA_2: TB = 0x20; TC = 0x01; break; + case XFER_UDMA_3: + case XFER_UDMA_1: TB = 0x40; TC = 0x02; break; + case XFER_UDMA_0: + case XFER_MW_DMA_2: TB = 0x60; TC = 0x03; break; + case XFER_MW_DMA_1: TB = 0x60; TC = 0x04; break; + case XFER_MW_DMA_0: + case XFER_SW_DMA_2: TB = 0x60; TC = 0x05; break; + case XFER_SW_DMA_1: TB = 0x80; TC = 0x06; break; + case XFER_SW_DMA_0: TB = 0xC0; TC = 0x0B; break; + case XFER_PIO_4: TA = 0x01; TB = 0x04; break; + case XFER_PIO_3: TA = 0x02; TB = 0x06; break; + case XFER_PIO_2: TA = 0x03; TB = 0x08; break; + case XFER_PIO_1: TA = 0x05; TB = 0x0C; break; + case XFER_PIO_0: + default: TA = 0x09; TB = 0x13; break; + } + + if (speed < XFER_SW_DMA_0) { + pci_write_config_byte(dev, (drive_pci), AP|TA); + pci_write_config_byte(dev, (drive_pci)|0x01, BP|TB); + } else { + pci_write_config_byte(dev, (drive_pci)|0x01, BP|TB); + pci_write_config_byte(dev, (drive_pci)|0x02, CP|TC); + } + +#if PDC202XX_DEBUG_DRIVE_INFO + printk(KERN_DEBUG "%s: %s drive%d 0x%08x ", + drive->name, ide_xfer_verbose(speed), + drive->dn, drive_conf); + pci_read_config_dword(dev, drive_pci, &drive_conf); + printk("0x%08x\n", drive_conf); +#endif /* PDC202XX_DEBUG_DRIVE_INFO */ + + return (ide_config_drive_speed(drive, speed)); +} + + +/* 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 void config_chipset_for_pio (ide_drive_t *drive, u8 pio) +{ + u8 speed = 0; + + if (pio == 5) pio = 4; + speed = XFER_PIO_0 + ide_get_best_pio_mode(drive, 255, pio, NULL); + + pdc202xx_tune_chipset(drive, speed); +} + +static u8 pdc202xx_old_cable_detect (ide_hwif_t *hwif) +{ + u16 CIS = 0, mask = (hwif->channel) ? (1<<11) : (1<<10); + pci_read_config_word(hwif->pci_dev, 0x50, &CIS); + return (CIS & mask) ? 1 : 0; +} + +/* + * Set the control register to use the 66MHz system + * clock for UDMA 3/4/5 mode operation when necessary. + * + * It may also be possible to leave the 66MHz clock on + * and readjust the timing parameters. + */ +static void pdc_old_enable_66MHz_clock(ide_hwif_t *hwif) +{ + unsigned long clock_reg = hwif->dma_master + 0x11; + u8 clock = hwif->INB(clock_reg); + + hwif->OUTB(clock | (hwif->channel ? 0x08 : 0x02), clock_reg); +} + +static void pdc_old_disable_66MHz_clock(ide_hwif_t *hwif) +{ + unsigned long clock_reg = hwif->dma_master + 0x11; + u8 clock = hwif->INB(clock_reg); + + hwif->OUTB(clock & ~(hwif->channel ? 0x08 : 0x02), clock_reg); +} + +static int config_chipset_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; + u32 drive_conf = 0; + u8 drive_pci = 0x60 + (drive->dn << 2); + u8 test1 = 0, test2 = 0, speed = -1; + u8 AP = 0, cable = 0; + + u8 ultra_66 = ((id->dma_ultra & 0x0010) || + (id->dma_ultra & 0x0008)) ? 1 : 0; + + if (dev->device != PCI_DEVICE_ID_PROMISE_20246) + cable = pdc202xx_old_cable_detect(hwif); + else + ultra_66 = 0; + + if (ultra_66 && cable) { + printk(KERN_WARNING "Warning: %s channel requires an 80-pin cable for operation.\n", hwif->channel ? "Secondary":"Primary"); + printk(KERN_WARNING "%s reduced to Ultra33 mode.\n", drive->name); + } + + if (dev->device != PCI_DEVICE_ID_PROMISE_20246) + pdc_old_disable_66MHz_clock(drive->hwif); + + drive_pci = 0x60 + (drive->dn << 2); + 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)) { + if (drive->select.b.unit & 0x01) { + pci_read_config_byte(dev, drive_pci - 4, &test2); + if ((test2 & SYNC_ERRDY_EN) && + !(test1 & SYNC_ERRDY_EN)) { + pci_write_config_byte(dev, drive_pci, + test1|SYNC_ERRDY_EN); + } + } else { + pci_write_config_byte(dev, drive_pci, + test1|SYNC_ERRDY_EN); + } + } + +chipset_is_set: + + if (drive->media == ide_disk) { + pci_read_config_byte(dev, (drive_pci), &AP); + 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); + } + + speed = ide_dma_speed(drive, pdc202xx_ratemask(drive)); + + if (!(speed)) { + /* restore original pci-config space */ + pci_write_config_dword(dev, drive_pci, drive_conf); + hwif->tuneproc(drive, 5); + return 0; + } + + (void) hwif->speedproc(drive, speed); + return ide_dma_enable(drive); +} + +static int pdc202xx_config_drive_xfer_rate (ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + struct hd_driveid *id = drive->id; + + drive->init_speed = 0; + + if (id && (id->capability & 1) && drive->autodma) { + + if (ide_use_dma(drive)) { + if (config_chipset_for_dma(drive)) + return hwif->ide_dma_on(drive); + } + + goto fast_ata_pio; + + } else if ((id->capability & 8) || (id->field_valid & 2)) { +fast_ata_pio: + hwif->tuneproc(drive, 5); + return hwif->ide_dma_off_quietly(drive); + } + /* IORDY not supported */ + return 0; +} + +static int pdc202xx_quirkproc (ide_drive_t *drive) +{ + return ((int) check_in_drive_lists(drive, pdc_quirk_drives)); +} + +static void pdc202xx_old_ide_dma_start(ide_drive_t *drive) +{ + if (drive->current_speed > XFER_UDMA_2) + pdc_old_enable_66MHz_clock(drive->hwif); + if (drive->addressing == 1) { + struct request *rq = HWGROUP(drive)->rq; + ide_hwif_t *hwif = HWIF(drive); +// struct pci_dev *dev = hwif->pci_dev; +// unsgned long high_16 = pci_resource_start(dev, 4); + unsigned long high_16 = hwif->dma_master; + unsigned long atapi_reg = high_16 + (hwif->channel ? 0x24 : 0x20); + u32 word_count = 0; + u8 clock = hwif->INB(high_16 + 0x11); + + hwif->OUTB(clock|(hwif->channel ? 0x08 : 0x02), high_16+0x11); + word_count = (rq->nr_sectors << 8); + word_count = (rq_data_dir(rq) == READ) ? + word_count | 0x05000000 : + word_count | 0x06000000; + hwif->OUTL(word_count, atapi_reg); + } + ide_dma_start(drive); +} + +static int pdc202xx_old_ide_dma_end(ide_drive_t *drive) +{ + if (drive->addressing == 1) { + ide_hwif_t *hwif = HWIF(drive); +// unsigned long high_16 = pci_resource_start(hwif->pci_dev, 4); + unsigned long high_16 = hwif->dma_master; + unsigned long atapi_reg = high_16 + (hwif->channel ? 0x24 : 0x20); + u8 clock = 0; + + hwif->OUTL(0, atapi_reg); /* zero out extra */ + clock = hwif->INB(high_16 + 0x11); + hwif->OUTB(clock & ~(hwif->channel ? 0x08:0x02), high_16+0x11); + } + if (drive->current_speed > XFER_UDMA_2) + pdc_old_disable_66MHz_clock(drive->hwif); + return __ide_dma_end(drive); +} + +static int pdc202xx_old_ide_dma_test_irq(ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); +// struct pci_dev *dev = hwif->pci_dev; +// unsigned long high_16 = pci_resource_start(dev, 4); + unsigned long high_16 = hwif->dma_master; + u8 dma_stat = hwif->INB(hwif->dma_status); + u8 sc1d = hwif->INB((high_16 + 0x001d)); + + if (hwif->channel) { + /* bit7: Error, bit6: Interrupting, bit5: FIFO Full, bit4: FIFO Empty */ + if ((sc1d & 0x50) == 0x50) + goto somebody_else; + else if ((sc1d & 0x40) == 0x40) + return (dma_stat & 4) == 4; + } else { + /* bit3: Error, bit2: Interrupting, bit1: FIFO Full, bit0: FIFO Empty */ + if ((sc1d & 0x05) == 0x05) + goto somebody_else; + else if ((sc1d & 0x04) == 0x04) + return (dma_stat & 4) == 4; + } +somebody_else: + return (dma_stat & 4) == 4; /* return 1 if INTR asserted */ +} + +static int pdc202xx_ide_dma_lostirq(ide_drive_t *drive) +{ + if (HWIF(drive)->resetproc != NULL) + HWIF(drive)->resetproc(drive); + return __ide_dma_lostirq(drive); +} + +static int pdc202xx_ide_dma_timeout(ide_drive_t *drive) +{ + if (HWIF(drive)->resetproc != NULL) + HWIF(drive)->resetproc(drive); + return __ide_dma_timeout(drive); +} + +static void pdc202xx_reset_host (ide_hwif_t *hwif) +{ +#ifdef CONFIG_BLK_DEV_IDEDMA +// unsigned long high_16 = hwif->dma_base - (8*(hwif->channel)); + unsigned long high_16 = hwif->dma_master; +#else /* !CONFIG_BLK_DEV_IDEDMA */ + unsigned long high_16 = pci_resource_start(hwif->pci_dev, 4); +#endif /* CONFIG_BLK_DEV_IDEDMA */ + u8 udma_speed_flag = hwif->INB(high_16|0x001f); + + hwif->OUTB((udma_speed_flag | 0x10), (high_16|0x001f)); + mdelay(100); + hwif->OUTB((udma_speed_flag & ~0x10), (high_16|0x001f)); + mdelay(2000); /* 2 seconds ?! */ + + printk(KERN_WARNING "PDC202XX: %s channel reset.\n", + hwif->channel ? "Secondary" : "Primary"); +} + +static void pdc202xx_reset (ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *mate = hwif->mate; + + pdc202xx_reset_host(hwif); + pdc202xx_reset_host(mate); +#if 0 + /* + * FIXME: Have to kick all the drives again :-/ + * What a pain in the ACE! + */ + if (hwif->present) { + u16 hunit = 0; + for (hunit = 0; hunit < MAX_DRIVES; ++hunit) { + ide_drive_t *hdrive = &hwif->drives[hunit]; + if (hdrive->present) { + if (hwif->ide_dma_check) + hwif->ide_dma_check(hdrive); + else + hwif->tuneproc(hdrive, 5); + } + } + } + if (mate->present) { + u16 munit = 0; + for (munit = 0; munit < MAX_DRIVES; ++munit) { + ide_drive_t *mdrive = &mate->drives[munit]; + if (mdrive->present) { + if (mate->ide_dma_check) + mate->ide_dma_check(mdrive); + else + mate->tuneproc(mdrive, 5); + } + } + } +#else + hwif->tuneproc(drive, 5); +#endif +} + +/* + * Since SUN Cobalt is attempting to do this operation, I should disclose + * this has been a long time ago Thu Jul 27 16:40:57 2000 was the patch date + * HOTSWAP ATA Infrastructure. + */ +static int pdc202xx_tristate (ide_drive_t * drive, int state) +{ + ide_hwif_t *hwif = HWIF(drive); +// unsigned long high_16 = hwif->dma_base - (8*(hwif->channel)); + unsigned long high_16 = hwif->dma_master; + u8 sc1f = hwif->INB(high_16|0x001f); + + if (!hwif) + return -EINVAL; + +// hwif->bus_state = state; + + if (state) { + hwif->OUTB(sc1f | 0x08, (high_16|0x001f)); + } else { + hwif->OUTB(sc1f & ~0x08, (high_16|0x001f)); + } + return 0; +} + +static unsigned int __devinit init_chipset_pdc202xx(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(KERN_INFO "%s: ROM enabled at 0x%08lx\n", + name, dev->resource[PCI_ROM_RESOURCE].start); + } + + /* + * 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. + */ +#if 0 + if ((dev->device == PCI_DEVICE_ID_PROMISE_20267) || + (dev->device == PCI_DEVICE_ID_PROMISE_20265) || + (dev->device == PCI_DEVICE_ID_PROMISE_20263) || + (dev->device == PCI_DEVICE_ID_PROMISE_20262)) { + unsigned long high_16 = pci_resource_start(dev, 4); + byte udma_speed_flag = inb(high_16 + 0x001f); + outb(udma_speed_flag | 0x10, high_16 + 0x001f); + mdelay(100); + outb(udma_speed_flag & ~0x10, high_16 + 0x001f); + mdelay(2000); /* 2 seconds ?! */ + } + +#endif + return dev->irq; +} + +static void __devinit init_hwif_pdc202xx(ide_hwif_t *hwif) +{ + struct pci_dev *dev = hwif->pci_dev; + + /* PDC20265 has problems with large LBA48 requests */ + if ((dev->device == PCI_DEVICE_ID_PROMISE_20267) || + (dev->device == PCI_DEVICE_ID_PROMISE_20265)) + hwif->rqsize = 256; + + hwif->autodma = 0; + hwif->tuneproc = &config_chipset_for_pio; + hwif->quirkproc = &pdc202xx_quirkproc; + + if (hwif->pci_dev->device != PCI_DEVICE_ID_PROMISE_20246) { + hwif->busproc = &pdc202xx_tristate; + hwif->resetproc = &pdc202xx_reset; + } + + hwif->speedproc = &pdc202xx_tune_chipset; + + hwif->drives[0].autotune = hwif->drives[1].autotune = 1; + + hwif->ultra_mask = 0x3f; + hwif->mwdma_mask = 0x07; + hwif->swdma_mask = 0x07; + + hwif->ide_dma_check = &pdc202xx_config_drive_xfer_rate; + hwif->ide_dma_lostirq = &pdc202xx_ide_dma_lostirq; + hwif->ide_dma_timeout = &pdc202xx_ide_dma_timeout; + + if (hwif->pci_dev->device != PCI_DEVICE_ID_PROMISE_20246) { + if (!(hwif->udma_four)) + hwif->udma_four = (pdc202xx_old_cable_detect(hwif)) ? 0 : 1; + hwif->dma_start = &pdc202xx_old_ide_dma_start; + hwif->ide_dma_end = &pdc202xx_old_ide_dma_end; + } + hwif->ide_dma_test_irq = &pdc202xx_old_ide_dma_test_irq; + + if (!noautodma) + hwif->autodma = 1; + hwif->drives[0].autodma = hwif->drives[1].autodma = hwif->autodma; +#if PDC202_DEBUG_CABLE + printk(KERN_DEBUG "%s: %s-pin cable\n", + hwif->name, hwif->udma_four ? "80" : "40"); +#endif /* PDC202_DEBUG_CABLE */ +} + +static void __devinit init_dma_pdc202xx(ide_hwif_t *hwif, unsigned long dmabase) +{ + u8 udma_speed_flag = 0, primary_mode = 0, secondary_mode = 0; + + if (hwif->channel) { + ide_setup_dma(hwif, dmabase, 8); + return; + } + + udma_speed_flag = hwif->INB((dmabase|0x1f)); + primary_mode = hwif->INB((dmabase|0x1a)); + secondary_mode = hwif->INB((dmabase|0x1b)); + printk(KERN_INFO "%s: (U)DMA Burst Bit %sABLED " \ + "Primary %s Mode " \ + "Secondary %s Mode.\n", hwif->cds->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(KERN_INFO "%s: FORCING BURST BIT 0x%02x->0x%02x ", + hwif->cds->name, udma_speed_flag, + (udma_speed_flag|1)); + hwif->OUTB(udma_speed_flag|1,(dmabase|0x1f)); + printk("%sACTIVE\n", + (hwif->INB(dmabase|0x1f)&1) ? "":"IN"); + } +#endif /* CONFIG_PDC202XX_BURST */ +#ifdef CONFIG_PDC202XX_MASTER + if (!(primary_mode & 1)) { + printk(KERN_INFO "%s: FORCING PRIMARY MODE BIT " + "0x%02x -> 0x%02x ", hwif->cds->name, + primary_mode, (primary_mode|1)); + hwif->OUTB(primary_mode|1, (dmabase|0x1a)); + printk("%s\n", + (hwif->INB((dmabase|0x1a)) & 1) ? "MASTER" : "PCI"); + } + + if (!(secondary_mode & 1)) { + printk(KERN_INFO "%s: FORCING SECONDARY MODE BIT " + "0x%02x -> 0x%02x ", hwif->cds->name, + secondary_mode, (secondary_mode|1)); + hwif->OUTB(secondary_mode|1, (dmabase|0x1b)); + printk("%s\n", + (hwif->INB((dmabase|0x1b)) & 1) ? "MASTER" : "PCI"); + } +#endif /* CONFIG_PDC202XX_MASTER */ + + ide_setup_dma(hwif, dmabase, 8); +} + +static int __devinit init_setup_pdc202ata4(struct pci_dev *dev, + ide_pci_device_t *d) +{ + if ((dev->class >> 8) != PCI_CLASS_STORAGE_IDE) { + u8 irq = 0, irq2 = 0; + pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq); + /* 0xbc */ + pci_read_config_byte(dev, (PCI_INTERRUPT_LINE)|0x80, &irq2); + if (irq != irq2) { + pci_write_config_byte(dev, + (PCI_INTERRUPT_LINE)|0x80, irq); /* 0xbc */ + printk(KERN_INFO "%s: pci-config space interrupt " + "mirror fixed.\n", d->name); + } + } + +#if 0 + if (dev->device == PCI_DEVICE_ID_PROMISE_20262) + if (e->reg && (pci_read_config_byte(dev, e->reg, &tmp) || + (tmp & e->mask) != e->val)) + + if (d->enablebits[0].reg != d->enablebits[1].reg) { + d->enablebits[0].reg = d->enablebits[1].reg; + d->enablebits[0].mask = d->enablebits[1].mask; + d->enablebits[0].val = d->enablebits[1].val; + } +#endif + + return ide_setup_pci_device(dev, d); +} + +static int __devinit init_setup_pdc20265(struct pci_dev *dev, + ide_pci_device_t *d) +{ + if ((dev->bus->self) && + (dev->bus->self->vendor == PCI_VENDOR_ID_INTEL) && + ((dev->bus->self->device == PCI_DEVICE_ID_INTEL_I960) || + (dev->bus->self->device == PCI_DEVICE_ID_INTEL_I960RM))) { + printk(KERN_INFO "ide: Skipping Promise PDC20265 " + "attached to I2O RAID controller.\n"); + return -ENODEV; + } + +#if 0 + { + u8 pri = 0, sec = 0; + + if (e->reg && (pci_read_config_byte(dev, e->reg, &tmp) || + (tmp & e->mask) != e->val)) + + if (d->enablebits[0].reg != d->enablebits[1].reg) { + d->enablebits[0].reg = d->enablebits[1].reg; + d->enablebits[0].mask = d->enablebits[1].mask; + d->enablebits[0].val = d->enablebits[1].val; + } + } +#endif + + return ide_setup_pci_device(dev, d); +} + +static int __devinit init_setup_pdc202xx(struct pci_dev *dev, + ide_pci_device_t *d) +{ + return ide_setup_pci_device(dev, d); +} + +static ide_pci_device_t pdc202xx_chipsets[] __devinitdata = { + { /* 0 */ + .name = "PDC20246", + .init_setup = init_setup_pdc202ata4, + .init_chipset = init_chipset_pdc202xx, + .init_hwif = init_hwif_pdc202xx, + .init_dma = init_dma_pdc202xx, + .channels = 2, + .autodma = AUTODMA, +#ifndef CONFIG_PDC202XX_FORCE + .enablebits = {{0x50,0x02,0x02}, {0x50,0x04,0x04}}, +#endif + .bootable = OFF_BOARD, + .extra = 16, + },{ /* 1 */ + .name = "PDC20262", + .init_setup = init_setup_pdc202ata4, + .init_chipset = init_chipset_pdc202xx, + .init_hwif = init_hwif_pdc202xx, + .init_dma = init_dma_pdc202xx, + .channels = 2, + .autodma = AUTODMA, +#ifndef CONFIG_PDC202XX_FORCE + .enablebits = {{0x50,0x02,0x02}, {0x50,0x04,0x04}}, +#endif + .bootable = OFF_BOARD, + .extra = 48, + .flags = IDEPCI_FLAG_FORCE_PDC, + },{ /* 2 */ + .name = "PDC20263", + .init_setup = init_setup_pdc202ata4, + .init_chipset = init_chipset_pdc202xx, + .init_hwif = init_hwif_pdc202xx, + .init_dma = init_dma_pdc202xx, + .channels = 2, + .autodma = AUTODMA, +#ifndef CONFIG_PDC202XX_FORCE + .enablebits = {{0x50,0x02,0x02}, {0x50,0x04,0x04}}, +#endif + .bootable = OFF_BOARD, + .extra = 48, + },{ /* 3 */ + .name = "PDC20265", + .init_setup = init_setup_pdc20265, + .init_chipset = init_chipset_pdc202xx, + .init_hwif = init_hwif_pdc202xx, + .init_dma = init_dma_pdc202xx, + .channels = 2, + .autodma = AUTODMA, +#ifndef CONFIG_PDC202XX_FORCE + .enablebits = {{0x50,0x02,0x02}, {0x50,0x04,0x04}}, +#endif + .bootable = OFF_BOARD, + .extra = 48, + .flags = IDEPCI_FLAG_FORCE_PDC, + },{ /* 4 */ + .name = "PDC20267", + .init_setup = init_setup_pdc202xx, + .init_chipset = init_chipset_pdc202xx, + .init_hwif = init_hwif_pdc202xx, + .init_dma = init_dma_pdc202xx, + .channels = 2, + .autodma = AUTODMA, +#ifndef CONFIG_PDC202XX_FORCE + .enablebits = {{0x50,0x02,0x02}, {0x50,0x04,0x04}}, +#endif + .bootable = OFF_BOARD, + .extra = 48, + } +}; + +/** + * pdc202xx_init_one - called when a PDC202xx is found + * @dev: the pdc202xx device + * @id: the matching pci id + * + * Called when the PCI registration layer (or the IDE initialization) + * finds a device matching our IDE device tables. + */ + +static int __devinit pdc202xx_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + ide_pci_device_t *d = &pdc202xx_chipsets[id->driver_data]; + + return d->init_setup(dev, d); +} + +static struct pci_device_id pdc202xx_pci_tbl[] = { + { PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20246, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20262, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1}, + { PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20263, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2}, + { PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20265, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 3}, + { PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20267, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4}, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, pdc202xx_pci_tbl); + +static struct pci_driver driver = { + .name = "Promise_Old_IDE", + .id_table = pdc202xx_pci_tbl, + .probe = pdc202xx_init_one, +}; + +static int pdc202xx_ide_init(void) +{ + return ide_pci_register_driver(&driver); +} + +module_init(pdc202xx_ide_init); + +MODULE_AUTHOR("Andre Hedrick, Frank Tiernan"); +MODULE_DESCRIPTION("PCI driver module for older Promise IDE"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/pci/piix.c b/drivers/ide/pci/piix.c new file mode 100644 index 0000000..b5a20ae --- /dev/null +++ b/drivers/ide/pci/piix.c @@ -0,0 +1,670 @@ +/* + * linux/drivers/ide/pci/piix.c Version 0.44 March 20, 2003 + * + * Copyright (C) 1998-1999 Andrzej Krzysztofowicz, Author and Maintainer + * Copyright (C) 1998-2000 Andre Hedrick <andre@linux-ide.org> + * Copyright (C) 2003 Red Hat Inc <alan@redhat.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_byte(HWIF(drive)->pci_dev, 0x48, ®48); + * pci_read_config_word(HWIF(drive)->pci_dev, 0x4a, ®4a); + * pci_read_config_byte(HWIF(drive)->pci_dev, 0x54, ®54); + * + * Documentation + * Publically available from Intel web site. Errata documentation + * is also publically available. As an aide to anyone hacking on this + * driver the list of errata that are relevant is below.going back to + * PIIX4. Older device documentation is now a bit tricky to find. + * + * Errata of note: + * + * Unfixable + * PIIX4 errata #9 - Only on ultra obscure hw + * ICH3 errata #13 - Not observed to affect real hw + * by Intel + * + * Things we must deal with + * PIIX4 errata #10 - BM IDE hang with non UDMA + * (must stop/start dma to recover) + * 440MX errata #15 - As PIIX4 errata #10 + * PIIX4 errata #15 - Must not read control registers + * during a PIO transfer + * 440MX errata #13 - As PIIX4 errata #15 + * ICH2 errata #21 - DMA mode 0 doesn't work right + * ICH0/1 errata #55 - As ICH2 errata #21 + * ICH2 spec c #9 - Extra operations needed to handle + * drive hotswap [NOT YET SUPPORTED] + * ICH2 spec c #20 - IDE PRD must not cross a 64K boundary + * and must be dword aligned + * ICH2 spec c #24 - UDMA mode 4,5 t85/86 should be 6ns not 3.3 + * + * Should have been BIOS fixed: + * 450NX: errata #19 - DMA hangs on old 450NX + * 450NX: errata #20 - DMA hangs on old 450NX + * 450NX: errata #25 - Corruption with DMA on old 450NX + * ICH3 errata #15 - IDE deadlock under high load + * (BIOS must set dev 31 fn 0 bit 23) + * ICH3 errata #18 - Don't use native mode + */ + +#include <linux/config.h> +#include <linux/types.h> +#include <linux/module.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 <linux/init.h> + +#include <asm/io.h> + +static int no_piix_dma; + +/** + * piix_ratemask - compute rate mask for PIIX IDE + * @drive: IDE drive to compute for + * + * Returns the available modes for the PIIX IDE controller. + */ + +static u8 piix_ratemask (ide_drive_t *drive) +{ + struct pci_dev *dev = HWIF(drive)->pci_dev; + u8 mode; + + switch(dev->device) { + case PCI_DEVICE_ID_INTEL_82801EB_1: + mode = 3; + break; + /* UDMA 100 capable */ + case PCI_DEVICE_ID_INTEL_82801BA_8: + case PCI_DEVICE_ID_INTEL_82801BA_9: + case PCI_DEVICE_ID_INTEL_82801CA_10: + case PCI_DEVICE_ID_INTEL_82801CA_11: + case PCI_DEVICE_ID_INTEL_82801E_11: + case PCI_DEVICE_ID_INTEL_82801DB_1: + case PCI_DEVICE_ID_INTEL_82801DB_10: + case PCI_DEVICE_ID_INTEL_82801DB_11: + case PCI_DEVICE_ID_INTEL_82801EB_11: + case PCI_DEVICE_ID_INTEL_ESB_2: + case PCI_DEVICE_ID_INTEL_ICH6_19: + case PCI_DEVICE_ID_INTEL_ICH7_21: + mode = 3; + break; + /* UDMA 66 capable */ + case PCI_DEVICE_ID_INTEL_82801AA_1: + case PCI_DEVICE_ID_INTEL_82372FB_1: + mode = 2; + break; + /* UDMA 33 capable */ + case PCI_DEVICE_ID_INTEL_82371AB: + case PCI_DEVICE_ID_INTEL_82443MX_1: + case PCI_DEVICE_ID_INTEL_82451NX: + case PCI_DEVICE_ID_INTEL_82801AB_1: + return 1; + /* Non UDMA capable (MWDMA2) */ + case PCI_DEVICE_ID_INTEL_82371SB_1: + case PCI_DEVICE_ID_INTEL_82371FB_1: + case PCI_DEVICE_ID_INTEL_82371FB_0: + case PCI_DEVICE_ID_INTEL_82371MX: + default: + return 0; + } + + /* + * If we are UDMA66 capable fall back to UDMA33 + * if the drive cannot see an 80pin cable. + */ + if (!eighty_ninty_three(drive)) + mode = min(mode, (u8)1); + return mode; +} + +/** + * piix_dma_2_pio - return the PIO mode matching DMA + * @xfer_rate: transfer speed + * + * Returns the nearest equivalent PIO timing for the PIO or DMA + * mode requested by the controller. + */ + +static u8 piix_dma_2_pio (u8 xfer_rate) { + switch(xfer_rate) { + case XFER_UDMA_6: + case XFER_UDMA_5: + 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; + } +} + +/** + * piix_tune_drive - tune a drive attached to a PIIX + * @drive: drive to tune + * @pio: desired PIO mode + * + * Set the interface PIO mode based upon the settings done by AMI BIOS + * (might be useful if drive is not registered in CMOS for any reason). + */ +static void piix_tune_drive (ide_drive_t *drive, u8 pio) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + int is_slave = (&hwif->drives[1] == drive); + int master_port = hwif->channel ? 0x42 : 0x40; + int slave_port = 0x44; + unsigned long flags; + u16 master_data; + u8 slave_data; + /* ISP RTC */ + u8 timings[][2] = { { 0, 0 }, + { 0, 0 }, + { 1, 0 }, + { 2, 1 }, + { 2, 3 }, }; + + pio = ide_get_best_pio_mode(drive, pio, 5, NULL); + spin_lock_irqsave(&ide_lock, flags); + pci_read_config_word(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(dev, slave_port, &slave_data); + slave_data = slave_data & (hwif->channel ? 0x0f : 0xf0); + slave_data = slave_data | (((timings[pio][0] << 2) | timings[pio][1]) << (hwif->channel ? 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); + } + pci_write_config_word(dev, master_port, master_data); + if (is_slave) + pci_write_config_byte(dev, slave_port, slave_data); + spin_unlock_irqrestore(&ide_lock, flags); +} + +/** + * piix_tune_chipset - tune a PIIX interface + * @drive: IDE drive to tune + * @xferspeed: speed to configure + * + * Set a PIIX interface channel to the desired speeds. This involves + * requires the right timing data into the PIIX configuration space + * then setting the drive parameters appropriately + */ + +static int piix_tune_chipset (ide_drive_t *drive, u8 xferspeed) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + u8 maslave = hwif->channel ? 0x42 : 0x40; + u8 speed = ide_rate_filter(piix_ratemask(drive), xferspeed); + int a_speed = 3 << (drive->dn * 4); + int u_flag = 1 << drive->dn; + int v_flag = 0x01 << drive->dn; + int w_flag = 0x10 << drive->dn; + int u_speed = 0; + int sitre; + u16 reg4042, reg4a; + u8 reg48, reg54, reg55; + + pci_read_config_word(dev, maslave, ®4042); + sitre = (reg4042 & 0x4000) ? 1 : 0; + pci_read_config_byte(dev, 0x48, ®48); + pci_read_config_word(dev, 0x4a, ®4a); + pci_read_config_byte(dev, 0x54, ®54); + pci_read_config_byte(dev, 0x55, ®55); + + switch(speed) { + case XFER_UDMA_4: + case XFER_UDMA_2: u_speed = 2 << (drive->dn * 4); break; + case XFER_UDMA_5: + case XFER_UDMA_3: + case XFER_UDMA_1: u_speed = 1 << (drive->dn * 4); break; + case XFER_UDMA_0: u_speed = 0 << (drive->dn * 4); break; + case XFER_MW_DMA_2: + case XFER_MW_DMA_1: + case XFER_SW_DMA_2: break; + case XFER_PIO_4: + case XFER_PIO_3: + case XFER_PIO_2: + case XFER_PIO_0: break; + default: return -1; + } + + if (speed >= XFER_UDMA_0) { + if (!(reg48 & u_flag)) + pci_write_config_byte(dev, 0x48, reg48 | u_flag); + if (speed == XFER_UDMA_5) { + pci_write_config_byte(dev, 0x55, (u8) reg55|w_flag); + } else { + pci_write_config_byte(dev, 0x55, (u8) reg55 & ~w_flag); + } + if ((reg4a & a_speed) != u_speed) + pci_write_config_word(dev, 0x4a, (reg4a & ~a_speed) | u_speed); + if (speed > XFER_UDMA_2) { + if (!(reg54 & v_flag)) + pci_write_config_byte(dev, 0x54, reg54 | v_flag); + } else + pci_write_config_byte(dev, 0x54, reg54 & ~v_flag); + } else { + if (reg48 & u_flag) + pci_write_config_byte(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_byte(dev, 0x54, reg54 & ~v_flag); + if (reg55 & w_flag) + pci_write_config_byte(dev, 0x55, (u8) reg55 & ~w_flag); + } + + piix_tune_drive(drive, piix_dma_2_pio(speed)); + return (ide_config_drive_speed(drive, speed)); +} + +/** + * piix_faulty_dma0 - check for DMA0 errata + * @hwif: IDE interface to check + * + * If an ICH/ICH0/ICH2 interface is is operating in multi-word + * DMA mode with 600nS cycle time the IDE PIO prefetch buffer will + * inadvertently provide an extra piece of secondary data to the primary + * device resulting in data corruption. + * + * With such a device this test function returns true. This allows + * our tuning code to follow Intel recommendations and use PIO on + * such devices. + */ + +static int piix_faulty_dma0(ide_hwif_t *hwif) +{ + switch(hwif->pci_dev->device) + { + case PCI_DEVICE_ID_INTEL_82801AA_1: /* ICH */ + case PCI_DEVICE_ID_INTEL_82801AB_1: /* ICH0 */ + case PCI_DEVICE_ID_INTEL_82801BA_8: /* ICH2 */ + case PCI_DEVICE_ID_INTEL_82801BA_9: /* ICH2 */ + return 1; + } + return 0; +} + +/** + * piix_config_drive_for_dma - configure drive for DMA + * @drive: IDE drive to configure + * + * Set up a PIIX interface channel for the best available speed. + * We prefer UDMA if it is available and then MWDMA. If DMA is + * not available we switch to PIO and return 0. + */ + +static int piix_config_drive_for_dma (ide_drive_t *drive) +{ + u8 speed = ide_dma_speed(drive, piix_ratemask(drive)); + + /* Some ICH devices cannot support DMA mode 0 */ + if(speed == XFER_MW_DMA_0 && piix_faulty_dma0(HWIF(drive))) + speed = 0; + + /* If no DMA speed was available or the chipset has DMA bugs + then disable DMA and use PIO */ + + if (!speed || no_piix_dma) { + u8 tspeed = ide_get_best_pio_mode(drive, 255, 5, NULL); + speed = piix_dma_2_pio(XFER_PIO_0 + tspeed); + } + + (void) piix_tune_chipset(drive, speed); + return ide_dma_enable(drive); +} + +/** + * piix_config_drive_xfer_rate - set up an IDE device + * @drive: IDE drive to configure + * + * Set up the PIIX interface for the best available speed on this + * interface, preferring DMA to PIO. + */ + +static int piix_config_drive_xfer_rate (ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + struct hd_driveid *id = drive->id; + + drive->init_speed = 0; + + if ((id->capability & 1) && drive->autodma) { + + if (ide_use_dma(drive)) { + if (piix_config_drive_for_dma(drive)) + return hwif->ide_dma_on(drive); + } + + goto fast_ata_pio; + + } else if ((id->capability & 8) || (id->field_valid & 2)) { +fast_ata_pio: + /* Find best PIO mode. */ + hwif->tuneproc(drive, 255); + return hwif->ide_dma_off_quietly(drive); + } + /* IORDY not supported */ + return 0; +} + +/** + * init_chipset_piix - set up the PIIX chipset + * @dev: PCI device to set up + * @name: Name of the device + * + * Initialize the PCI device as required. For the PIIX this turns + * out to be nice and simple + */ + +static unsigned int __devinit init_chipset_piix (struct pci_dev *dev, const char *name) +{ + switch(dev->device) { + case PCI_DEVICE_ID_INTEL_82801EB_1: + case PCI_DEVICE_ID_INTEL_82801AA_1: + case PCI_DEVICE_ID_INTEL_82801AB_1: + case PCI_DEVICE_ID_INTEL_82801BA_8: + case PCI_DEVICE_ID_INTEL_82801BA_9: + case PCI_DEVICE_ID_INTEL_82801CA_10: + case PCI_DEVICE_ID_INTEL_82801CA_11: + case PCI_DEVICE_ID_INTEL_82801DB_1: + case PCI_DEVICE_ID_INTEL_82801DB_10: + case PCI_DEVICE_ID_INTEL_82801DB_11: + case PCI_DEVICE_ID_INTEL_82801EB_11: + case PCI_DEVICE_ID_INTEL_82801E_11: + case PCI_DEVICE_ID_INTEL_ESB_2: + case PCI_DEVICE_ID_INTEL_ICH6_19: + case PCI_DEVICE_ID_INTEL_ICH7_21: + { + unsigned int extra = 0; + pci_read_config_dword(dev, 0x54, &extra); + pci_write_config_dword(dev, 0x54, extra|0x400); + } + default: + break; + } + + return 0; +} + +/** + * init_hwif_piix - fill in the hwif for the PIIX + * @hwif: IDE interface + * + * Set up the ide_hwif_t for the PIIX interface according to the + * capabilities of the hardware. + */ + +static void __devinit init_hwif_piix(ide_hwif_t *hwif) +{ + u8 reg54h = 0, reg55h = 0, ata66 = 0; + u8 mask = hwif->channel ? 0xc0 : 0x30; + +#ifndef CONFIG_IA64 + if (!hwif->irq) + hwif->irq = hwif->channel ? 15 : 14; +#endif /* CONFIG_IA64 */ + + if (hwif->pci_dev->device == PCI_DEVICE_ID_INTEL_82371MX) { + /* This is a painful system best to let it self tune for now */ + return; + } + + hwif->autodma = 0; + hwif->tuneproc = &piix_tune_drive; + hwif->speedproc = &piix_tune_chipset; + hwif->drives[0].autotune = 1; + hwif->drives[1].autotune = 1; + + if (!hwif->dma_base) + return; + + hwif->atapi_dma = 1; + hwif->ultra_mask = 0x3f; + hwif->mwdma_mask = 0x06; + hwif->swdma_mask = 0x04; + + switch(hwif->pci_dev->device) { + case PCI_DEVICE_ID_INTEL_82371MX: + hwif->mwdma_mask = 0x80; + hwif->swdma_mask = 0x80; + case PCI_DEVICE_ID_INTEL_82371FB_0: + case PCI_DEVICE_ID_INTEL_82371FB_1: + case PCI_DEVICE_ID_INTEL_82371SB_1: + hwif->ultra_mask = 0x80; + break; + case PCI_DEVICE_ID_INTEL_82371AB: + case PCI_DEVICE_ID_INTEL_82443MX_1: + case PCI_DEVICE_ID_INTEL_82451NX: + case PCI_DEVICE_ID_INTEL_82801AB_1: + hwif->ultra_mask = 0x07; + break; + default: + pci_read_config_byte(hwif->pci_dev, 0x54, ®54h); + pci_read_config_byte(hwif->pci_dev, 0x55, ®55h); + ata66 = (reg54h & mask) ? 1 : 0; + break; + } + + if (!(hwif->udma_four)) + hwif->udma_four = ata66; + hwif->ide_dma_check = &piix_config_drive_xfer_rate; + if (!noautodma) + hwif->autodma = 1; + + hwif->drives[1].autodma = hwif->autodma; + hwif->drives[0].autodma = hwif->autodma; +} + +#define DECLARE_PIIX_DEV(name_str) \ + { \ + .name = name_str, \ + .init_chipset = init_chipset_piix, \ + .init_hwif = init_hwif_piix, \ + .channels = 2, \ + .autodma = AUTODMA, \ + .enablebits = {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, \ + .bootable = ON_BOARD, \ + } + +static ide_pci_device_t piix_pci_info[] __devinitdata = { + /* 0 */ DECLARE_PIIX_DEV("PIIXa"), + /* 1 */ DECLARE_PIIX_DEV("PIIXb"), + + { /* 2 */ + .name = "MPIIX", + .init_hwif = init_hwif_piix, + .channels = 2, + .autodma = NODMA, + .enablebits = {{0x6D,0x80,0x80}, {0x6F,0x80,0x80}}, + .bootable = ON_BOARD, + }, + + /* 3 */ DECLARE_PIIX_DEV("PIIX3"), + /* 4 */ DECLARE_PIIX_DEV("PIIX4"), + /* 5 */ DECLARE_PIIX_DEV("ICH0"), + /* 6 */ DECLARE_PIIX_DEV("PIIX4"), + /* 7 */ DECLARE_PIIX_DEV("ICH"), + /* 8 */ DECLARE_PIIX_DEV("PIIX4"), + /* 9 */ DECLARE_PIIX_DEV("PIIX4"), + /* 10 */ DECLARE_PIIX_DEV("ICH2"), + /* 11 */ DECLARE_PIIX_DEV("ICH2M"), + /* 12 */ DECLARE_PIIX_DEV("ICH3M"), + /* 13 */ DECLARE_PIIX_DEV("ICH3"), + /* 14 */ DECLARE_PIIX_DEV("ICH4"), + /* 15 */ DECLARE_PIIX_DEV("ICH5"), + /* 16 */ DECLARE_PIIX_DEV("C-ICH"), + /* 17 */ DECLARE_PIIX_DEV("ICH4"), + /* 18 */ DECLARE_PIIX_DEV("ICH5-SATA"), + /* 19 */ DECLARE_PIIX_DEV("ICH5"), + /* 20 */ DECLARE_PIIX_DEV("ICH6"), + /* 21 */ DECLARE_PIIX_DEV("ICH7"), + /* 22 */ DECLARE_PIIX_DEV("ICH4"), +}; + +/** + * piix_init_one - called when a PIIX is found + * @dev: the piix device + * @id: the matching pci id + * + * Called when the PCI registration layer (or the IDE initialization) + * finds a device matching our IDE device tables. + */ + +static int __devinit piix_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + ide_pci_device_t *d = &piix_pci_info[id->driver_data]; + + return ide_setup_pci_device(dev, d); +} + +/** + * piix_check_450nx - Check for problem 450NX setup + * + * Check for the present of 450NX errata #19 and errata #25. If + * they are found, disable use of DMA IDE + */ + +static void __devinit piix_check_450nx(void) +{ + struct pci_dev *pdev = NULL; + u16 cfg; + u8 rev; + while((pdev=pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82454NX, pdev))!=NULL) + { + /* Look for 450NX PXB. Check for problem configurations + A PCI quirk checks bit 6 already */ + pci_read_config_byte(pdev, PCI_REVISION_ID, &rev); + pci_read_config_word(pdev, 0x41, &cfg); + /* Only on the original revision: IDE DMA can hang */ + if(rev == 0x00) + no_piix_dma = 1; + /* On all revisions below 5 PXB bus lock must be disabled for IDE */ + else if(cfg & (1<<14) && rev < 5) + no_piix_dma = 2; + } + if(no_piix_dma) + printk(KERN_WARNING "piix: 450NX errata present, disabling IDE DMA.\n"); + if(no_piix_dma == 2) + printk(KERN_WARNING "piix: A BIOS update may resolve this.\n"); +} + +static struct pci_device_id piix_pci_tbl[] = { + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371FB_0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371FB_1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1}, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371MX, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2}, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 3}, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4}, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AB_1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 5}, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443MX_1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 6}, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 7}, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82372FB_1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 8}, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82451NX, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 9}, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 10}, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 11}, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_10,PCI_ANY_ID, PCI_ANY_ID, 0, 0, 12}, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_11,PCI_ANY_ID, PCI_ANY_ID, 0, 0, 13}, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_11,PCI_ANY_ID, PCI_ANY_ID, 0, 0, 14}, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_11,PCI_ANY_ID, PCI_ANY_ID, 0, 0, 15}, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801E_11, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 16}, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_10,PCI_ANY_ID, PCI_ANY_ID, 0, 0, 17}, +#ifdef CONFIG_BLK_DEV_IDE_SATA + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 18}, +#endif + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 19}, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_19, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 20}, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_21, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 21}, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 22}, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, piix_pci_tbl); + +static struct pci_driver driver = { + .name = "PIIX_IDE", + .id_table = piix_pci_tbl, + .probe = piix_init_one, +}; + +static int __init piix_ide_init(void) +{ + piix_check_450nx(); + return ide_pci_register_driver(&driver); +} + +module_init(piix_ide_init); + +MODULE_AUTHOR("Andre Hedrick, Andrzej Krzysztofowicz"); +MODULE_DESCRIPTION("PCI driver module for Intel PIIX IDE"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/pci/rz1000.c b/drivers/ide/pci/rz1000.c new file mode 100644 index 0000000..608cd76 --- /dev/null +++ b/drivers/ide/pci/rz1000.c @@ -0,0 +1,91 @@ +/* + * linux/drivers/ide/pci/rz1000.c Version 0.06 January 12, 2003 + * + * 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/module.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 <linux/init.h> + +#include <asm/io.h> + +static void __devinit init_hwif_rz1000 (ide_hwif_t *hwif) +{ + u16 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(KERN_INFO "%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(KERN_INFO "%s: serialized, disabled unmasking " + "(buggy RZ1000/RZ1001)\n", hwif->name); + } +} + +static ide_pci_device_t rz1000_chipset __devinitdata = { + .name = "RZ100x", + .init_hwif = init_hwif_rz1000, + .channels = 2, + .autodma = NODMA, + .bootable = ON_BOARD, +}; + +static int __devinit rz1000_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + return ide_setup_pci_device(dev, &rz1000_chipset); +} + +static struct pci_device_id rz1000_pci_tbl[] = { + { PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, rz1000_pci_tbl); + +static struct pci_driver driver = { + .name = "RZ1000_IDE", + .id_table = rz1000_pci_tbl, + .probe = rz1000_init_one, +}; + +static int rz1000_ide_init(void) +{ + return ide_pci_register_driver(&driver); +} + +module_init(rz1000_ide_init); + +MODULE_AUTHOR("Andre Hedrick"); +MODULE_DESCRIPTION("PCI driver module for RZ1000 IDE"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/ide/pci/sc1200.c b/drivers/ide/pci/sc1200.c new file mode 100644 index 0000000..84fda21 --- /dev/null +++ b/drivers/ide/pci/sc1200.c @@ -0,0 +1,518 @@ +/* + * linux/drivers/ide/pci/sc1200.c Version 0.91 28-Jan-2003 + * + * Copyright (C) 2000-2002 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. + * + * Documentation: + * Available from National Semiconductor + */ + +#include <linux/config.h> +#include <linux/module.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 <linux/pm.h> +#include <asm/io.h> +#include <asm/irq.h> + +#define SC1200_REV_A 0x00 +#define SC1200_REV_B1 0x01 +#define SC1200_REV_B3 0x02 +#define SC1200_REV_C1 0x03 +#define SC1200_REV_D1 0x04 + +#define PCI_CLK_33 0x00 +#define PCI_CLK_48 0x01 +#define PCI_CLK_66 0x02 +#define PCI_CLK_33A 0x03 + +static unsigned short sc1200_get_pci_clock (void) +{ + unsigned char chip_id, silicon_revision; + unsigned int pci_clock; + /* + * Check the silicon revision, as not all versions of the chip + * have the register with the fast PCI bus timings. + */ + chip_id = inb (0x903c); + silicon_revision = inb (0x903d); + + // Read the fast pci clock frequency + if (chip_id == 0x04 && silicon_revision < SC1200_REV_B1) { + pci_clock = PCI_CLK_33; + } else { + // check clock generator configuration (cfcc) + // the clock is in bits 8 and 9 of this word + + pci_clock = inw (0x901e); + pci_clock >>= 8; + pci_clock &= 0x03; + if (pci_clock == PCI_CLK_33A) + pci_clock = PCI_CLK_33; + } + return pci_clock; +} + +extern char *ide_xfer_verbose (byte xfer_rate); + +/* + * Set a new transfer mode at the drive + */ +static int sc1200_set_xfer_mode (ide_drive_t *drive, byte mode) +{ + printk("%s: sc1200_set_xfer_mode(%s)\n", drive->name, ide_xfer_verbose(mode)); + return ide_config_drive_speed(drive, mode); +} + +/* + * 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 const unsigned int sc1200_pio_timings[4][5] = + {{0x00009172, 0x00012171, 0x00020080, 0x00032010, 0x00040010}, // format0 33Mhz + {0xd1329172, 0x71212171, 0x30200080, 0x20102010, 0x00100010}, // format1, 33Mhz + {0xfaa3f4f3, 0xc23232b2, 0x513101c1, 0x31213121, 0x10211021}, // format1, 48Mhz + {0xfff4fff4, 0xf35353d3, 0x814102f1, 0x42314231, 0x11311131}}; // format1, 66Mhz + +/* + * After chip reset, the PIO timings are set to 0x00009172, which is not valid. + */ +//#define SC1200_BAD_PIO(timings) (((timings)&~0x80000000)==0x00009172) + +static int sc1200_autoselect_dma_mode (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; + + /* + * The SC1200 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) && !__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 && !__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; + } + } + return mode; +} + +/* + * sc1200_config_dma2() handles selection/setting of DMA/UDMA modes + * for both the chipset and drive. + */ +static int sc1200_config_dma2 (ide_drive_t *drive, int mode) +{ + ide_hwif_t *hwif = HWIF(drive); + int unit = drive->select.b.unit; + unsigned int reg, timings; + unsigned short pci_clock; + unsigned int basereg = hwif->channel ? 0x50 : 0x40; + + /* + * Default to DMA-off in case we run into trouble here. + */ + hwif->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 */ + + /* + * Tell the drive to switch to the new mode; abort on failure. + */ + if (!mode || sc1200_set_xfer_mode(drive, mode)) { + printk("SC1200: set xfer mode failure\n"); + return 1; /* failure */ + } + + pci_clock = sc1200_get_pci_clock(); + + /* + * Now tune the chipset to match the drive: + * + * Note that each DMA mode has several timings associated with it. + * The correct timing depends on the fast PCI clock freq. + */ + timings = 0; + switch (mode) { + case XFER_UDMA_0: + switch (pci_clock) { + case PCI_CLK_33: timings = 0x00921250; break; + case PCI_CLK_48: timings = 0x00932470; break; + case PCI_CLK_66: timings = 0x009436a1; break; + } + break; + case XFER_UDMA_1: + switch (pci_clock) { + case PCI_CLK_33: timings = 0x00911140; break; + case PCI_CLK_48: timings = 0x00922260; break; + case PCI_CLK_66: timings = 0x00933481; break; + } + break; + case XFER_UDMA_2: + switch (pci_clock) { + case PCI_CLK_33: timings = 0x00911030; break; + case PCI_CLK_48: timings = 0x00922140; break; + case PCI_CLK_66: timings = 0x00923261; break; + } + break; + case XFER_MW_DMA_0: + switch (pci_clock) { + case PCI_CLK_33: timings = 0x00077771; break; + case PCI_CLK_48: timings = 0x000bbbb2; break; + case PCI_CLK_66: timings = 0x000ffff3; break; + } + break; + case XFER_MW_DMA_1: + switch (pci_clock) { + case PCI_CLK_33: timings = 0x00012121; break; + case PCI_CLK_48: timings = 0x00024241; break; + case PCI_CLK_66: timings = 0x00035352; break; + } + break; + case XFER_MW_DMA_2: + switch (pci_clock) { + case PCI_CLK_33: timings = 0x00002020; break; + case PCI_CLK_48: timings = 0x00013131; break; + case PCI_CLK_66: timings = 0x00015151; break; + } + break; + } + + if (timings == 0) { + printk("%s: sc1200_config_dma: huh? mode=%02x clk=%x \n", drive->name, mode, pci_clock); + return 1; /* failure */ + } + + if (unit == 0) { /* are we configuring drive0? */ + pci_read_config_dword(hwif->pci_dev, basereg+4, ®); + timings |= reg & 0x80000000; /* preserve PIO format bit */ + pci_write_config_dword(hwif->pci_dev, basereg+4, timings); + } else { + pci_write_config_dword(hwif->pci_dev, basereg+12, timings); + } + + 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->ide_dma_on(drive); /* success */ +} + +/* + * sc1200_config_dma() handles selection/setting of DMA/UDMA modes + * for both the chipset and drive. + */ +static int sc1200_config_dma (ide_drive_t *drive) +{ + return sc1200_config_dma2(drive, sc1200_autoselect_dma_mode(drive)); +} + + +/* Replacement for the standard ide_dma_end action in + * dma_proc. + * + * returns 1 on error, 0 otherwise + */ +static int sc1200_ide_dma_end (ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + unsigned long dma_base = hwif->dma_base; + byte dma_stat; + + dma_stat = inb(dma_base+2); /* get DMA status */ + + if (!(dma_stat & 4)) + printk(" ide_dma_end dma_stat=%0x err=%x newerr=%x\n", + dma_stat, ((dma_stat&7)!=4), ((dma_stat&2)==2)); + + outb(dma_stat|0x1b, dma_base+2); /* clear the INTR & ERROR bits */ + outb(inb(dma_base)&~1, dma_base); /* !! DO THIS HERE !! stop DMA */ + + drive->waiting_for_dma = 0; + ide_destroy_dmatable(drive); /* purge DMA mappings */ + + return (dma_stat & 7) != 4; /* verify good DMA status */ +} + +/* + * sc1200_tuneproc() handles selection/setting of PIO modes + * for both the chipset and drive. + * + * All existing BIOSs for this chipset guarantee that all drives + * will have valid default PIO timings set up before we get here. + */ +static void sc1200_tuneproc (ide_drive_t *drive, byte pio) /* mode=255 means "autotune" */ +{ + ide_hwif_t *hwif = HWIF(drive); + unsigned int format; + static byte modes[5] = {XFER_PIO_0, XFER_PIO_1, XFER_PIO_2, XFER_PIO_3, XFER_PIO_4}; + int mode = -1; + + switch (pio) { + case 200: mode = XFER_UDMA_0; break; + case 201: mode = XFER_UDMA_1; break; + case 202: mode = XFER_UDMA_2; break; + case 100: mode = XFER_MW_DMA_0; break; + case 101: mode = XFER_MW_DMA_1; break; + case 102: mode = XFER_MW_DMA_2; break; + } + if (mode != -1) { + printk("SC1200: %s: changing (U)DMA mode\n", drive->name); + (void)sc1200_config_dma2(drive, mode); + return; + } + + pio = ide_get_best_pio_mode(drive, pio, 4, NULL); + printk("SC1200: %s: setting PIO mode%d\n", drive->name, pio); + if (!sc1200_set_xfer_mode(drive, modes[pio])) { + unsigned int basereg = hwif->channel ? 0x50 : 0x40; + pci_read_config_dword (hwif->pci_dev, basereg+4, &format); + format = (format >> 31) & 1; + if (format) + format += sc1200_get_pci_clock(); + pci_write_config_dword(hwif->pci_dev, basereg + (drive->select.b.unit << 3), sc1200_pio_timings[format][pio]); + } +} + +static ide_hwif_t *lookup_pci_dev (ide_hwif_t *prev, struct pci_dev *dev) +{ + int h; + + for (h = 0; h < MAX_HWIFS; h++) { + ide_hwif_t *hwif = &ide_hwifs[h]; + if (prev) { + if (hwif == prev) + prev = NULL; // found previous, now look for next match + } else { + if (hwif && hwif->pci_dev == dev) + return hwif; // found next match + } + } + return NULL; // not found +} + +typedef struct sc1200_saved_state_s { + __u32 regs[4]; +} sc1200_saved_state_t; + + +static int sc1200_suspend (struct pci_dev *dev, u32 state) +{ + ide_hwif_t *hwif = NULL; + + printk("SC1200: suspend(%u)\n", state); + + if (state == 0) { + // we only save state when going from full power to less + + // + // Loop over all interfaces that are part of this PCI device: + // + while ((hwif = lookup_pci_dev(hwif, dev)) != NULL) { + sc1200_saved_state_t *ss; + unsigned int basereg, r; + // + // allocate a permanent save area, if not already allocated + // + ss = (sc1200_saved_state_t *)hwif->config_data; + if (ss == NULL) { + ss = kmalloc(sizeof(sc1200_saved_state_t), GFP_KERNEL); + if (ss == NULL) + return -ENOMEM; + hwif->config_data = (unsigned long)ss; + } + ss = (sc1200_saved_state_t *)hwif->config_data; + // + // Save timing registers: this may be unnecessary if + // BIOS also does it + // + basereg = hwif->channel ? 0x50 : 0x40; + for (r = 0; r < 4; ++r) { + pci_read_config_dword (hwif->pci_dev, basereg + (r<<2), &ss->regs[r]); + } + } + } + + /* You don't need to iterate over disks -- sysfs should have done that for you already */ + + pci_disable_device(dev); + pci_set_power_state(dev,state); + dev->current_state = state; + return 0; +} + +static int sc1200_resume (struct pci_dev *dev) +{ + ide_hwif_t *hwif = NULL; + +printk("SC1200: resume\n"); + pci_set_power_state(dev,0); // bring chip back from sleep state + dev->current_state = 0; + pci_enable_device(dev); + // + // loop over all interfaces that are part of this pci device: + // + while ((hwif = lookup_pci_dev(hwif, dev)) != NULL) { + unsigned int basereg, r, d, format; + sc1200_saved_state_t *ss = (sc1200_saved_state_t *)hwif->config_data; +printk("%s: SC1200: resume\n", hwif->name); + + // + // Restore timing registers: this may be unnecessary if BIOS also does it + // + basereg = hwif->channel ? 0x50 : 0x40; + if (ss != NULL) { + for (r = 0; r < 4; ++r) { + pci_write_config_dword(hwif->pci_dev, basereg + (r<<2), ss->regs[r]); + } + } + // + // Re-program drive PIO modes + // + pci_read_config_dword(hwif->pci_dev, basereg+4, &format); + format = (format >> 31) & 1; + if (format) + format += sc1200_get_pci_clock(); + for (d = 0; d < 2; ++d) { + ide_drive_t *drive = &(hwif->drives[d]); + if (drive->present) { + unsigned int pio, timings; + pci_read_config_dword(hwif->pci_dev, basereg+(drive->select.b.unit << 3), &timings); + for (pio = 0; pio <= 4; ++pio) { + if (sc1200_pio_timings[format][pio] == timings) + break; + } + if (pio > 4) + pio = 255; /* autotune */ + (void)sc1200_tuneproc(drive, pio); + } + } + // + // Re-program drive DMA modes + // + for (d = 0; d < MAX_DRIVES; ++d) { + ide_drive_t *drive = &(hwif->drives[d]); + if (drive->present && !__ide_dma_bad_drive(drive)) { + int was_using_dma = drive->using_dma; + hwif->ide_dma_off_quietly(drive); + sc1200_config_dma(drive); + if (!was_using_dma && drive->using_dma) { + hwif->ide_dma_off_quietly(drive); + } + } + } + } + return 0; +} + +/* + * This gets invoked by the IDE driver once for each channel, + * and performs channel-specific pre-initialization before drive probing. + */ +static void __init init_hwif_sc1200 (ide_hwif_t *hwif) +{ + if (hwif->mate) + hwif->serialized = hwif->mate->serialized = 1; + hwif->autodma = 0; + if (hwif->dma_base) { + hwif->ide_dma_check = &sc1200_config_dma; + hwif->ide_dma_end = &sc1200_ide_dma_end; + if (!noautodma) + hwif->autodma = 1; + hwif->tuneproc = &sc1200_tuneproc; + } + hwif->atapi_dma = 1; + hwif->ultra_mask = 0x07; + hwif->mwdma_mask = 0x07; + + hwif->drives[0].autodma = hwif->autodma; + hwif->drives[1].autodma = hwif->autodma; +} + +static ide_pci_device_t sc1200_chipset __devinitdata = { + .name = "SC1200", + .init_hwif = init_hwif_sc1200, + .channels = 2, + .autodma = AUTODMA, + .bootable = ON_BOARD, +}; + +static int __devinit sc1200_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + return ide_setup_pci_device(dev, &sc1200_chipset); +} + +static struct pci_device_id sc1200_pci_tbl[] = { + { PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SCx200_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, sc1200_pci_tbl); + +static struct pci_driver driver = { + .name = "SC1200_IDE", + .id_table = sc1200_pci_tbl, + .probe = sc1200_init_one, + .suspend = sc1200_suspend, + .resume = sc1200_resume, +}; + +static int sc1200_ide_init(void) +{ + return ide_pci_register_driver(&driver); +} + +module_init(sc1200_ide_init); + +MODULE_AUTHOR("Mark Lord"); +MODULE_DESCRIPTION("PCI driver module for NS SC1200 IDE"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/pci/serverworks.c b/drivers/ide/pci/serverworks.c new file mode 100644 index 0000000..82a1103 --- /dev/null +++ b/drivers/ide/pci/serverworks.c @@ -0,0 +1,675 @@ +/* + * linux/drivers/ide/pci/serverworks.c Version 0.8 25 Ebr 2003 + * + * Copyright (C) 1998-2000 Michel Aubry + * Copyright (C) 1998-2000 Andrzej Krzysztofowicz + * Copyright (C) 1998-2000 Andre Hedrick <andre@linux-ide.org> + * Portions copyright (c) 2001 Sun Microsystems + * + * + * RCC/ServerWorks IDE driver for Linux + * + * OSB4: `Open South Bridge' IDE Interface (fn 1) + * supports UDMA mode 2 (33 MB/s) + * + * CSB5: `Champion South Bridge' IDE Interface (fn 1) + * all revisions support UDMA mode 4 (66 MB/s) + * revision A2.0 and up support UDMA mode 5 (100 MB/s) + * + * *** The CSB5 does not provide ANY register *** + * *** to detect 80-conductor cable presence. *** + * + * CSB6: `Champion South Bridge' IDE Interface (optional: third channel) + * + * Documentation: + * Available under NDA only. Errata info very hard to get. + * + */ + +#include <linux/config.h> +#include <linux/types.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/ioport.h> +#include <linux/pci.h> +#include <linux/hdreg.h> +#include <linux/ide.h> +#include <linux/init.h> +#include <linux/delay.h> + +#include <asm/io.h> + +#define SVWKS_CSB5_REVISION_NEW 0x92 /* min PCI_REVISION_ID for UDMA5 (A2.0) */ +#define SVWKS_CSB6_REVISION 0xa0 /* min PCI_REVISION_ID for UDMA4 (A1.0) */ + +/* Seagate Barracuda ATA IV Family drives in UDMA mode 5 + * can overrun their FIFOs when used with the CSB5 */ +static const char *svwks_bad_ata100[] = { + "ST320011A", + "ST340016A", + "ST360021A", + "ST380021A", + NULL +}; + +static u8 svwks_revision = 0; +static struct pci_dev *isa_dev; + +static int check_in_drive_lists (ide_drive_t *drive, const char **list) +{ + while (*list) + if (!strcmp(*list++, drive->id->model)) + return 1; + return 0; +} + +static u8 svwks_ratemask (ide_drive_t *drive) +{ + struct pci_dev *dev = HWIF(drive)->pci_dev; + u8 mode; + + if (!svwks_revision) + pci_read_config_byte(dev, PCI_REVISION_ID, &svwks_revision); + + if (dev->device == PCI_DEVICE_ID_SERVERWORKS_OSB4IDE) { + u32 reg = 0; + if (isa_dev) + pci_read_config_dword(isa_dev, 0x64, ®); + + /* + * Don't enable UDMA on disk devices for the moment + */ + if(drive->media == ide_disk) + return 0; + /* Check the OSB4 DMA33 enable bit */ + return ((reg & 0x00004000) == 0x00004000) ? 1 : 0; + } else if (svwks_revision < SVWKS_CSB5_REVISION_NEW) { + return 1; + } else if (svwks_revision >= SVWKS_CSB5_REVISION_NEW) { + u8 btr = 0; + pci_read_config_byte(dev, 0x5A, &btr); + mode = btr & 0x3; + if (!eighty_ninty_three(drive)) + mode = min(mode, (u8)1); + /* If someone decides to do UDMA133 on CSB5 the same + issue will bite so be inclusive */ + if (mode > 2 && check_in_drive_lists(drive, svwks_bad_ata100)) + mode = 2; + } + if (((dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE) || + (dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2)) && + (!(PCI_FUNC(dev->devfn) & 1))) + mode = 2; + return mode; +} + +static u8 svwks_csb_check (struct pci_dev *dev) +{ + switch (dev->device) { + case PCI_DEVICE_ID_SERVERWORKS_CSB5IDE: + case PCI_DEVICE_ID_SERVERWORKS_CSB6IDE: + case PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2: + return 1; + default: + break; + } + return 0; +} +static int svwks_tune_chipset (ide_drive_t *drive, u8 xferspeed) +{ + u8 udma_modes[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 }; + u8 dma_modes[] = { 0x77, 0x21, 0x20 }; + u8 pio_modes[] = { 0x5d, 0x47, 0x34, 0x22, 0x20 }; + u8 drive_pci[] = { 0x41, 0x40, 0x43, 0x42 }; + u8 drive_pci2[] = { 0x45, 0x44, 0x47, 0x46 }; + + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + u8 speed; + u8 pio = ide_get_best_pio_mode(drive, 255, 5, NULL); + u8 unit = (drive->select.b.unit & 0x01); + u8 csb5 = svwks_csb_check(dev); + u8 ultra_enable = 0, ultra_timing = 0; + u8 dma_timing = 0, pio_timing = 0; + u16 csb5_pio = 0; + + if (xferspeed == 255) /* PIO auto-tuning */ + speed = XFER_PIO_0 + pio; + else + speed = ide_rate_filter(svwks_ratemask(drive), xferspeed); + + /* If we are about to put a disk into UDMA mode we screwed up. + Our code assumes we never _ever_ do this on an OSB4 */ + + if(dev->device == PCI_DEVICE_ID_SERVERWORKS_OSB4 && + drive->media == ide_disk && speed >= XFER_UDMA_0) + BUG(); + + pci_read_config_byte(dev, drive_pci[drive->dn], &pio_timing); + pci_read_config_byte(dev, drive_pci2[drive->dn], &dma_timing); + pci_read_config_byte(dev, (0x56|hwif->channel), &ultra_timing); + pci_read_config_word(dev, 0x4A, &csb5_pio); + pci_read_config_byte(dev, 0x54, &ultra_enable); + + /* Per Specified Design by OEM, and ASIC Architect */ + if ((dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE) || + (dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2)) { + if (!drive->init_speed) { + u8 dma_stat = hwif->INB(hwif->dma_status); + +dma_pio: + if (((ultra_enable << (7-drive->dn) & 0x80) == 0x80) && + ((dma_stat & (1<<(5+unit))) == (1<<(5+unit)))) { + drive->current_speed = drive->init_speed = XFER_UDMA_0 + udma_modes[(ultra_timing >> (4*unit)) & ~(0xF0)]; + return 0; + } else if ((dma_timing) && + ((dma_stat&(1<<(5+unit)))==(1<<(5+unit)))) { + u8 dmaspeed = dma_timing; + + dma_timing &= ~0xFF; + if ((dmaspeed & 0x20) == 0x20) + dmaspeed = XFER_MW_DMA_2; + else if ((dmaspeed & 0x21) == 0x21) + dmaspeed = XFER_MW_DMA_1; + else if ((dmaspeed & 0x77) == 0x77) + dmaspeed = XFER_MW_DMA_0; + else + goto dma_pio; + drive->current_speed = drive->init_speed = dmaspeed; + return 0; + } else if (pio_timing) { + u8 piospeed = pio_timing; + + pio_timing &= ~0xFF; + if ((piospeed & 0x20) == 0x20) + piospeed = XFER_PIO_4; + else if ((piospeed & 0x22) == 0x22) + piospeed = XFER_PIO_3; + else if ((piospeed & 0x34) == 0x34) + piospeed = XFER_PIO_2; + else if ((piospeed & 0x47) == 0x47) + piospeed = XFER_PIO_1; + else if ((piospeed & 0x5d) == 0x5d) + piospeed = XFER_PIO_0; + else + goto oem_setup_failed; + drive->current_speed = drive->init_speed = piospeed; + return 0; + } + } + } + +oem_setup_failed: + + pio_timing &= ~0xFF; + dma_timing &= ~0xFF; + ultra_timing &= ~(0x0F << (4*unit)); + ultra_enable &= ~(0x01 << drive->dn); + csb5_pio &= ~(0x0F << (4*drive->dn)); + + switch(speed) { + case XFER_PIO_4: + case XFER_PIO_3: + case XFER_PIO_2: + case XFER_PIO_1: + case XFER_PIO_0: + pio_timing |= pio_modes[speed - XFER_PIO_0]; + csb5_pio |= ((speed - XFER_PIO_0) << (4*drive->dn)); + break; + + case XFER_MW_DMA_2: + case XFER_MW_DMA_1: + case XFER_MW_DMA_0: + pio_timing |= pio_modes[pio]; + csb5_pio |= (pio << (4*drive->dn)); + dma_timing |= dma_modes[speed - XFER_MW_DMA_0]; + break; + + case XFER_UDMA_5: + case XFER_UDMA_4: + case XFER_UDMA_3: + case XFER_UDMA_2: + case XFER_UDMA_1: + case XFER_UDMA_0: + pio_timing |= pio_modes[pio]; + csb5_pio |= (pio << (4*drive->dn)); + dma_timing |= dma_modes[2]; + ultra_timing |= ((udma_modes[speed - XFER_UDMA_0]) << (4*unit)); + ultra_enable |= (0x01 << drive->dn); + default: + break; + } + + pci_write_config_byte(dev, drive_pci[drive->dn], pio_timing); + if (csb5) + pci_write_config_word(dev, 0x4A, csb5_pio); + + pci_write_config_byte(dev, drive_pci2[drive->dn], dma_timing); + pci_write_config_byte(dev, (0x56|hwif->channel), ultra_timing); + pci_write_config_byte(dev, 0x54, ultra_enable); + + return (ide_config_drive_speed(drive, speed)); +} + +static void config_chipset_for_pio (ide_drive_t *drive) +{ + u16 eide_pio_timing[6] = {960, 480, 240, 180, 120, 90}; + u16 xfer_pio = drive->id->eide_pio_modes; + u8 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) svwks_tune_chipset(drive, speed); + drive->current_speed = speed; +} + +static void svwks_tune_drive (ide_drive_t *drive, u8 pio) +{ + if(pio == 255) + (void) svwks_tune_chipset(drive, 255); + else + (void) svwks_tune_chipset(drive, (XFER_PIO_0 + pio)); +} + +static int config_chipset_for_dma (ide_drive_t *drive) +{ + u8 speed = ide_dma_speed(drive, svwks_ratemask(drive)); + + if (!(speed)) + speed = XFER_PIO_0 + ide_get_best_pio_mode(drive, 255, 5, NULL); + + (void) svwks_tune_chipset(drive, speed); + return ide_dma_enable(drive); +} + +static int svwks_config_drive_xfer_rate (ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + struct hd_driveid *id = drive->id; + + drive->init_speed = 0; + + if ((id->capability & 1) && drive->autodma) { + + if (ide_use_dma(drive)) { + if (config_chipset_for_dma(drive)) + return hwif->ide_dma_on(drive); + } + + goto fast_ata_pio; + + } else if ((id->capability & 8) || (id->field_valid & 2)) { +fast_ata_pio: + config_chipset_for_pio(drive); + // hwif->tuneproc(drive, 5); + return hwif->ide_dma_off_quietly(drive); + } + /* IORDY not supported */ + return 0; +} + +/* This can go soon */ + +static int svwks_ide_dma_end (ide_drive_t *drive) +{ + return __ide_dma_end(drive); +} + +static unsigned int __devinit init_chipset_svwks (struct pci_dev *dev, const char *name) +{ + unsigned int reg; + u8 btr; + + /* save revision id to determine DMA capability */ + pci_read_config_byte(dev, PCI_REVISION_ID, &svwks_revision); + + /* force Master Latency Timer value to 64 PCICLKs */ + pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x40); + + /* OSB4 : South Bridge and IDE */ + if (dev->device == PCI_DEVICE_ID_SERVERWORKS_OSB4IDE) { + isa_dev = pci_find_device(PCI_VENDOR_ID_SERVERWORKS, + PCI_DEVICE_ID_SERVERWORKS_OSB4, NULL); + if (isa_dev) { + pci_read_config_dword(isa_dev, 0x64, ®); + reg &= ~0x00002000; /* disable 600ns interrupt mask */ + if(!(reg & 0x00004000)) + printk(KERN_DEBUG "%s: UDMA not BIOS enabled.\n", name); + reg |= 0x00004000; /* enable UDMA/33 support */ + pci_write_config_dword(isa_dev, 0x64, reg); + } + } + + /* setup CSB5/CSB6 : South Bridge and IDE option RAID */ + else if ((dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB5IDE) || + (dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE) || + (dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2)) { + + /* Third Channel Test */ + if (!(PCI_FUNC(dev->devfn) & 1)) { + struct pci_dev * findev = NULL; + u32 reg4c = 0; + findev = pci_find_device(PCI_VENDOR_ID_SERVERWORKS, + PCI_DEVICE_ID_SERVERWORKS_CSB5, NULL); + if (findev) { + pci_read_config_dword(findev, 0x4C, ®4c); + reg4c &= ~0x000007FF; + reg4c |= 0x00000040; + reg4c |= 0x00000020; + pci_write_config_dword(findev, 0x4C, reg4c); + } + outb_p(0x06, 0x0c00); + dev->irq = inb_p(0x0c01); +#if 0 + printk("%s: device class (0x%04x)\n", + name, dev->class); + if ((dev->class >> 8) != PCI_CLASS_STORAGE_IDE) { + dev->class &= ~0x000F0F00; + // dev->class |= ~0x00000400; + dev->class |= ~0x00010100; + /**/ + } +#endif + } else { + struct pci_dev * findev = NULL; + u8 reg41 = 0; + + findev = pci_find_device(PCI_VENDOR_ID_SERVERWORKS, + PCI_DEVICE_ID_SERVERWORKS_CSB6, NULL); + if (findev) { + pci_read_config_byte(findev, 0x41, ®41); + reg41 &= ~0x40; + pci_write_config_byte(findev, 0x41, reg41); + } + /* + * This is a device pin issue on CSB6. + * Since there will be a future raid mode, + * early versions of the chipset require the + * interrupt pin to be set, and it is a compatibility + * mode issue. + */ + if ((dev->class >> 8) == PCI_CLASS_STORAGE_IDE) + dev->irq = 0; + } +// pci_read_config_dword(dev, 0x40, &pioreg) +// pci_write_config_dword(dev, 0x40, 0x99999999); +// pci_read_config_dword(dev, 0x44, &dmareg); +// pci_write_config_dword(dev, 0x44, 0xFFFFFFFF); + /* setup the UDMA Control register + * + * 1. clear bit 6 to enable DMA + * 2. enable DMA modes with bits 0-1 + * 00 : legacy + * 01 : udma2 + * 10 : udma2/udma4 + * 11 : udma2/udma4/udma5 + */ + pci_read_config_byte(dev, 0x5A, &btr); + btr &= ~0x40; + if (!(PCI_FUNC(dev->devfn) & 1)) + btr |= 0x2; + else + btr |= (svwks_revision >= SVWKS_CSB5_REVISION_NEW) ? 0x3 : 0x2; + pci_write_config_byte(dev, 0x5A, btr); + } + + return (dev->irq) ? dev->irq : 0; +} + +static unsigned int __init ata66_svwks_svwks (ide_hwif_t *hwif) +{ + return 1; +} + +/* On Dell PowerEdge servers with a CSB5/CSB6, the top two bits + * of the subsystem device ID indicate presence of an 80-pin cable. + * Bit 15 clear = secondary IDE channel does not have 80-pin cable. + * Bit 15 set = secondary IDE channel has 80-pin cable. + * Bit 14 clear = primary IDE channel does not have 80-pin cable. + * Bit 14 set = primary IDE channel has 80-pin cable. + */ +static unsigned int __init ata66_svwks_dell (ide_hwif_t *hwif) +{ + struct pci_dev *dev = hwif->pci_dev; + if (dev->subsystem_vendor == PCI_VENDOR_ID_DELL && + dev->vendor == PCI_VENDOR_ID_SERVERWORKS && + (dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB5IDE || + dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE)) + return ((1 << (hwif->channel + 14)) & + dev->subsystem_device) ? 1 : 0; + return 0; +} + +/* Sun Cobalt Alpine hardware avoids the 80-pin cable + * detect issue by attaching the drives directly to the board. + * This check follows the Dell precedent (how scary is that?!) + * + * WARNING: this only works on Alpine hardware! + */ +static unsigned int __init ata66_svwks_cobalt (ide_hwif_t *hwif) +{ + struct pci_dev *dev = hwif->pci_dev; + if (dev->subsystem_vendor == PCI_VENDOR_ID_SUN && + dev->vendor == PCI_VENDOR_ID_SERVERWORKS && + dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB5IDE) + return ((1 << (hwif->channel + 14)) & + dev->subsystem_device) ? 1 : 0; + return 0; +} + +static unsigned int __init ata66_svwks (ide_hwif_t *hwif) +{ + struct pci_dev *dev = hwif->pci_dev; + + /* Per Specified Design by OEM, and ASIC Architect */ + if ((dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE) || + (dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2)) + return 1; + + /* Server Works */ + if (dev->subsystem_vendor == PCI_VENDOR_ID_SERVERWORKS) + return ata66_svwks_svwks (hwif); + + /* Dell PowerEdge */ + if (dev->subsystem_vendor == PCI_VENDOR_ID_DELL) + return ata66_svwks_dell (hwif); + + /* Cobalt Alpine */ + if (dev->subsystem_vendor == PCI_VENDOR_ID_SUN) + return ata66_svwks_cobalt (hwif); + + return 0; +} + +#undef CAN_SW_DMA +static void __devinit init_hwif_svwks (ide_hwif_t *hwif) +{ + u8 dma_stat = 0; + + if (!hwif->irq) + hwif->irq = hwif->channel ? 15 : 14; + + hwif->tuneproc = &svwks_tune_drive; + hwif->speedproc = &svwks_tune_chipset; + + hwif->atapi_dma = 1; + + if (hwif->pci_dev->device != PCI_DEVICE_ID_SERVERWORKS_OSB4IDE) + hwif->ultra_mask = 0x3f; + + hwif->mwdma_mask = 0x07; +#ifdef CAN_SW_DMA + hwif->swdma_mask = 0x07; +#endif /* CAN_SW_DMA */ + + hwif->autodma = 0; + + if (!hwif->dma_base) { + hwif->drives[0].autotune = 1; + hwif->drives[1].autotune = 1; + return; + } + + hwif->ide_dma_check = &svwks_config_drive_xfer_rate; + if (hwif->pci_dev->device == PCI_DEVICE_ID_SERVERWORKS_OSB4IDE) + hwif->ide_dma_end = &svwks_ide_dma_end; + else if (!(hwif->udma_four)) + hwif->udma_four = ata66_svwks(hwif); + if (!noautodma) + hwif->autodma = 1; + + dma_stat = hwif->INB(hwif->dma_status); + hwif->drives[0].autodma = (dma_stat & 0x20); + hwif->drives[1].autodma = (dma_stat & 0x40); + hwif->drives[0].autotune = (!(dma_stat & 0x20)); + hwif->drives[1].autotune = (!(dma_stat & 0x40)); +// hwif->drives[0].autodma = hwif->autodma; +// hwif->drives[1].autodma = hwif->autodma; +} + +/* + * We allow the BM-DMA driver to only work on enabled interfaces. + */ +static void __devinit init_dma_svwks (ide_hwif_t *hwif, unsigned long dmabase) +{ + struct pci_dev *dev = hwif->pci_dev; + + if (((dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE) || + (dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2)) && + (!(PCI_FUNC(dev->devfn) & 1)) && (hwif->channel)) + return; + + ide_setup_dma(hwif, dmabase, 8); +} + +static int __devinit init_setup_svwks (struct pci_dev *dev, ide_pci_device_t *d) +{ + return ide_setup_pci_device(dev, d); +} + +static int __init init_setup_csb6 (struct pci_dev *dev, ide_pci_device_t *d) +{ + if (!(PCI_FUNC(dev->devfn) & 1)) { + d->bootable = NEVER_BOARD; + if (dev->resource[0].start == 0x01f1) + d->bootable = ON_BOARD; + } +#if 0 + if ((IDE_PCI_DEVID_EQ(d->devid, DEVID_CSB6) && + (!(PCI_FUNC(dev->devfn) & 1))) + d->autodma = AUTODMA; +#endif + + d->channels = ((dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE || + dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2) && + (!(PCI_FUNC(dev->devfn) & 1))) ? 1 : 2; + + return ide_setup_pci_device(dev, d); +} + +static ide_pci_device_t serverworks_chipsets[] __devinitdata = { + { /* 0 */ + .name = "SvrWks OSB4", + .init_setup = init_setup_svwks, + .init_chipset = init_chipset_svwks, + .init_hwif = init_hwif_svwks, + .channels = 2, + .autodma = AUTODMA, + .bootable = ON_BOARD, + },{ /* 1 */ + .name = "SvrWks CSB5", + .init_setup = init_setup_svwks, + .init_chipset = init_chipset_svwks, + .init_hwif = init_hwif_svwks, + .init_dma = init_dma_svwks, + .channels = 2, + .autodma = AUTODMA, + .bootable = ON_BOARD, + },{ /* 2 */ + .name = "SvrWks CSB6", + .init_setup = init_setup_csb6, + .init_chipset = init_chipset_svwks, + .init_hwif = init_hwif_svwks, + .init_dma = init_dma_svwks, + .channels = 2, + .autodma = AUTODMA, + .bootable = ON_BOARD, + },{ /* 3 */ + .name = "SvrWks CSB6", + .init_setup = init_setup_csb6, + .init_chipset = init_chipset_svwks, + .init_hwif = init_hwif_svwks, + .init_dma = init_dma_svwks, + .channels = 1, /* 2 */ + .autodma = AUTODMA, + .bootable = ON_BOARD, + } +}; + +/** + * svwks_init_one - called when a OSB/CSB is found + * @dev: the svwks device + * @id: the matching pci id + * + * Called when the PCI registration layer (or the IDE initialization) + * finds a device matching our IDE device tables. + */ + +static int __devinit svwks_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + ide_pci_device_t *d = &serverworks_chipsets[id->driver_data]; + + return d->init_setup(dev, d); +} + +static struct pci_device_id svwks_pci_tbl[] = { + { PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_OSB4IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB5IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1}, + { PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB6IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2}, + { PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 3}, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, svwks_pci_tbl); + +static struct pci_driver driver = { + .name = "Serverworks_IDE", + .id_table = svwks_pci_tbl, + .probe = svwks_init_one, +}; + +static int svwks_ide_init(void) +{ + return ide_pci_register_driver(&driver); +} + +module_init(svwks_ide_init); + +MODULE_AUTHOR("Michael Aubry. Andrzej Krzysztofowicz, Andre Hedrick"); +MODULE_DESCRIPTION("PCI driver module for Serverworks OSB4/CSB5/CSB6 IDE"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/pci/sgiioc4.c b/drivers/ide/pci/sgiioc4.c new file mode 100644 index 0000000..4651a22 --- /dev/null +++ b/drivers/ide/pci/sgiioc4.c @@ -0,0 +1,728 @@ +/* + * Copyright (c) 2003 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, + * Mountain View, CA 94043, or: + * + * http://www.sgi.com + * + * For further information regarding this notice, see: + * + * http://oss.sgi.com/projects/GenInfo/NoticeExplan + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/hdreg.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/timer.h> +#include <linux/mm.h> +#include <linux/ioport.h> +#include <linux/blkdev.h> +#include <linux/ioc4_common.h> +#include <asm/io.h> + +#include <linux/ide.h> + +/* IOC4 Specific Definitions */ +#define IOC4_CMD_OFFSET 0x100 +#define IOC4_CTRL_OFFSET 0x120 +#define IOC4_DMA_OFFSET 0x140 +#define IOC4_INTR_OFFSET 0x0 + +#define IOC4_TIMING 0x00 +#define IOC4_DMA_PTR_L 0x01 +#define IOC4_DMA_PTR_H 0x02 +#define IOC4_DMA_ADDR_L 0x03 +#define IOC4_DMA_ADDR_H 0x04 +#define IOC4_BC_DEV 0x05 +#define IOC4_BC_MEM 0x06 +#define IOC4_DMA_CTRL 0x07 +#define IOC4_DMA_END_ADDR 0x08 + +/* Bits in the IOC4 Control/Status Register */ +#define IOC4_S_DMA_START 0x01 +#define IOC4_S_DMA_STOP 0x02 +#define IOC4_S_DMA_DIR 0x04 +#define IOC4_S_DMA_ACTIVE 0x08 +#define IOC4_S_DMA_ERROR 0x10 +#define IOC4_ATA_MEMERR 0x02 + +/* Read/Write Directions */ +#define IOC4_DMA_WRITE 0x04 +#define IOC4_DMA_READ 0x00 + +/* Interrupt Register Offsets */ +#define IOC4_INTR_REG 0x03 +#define IOC4_INTR_SET 0x05 +#define IOC4_INTR_CLEAR 0x07 + +#define IOC4_IDE_CACHELINE_SIZE 128 +#define IOC4_CMD_CTL_BLK_SIZE 0x20 +#define IOC4_SUPPORTED_FIRMWARE_REV 46 + +typedef struct { + u32 timing_reg0; + u32 timing_reg1; + u32 low_mem_ptr; + u32 high_mem_ptr; + u32 low_mem_addr; + u32 high_mem_addr; + u32 dev_byte_count; + u32 mem_byte_count; + u32 status; +} ioc4_dma_regs_t; + +/* Each Physical Region Descriptor Entry size is 16 bytes (2 * 64 bits) */ +/* IOC4 has only 1 IDE channel */ +#define IOC4_PRD_BYTES 16 +#define IOC4_PRD_ENTRIES (PAGE_SIZE /(4*IOC4_PRD_BYTES)) + + +static void +sgiioc4_init_hwif_ports(hw_regs_t * hw, unsigned long data_port, + unsigned long ctrl_port, unsigned long irq_port) +{ + unsigned long reg = data_port; + int i; + + /* Registers are word (32 bit) aligned */ + for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) + hw->io_ports[i] = reg + i * 4; + + if (ctrl_port) + hw->io_ports[IDE_CONTROL_OFFSET] = ctrl_port; + + if (irq_port) + hw->io_ports[IDE_IRQ_OFFSET] = irq_port; +} + +static void +sgiioc4_maskproc(ide_drive_t * drive, int mask) +{ + ide_hwif_t *hwif = HWIF(drive); + hwif->OUTB(mask ? (drive->ctl | 2) : (drive->ctl & ~2), + IDE_CONTROL_REG); +} + + +static int +sgiioc4_checkirq(ide_hwif_t * hwif) +{ + u8 intr_reg = + hwif->INL(hwif->io_ports[IDE_IRQ_OFFSET] + IOC4_INTR_REG * 4); + + if (intr_reg & 0x03) + return 1; + + return 0; +} + + +static int +sgiioc4_clearirq(ide_drive_t * drive) +{ + u32 intr_reg; + ide_hwif_t *hwif = HWIF(drive); + unsigned long other_ir = + hwif->io_ports[IDE_IRQ_OFFSET] + (IOC4_INTR_REG << 2); + + /* Code to check for PCI error conditions */ + intr_reg = hwif->INL(other_ir); + if (intr_reg & 0x03) { /* Valid IOC4-IDE interrupt */ + /* + * Using hwif->INB to read the IDE_STATUS_REG has a side effect + * of clearing the interrupt. The first read should clear it + * if it is set. The second read should return a "clear" status + * if it got cleared. If not, then spin for a bit trying to + * clear it. + */ + u8 stat = hwif->INB(IDE_STATUS_REG); + int count = 0; + stat = hwif->INB(IDE_STATUS_REG); + while ((stat & 0x80) && (count++ < 100)) { + udelay(1); + stat = hwif->INB(IDE_STATUS_REG); + } + + if (intr_reg & 0x02) { + /* Error when transferring DMA data on PCI bus */ + u32 pci_err_addr_low, pci_err_addr_high, + pci_stat_cmd_reg; + + pci_err_addr_low = + hwif->INL(hwif->io_ports[IDE_IRQ_OFFSET]); + pci_err_addr_high = + hwif->INL(hwif->io_ports[IDE_IRQ_OFFSET] + 4); + pci_read_config_dword(hwif->pci_dev, PCI_COMMAND, + &pci_stat_cmd_reg); + printk(KERN_ERR + "%s(%s) : PCI Bus Error when doing DMA:" + " status-cmd reg is 0x%x\n", + __FUNCTION__, drive->name, pci_stat_cmd_reg); + printk(KERN_ERR + "%s(%s) : PCI Error Address is 0x%x%x\n", + __FUNCTION__, drive->name, + pci_err_addr_high, pci_err_addr_low); + /* Clear the PCI Error indicator */ + pci_write_config_dword(hwif->pci_dev, PCI_COMMAND, + 0x00000146); + } + + /* Clear the Interrupt, Error bits on the IOC4 */ + hwif->OUTL(0x03, other_ir); + + intr_reg = hwif->INL(other_ir); + } + + return intr_reg & 3; +} + +static void sgiioc4_ide_dma_start(ide_drive_t * drive) +{ + ide_hwif_t *hwif = HWIF(drive); + unsigned int reg = hwif->INL(hwif->dma_base + IOC4_DMA_CTRL * 4); + unsigned int temp_reg = reg | IOC4_S_DMA_START; + + hwif->OUTL(temp_reg, hwif->dma_base + IOC4_DMA_CTRL * 4); +} + +static u32 +sgiioc4_ide_dma_stop(ide_hwif_t *hwif, u64 dma_base) +{ + u32 ioc4_dma; + int count; + + count = 0; + ioc4_dma = hwif->INL(dma_base + IOC4_DMA_CTRL * 4); + while ((ioc4_dma & IOC4_S_DMA_STOP) && (count++ < 200)) { + udelay(1); + ioc4_dma = hwif->INL(dma_base + IOC4_DMA_CTRL * 4); + } + return ioc4_dma; +} + +/* Stops the IOC4 DMA Engine */ +static int +sgiioc4_ide_dma_end(ide_drive_t * drive) +{ + u32 ioc4_dma, bc_dev, bc_mem, num, valid = 0, cnt = 0; + ide_hwif_t *hwif = HWIF(drive); + u64 dma_base = hwif->dma_base; + int dma_stat = 0; + unsigned long *ending_dma = (unsigned long *) hwif->dma_base2; + + hwif->OUTL(IOC4_S_DMA_STOP, dma_base + IOC4_DMA_CTRL * 4); + + ioc4_dma = sgiioc4_ide_dma_stop(hwif, dma_base); + + if (ioc4_dma & IOC4_S_DMA_STOP) { + printk(KERN_ERR + "%s(%s): IOC4 DMA STOP bit is still 1 :" + "ioc4_dma_reg 0x%x\n", + __FUNCTION__, drive->name, ioc4_dma); + dma_stat = 1; + } + + /* + * The IOC4 will DMA 1's to the ending dma area to indicate that + * previous data DMA is complete. This is necessary because of relaxed + * ordering between register reads and DMA writes on the Altix. + */ + while ((cnt++ < 200) && (!valid)) { + for (num = 0; num < 16; num++) { + if (ending_dma[num]) { + valid = 1; + break; + } + } + udelay(1); + } + if (!valid) { + printk(KERN_ERR "%s(%s) : DMA incomplete\n", __FUNCTION__, + drive->name); + dma_stat = 1; + } + + bc_dev = hwif->INL(dma_base + IOC4_BC_DEV * 4); + bc_mem = hwif->INL(dma_base + IOC4_BC_MEM * 4); + + if ((bc_dev & 0x01FF) || (bc_mem & 0x1FF)) { + if (bc_dev > bc_mem + 8) { + printk(KERN_ERR + "%s(%s): WARNING!! byte_count_dev %d " + "!= byte_count_mem %d\n", + __FUNCTION__, drive->name, bc_dev, bc_mem); + } + } + + drive->waiting_for_dma = 0; + ide_destroy_dmatable(drive); + + return dma_stat; +} + +static int +sgiioc4_ide_dma_check(ide_drive_t * drive) +{ + if (ide_config_drive_speed(drive, XFER_MW_DMA_2) != 0) { + printk(KERN_INFO + "Couldnot set %s in Multimode-2 DMA mode | " + "Drive %s using PIO instead\n", + drive->name, drive->name); + drive->using_dma = 0; + } else + drive->using_dma = 1; + + return 0; +} + +static int +sgiioc4_ide_dma_on(ide_drive_t * drive) +{ + drive->using_dma = 1; + + return HWIF(drive)->ide_dma_host_on(drive); +} + +static int +sgiioc4_ide_dma_off_quietly(ide_drive_t * drive) +{ + drive->using_dma = 0; + + return HWIF(drive)->ide_dma_host_off(drive); +} + +/* returns 1 if dma irq issued, 0 otherwise */ +static int +sgiioc4_ide_dma_test_irq(ide_drive_t * drive) +{ + return sgiioc4_checkirq(HWIF(drive)); +} + +static int +sgiioc4_ide_dma_host_on(ide_drive_t * drive) +{ + if (drive->using_dma) + return 0; + + return 1; +} + +static int +sgiioc4_ide_dma_host_off(ide_drive_t * drive) +{ + sgiioc4_clearirq(drive); + + return 0; +} + +static int +sgiioc4_ide_dma_lostirq(ide_drive_t * drive) +{ + HWIF(drive)->resetproc(drive); + + return __ide_dma_lostirq(drive); +} + +static void +sgiioc4_resetproc(ide_drive_t * drive) +{ + sgiioc4_ide_dma_end(drive); + sgiioc4_clearirq(drive); +} + +static u8 +sgiioc4_INB(unsigned long port) +{ + u8 reg = (u8) inb(port); + + if ((port & 0xFFF) == 0x11C) { /* Status register of IOC4 */ + if (reg & 0x51) { /* Not busy...check for interrupt */ + unsigned long other_ir = port - 0x110; + unsigned int intr_reg = (u32) inl(other_ir); + + /* Clear the Interrupt, Error bits on the IOC4 */ + if (intr_reg & 0x03) { + outl(0x03, other_ir); + intr_reg = (u32) inl(other_ir); + } + } + } + + return reg; +} + +/* Creates a dma map for the scatter-gather list entries */ +static void __devinit +ide_dma_sgiioc4(ide_hwif_t * hwif, unsigned long dma_base) +{ + int num_ports = sizeof (ioc4_dma_regs_t); + + printk(KERN_INFO "%s: BM-DMA at 0x%04lx-0x%04lx\n", hwif->name, + dma_base, dma_base + num_ports - 1); + + if (!request_region(dma_base, num_ports, hwif->name)) { + printk(KERN_ERR + "%s(%s) -- ERROR, Addresses 0x%p to 0x%p " + "ALREADY in use\n", + __FUNCTION__, hwif->name, (void *) dma_base, + (void *) dma_base + num_ports - 1); + goto dma_alloc_failure; + } + + hwif->dma_base = dma_base; + hwif->dmatable_cpu = pci_alloc_consistent(hwif->pci_dev, + IOC4_PRD_ENTRIES * IOC4_PRD_BYTES, + &hwif->dmatable_dma); + + if (!hwif->dmatable_cpu) + goto dma_alloc_failure; + + hwif->sg_max_nents = IOC4_PRD_ENTRIES; + + hwif->dma_base2 = (unsigned long) + pci_alloc_consistent(hwif->pci_dev, + IOC4_IDE_CACHELINE_SIZE, + (dma_addr_t *) &(hwif->dma_status)); + + if (!hwif->dma_base2) + goto dma_base2alloc_failure; + + return; + +dma_base2alloc_failure: + pci_free_consistent(hwif->pci_dev, + IOC4_PRD_ENTRIES * IOC4_PRD_BYTES, + hwif->dmatable_cpu, hwif->dmatable_dma); + printk(KERN_INFO + "%s() -- Error! Unable to allocate DMA Maps for drive %s\n", + __FUNCTION__, hwif->name); + printk(KERN_INFO + "Changing from DMA to PIO mode for Drive %s\n", hwif->name); + +dma_alloc_failure: + /* Disable DMA because we couldnot allocate any DMA maps */ + hwif->autodma = 0; + hwif->atapi_dma = 0; +} + +/* Initializes the IOC4 DMA Engine */ +static void +sgiioc4_configure_for_dma(int dma_direction, ide_drive_t * drive) +{ + u32 ioc4_dma; + ide_hwif_t *hwif = HWIF(drive); + u64 dma_base = hwif->dma_base; + u32 dma_addr, ending_dma_addr; + + ioc4_dma = hwif->INL(dma_base + IOC4_DMA_CTRL * 4); + + if (ioc4_dma & IOC4_S_DMA_ACTIVE) { + printk(KERN_WARNING + "%s(%s):Warning!! DMA from previous transfer was still active\n", + __FUNCTION__, drive->name); + hwif->OUTL(IOC4_S_DMA_STOP, dma_base + IOC4_DMA_CTRL * 4); + ioc4_dma = sgiioc4_ide_dma_stop(hwif, dma_base); + + if (ioc4_dma & IOC4_S_DMA_STOP) + printk(KERN_ERR + "%s(%s) : IOC4 Dma STOP bit is still 1\n", + __FUNCTION__, drive->name); + } + + ioc4_dma = hwif->INL(dma_base + IOC4_DMA_CTRL * 4); + if (ioc4_dma & IOC4_S_DMA_ERROR) { + printk(KERN_WARNING + "%s(%s) : Warning!! - DMA Error during Previous" + " transfer | status 0x%x\n", + __FUNCTION__, drive->name, ioc4_dma); + hwif->OUTL(IOC4_S_DMA_STOP, dma_base + IOC4_DMA_CTRL * 4); + ioc4_dma = sgiioc4_ide_dma_stop(hwif, dma_base); + + if (ioc4_dma & IOC4_S_DMA_STOP) + printk(KERN_ERR + "%s(%s) : IOC4 DMA STOP bit is still 1\n", + __FUNCTION__, drive->name); + } + + /* Address of the Scatter Gather List */ + dma_addr = cpu_to_le32(hwif->dmatable_dma); + hwif->OUTL(dma_addr, dma_base + IOC4_DMA_PTR_L * 4); + + /* Address of the Ending DMA */ + memset((unsigned int *) hwif->dma_base2, 0, IOC4_IDE_CACHELINE_SIZE); + ending_dma_addr = cpu_to_le32(hwif->dma_status); + hwif->OUTL(ending_dma_addr, dma_base + IOC4_DMA_END_ADDR * 4); + + hwif->OUTL(dma_direction, dma_base + IOC4_DMA_CTRL * 4); + drive->waiting_for_dma = 1; +} + +/* IOC4 Scatter Gather list Format */ +/* 128 Bit entries to support 64 bit addresses in the future */ +/* The Scatter Gather list Entry should be in the BIG-ENDIAN Format */ +/* --------------------------------------------------------------------- */ +/* | Upper 32 bits - Zero | Lower 32 bits- address | */ +/* --------------------------------------------------------------------- */ +/* | Upper 32 bits - Zero |EOL| 15 unused | 16 Bit Length| */ +/* --------------------------------------------------------------------- */ +/* Creates the scatter gather list, DMA Table */ +static unsigned int +sgiioc4_build_dma_table(ide_drive_t * drive, struct request *rq, int ddir) +{ + ide_hwif_t *hwif = HWIF(drive); + unsigned int *table = hwif->dmatable_cpu; + unsigned int count = 0, i = 1; + struct scatterlist *sg; + + hwif->sg_nents = i = ide_build_sglist(drive, rq); + + if (!i) + return 0; /* sglist of length Zero */ + + sg = hwif->sg_table; + while (i && sg_dma_len(sg)) { + dma_addr_t cur_addr; + int cur_len; + cur_addr = sg_dma_address(sg); + cur_len = sg_dma_len(sg); + + while (cur_len) { + if (count++ >= IOC4_PRD_ENTRIES) { + printk(KERN_WARNING + "%s: DMA table too small\n", + drive->name); + goto use_pio_instead; + } else { + u32 xcount, bcount = + 0x10000 - (cur_addr & 0xffff); + + if (bcount > cur_len) + bcount = cur_len; + + /* put the addr, length in + * the IOC4 dma-table format */ + *table = 0x0; + table++; + *table = cpu_to_be32(cur_addr); + table++; + *table = 0x0; + table++; + + xcount = bcount & 0xffff; + *table = cpu_to_be32(xcount); + table++; + + cur_addr += bcount; + cur_len -= bcount; + } + } + + sg++; + i--; + } + + if (count) { + table--; + *table |= cpu_to_be32(0x80000000); + return count; + } + +use_pio_instead: + pci_unmap_sg(hwif->pci_dev, hwif->sg_table, hwif->sg_nents, + hwif->sg_dma_direction); + + return 0; /* revert to PIO for this request */ +} + +static int sgiioc4_ide_dma_setup(ide_drive_t *drive) +{ + struct request *rq = HWGROUP(drive)->rq; + unsigned int count = 0; + int ddir; + + if (rq_data_dir(rq)) + ddir = PCI_DMA_TODEVICE; + else + ddir = PCI_DMA_FROMDEVICE; + + if (!(count = sgiioc4_build_dma_table(drive, rq, ddir))) { + /* try PIO instead of DMA */ + ide_map_sg(drive, rq); + return 1; + } + + if (rq_data_dir(rq)) + /* Writes TO the IOC4 FROM Main Memory */ + ddir = IOC4_DMA_READ; + else + /* Writes FROM the IOC4 TO Main Memory */ + ddir = IOC4_DMA_WRITE; + + sgiioc4_configure_for_dma(ddir, drive); + + return 0; +} + +static void __devinit +ide_init_sgiioc4(ide_hwif_t * hwif) +{ + hwif->mmio = 2; + hwif->autodma = 1; + hwif->atapi_dma = 1; + hwif->ultra_mask = 0x0; /* Disable Ultra DMA */ + hwif->mwdma_mask = 0x2; /* Multimode-2 DMA */ + hwif->swdma_mask = 0x2; + hwif->tuneproc = NULL; /* Sets timing for PIO mode */ + hwif->speedproc = NULL; /* Sets timing for DMA &/or PIO modes */ + hwif->selectproc = NULL;/* Use the default routine to select drive */ + hwif->reset_poll = NULL;/* No HBA specific reset_poll needed */ + hwif->pre_reset = NULL; /* No HBA specific pre_set needed */ + hwif->resetproc = &sgiioc4_resetproc;/* Reset DMA engine, + clear interrupts */ + hwif->intrproc = NULL; /* Enable or Disable interrupt from drive */ + hwif->maskproc = &sgiioc4_maskproc; /* Mask on/off NIEN register */ + hwif->quirkproc = NULL; + hwif->busproc = NULL; + + hwif->dma_setup = &sgiioc4_ide_dma_setup; + hwif->dma_start = &sgiioc4_ide_dma_start; + hwif->ide_dma_end = &sgiioc4_ide_dma_end; + hwif->ide_dma_check = &sgiioc4_ide_dma_check; + hwif->ide_dma_on = &sgiioc4_ide_dma_on; + hwif->ide_dma_off_quietly = &sgiioc4_ide_dma_off_quietly; + hwif->ide_dma_test_irq = &sgiioc4_ide_dma_test_irq; + hwif->ide_dma_host_on = &sgiioc4_ide_dma_host_on; + hwif->ide_dma_host_off = &sgiioc4_ide_dma_host_off; + hwif->ide_dma_lostirq = &sgiioc4_ide_dma_lostirq; + hwif->ide_dma_timeout = &__ide_dma_timeout; + hwif->INB = &sgiioc4_INB; +} + +static int __devinit +sgiioc4_ide_setup_pci_device(struct pci_dev *dev, ide_pci_device_t * d) +{ + unsigned long base, ctl, dma_base, irqport; + ide_hwif_t *hwif; + int h; + + for (h = 0; h < MAX_HWIFS; ++h) { + hwif = &ide_hwifs[h]; + /* Find an empty HWIF */ + if (hwif->chipset == ide_unknown) + break; + } + + /* Get the CmdBlk and CtrlBlk Base Registers */ + base = pci_resource_start(dev, 0) + IOC4_CMD_OFFSET; + ctl = pci_resource_start(dev, 0) + IOC4_CTRL_OFFSET; + irqport = pci_resource_start(dev, 0) + IOC4_INTR_OFFSET; + dma_base = pci_resource_start(dev, 0) + IOC4_DMA_OFFSET; + + if (!request_region(base, IOC4_CMD_CTL_BLK_SIZE, hwif->name)) { + printk(KERN_ERR + "%s : %s -- ERROR, Port Addresses " + "0x%p to 0x%p ALREADY in use\n", + __FUNCTION__, hwif->name, (void *) base, + (void *) base + IOC4_CMD_CTL_BLK_SIZE); + return -ENOMEM; + } + + if (hwif->io_ports[IDE_DATA_OFFSET] != base) { + /* Initialize the IO registers */ + sgiioc4_init_hwif_ports(&hwif->hw, base, ctl, irqport); + memcpy(hwif->io_ports, hwif->hw.io_ports, + sizeof (hwif->io_ports)); + hwif->noprobe = !hwif->io_ports[IDE_DATA_OFFSET]; + } + + hwif->irq = dev->irq; + hwif->chipset = ide_pci; + hwif->pci_dev = dev; + hwif->channel = 0; /* Single Channel chip */ + hwif->cds = (struct ide_pci_device_s *) d; + hwif->gendev.parent = &dev->dev;/* setup proper ancestral information */ + + /* Initializing chipset IRQ Registers */ + hwif->OUTL(0x03, irqport + IOC4_INTR_SET * 4); + + ide_init_sgiioc4(hwif); + + if (dma_base) + ide_dma_sgiioc4(hwif, dma_base); + else + printk(KERN_INFO "%s: %s Bus-Master DMA disabled\n", + hwif->name, d->name); + + if (probe_hwif_init(hwif)) + return -EIO; + + /* Create /proc/ide entries */ + create_proc_ide_interfaces(); + + return 0; +} + +static unsigned int __devinit +pci_init_sgiioc4(struct pci_dev *dev, ide_pci_device_t * d) +{ + unsigned int class_rev; + int ret; + + pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev); + class_rev &= 0xff; + printk(KERN_INFO "%s: IDE controller at PCI slot %s, revision %d\n", + d->name, pci_name(dev), class_rev); + if (class_rev < IOC4_SUPPORTED_FIRMWARE_REV) { + printk(KERN_ERR "Skipping %s IDE controller in slot %s: " + "firmware is obsolete - please upgrade to revision" + "46 or higher\n", d->name, pci_name(dev)); + ret = -EAGAIN; + goto out; + } + ret = sgiioc4_ide_setup_pci_device(dev, d); +out: + return ret; +} + +static ide_pci_device_t sgiioc4_chipsets[] __devinitdata = { + { + /* Channel 0 */ + .name = "SGIIOC4", + .init_hwif = ide_init_sgiioc4, + .init_dma = ide_dma_sgiioc4, + .channels = 1, + .autodma = AUTODMA, + /* SGI IOC4 doesn't have enablebits. */ + .bootable = ON_BOARD, + } +}; + +int +ioc4_ide_attach_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + return pci_init_sgiioc4(dev, &sgiioc4_chipsets[id->driver_data]); +} + + +MODULE_AUTHOR("Aniket Malatpure - Silicon Graphics Inc. (SGI)"); +MODULE_DESCRIPTION("IDE PCI driver module for SGI IOC4 Base-IO Card"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(ioc4_ide_attach_one); diff --git a/drivers/ide/pci/siimage.c b/drivers/ide/pci/siimage.c new file mode 100644 index 0000000..2b9961b --- /dev/null +++ b/drivers/ide/pci/siimage.c @@ -0,0 +1,1133 @@ +/* + * linux/drivers/ide/pci/siimage.c Version 1.07 Nov 30, 2003 + * + * Copyright (C) 2001-2002 Andre Hedrick <andre@linux-ide.org> + * Copyright (C) 2003 Red Hat <alan@redhat.com> + * + * May be copied or modified under the terms of the GNU General Public License + * + * Documentation available under NDA only + * + * + * FAQ Items: + * If you are using Marvell SATA-IDE adapters with Maxtor drives + * ensure the system is set up for ATA100/UDMA5 not UDMA6. + * + * If you are using WD drives with SATA bridges you must set the + * drive to "Single". "Master" will hang + * + * If you have strange problems with nVidia chipset systems please + * see the SI support documentation and update your system BIOS + * if neccessary + */ + +#include <linux/config.h> +#include <linux/types.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/hdreg.h> +#include <linux/ide.h> +#include <linux/init.h> + +#include <asm/io.h> + +#undef SIIMAGE_VIRTUAL_DMAPIO +#undef SIIMAGE_LARGE_DMA + +/** + * pdev_is_sata - check if device is SATA + * @pdev: PCI device to check + * + * Returns true if this is a SATA controller + */ + +static int pdev_is_sata(struct pci_dev *pdev) +{ + switch(pdev->device) + { + case PCI_DEVICE_ID_SII_3112: + case PCI_DEVICE_ID_SII_1210SA: + return 1; + case PCI_DEVICE_ID_SII_680: + return 0; + } + BUG(); + return 0; +} + +/** + * is_sata - check if hwif is SATA + * @hwif: interface to check + * + * Returns true if this is a SATA controller + */ + +static inline int is_sata(ide_hwif_t *hwif) +{ + return pdev_is_sata(hwif->pci_dev); +} + +/** + * siimage_selreg - return register base + * @hwif: interface + * @r: config offset + * + * Turn a config register offset into the right address in either + * PCI space or MMIO space to access the control register in question + * Thankfully this is a configuration operation so isnt performance + * criticial. + */ + +static unsigned long siimage_selreg(ide_hwif_t *hwif, int r) +{ + unsigned long base = (unsigned long)hwif->hwif_data; + base += 0xA0 + r; + if(hwif->mmio) + base += (hwif->channel << 6); + else + base += (hwif->channel << 4); + return base; +} + +/** + * siimage_seldev - return register base + * @hwif: interface + * @r: config offset + * + * Turn a config register offset into the right address in either + * PCI space or MMIO space to access the control register in question + * including accounting for the unit shift. + */ + +static inline unsigned long siimage_seldev(ide_drive_t *drive, int r) +{ + ide_hwif_t *hwif = HWIF(drive); + unsigned long base = (unsigned long)hwif->hwif_data; + base += 0xA0 + r; + if(hwif->mmio) + base += (hwif->channel << 6); + else + base += (hwif->channel << 4); + base |= drive->select.b.unit << drive->select.b.unit; + return base; +} + +/** + * siimage_ratemask - Compute available modes + * @drive: IDE drive + * + * Compute the available speeds for the devices on the interface. + * For the CMD680 this depends on the clocking mode (scsc), for the + * SI3312 SATA controller life is a bit simpler. Enforce UDMA33 + * as a limit if there is no 80pin cable present. + */ + +static byte siimage_ratemask (ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + u8 mode = 0, scsc = 0; + unsigned long base = (unsigned long) hwif->hwif_data; + + if (hwif->mmio) + scsc = hwif->INB(base + 0x4A); + else + pci_read_config_byte(hwif->pci_dev, 0x8A, &scsc); + + if(is_sata(hwif)) + { + if(strstr(drive->id->model, "Maxtor")) + return 3; + return 4; + } + + if ((scsc & 0x30) == 0x10) /* 133 */ + mode = 4; + else if ((scsc & 0x30) == 0x20) /* 2xPCI */ + mode = 4; + else if ((scsc & 0x30) == 0x00) /* 100 */ + mode = 3; + else /* Disabled ? */ + BUG(); + + if (!eighty_ninty_three(drive)) + mode = min(mode, (u8)1); + return mode; +} + +/** + * siimage_taskfile_timing - turn timing data to a mode + * @hwif: interface to query + * + * Read the timing data for the interface and return the + * mode that is being used. + */ + +static byte siimage_taskfile_timing (ide_hwif_t *hwif) +{ + u16 timing = 0x328a; + unsigned long addr = siimage_selreg(hwif, 2); + + if (hwif->mmio) + timing = hwif->INW(addr); + else + pci_read_config_word(hwif->pci_dev, addr, &timing); + + switch (timing) { + case 0x10c1: return 4; + case 0x10c3: return 3; + case 0x1104: + case 0x1281: return 2; + case 0x2283: return 1; + case 0x328a: + default: return 0; + } +} + +/** + * simmage_tuneproc - tune a drive + * @drive: drive to tune + * @mode_wanted: the target operating mode + * + * Load the timing settings for this device mode into the + * controller. If we are in PIO mode 3 or 4 turn on IORDY + * monitoring (bit 9). The TF timing is bits 31:16 + */ + +static void siimage_tuneproc (ide_drive_t *drive, byte mode_wanted) +{ + ide_hwif_t *hwif = HWIF(drive); + u32 speedt = 0; + u16 speedp = 0; + unsigned long addr = siimage_seldev(drive, 0x04); + unsigned long tfaddr = siimage_selreg(hwif, 0x02); + + /* cheat for now and use the docs */ + switch(mode_wanted) { + case 4: + speedp = 0x10c1; + speedt = 0x10c1; + break; + case 3: + speedp = 0x10C3; + speedt = 0x10C3; + break; + case 2: + speedp = 0x1104; + speedt = 0x1281; + break; + case 1: + speedp = 0x2283; + speedt = 0x1281; + break; + case 0: + default: + speedp = 0x328A; + speedt = 0x328A; + break; + } + if (hwif->mmio) + { + hwif->OUTW(speedt, addr); + hwif->OUTW(speedp, tfaddr); + /* Now set up IORDY */ + if(mode_wanted == 3 || mode_wanted == 4) + hwif->OUTW(hwif->INW(tfaddr-2)|0x200, tfaddr-2); + else + hwif->OUTW(hwif->INW(tfaddr-2)&~0x200, tfaddr-2); + } + else + { + pci_write_config_word(hwif->pci_dev, addr, speedp); + pci_write_config_word(hwif->pci_dev, tfaddr, speedt); + pci_read_config_word(hwif->pci_dev, tfaddr-2, &speedp); + speedp &= ~0x200; + /* Set IORDY for mode 3 or 4 */ + if(mode_wanted == 3 || mode_wanted == 4) + speedp |= 0x200; + pci_write_config_word(hwif->pci_dev, tfaddr-2, speedp); + } +} + +/** + * config_siimage_chipset_for_pio - set drive timings + * @drive: drive to tune + * @speed we want + * + * Compute the best pio mode we can for a given device. Also honour + * the timings for the driver when dealing with mixed devices. Some + * of this is ugly but its all wrapped up here + * + * The SI680 can also do VDMA - we need to start using that + * + * FIXME: we use the BIOS channel timings to avoid driving the task + * files too fast at the disk. We need to compute the master/slave + * drive PIO mode properly so that we can up the speed on a hotplug + * system. + */ + +static void config_siimage_chipset_for_pio (ide_drive_t *drive, byte set_speed) +{ + u8 channel_timings = siimage_taskfile_timing(HWIF(drive)); + u8 speed = 0, set_pio = ide_get_best_pio_mode(drive, 4, 5, NULL); + + /* WARNING PIO timing mess is going to happen b/w devices, argh */ + if ((channel_timings != set_pio) && (set_pio > channel_timings)) + set_pio = channel_timings; + + siimage_tuneproc(drive, set_pio); + speed = XFER_PIO_0 + set_pio; + if (set_speed) + (void) ide_config_drive_speed(drive, speed); +} + +static void config_chipset_for_pio (ide_drive_t *drive, byte set_speed) +{ + config_siimage_chipset_for_pio(drive, set_speed); +} + +/** + * siimage_tune_chipset - set controller timings + * @drive: Drive to set up + * @xferspeed: speed we want to achieve + * + * Tune the SII chipset for the desired mode. If we can't achieve + * the desired mode then tune for a lower one, but ultimately + * make the thing work. + */ + +static int siimage_tune_chipset (ide_drive_t *drive, byte xferspeed) +{ + u8 ultra6[] = { 0x0F, 0x0B, 0x07, 0x05, 0x03, 0x02, 0x01 }; + u8 ultra5[] = { 0x0C, 0x07, 0x05, 0x04, 0x02, 0x01 }; + u16 dma[] = { 0x2208, 0x10C2, 0x10C1 }; + + ide_hwif_t *hwif = HWIF(drive); + u16 ultra = 0, multi = 0; + u8 mode = 0, unit = drive->select.b.unit; + u8 speed = ide_rate_filter(siimage_ratemask(drive), xferspeed); + unsigned long base = (unsigned long)hwif->hwif_data; + u8 scsc = 0, addr_mask = ((hwif->channel) ? + ((hwif->mmio) ? 0xF4 : 0x84) : + ((hwif->mmio) ? 0xB4 : 0x80)); + + unsigned long ma = siimage_seldev(drive, 0x08); + unsigned long ua = siimage_seldev(drive, 0x0C); + + if (hwif->mmio) { + scsc = hwif->INB(base + 0x4A); + mode = hwif->INB(base + addr_mask); + multi = hwif->INW(ma); + ultra = hwif->INW(ua); + } else { + pci_read_config_byte(hwif->pci_dev, 0x8A, &scsc); + pci_read_config_byte(hwif->pci_dev, addr_mask, &mode); + pci_read_config_word(hwif->pci_dev, ma, &multi); + pci_read_config_word(hwif->pci_dev, ua, &ultra); + } + + mode &= ~((unit) ? 0x30 : 0x03); + ultra &= ~0x3F; + scsc = ((scsc & 0x30) == 0x00) ? 0 : 1; + + scsc = is_sata(hwif) ? 1 : scsc; + + switch(speed) { + case XFER_PIO_4: + case XFER_PIO_3: + case XFER_PIO_2: + case XFER_PIO_1: + case XFER_PIO_0: + siimage_tuneproc(drive, (speed - XFER_PIO_0)); + mode |= ((unit) ? 0x10 : 0x01); + break; + case XFER_MW_DMA_2: + case XFER_MW_DMA_1: + case XFER_MW_DMA_0: + multi = dma[speed - XFER_MW_DMA_0]; + mode |= ((unit) ? 0x20 : 0x02); + config_siimage_chipset_for_pio(drive, 0); + break; + case XFER_UDMA_6: + case XFER_UDMA_5: + case XFER_UDMA_4: + case XFER_UDMA_3: + case XFER_UDMA_2: + case XFER_UDMA_1: + case XFER_UDMA_0: + multi = dma[2]; + ultra |= ((scsc) ? (ultra6[speed - XFER_UDMA_0]) : + (ultra5[speed - XFER_UDMA_0])); + mode |= ((unit) ? 0x30 : 0x03); + config_siimage_chipset_for_pio(drive, 0); + break; + default: + return 1; + } + + if (hwif->mmio) { + hwif->OUTB(mode, base + addr_mask); + hwif->OUTW(multi, ma); + hwif->OUTW(ultra, ua); + } else { + pci_write_config_byte(hwif->pci_dev, addr_mask, mode); + pci_write_config_word(hwif->pci_dev, ma, multi); + pci_write_config_word(hwif->pci_dev, ua, ultra); + } + return (ide_config_drive_speed(drive, speed)); +} + +/** + * config_chipset_for_dma - configure for DMA + * @drive: drive to configure + * + * Called by the IDE layer when it wants the timings set up. + * For the CMD680 we also need to set up the PIO timings and + * enable DMA. + */ + +static int config_chipset_for_dma (ide_drive_t *drive) +{ + u8 speed = ide_dma_speed(drive, siimage_ratemask(drive)); + + config_chipset_for_pio(drive, !speed); + + if (!speed) + return 0; + + if (ide_set_xfer_rate(drive, speed)) + return 0; + + if (!drive->init_speed) + drive->init_speed = speed; + + return ide_dma_enable(drive); +} + +/** + * siimage_configure_drive_for_dma - set up for DMA transfers + * @drive: drive we are going to set up + * + * Set up the drive for DMA, tune the controller and drive as + * required. If the drive isn't suitable for DMA or we hit + * other problems then we will drop down to PIO and set up + * PIO appropriately + */ + +static int siimage_config_drive_for_dma (ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + struct hd_driveid *id = drive->id; + + if ((id->capability & 1) != 0 && drive->autodma) { + + if (ide_use_dma(drive)) { + if (config_chipset_for_dma(drive)) + return hwif->ide_dma_on(drive); + } + + goto fast_ata_pio; + + } else if ((id->capability & 8) || (id->field_valid & 2)) { +fast_ata_pio: + config_chipset_for_pio(drive, 1); + return hwif->ide_dma_off_quietly(drive); + } + /* IORDY not supported */ + return 0; +} + +/* returns 1 if dma irq issued, 0 otherwise */ +static int siimage_io_ide_dma_test_irq (ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + u8 dma_altstat = 0; + unsigned long addr = siimage_selreg(hwif, 1); + + /* return 1 if INTR asserted */ + if ((hwif->INB(hwif->dma_status) & 4) == 4) + return 1; + + /* return 1 if Device INTR asserted */ + pci_read_config_byte(hwif->pci_dev, addr, &dma_altstat); + if (dma_altstat & 8) + return 0; //return 1; + return 0; +} + +#if 0 +/** + * siimage_mmio_ide_dma_count - DMA bytes done + * @drive + * + * If we are doing VDMA the CMD680 requires a little bit + * of more careful handling and we have to read the counts + * off ourselves. For non VDMA life is normal. + */ + +static int siimage_mmio_ide_dma_count (ide_drive_t *drive) +{ +#ifdef SIIMAGE_VIRTUAL_DMAPIO + struct request *rq = HWGROUP(drive)->rq; + ide_hwif_t *hwif = HWIF(drive); + u32 count = (rq->nr_sectors * SECTOR_SIZE); + u32 rcount = 0; + unsigned long addr = siimage_selreg(hwif, 0x1C); + + hwif->OUTL(count, addr); + rcount = hwif->INL(addr); + + printk("\n%s: count = %d, rcount = %d, nr_sectors = %lu\n", + drive->name, count, rcount, rq->nr_sectors); + +#endif /* SIIMAGE_VIRTUAL_DMAPIO */ + return __ide_dma_count(drive); +} +#endif + +/** + * siimage_mmio_ide_dma_test_irq - check we caused an IRQ + * @drive: drive we are testing + * + * Check if we caused an IDE DMA interrupt. We may also have caused + * SATA status interrupts, if so we clean them up and continue. + */ + +static int siimage_mmio_ide_dma_test_irq (ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + unsigned long base = (unsigned long)hwif->hwif_data; + unsigned long addr = siimage_selreg(hwif, 0x1); + + if (SATA_ERROR_REG) { + u32 ext_stat = hwif->INL(base + 0x10); + u8 watchdog = 0; + if (ext_stat & ((hwif->channel) ? 0x40 : 0x10)) { + u32 sata_error = hwif->INL(SATA_ERROR_REG); + hwif->OUTL(sata_error, SATA_ERROR_REG); + watchdog = (sata_error & 0x00680000) ? 1 : 0; +#if 1 + printk(KERN_WARNING "%s: sata_error = 0x%08x, " + "watchdog = %d, %s\n", + drive->name, sata_error, watchdog, + __FUNCTION__); +#endif + + } else { + watchdog = (ext_stat & 0x8000) ? 1 : 0; + } + ext_stat >>= 16; + + if (!(ext_stat & 0x0404) && !watchdog) + return 0; + } + + /* return 1 if INTR asserted */ + if ((hwif->INB(hwif->dma_status) & 0x04) == 0x04) + return 1; + + /* return 1 if Device INTR asserted */ + if ((hwif->INB(addr) & 8) == 8) + return 0; //return 1; + + return 0; +} + +/** + * siimage_busproc - bus isolation ioctl + * @drive: drive to isolate/restore + * @state: bus state to set + * + * Used by the SII3112 to handle bus isolation. As this is a + * SATA controller the work required is quite limited, we + * just have to clean up the statistics + */ + +static int siimage_busproc (ide_drive_t * drive, int state) +{ + ide_hwif_t *hwif = HWIF(drive); + u32 stat_config = 0; + unsigned long addr = siimage_selreg(hwif, 0); + + if (hwif->mmio) { + stat_config = hwif->INL(addr); + } else + pci_read_config_dword(hwif->pci_dev, addr, &stat_config); + + switch (state) { + case BUSSTATE_ON: + hwif->drives[0].failures = 0; + hwif->drives[1].failures = 0; + break; + case BUSSTATE_OFF: + hwif->drives[0].failures = hwif->drives[0].max_failures + 1; + hwif->drives[1].failures = hwif->drives[1].max_failures + 1; + break; + case BUSSTATE_TRISTATE: + hwif->drives[0].failures = hwif->drives[0].max_failures + 1; + hwif->drives[1].failures = hwif->drives[1].max_failures + 1; + break; + default: + return -EINVAL; + } + hwif->bus_state = state; + return 0; +} + +/** + * siimage_reset_poll - wait for sata reset + * @drive: drive we are resetting + * + * Poll the SATA phy and see whether it has come back from the dead + * yet. + */ + +static int siimage_reset_poll (ide_drive_t *drive) +{ + if (SATA_STATUS_REG) { + ide_hwif_t *hwif = HWIF(drive); + + if ((hwif->INL(SATA_STATUS_REG) & 0x03) != 0x03) { + printk(KERN_WARNING "%s: reset phy dead, status=0x%08x\n", + hwif->name, hwif->INL(SATA_STATUS_REG)); + HWGROUP(drive)->polling = 0; + return ide_started; + } + return 0; + } else { + return 0; + } +} + +/** + * siimage_pre_reset - reset hook + * @drive: IDE device being reset + * + * For the SATA devices we need to handle recalibration/geometry + * differently + */ + +static void siimage_pre_reset (ide_drive_t *drive) +{ + if (drive->media != ide_disk) + return; + + if (is_sata(HWIF(drive))) + { + drive->special.b.set_geometry = 0; + drive->special.b.recalibrate = 0; + } +} + +/** + * siimage_reset - reset a device on an siimage controller + * @drive: drive to reset + * + * Perform a controller level reset fo the device. For + * SATA we must also check the PHY. + */ + +static void siimage_reset (ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + u8 reset = 0; + unsigned long addr = siimage_selreg(hwif, 0); + + if (hwif->mmio) { + reset = hwif->INB(addr); + hwif->OUTB((reset|0x03), addr); + /* FIXME:posting */ + udelay(25); + hwif->OUTB(reset, addr); + (void) hwif->INB(addr); + } else { + pci_read_config_byte(hwif->pci_dev, addr, &reset); + pci_write_config_byte(hwif->pci_dev, addr, reset|0x03); + udelay(25); + pci_write_config_byte(hwif->pci_dev, addr, reset); + pci_read_config_byte(hwif->pci_dev, addr, &reset); + } + + if (SATA_STATUS_REG) { + u32 sata_stat = hwif->INL(SATA_STATUS_REG); + printk(KERN_WARNING "%s: reset phy, status=0x%08x, %s\n", + hwif->name, sata_stat, __FUNCTION__); + if (!(sata_stat)) { + printk(KERN_WARNING "%s: reset phy dead, status=0x%08x\n", + hwif->name, sata_stat); + drive->failures++; + } + } + +} + +/** + * proc_reports_siimage - add siimage controller to proc + * @dev: PCI device + * @clocking: SCSC value + * @name: controller name + * + * Report the clocking mode of the controller and add it to + * the /proc interface layer + */ + +static void proc_reports_siimage (struct pci_dev *dev, u8 clocking, const char *name) +{ + if (!pdev_is_sata(dev)) { + printk(KERN_INFO "%s: BASE CLOCK ", name); + clocking &= 0x03; + switch (clocking) { + case 0x03: printk("DISABLED!\n"); break; + case 0x02: printk("== 2X PCI\n"); break; + case 0x01: printk("== 133\n"); break; + case 0x00: printk("== 100\n"); break; + } + } +} + +/** + * setup_mmio_siimage - switch an SI controller into MMIO + * @dev: PCI device we are configuring + * @name: device name + * + * Attempt to put the device into mmio mode. There are some slight + * complications here with certain systems where the mmio bar isnt + * mapped so we have to be sure we can fall back to I/O. + */ + +static unsigned int setup_mmio_siimage (struct pci_dev *dev, const char *name) +{ + unsigned long bar5 = pci_resource_start(dev, 5); + unsigned long barsize = pci_resource_len(dev, 5); + u8 tmpbyte = 0; + void __iomem *ioaddr; + + /* + * Drop back to PIO if we can't map the mmio. Some + * systems seem to get terminally confused in the PCI + * spaces. + */ + + if(!request_mem_region(bar5, barsize, name)) + { + printk(KERN_WARNING "siimage: IDE controller MMIO ports not available.\n"); + return 0; + } + + ioaddr = ioremap(bar5, barsize); + + if (ioaddr == NULL) + { + release_mem_region(bar5, barsize); + return 0; + } + + pci_set_master(dev); + pci_set_drvdata(dev, (void *) ioaddr); + + if (pdev_is_sata(dev)) { + writel(0, ioaddr + 0x148); + writel(0, ioaddr + 0x1C8); + } + + writeb(0, ioaddr + 0xB4); + writeb(0, ioaddr + 0xF4); + tmpbyte = readb(ioaddr + 0x4A); + + switch(tmpbyte & 0x30) { + case 0x00: + /* In 100 MHz clocking, try and switch to 133 */ + writeb(tmpbyte|0x10, ioaddr + 0x4A); + break; + case 0x10: + /* On 133Mhz clocking */ + break; + case 0x20: + /* On PCIx2 clocking */ + break; + case 0x30: + /* Clocking is disabled */ + /* 133 clock attempt to force it on */ + writeb(tmpbyte & ~0x20, ioaddr + 0x4A); + break; + } + + writeb( 0x72, ioaddr + 0xA1); + writew( 0x328A, ioaddr + 0xA2); + writel(0x62DD62DD, ioaddr + 0xA4); + writel(0x43924392, ioaddr + 0xA8); + writel(0x40094009, ioaddr + 0xAC); + writeb( 0x72, ioaddr + 0xE1); + writew( 0x328A, ioaddr + 0xE2); + writel(0x62DD62DD, ioaddr + 0xE4); + writel(0x43924392, ioaddr + 0xE8); + writel(0x40094009, ioaddr + 0xEC); + + if (pdev_is_sata(dev)) { + writel(0xFFFF0000, ioaddr + 0x108); + writel(0xFFFF0000, ioaddr + 0x188); + writel(0x00680000, ioaddr + 0x148); + writel(0x00680000, ioaddr + 0x1C8); + } + + tmpbyte = readb(ioaddr + 0x4A); + + proc_reports_siimage(dev, (tmpbyte>>4), name); + return 1; +} + +/** + * init_chipset_siimage - set up an SI device + * @dev: PCI device + * @name: device name + * + * Perform the initial PCI set up for this device. Attempt to switch + * to 133MHz clocking if the system isn't already set up to do it. + */ + +static unsigned int __devinit init_chipset_siimage(struct pci_dev *dev, const char *name) +{ + u32 class_rev = 0; + u8 tmpbyte = 0; + u8 BA5_EN = 0; + + pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev); + class_rev &= 0xff; + pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, (class_rev) ? 1 : 255); + + pci_read_config_byte(dev, 0x8A, &BA5_EN); + if ((BA5_EN & 0x01) || (pci_resource_start(dev, 5))) { + if (setup_mmio_siimage(dev, name)) { + return 0; + } + } + + pci_write_config_byte(dev, 0x80, 0x00); + pci_write_config_byte(dev, 0x84, 0x00); + pci_read_config_byte(dev, 0x8A, &tmpbyte); + switch(tmpbyte & 0x30) { + case 0x00: + /* 133 clock attempt to force it on */ + pci_write_config_byte(dev, 0x8A, tmpbyte|0x10); + case 0x30: + /* if clocking is disabled */ + /* 133 clock attempt to force it on */ + pci_write_config_byte(dev, 0x8A, tmpbyte & ~0x20); + case 0x10: + /* 133 already */ + break; + case 0x20: + /* BIOS set PCI x2 clocking */ + break; + } + + pci_read_config_byte(dev, 0x8A, &tmpbyte); + + pci_write_config_byte(dev, 0xA1, 0x72); + pci_write_config_word(dev, 0xA2, 0x328A); + pci_write_config_dword(dev, 0xA4, 0x62DD62DD); + pci_write_config_dword(dev, 0xA8, 0x43924392); + pci_write_config_dword(dev, 0xAC, 0x40094009); + pci_write_config_byte(dev, 0xB1, 0x72); + pci_write_config_word(dev, 0xB2, 0x328A); + pci_write_config_dword(dev, 0xB4, 0x62DD62DD); + pci_write_config_dword(dev, 0xB8, 0x43924392); + pci_write_config_dword(dev, 0xBC, 0x40094009); + + proc_reports_siimage(dev, (tmpbyte>>4), name); + return 0; +} + +/** + * init_mmio_iops_siimage - set up the iops for MMIO + * @hwif: interface to set up + * + * The basic setup here is fairly simple, we can use standard MMIO + * operations. However we do have to set the taskfile register offsets + * by hand as there isnt a standard defined layout for them this + * time. + * + * The hardware supports buffered taskfiles and also some rather nice + * extended PRD tables. Unfortunately right now we don't. + */ + +static void __devinit init_mmio_iops_siimage(ide_hwif_t *hwif) +{ + struct pci_dev *dev = hwif->pci_dev; + void *addr = pci_get_drvdata(dev); + u8 ch = hwif->channel; + hw_regs_t hw; + unsigned long base; + + /* + * Fill in the basic HWIF bits + */ + + default_hwif_mmiops(hwif); + hwif->hwif_data = addr; + + /* + * Now set up the hw. We have to do this ourselves as + * the MMIO layout isnt the same as the the standard port + * based I/O + */ + + memset(&hw, 0, sizeof(hw_regs_t)); + + base = (unsigned long)addr; + if (ch) + base += 0xC0; + else + base += 0x80; + + /* + * The buffered task file doesn't have status/control + * so we can't currently use it sanely since we want to + * use LBA48 mode. + */ +// base += 0x10; +// hwif->no_lba48 = 1; + + hw.io_ports[IDE_DATA_OFFSET] = base; + hw.io_ports[IDE_ERROR_OFFSET] = base + 1; + hw.io_ports[IDE_NSECTOR_OFFSET] = base + 2; + hw.io_ports[IDE_SECTOR_OFFSET] = base + 3; + hw.io_ports[IDE_LCYL_OFFSET] = base + 4; + hw.io_ports[IDE_HCYL_OFFSET] = base + 5; + hw.io_ports[IDE_SELECT_OFFSET] = base + 6; + hw.io_ports[IDE_STATUS_OFFSET] = base + 7; + hw.io_ports[IDE_CONTROL_OFFSET] = base + 10; + + hw.io_ports[IDE_IRQ_OFFSET] = 0; + + if (pdev_is_sata(dev)) { + base = (unsigned long)addr; + if (ch) + base += 0x80; + hwif->sata_scr[SATA_STATUS_OFFSET] = base + 0x104; + hwif->sata_scr[SATA_ERROR_OFFSET] = base + 0x108; + hwif->sata_scr[SATA_CONTROL_OFFSET] = base + 0x100; + hwif->sata_misc[SATA_MISC_OFFSET] = base + 0x140; + hwif->sata_misc[SATA_PHY_OFFSET] = base + 0x144; + hwif->sata_misc[SATA_IEN_OFFSET] = base + 0x148; + } + + hw.irq = hwif->pci_dev->irq; + + memcpy(&hwif->hw, &hw, sizeof(hw)); + memcpy(hwif->io_ports, hwif->hw.io_ports, sizeof(hwif->hw.io_ports)); + + hwif->irq = hw.irq; + + base = (unsigned long) addr; + +#ifdef SIIMAGE_LARGE_DMA +/* Watch the brackets - even Ken and Dennis get some language design wrong */ + hwif->dma_base = base + (ch ? 0x18 : 0x10); + hwif->dma_base2 = base + (ch ? 0x08 : 0x00); + hwif->dma_prdtable = hwif->dma_base2 + 4; +#else /* ! SIIMAGE_LARGE_DMA */ + hwif->dma_base = base + (ch ? 0x08 : 0x00); + hwif->dma_base2 = base + (ch ? 0x18 : 0x10); +#endif /* SIIMAGE_LARGE_DMA */ + hwif->mmio = 2; +} + +static int is_dev_seagate_sata(ide_drive_t *drive) +{ + const char *s = &drive->id->model[0]; + unsigned len; + + if (!drive->present) + return 0; + + len = strnlen(s, sizeof(drive->id->model)); + + if ((len > 4) && (!memcmp(s, "ST", 2))) { + if ((!memcmp(s + len - 2, "AS", 2)) || + (!memcmp(s + len - 3, "ASL", 3))) { + printk(KERN_INFO "%s: applying pessimistic Seagate " + "errata fix\n", drive->name); + return 1; + } + } + return 0; +} + +/** + * siimage_fixup - post probe fixups + * @hwif: interface to fix up + * + * Called after drive probe we use this to decide whether the + * Seagate fixup must be applied. This used to be in init_iops but + * that can occur before we know what drives are present. + */ + +static void __devinit siimage_fixup(ide_hwif_t *hwif) +{ + /* Try and raise the rqsize */ + if (!is_sata(hwif) || !is_dev_seagate_sata(&hwif->drives[0])) + hwif->rqsize = 128; +} + +/** + * init_iops_siimage - set up iops + * @hwif: interface to set up + * + * Do the basic setup for the SIIMAGE hardware interface + * and then do the MMIO setup if we can. This is the first + * look in we get for setting up the hwif so that we + * can get the iops right before using them. + */ + +static void __devinit init_iops_siimage(ide_hwif_t *hwif) +{ + struct pci_dev *dev = hwif->pci_dev; + u32 class_rev = 0; + + pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev); + class_rev &= 0xff; + + hwif->hwif_data = NULL; + + /* Pessimal until we finish probing */ + hwif->rqsize = 15; + + if (pci_get_drvdata(dev) == NULL) + return; + init_mmio_iops_siimage(hwif); +} + +/** + * ata66_siimage - check for 80 pin cable + * @hwif: interface to check + * + * Check for the presence of an ATA66 capable cable on the + * interface. + */ + +static unsigned int __devinit ata66_siimage(ide_hwif_t *hwif) +{ + unsigned long addr = siimage_selreg(hwif, 0); + if (pci_get_drvdata(hwif->pci_dev) == NULL) { + u8 ata66 = 0; + pci_read_config_byte(hwif->pci_dev, addr, &ata66); + return (ata66 & 0x01) ? 1 : 0; + } + + return (hwif->INB(addr) & 0x01) ? 1 : 0; +} + +/** + * init_hwif_siimage - set up hwif structs + * @hwif: interface to set up + * + * We do the basic set up of the interface structure. The SIIMAGE + * requires several custom handlers so we override the default + * ide DMA handlers appropriately + */ + +static void __devinit init_hwif_siimage(ide_hwif_t *hwif) +{ + hwif->autodma = 0; + + hwif->resetproc = &siimage_reset; + hwif->speedproc = &siimage_tune_chipset; + hwif->tuneproc = &siimage_tuneproc; + hwif->reset_poll = &siimage_reset_poll; + hwif->pre_reset = &siimage_pre_reset; + + if(is_sata(hwif)) + hwif->busproc = &siimage_busproc; + + if (!hwif->dma_base) { + hwif->drives[0].autotune = 1; + hwif->drives[1].autotune = 1; + return; + } + + hwif->ultra_mask = 0x7f; + hwif->mwdma_mask = 0x07; + hwif->swdma_mask = 0x07; + + if (!is_sata(hwif)) + hwif->atapi_dma = 1; + + hwif->ide_dma_check = &siimage_config_drive_for_dma; + if (!(hwif->udma_four)) + hwif->udma_four = ata66_siimage(hwif); + + if (hwif->mmio) { + hwif->ide_dma_test_irq = &siimage_mmio_ide_dma_test_irq; + } else { + hwif->ide_dma_test_irq = & siimage_io_ide_dma_test_irq; + } + + /* + * The BIOS often doesn't set up DMA on this controller + * so we always do it. + */ + + hwif->autodma = 1; + hwif->drives[0].autodma = hwif->autodma; + hwif->drives[1].autodma = hwif->autodma; +} + +#define DECLARE_SII_DEV(name_str) \ + { \ + .name = name_str, \ + .init_chipset = init_chipset_siimage, \ + .init_iops = init_iops_siimage, \ + .init_hwif = init_hwif_siimage, \ + .fixup = siimage_fixup, \ + .channels = 2, \ + .autodma = AUTODMA, \ + .bootable = ON_BOARD, \ + } + +static ide_pci_device_t siimage_chipsets[] __devinitdata = { + /* 0 */ DECLARE_SII_DEV("SiI680"), + /* 1 */ DECLARE_SII_DEV("SiI3112 Serial ATA"), + /* 2 */ DECLARE_SII_DEV("Adaptec AAR-1210SA") +}; + +/** + * siimage_init_one - pci layer discovery entry + * @dev: PCI device + * @id: ident table entry + * + * Called by the PCI code when it finds an SI680 or SI3112 controller. + * We then use the IDE PCI generic helper to do most of the work. + */ + +static int __devinit siimage_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + return ide_setup_pci_device(dev, &siimage_chipsets[id->driver_data]); +} + +static struct pci_device_id siimage_pci_tbl[] = { + { PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_SII_680, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, +#ifdef CONFIG_BLK_DEV_IDE_SATA + { PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_SII_3112, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1}, + { PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_SII_1210SA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2}, +#endif + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, siimage_pci_tbl); + +static struct pci_driver driver = { + .name = "SiI_IDE", + .id_table = siimage_pci_tbl, + .probe = siimage_init_one, +}; + +static int siimage_ide_init(void) +{ + return ide_pci_register_driver(&driver); +} + +module_init(siimage_ide_init); + +MODULE_AUTHOR("Andre Hedrick, Alan Cox"); +MODULE_DESCRIPTION("PCI driver module for SiI IDE"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/pci/sis5513.c b/drivers/ide/pci/sis5513.c new file mode 100644 index 0000000..9d70ba5 --- /dev/null +++ b/drivers/ide/pci/sis5513.c @@ -0,0 +1,984 @@ +/* + * linux/drivers/ide/pci/sis5513.c Version 0.16ac+vp Jun 18, 2003 + * + * Copyright (C) 1999-2000 Andre Hedrick <andre@linux-ide.org> + * Copyright (C) 2002 Lionel Bouton <Lionel.Bouton@inet6.fr>, Maintainer + * Copyright (C) 2003 Vojtech Pavlik <vojtech@suse.cz> + * May be copied or modified under the terms of the GNU General Public License + * + * + * Thanks : + * + * SiS Taiwan : for direct support and hardware. + * Daniela Engert : for initial ATA100 advices and numerous others. + * John Fremlin, Manfred Spraul, Dave Morgan, Peter Kjellerstedt : + * for checking code correctness, providing patches. + * + * + * Original tests and design on the SiS620 chipset. + * ATA100 tests and design on the SiS735 chipset. + * ATA16/33 support from specs + * ATA133 support for SiS961/962 by L.C. Chang <lcchang@sis.com.tw> + * ATA133 961/962/963 fixes by Vojtech Pavlik <vojtech@suse.cz> + * + * Documentation: + * SiS chipset documentation available under NDA to companies only + * (not to individuals). + */ + +/* + * The original SiS5513 comes from a SiS5511/55112/5513 chipset. The original + * SiS5513 was also used in the SiS5596/5513 chipset. Thus if we see a SiS5511 + * or SiS5596, we can assume we see the first MWDMA-16 capable SiS5513 chip. + * + * Later SiS chipsets integrated the 5513 functionality into the NorthBridge, + * starting with SiS5571 and up to SiS745. The PCI ID didn't change, though. We + * can figure out that we have a more modern and more capable 5513 by looking + * for the respective NorthBridge IDs. + * + * Even later (96x family) SiS chipsets use the MuTIOL link and place the 5513 + * into the SouthBrige. Here we cannot rely on looking up the NorthBridge PCI + * ID, while the now ATA-133 capable 5513 still has the same PCI ID. + * Fortunately the 5513 can be 'unmasked' by fiddling with some config space + * bits, changing its device id to the true one - 5517 for 961 and 5518 for + * 962/963. + */ + +#include <linux/config.h> +#include <linux/types.h> +#include <linux/module.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/irq.h> + +#include "ide-timing.h" + +#define DISPLAY_SIS_TIMINGS + +/* registers layout and init values are chipset family dependant */ + +#define ATA_16 0x01 +#define ATA_33 0x02 +#define ATA_66 0x03 +#define ATA_100a 0x04 // SiS730/SiS550 is ATA100 with ATA66 layout +#define ATA_100 0x05 +#define ATA_133a 0x06 // SiS961b with 133 support +#define ATA_133 0x07 // SiS962/963 + +static u8 chipset_family; + +/* + * Devices supported + */ +static const struct { + const char *name; + u16 host_id; + u8 chipset_family; + u8 flags; +} SiSHostChipInfo[] = { + { "SiS745", PCI_DEVICE_ID_SI_745, ATA_100 }, + { "SiS735", PCI_DEVICE_ID_SI_735, ATA_100 }, + { "SiS733", PCI_DEVICE_ID_SI_733, ATA_100 }, + { "SiS635", PCI_DEVICE_ID_SI_635, ATA_100 }, + { "SiS633", PCI_DEVICE_ID_SI_633, ATA_100 }, + + { "SiS730", PCI_DEVICE_ID_SI_730, ATA_100a }, + { "SiS550", PCI_DEVICE_ID_SI_550, ATA_100a }, + + { "SiS640", PCI_DEVICE_ID_SI_640, ATA_66 }, + { "SiS630", PCI_DEVICE_ID_SI_630, ATA_66 }, + { "SiS620", PCI_DEVICE_ID_SI_620, ATA_66 }, + { "SiS540", PCI_DEVICE_ID_SI_540, ATA_66 }, + { "SiS530", PCI_DEVICE_ID_SI_530, ATA_66 }, + + { "SiS5600", PCI_DEVICE_ID_SI_5600, ATA_33 }, + { "SiS5598", PCI_DEVICE_ID_SI_5598, ATA_33 }, + { "SiS5597", PCI_DEVICE_ID_SI_5597, ATA_33 }, + { "SiS5591/2", PCI_DEVICE_ID_SI_5591, ATA_33 }, + { "SiS5582", PCI_DEVICE_ID_SI_5582, ATA_33 }, + { "SiS5581", PCI_DEVICE_ID_SI_5581, ATA_33 }, + + { "SiS5596", PCI_DEVICE_ID_SI_5596, ATA_16 }, + { "SiS5571", PCI_DEVICE_ID_SI_5571, ATA_16 }, + { "SiS551x", PCI_DEVICE_ID_SI_5511, ATA_16 }, +}; + +/* Cycle time bits and values vary across chip dma capabilities + These three arrays hold the register layout and the values to set. + Indexed by chipset_family and (dma_mode - XFER_UDMA_0) */ + +/* {0, ATA_16, ATA_33, ATA_66, ATA_100a, ATA_100, ATA_133} */ +static u8 cycle_time_offset[] = {0,0,5,4,4,0,0}; +static u8 cycle_time_range[] = {0,0,2,3,3,4,4}; +static u8 cycle_time_value[][XFER_UDMA_6 - XFER_UDMA_0 + 1] = { + {0,0,0,0,0,0,0}, /* no udma */ + {0,0,0,0,0,0,0}, /* no udma */ + {3,2,1,0,0,0,0}, /* ATA_33 */ + {7,5,3,2,1,0,0}, /* ATA_66 */ + {7,5,3,2,1,0,0}, /* ATA_100a (730 specific), differences are on cycle_time range and offset */ + {11,7,5,4,2,1,0}, /* ATA_100 */ + {15,10,7,5,3,2,1}, /* ATA_133a (earliest 691 southbridges) */ + {15,10,7,5,3,2,1}, /* ATA_133 */ +}; +/* CRC Valid Setup Time vary across IDE clock setting 33/66/100/133 + See SiS962 data sheet for more detail */ +static u8 cvs_time_value[][XFER_UDMA_6 - XFER_UDMA_0 + 1] = { + {0,0,0,0,0,0,0}, /* no udma */ + {0,0,0,0,0,0,0}, /* no udma */ + {2,1,1,0,0,0,0}, + {4,3,2,1,0,0,0}, + {4,3,2,1,0,0,0}, + {6,4,3,1,1,1,0}, + {9,6,4,2,2,2,2}, + {9,6,4,2,2,2,2}, +}; +/* Initialize time, Active time, Recovery time vary across + IDE clock settings. These 3 arrays hold the register value + for PIO0/1/2/3/4 and DMA0/1/2 mode in order */ +static u8 ini_time_value[][8] = { + {0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0}, + {2,1,0,0,0,1,0,0}, + {4,3,1,1,1,3,1,1}, + {4,3,1,1,1,3,1,1}, + {6,4,2,2,2,4,2,2}, + {9,6,3,3,3,6,3,3}, + {9,6,3,3,3,6,3,3}, +}; +static u8 act_time_value[][8] = { + {0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0}, + {9,9,9,2,2,7,2,2}, + {19,19,19,5,4,14,5,4}, + {19,19,19,5,4,14,5,4}, + {28,28,28,7,6,21,7,6}, + {38,38,38,10,9,28,10,9}, + {38,38,38,10,9,28,10,9}, +}; +static u8 rco_time_value[][8] = { + {0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0}, + {9,2,0,2,0,7,1,1}, + {19,5,1,5,2,16,3,2}, + {19,5,1,5,2,16,3,2}, + {30,9,3,9,4,25,6,4}, + {40,12,4,12,5,34,12,5}, + {40,12,4,12,5,34,12,5}, +}; + +/* + * Printing configuration + */ +/* Used for chipset type printing at boot time */ +static char* chipset_capability[] = { + "ATA", "ATA 16", + "ATA 33", "ATA 66", + "ATA 100 (1st gen)", "ATA 100 (2nd gen)", + "ATA 133 (1st gen)", "ATA 133 (2nd gen)" +}; + +#if defined(DISPLAY_SIS_TIMINGS) && defined(CONFIG_PROC_FS) +#include <linux/stat.h> +#include <linux/proc_fs.h> + +static u8 sis_proc = 0; + +static 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* active_time[] = { + "8 PCICLK", "1 PCICLCK", + "2 PCICLK", "3 PCICLK", + "4 PCICLK", "5 PCICLK", + "6 PCICLK", "12 PCICLK" +}; + +static char* cycle_time[] = { + "Reserved", "2 CLK", + "3 CLK", "4 CLK", + "5 CLK", "6 CLK", + "7 CLK", "8 CLK", + "9 CLK", "10 CLK", + "11 CLK", "12 CLK", + "13 CLK", "14 CLK", + "15 CLK", "16 CLK" +}; + +/* Generic add master or slave info function */ +static char* get_drives_info (char *buffer, u8 pos) +{ + u8 reg00, reg01, reg10, reg11; /* timing registers */ + u32 regdw0, regdw1; + char* p = buffer; + +/* Postwrite/Prefetch */ + if (chipset_family < ATA_133) { + pci_read_config_byte(bmide_dev, 0x4b, ®00); + p += sprintf(p, "Drive %d: Postwrite %s \t \t Postwrite %s\n", + pos, (reg00 & (0x10 << pos)) ? "Enabled" : "Disabled", + (reg00 & (0x40 << pos)) ? "Enabled" : "Disabled"); + p += sprintf(p, " Prefetch %s \t \t Prefetch %s\n", + (reg00 & (0x01 << pos)) ? "Enabled" : "Disabled", + (reg00 & (0x04 << pos)) ? "Enabled" : "Disabled"); + pci_read_config_byte(bmide_dev, 0x40+2*pos, ®00); + pci_read_config_byte(bmide_dev, 0x41+2*pos, ®01); + pci_read_config_byte(bmide_dev, 0x44+2*pos, ®10); + pci_read_config_byte(bmide_dev, 0x45+2*pos, ®11); + } else { + u32 reg54h; + u8 drive_pci = 0x40; + pci_read_config_dword(bmide_dev, 0x54, ®54h); + if (reg54h & 0x40000000) { + // Configuration space remapped to 0x70 + drive_pci = 0x70; + } + pci_read_config_dword(bmide_dev, (unsigned long)drive_pci+4*pos, ®dw0); + pci_read_config_dword(bmide_dev, (unsigned long)drive_pci+4*pos+8, ®dw1); + + p += sprintf(p, "Drive %d:\n", pos); + } + + +/* UDMA */ + if (chipset_family >= ATA_133) { + p += sprintf(p, " UDMA %s \t \t \t UDMA %s\n", + (regdw0 & 0x04) ? "Enabled" : "Disabled", + (regdw1 & 0x04) ? "Enabled" : "Disabled"); + p += sprintf(p, " UDMA Cycle Time %s \t UDMA Cycle Time %s\n", + cycle_time[(regdw0 & 0xF0) >> 4], + cycle_time[(regdw1 & 0xF0) >> 4]); + } else if (chipset_family >= ATA_33) { + p += sprintf(p, " UDMA %s \t \t \t UDMA %s\n", + (reg01 & 0x80) ? "Enabled" : "Disabled", + (reg11 & 0x80) ? "Enabled" : "Disabled"); + + p += sprintf(p, " UDMA Cycle Time "); + switch(chipset_family) { + case ATA_33: p += sprintf(p, cycle_time[(reg01 & 0x60) >> 5]); break; + case ATA_66: + case ATA_100a: p += sprintf(p, cycle_time[(reg01 & 0x70) >> 4]); break; + case ATA_100: + case ATA_133a: p += sprintf(p, cycle_time[reg01 & 0x0F]); break; + default: p += sprintf(p, "?"); break; + } + p += sprintf(p, " \t UDMA Cycle Time "); + switch(chipset_family) { + case ATA_33: p += sprintf(p, cycle_time[(reg11 & 0x60) >> 5]); break; + case ATA_66: + case ATA_100a: p += sprintf(p, cycle_time[(reg11 & 0x70) >> 4]); break; + case ATA_100: + case ATA_133a: p += sprintf(p, cycle_time[reg11 & 0x0F]); break; + default: p += sprintf(p, "?"); break; + } + p += sprintf(p, "\n"); + } + + + if (chipset_family < ATA_133) { /* else case TODO */ + +/* Data Active */ + p += sprintf(p, " Data Active Time "); + switch(chipset_family) { + case ATA_16: /* confirmed */ + case ATA_33: + case ATA_66: + case ATA_100a: p += sprintf(p, active_time[reg01 & 0x07]); break; + case ATA_100: + case ATA_133a: p += sprintf(p, active_time[(reg00 & 0x70) >> 4]); break; + default: p += sprintf(p, "?"); break; + } + p += sprintf(p, " \t Data Active Time "); + switch(chipset_family) { + case ATA_16: + case ATA_33: + case ATA_66: + case ATA_100a: p += sprintf(p, active_time[reg11 & 0x07]); break; + case ATA_100: + case ATA_133a: p += sprintf(p, active_time[(reg10 & 0x70) >> 4]); break; + default: p += sprintf(p, "?"); break; + } + p += sprintf(p, "\n"); + +/* Data Recovery */ + /* warning: may need (reg&0x07) for pre ATA66 chips */ + p += sprintf(p, " Data Recovery Time %s \t Data Recovery Time %s\n", + recovery_time[reg00 & 0x0f], recovery_time[reg10 & 0x0f]); + } + + return p; +} + +static char* get_masters_info(char* buffer) +{ + return get_drives_info(buffer, 0); +} + +static char* get_slaves_info(char* buffer) +{ + return get_drives_info(buffer, 1); +} + +/* Main get_info, called on /proc/ide/sis reads */ +static int sis_get_info (char *buffer, char **addr, off_t offset, int count) +{ + char *p = buffer; + int len; + u8 reg; + u16 reg2, reg3; + + p += sprintf(p, "\nSiS 5513 "); + switch(chipset_family) { + case ATA_16: p += sprintf(p, "DMA 16"); break; + case ATA_33: p += sprintf(p, "Ultra 33"); break; + case ATA_66: p += sprintf(p, "Ultra 66"); break; + case ATA_100a: + case ATA_100: p += sprintf(p, "Ultra 100"); break; + case ATA_133a: + case ATA_133: p += sprintf(p, "Ultra 133"); break; + default: p+= sprintf(p, "Unknown???"); break; + } + p += sprintf(p, " chipset\n"); + p += sprintf(p, "--------------- Primary Channel " + "---------------- Secondary Channel " + "-------------\n"); + +/* Status */ + pci_read_config_byte(bmide_dev, 0x4a, ®); + if (chipset_family == ATA_133) { + pci_read_config_word(bmide_dev, 0x50, ®2); + pci_read_config_word(bmide_dev, 0x52, ®3); + } + p += sprintf(p, "Channel Status: "); + if (chipset_family < ATA_66) { + p += sprintf(p, "%s \t \t \t \t %s\n", + (reg & 0x04) ? "On" : "Off", + (reg & 0x02) ? "On" : "Off"); + } else if (chipset_family < ATA_133) { + p += sprintf(p, "%s \t \t \t \t %s \n", + (reg & 0x02) ? "On" : "Off", + (reg & 0x04) ? "On" : "Off"); + } else { /* ATA_133 */ + p += sprintf(p, "%s \t \t \t \t %s \n", + (reg2 & 0x02) ? "On" : "Off", + (reg3 & 0x02) ? "On" : "Off"); + } + +/* Operation Mode */ + 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"); + +/* 80-pin cable ? */ + if (chipset_family >= ATA_133) { + p += sprintf(p, "Cable Type: %s \t \t \t %s\n", + (reg2 & 0x01) ? cable_type[1] : cable_type[0], + (reg3 & 0x01) ? cable_type[1] : cable_type[0]); + } else if (chipset_family > ATA_33) { + 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]); + } + +/* Prefetch Count */ + if (chipset_family < ATA_133) { + pci_read_config_word(bmide_dev, 0x4c, ®2); + pci_read_config_word(bmide_dev, 0x4e, ®3); + p += sprintf(p, "Prefetch Count: %d \t \t \t \t %d\n", + reg2, reg3); + } + + p = get_masters_info(p); + p = get_slaves_info(p); + + len = (p - buffer) - offset; + *addr = buffer + offset; + + return len > count ? count : len; +} +#endif /* defined(DISPLAY_SIS_TIMINGS) && defined(CONFIG_PROC_FS) */ + +static u8 sis5513_ratemask (ide_drive_t *drive) +{ + u8 rates[] = { 0, 0, 1, 2, 3, 3, 4, 4 }; + u8 mode = rates[chipset_family]; + + if (!eighty_ninty_three(drive)) + mode = min(mode, (u8)1); + return mode; +} + +/* + * Configuration functions + */ +/* Enables per-drive prefetch and postwrite */ +static void config_drive_art_rwp (ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + + u8 reg4bh = 0; + u8 rw_prefetch = (0x11 << drive->dn); + + if (drive->media != ide_disk) + return; + pci_read_config_byte(dev, 0x4b, ®4bh); + + if ((reg4bh & rw_prefetch) != rw_prefetch) + pci_write_config_byte(dev, 0x4b, reg4bh|rw_prefetch); +} + + +/* Set per-drive active and recovery time */ +static void config_art_rwp_pio (ide_drive_t *drive, u8 pio) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + + u8 timing, drive_pci, test1, test2; + + u16 eide_pio_timing[6] = {600, 390, 240, 180, 120, 90}; + u16 xfer_pio = drive->id->eide_pio_modes; + + config_drive_art_rwp(drive); + pio = ide_get_best_pio_mode(drive, 255, pio, 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; + + /* In pre ATA_133 case, drives sit at 0x40 + 4*drive->dn */ + drive_pci = 0x40; + /* In SiS962 case drives sit at (0x40 or 0x70) + 8*drive->dn) */ + if (chipset_family >= ATA_133) { + u32 reg54h; + pci_read_config_dword(dev, 0x54, ®54h); + if (reg54h & 0x40000000) drive_pci = 0x70; + drive_pci += ((drive->dn)*0x4); + } else { + drive_pci += ((drive->dn)*0x2); + } + + /* register layout changed with newer ATA100 chips */ + if (chipset_family < ATA_100) { + pci_read_config_byte(dev, drive_pci, &test1); + pci_read_config_byte(dev, drive_pci+1, &test2); + + /* Clear active and recovery timings */ + test1 &= ~0x0F; + test2 &= ~0x07; + + 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+1, test2); + } else if (chipset_family < ATA_133) { + switch(timing) { /* active recovery + v v */ + case 4: test1 = 0x30|0x01; break; + case 3: test1 = 0x30|0x03; break; + case 2: test1 = 0x40|0x04; break; + case 1: test1 = 0x60|0x07; break; + default: break; + } + pci_write_config_byte(dev, drive_pci, test1); + } else { /* ATA_133 */ + u32 test3; + pci_read_config_dword(dev, drive_pci, &test3); + test3 &= 0xc0c00fff; + if (test3 & 0x08) { + test3 |= (unsigned long)ini_time_value[ATA_133][timing] << 12; + test3 |= (unsigned long)act_time_value[ATA_133][timing] << 16; + test3 |= (unsigned long)rco_time_value[ATA_133][timing] << 24; + } else { + test3 |= (unsigned long)ini_time_value[ATA_100][timing] << 12; + test3 |= (unsigned long)act_time_value[ATA_100][timing] << 16; + test3 |= (unsigned long)rco_time_value[ATA_100][timing] << 24; + } + pci_write_config_dword(dev, drive_pci, test3); + } +} + +static int config_chipset_for_pio (ide_drive_t *drive, u8 pio) +{ + if (pio == 255) + pio = ide_find_best_mode(drive, XFER_PIO | XFER_EPIO) - XFER_PIO_0; + config_art_rwp_pio(drive, pio); + return ide_config_drive_speed(drive, XFER_PIO_0 + min_t(u8, pio, 4)); +} + +static int sis5513_tune_chipset (ide_drive_t *drive, u8 xferspeed) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + + u8 drive_pci, reg, speed; + u32 regdw; + + speed = ide_rate_filter(sis5513_ratemask(drive), xferspeed); + + /* See config_art_rwp_pio for drive pci config registers */ + drive_pci = 0x40; + if (chipset_family >= ATA_133) { + u32 reg54h; + pci_read_config_dword(dev, 0x54, ®54h); + if (reg54h & 0x40000000) drive_pci = 0x70; + drive_pci += ((drive->dn)*0x4); + pci_read_config_dword(dev, (unsigned long)drive_pci, ®dw); + /* Disable UDMA bit for non UDMA modes on UDMA chips */ + if (speed < XFER_UDMA_0) { + regdw &= 0xfffffffb; + pci_write_config_dword(dev, (unsigned long)drive_pci, regdw); + } + + } else { + drive_pci += ((drive->dn)*0x2); + pci_read_config_byte(dev, drive_pci+1, ®); + /* Disable UDMA bit for non UDMA modes on UDMA chips */ + if ((speed < XFER_UDMA_0) && (chipset_family > ATA_16)) { + reg &= 0x7F; + pci_write_config_byte(dev, drive_pci+1, reg); + } + } + + /* Config chip for mode */ + switch(speed) { + case XFER_UDMA_6: + case XFER_UDMA_5: + case XFER_UDMA_4: + case XFER_UDMA_3: + case XFER_UDMA_2: + case XFER_UDMA_1: + case XFER_UDMA_0: + if (chipset_family >= ATA_133) { + regdw |= 0x04; + regdw &= 0xfffff00f; + /* check if ATA133 enable */ + if (regdw & 0x08) { + regdw |= (unsigned long)cycle_time_value[ATA_133][speed-XFER_UDMA_0] << 4; + regdw |= (unsigned long)cvs_time_value[ATA_133][speed-XFER_UDMA_0] << 8; + } else { + /* if ATA133 disable, we should not set speed above UDMA5 */ + if (speed > XFER_UDMA_5) + speed = XFER_UDMA_5; + regdw |= (unsigned long)cycle_time_value[ATA_100][speed-XFER_UDMA_0] << 4; + regdw |= (unsigned long)cvs_time_value[ATA_100][speed-XFER_UDMA_0] << 8; + } + pci_write_config_dword(dev, (unsigned long)drive_pci, regdw); + } else { + /* Force the UDMA bit on if we want to use UDMA */ + reg |= 0x80; + /* clean reg cycle time bits */ + reg &= ~((0xFF >> (8 - cycle_time_range[chipset_family])) + << cycle_time_offset[chipset_family]); + /* set reg cycle time bits */ + reg |= cycle_time_value[chipset_family][speed-XFER_UDMA_0] + << cycle_time_offset[chipset_family]; + pci_write_config_byte(dev, drive_pci+1, reg); + } + break; + 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: + break; + case XFER_PIO_4: return((int) config_chipset_for_pio(drive, 4)); + case XFER_PIO_3: return((int) config_chipset_for_pio(drive, 3)); + case XFER_PIO_2: return((int) config_chipset_for_pio(drive, 2)); + case XFER_PIO_1: return((int) config_chipset_for_pio(drive, 1)); + case XFER_PIO_0: + default: return((int) config_chipset_for_pio(drive, 0)); + } + + return ((int) ide_config_drive_speed(drive, speed)); +} + +static void sis5513_tune_drive (ide_drive_t *drive, u8 pio) +{ + (void) config_chipset_for_pio(drive, pio); +} + +/* + * ((id->hw_config & 0x4000|0x2000) && (HWIF(drive)->udma_four)) + */ +static int config_chipset_for_dma (ide_drive_t *drive) +{ + u8 speed = ide_dma_speed(drive, sis5513_ratemask(drive)); + +#ifdef DEBUG + printk("SIS5513: config_chipset_for_dma, drive %d, ultra %x\n", + drive->dn, drive->id->dma_ultra); +#endif + + if (!(speed)) + return 0; + + sis5513_tune_chipset(drive, speed); + return ide_dma_enable(drive); +} + +static int sis5513_config_drive_xfer_rate (ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + struct hd_driveid *id = drive->id; + + drive->init_speed = 0; + + if (id && (id->capability & 1) && drive->autodma) { + + if (ide_use_dma(drive)) { + if (config_chipset_for_dma(drive)) + return hwif->ide_dma_on(drive); + } + + goto fast_ata_pio; + + } else if ((id->capability & 8) || (id->field_valid & 2)) { +fast_ata_pio: + sis5513_tune_drive(drive, 5); + return hwif->ide_dma_off_quietly(drive); + } + /* IORDY not supported */ + return 0; +} + +/* initiates/aborts (U)DMA read/write operations on a drive. */ +static int sis5513_config_xfer_rate (ide_drive_t *drive) +{ + config_drive_art_rwp(drive); + config_art_rwp_pio(drive, 5); + return sis5513_config_drive_xfer_rate(drive); +} + +/* + Future simpler config_xfer_rate : + When ide_find_best_mode is made bad-drive aware + - remove config_drive_xfer_rate and config_chipset_for_dma, + - replace config_xfer_rate with the following + +static int sis5513_config_xfer_rate (ide_drive_t *drive) +{ + u16 w80 = HWIF(drive)->udma_four; + u16 speed; + + config_drive_art_rwp(drive); + config_art_rwp_pio(drive, 5); + + speed = ide_find_best_mode(drive, + XFER_PIO | XFER_EPIO | XFER_SWDMA | XFER_MWDMA | + (chipset_family >= ATA_33 ? XFER_UDMA : 0) | + (w80 && chipset_family >= ATA_66 ? XFER_UDMA_66 : 0) | + (w80 && chipset_family >= ATA_100a ? XFER_UDMA_100 : 0) | + (w80 && chipset_family >= ATA_133a ? XFER_UDMA_133 : 0)); + + sis5513_tune_chipset(drive, speed); + + if (drive->autodma && (speed & XFER_MODE) != XFER_PIO) + return HWIF(drive)->ide_dma_on(drive); + return HWIF(drive)->ide_dma_off_quietly(drive); +} +*/ + +/* Chip detection and general config */ +static unsigned int __init init_chipset_sis5513 (struct pci_dev *dev, const char *name) +{ + struct pci_dev *host; + int i = 0; + + chipset_family = 0; + + for (i = 0; i < ARRAY_SIZE(SiSHostChipInfo) && !chipset_family; i++) { + + host = pci_find_device(PCI_VENDOR_ID_SI, SiSHostChipInfo[i].host_id, NULL); + + if (!host) + continue; + + chipset_family = SiSHostChipInfo[i].chipset_family; + + /* Special case for SiS630 : 630S/ET is ATA_100a */ + if (SiSHostChipInfo[i].host_id == PCI_DEVICE_ID_SI_630) { + u8 hostrev; + pci_read_config_byte(host, PCI_REVISION_ID, &hostrev); + if (hostrev >= 0x30) + chipset_family = ATA_100a; + } + + printk(KERN_INFO "SIS5513: %s %s controller\n", + SiSHostChipInfo[i].name, chipset_capability[chipset_family]); + } + + if (!chipset_family) { /* Belongs to pci-quirks */ + + u32 idemisc; + u16 trueid; + + /* Disable ID masking and register remapping */ + pci_read_config_dword(dev, 0x54, &idemisc); + pci_write_config_dword(dev, 0x54, (idemisc & 0x7fffffff)); + pci_read_config_word(dev, PCI_DEVICE_ID, &trueid); + pci_write_config_dword(dev, 0x54, idemisc); + + if (trueid == 0x5518) { + printk(KERN_INFO "SIS5513: SiS 962/963 MuTIOL IDE UDMA133 controller\n"); + chipset_family = ATA_133; + + /* Check for 5513 compability mapping + * We must use this, else the port enabled code will fail, + * as it expects the enablebits at 0x4a. + */ + if ((idemisc & 0x40000000) == 0) { + pci_write_config_dword(dev, 0x54, idemisc | 0x40000000); + printk(KERN_INFO "SIS5513: Switching to 5513 register mapping\n"); + } + } + } + + if (!chipset_family) { /* Belongs to pci-quirks */ + + struct pci_dev *lpc_bridge; + u16 trueid; + u8 prefctl; + u8 idecfg; + u8 sbrev; + + pci_read_config_byte(dev, 0x4a, &idecfg); + pci_write_config_byte(dev, 0x4a, idecfg | 0x10); + pci_read_config_word(dev, PCI_DEVICE_ID, &trueid); + pci_write_config_byte(dev, 0x4a, idecfg); + + if (trueid == 0x5517) { /* SiS 961/961B */ + + lpc_bridge = pci_find_slot(0x00, 0x10); /* Bus 0, Dev 2, Fn 0 */ + pci_read_config_byte(lpc_bridge, PCI_REVISION_ID, &sbrev); + pci_read_config_byte(dev, 0x49, &prefctl); + + if (sbrev == 0x10 && (prefctl & 0x80)) { + printk(KERN_INFO "SIS5513: SiS 961B MuTIOL IDE UDMA133 controller\n"); + chipset_family = ATA_133a; + } else { + printk(KERN_INFO "SIS5513: SiS 961 MuTIOL IDE UDMA100 controller\n"); + chipset_family = ATA_100; + } + } + } + + if (!chipset_family) + return -1; + + /* Make general config ops here + 1/ tell IDE channels to operate in Compatibility mode only + 2/ tell old chips to allow per drive IDE timings */ + + { + u8 reg; + u16 regw; + + switch(chipset_family) { + case ATA_133: + /* SiS962 operation mode */ + pci_read_config_word(dev, 0x50, ®w); + if (regw & 0x08) + pci_write_config_word(dev, 0x50, regw&0xfff7); + pci_read_config_word(dev, 0x52, ®w); + if (regw & 0x08) + pci_write_config_word(dev, 0x52, regw&0xfff7); + break; + case ATA_133a: + case ATA_100: + /* Fixup latency */ + pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x80); + /* Set compatibility bit */ + pci_read_config_byte(dev, 0x49, ®); + if (!(reg & 0x01)) { + pci_write_config_byte(dev, 0x49, reg|0x01); + } + break; + case ATA_100a: + case ATA_66: + /* Fixup latency */ + pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x10); + + /* On ATA_66 chips the bit was elsewhere */ + pci_read_config_byte(dev, 0x52, ®); + if (!(reg & 0x04)) { + pci_write_config_byte(dev, 0x52, reg|0x04); + } + break; + case ATA_33: + /* On ATA_33 we didn't have a single bit to set */ + pci_read_config_byte(dev, 0x09, ®); + if ((reg & 0x0f) != 0x00) { + pci_write_config_byte(dev, 0x09, reg&0xf0); + } + case ATA_16: + /* force per drive recovery and active timings + needed on ATA_33 and below chips */ + pci_read_config_byte(dev, 0x52, ®); + if (!(reg & 0x08)) { + pci_write_config_byte(dev, 0x52, reg|0x08); + } + break; + } + +#if defined(DISPLAY_SIS_TIMINGS) && defined(CONFIG_PROC_FS) + if (!sis_proc) { + sis_proc = 1; + bmide_dev = dev; + ide_pci_create_host_proc("sis", sis_get_info); + } +#endif + } + + return 0; +} + +static unsigned int __init ata66_sis5513 (ide_hwif_t *hwif) +{ + u8 ata66 = 0; + + if (chipset_family >= ATA_133) { + u16 regw = 0; + u16 reg_addr = hwif->channel ? 0x52: 0x50; + pci_read_config_word(hwif->pci_dev, reg_addr, ®w); + ata66 = (regw & 0x8000) ? 0 : 1; + } else if (chipset_family >= ATA_66) { + u8 reg48h = 0; + u8 mask = hwif->channel ? 0x20 : 0x10; + pci_read_config_byte(hwif->pci_dev, 0x48, ®48h); + ata66 = (reg48h & mask) ? 0 : 1; + } + return ata66; +} + +static void __init init_hwif_sis5513 (ide_hwif_t *hwif) +{ + hwif->autodma = 0; + + if (!hwif->irq) + hwif->irq = hwif->channel ? 15 : 14; + + hwif->tuneproc = &sis5513_tune_drive; + hwif->speedproc = &sis5513_tune_chipset; + + if (!(hwif->dma_base)) { + hwif->drives[0].autotune = 1; + hwif->drives[1].autotune = 1; + return; + } + + hwif->atapi_dma = 1; + hwif->ultra_mask = 0x7f; + hwif->mwdma_mask = 0x07; + hwif->swdma_mask = 0x07; + + if (!chipset_family) + return; + + if (!(hwif->udma_four)) + hwif->udma_four = ata66_sis5513(hwif); + + if (chipset_family > ATA_16) { + hwif->ide_dma_check = &sis5513_config_xfer_rate; + if (!noautodma) + hwif->autodma = 1; + } + hwif->drives[0].autodma = hwif->autodma; + hwif->drives[1].autodma = hwif->autodma; + return; +} + +static ide_pci_device_t sis5513_chipset __devinitdata = { + .name = "SIS5513", + .init_chipset = init_chipset_sis5513, + .init_hwif = init_hwif_sis5513, + .channels = 2, + .autodma = NOAUTODMA, + .enablebits = {{0x4a,0x02,0x02}, {0x4a,0x04,0x04}}, + .bootable = ON_BOARD, +}; + +static int __devinit sis5513_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + return ide_setup_pci_device(dev, &sis5513_chipset); +} + +static struct pci_device_id sis5513_pci_tbl[] = { + { PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_5513, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_5518, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, sis5513_pci_tbl); + +static struct pci_driver driver = { + .name = "SIS_IDE", + .id_table = sis5513_pci_tbl, + .probe = sis5513_init_one, +}; + +static int sis5513_ide_init(void) +{ + return ide_pci_register_driver(&driver); +} + +module_init(sis5513_ide_init); + +MODULE_AUTHOR("Lionel Bouton, L C Chang, Andre Hedrick, Vojtech Pavlik"); +MODULE_DESCRIPTION("PCI driver module for SIS IDE"); +MODULE_LICENSE("GPL"); + +/* + * TODO: + * - CLEANUP + * - Use drivers/ide/ide-timing.h ! + * - More checks in the config registers (force values instead of + * relying on the BIOS setting them correctly). + * - Further optimisations ? + * . for example ATA66+ regs 0x48 & 0x4A + */ diff --git a/drivers/ide/pci/sl82c105.c b/drivers/ide/pci/sl82c105.c new file mode 100644 index 0000000..1d970a0 --- /dev/null +++ b/drivers/ide/pci/sl82c105.c @@ -0,0 +1,516 @@ +/* + * linux/drivers/ide/pci/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 + * + * Merge in Russell's HW workarounds, fix various problems + * with the timing registers setup. + * -- Benjamin Herrenschmidt (01/11/03) benh@kernel.crashing.org + */ + +#include <linux/config.h> +#include <linux/types.h> +#include <linux/module.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> + +#undef DEBUG + +#ifdef DEBUG +#define DBG(arg) printk arg +#else +#define DBG(fmt,...) +#endif +/* + * SL82C105 PCI config register 0x40 bits. + */ +#define CTRL_IDE_IRQB (1 << 30) +#define CTRL_IDE_IRQA (1 << 28) +#define CTRL_LEGIRQ (1 << 11) +#define CTRL_P1F16 (1 << 5) +#define CTRL_P1EN (1 << 4) +#define CTRL_P0F16 (1 << 1) +#define CTRL_P0EN (1 << 0) + +/* + * 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); +} + +/* + * Configure the drive and chipset for PIO + */ +static void config_for_pio(ide_drive_t *drive, int pio, int report, int chipset_only) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + ide_pio_data_t p; + u16 drv_ctrl = 0x909; + unsigned int xfer_mode, reg; + + DBG(("config_for_pio(drive:%s, pio:%d, report:%d, chipset_only:%d)\n", + drive->name, pio, report, chipset_only)); + + reg = (hwif->channel ? 0x4c : 0x44) + (drive->select.b.unit ? 4 : 0); + + pio = ide_get_best_pio_mode(drive, pio, 5, &p); + + xfer_mode = XFER_PIO_0 + pio; + + if (chipset_only || ide_config_drive_speed(drive, xfer_mode) == 0) { + drv_ctrl = get_timing_sl82c105(&p); + drive->pio_speed = xfer_mode; + } else + drive->pio_speed = XFER_PIO_0; + + if (drive->using_dma == 0) { + /* + * If we are actually using MW DMA, then we can not + * reprogram the interface drive control register. + */ + pci_write_config_word(dev, reg, drv_ctrl); + pci_read_config_word(dev, reg, &drv_ctrl); + + if (report) { + printk("%s: selected %s (%dns) (%04X)\n", drive->name, + ide_xfer_verbose(xfer_mode), p.cycle_time, drv_ctrl); + } + } +} + +/* + * Configure the drive and the chipset for DMA + */ +static int config_for_dma (ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + unsigned int reg; + + DBG(("config_for_dma(drive:%s)\n", drive->name)); + + reg = (hwif->channel ? 0x4c : 0x44) + (drive->select.b.unit ? 4 : 0); + + if (ide_config_drive_speed(drive, XFER_MW_DMA_2) != 0) + return 1; + + pci_write_config_word(dev, reg, 0x0240); + + return 0; +} + +/* + * Check to see if the drive and + * chipset is capable of DMA mode + */ + +static int sl82c105_check_drive (ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + + DBG(("sl82c105_check_drive(drive:%s)\n", drive->name)); + + do { + struct hd_driveid *id = drive->id; + + if (!drive->autodma) + break; + + if (!id || !(id->capability & 1)) + break; + + /* Consult the list of known "bad" drives */ + if (__ide_dma_bad_drive(drive)) + break; + + if (id->field_valid & 2) { + if ((id->dma_mword & hwif->mwdma_mask) || + (id->dma_1word & hwif->swdma_mask)) + return hwif->ide_dma_on(drive); + } + + if (__ide_dma_good_drive(drive)) + return hwif->ide_dma_on(drive); + } while (0); + + return hwif->ide_dma_off_quietly(drive); +} + +/* + * The SL82C105 holds off all IDE interrupts while in DMA mode until + * all DMA activity is completed. Sometimes this causes problems (eg, + * when the drive wants to report an error condition). + * + * 0x7e is a "chip testing" register. Bit 2 resets the DMA controller + * state machine. We need to kick this to work around various bugs. + */ +static inline void sl82c105_reset_host(struct pci_dev *dev) +{ + u16 val; + + pci_read_config_word(dev, 0x7e, &val); + pci_write_config_word(dev, 0x7e, val | (1 << 2)); + pci_write_config_word(dev, 0x7e, val & ~(1 << 2)); +} + +/* + * If we get an IRQ timeout, it might be that the DMA state machine + * got confused. Fix from Todd Inglett. Details from Winbond. + * + * This function is called when the IDE timer expires, the drive + * indicates that it is READY, and we were waiting for DMA to complete. + */ +static int sl82c105_ide_dma_lost_irq(ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + u32 val, mask = hwif->channel ? CTRL_IDE_IRQB : CTRL_IDE_IRQA; + unsigned long dma_base = hwif->dma_base; + + printk("sl82c105: lost IRQ: resetting host\n"); + + /* + * Check the raw interrupt from the drive. + */ + pci_read_config_dword(dev, 0x40, &val); + if (val & mask) + printk("sl82c105: drive was requesting IRQ, but host lost it\n"); + + /* + * Was DMA enabled? If so, disable it - we're resetting the + * host. The IDE layer will be handling the drive for us. + */ + val = hwif->INB(dma_base); + if (val & 1) { + outb(val & ~1, dma_base); + printk("sl82c105: DMA was enabled\n"); + } + + sl82c105_reset_host(dev); + + /* ide_dmaproc would return 1, so we do as well */ + return 1; +} + +/* + * ATAPI devices can cause the SL82C105 DMA state machine to go gaga. + * Winbond recommend that the DMA state machine is reset prior to + * setting the bus master DMA enable bit. + * + * The generic IDE core will have disabled the BMEN bit before this + * function is called. + */ +static void sl82c105_ide_dma_start(ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + + sl82c105_reset_host(dev); + ide_dma_start(drive); +} + +static int sl82c105_ide_dma_timeout(ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + + DBG(("sl82c105_ide_dma_timeout(drive:%s)\n", drive->name)); + + sl82c105_reset_host(dev); + return __ide_dma_timeout(drive); +} + +static int sl82c105_ide_dma_on (ide_drive_t *drive) +{ + DBG(("sl82c105_ide_dma_on(drive:%s)\n", drive->name)); + + if (config_for_dma(drive)) { + config_for_pio(drive, 4, 0, 0); + return HWIF(drive)->ide_dma_off_quietly(drive); + } + printk(KERN_INFO "%s: DMA enabled\n", drive->name); + return __ide_dma_on(drive); +} + +static int sl82c105_ide_dma_off_quietly (ide_drive_t *drive) +{ + u8 speed = XFER_PIO_0; + int rc; + + DBG(("sl82c105_ide_dma_off_quietly(drive:%s)\n", drive->name)); + + rc = __ide_dma_off_quietly(drive); + if (drive->pio_speed) + speed = drive->pio_speed - XFER_PIO_0; + config_for_pio(drive, speed, 0, 1); + drive->current_speed = drive->pio_speed; + + return rc; +} + +/* + * Ok, that is nasty, but we must make sure the DMA timings + * won't be used for a PIO access. The solution here is + * to make sure the 16 bits mode is diabled on the channel + * when DMA is enabled, thus causing the chip to use PIO0 + * timings for those operations. + */ +static void sl82c105_selectproc(ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + u32 val, old, mask; + + //DBG(("sl82c105_selectproc(drive:%s)\n", drive->name)); + + mask = hwif->channel ? CTRL_P1F16 : CTRL_P0F16; + old = val = *((u32 *)&hwif->hwif_data); + if (drive->using_dma) + val &= ~mask; + else + val |= mask; + if (old != val) { + pci_write_config_dword(dev, 0x40, val); + *((u32 *)&hwif->hwif_data) = val; + } +} + +/* + * ATA reset will clear the 16 bits mode in the control + * register, we need to update our cache + */ +static void sl82c105_resetproc(ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + u32 val; + + DBG(("sl82c105_resetproc(drive:%s)\n", drive->name)); + + pci_read_config_dword(dev, 0x40, &val); + *((u32 *)&hwif->hwif_data) = val; +} + +/* + * 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, u8 pio) +{ + DBG(("tune_sl82c105(drive:%s)\n", drive->name)); + + config_for_pio(drive, pio, 1, 0); + + /* + * We support 32-bit I/O on this interface, and it + * doesn't have problems with interrupts. + */ + drive->io_32bit = 1; + drive->unmask = 1; +} + +/* + * Return the revision of the Winbond bridge + * which this function is part of. + */ +static unsigned int sl82c105_bridge_revision(struct pci_dev *dev) +{ + struct pci_dev *bridge; + u8 rev; + + /* + * The bridge should be part of the same device, but function 0. + */ + bridge = pci_find_slot(dev->bus->number, + PCI_DEVFN(PCI_SLOT(dev->devfn), 0)); + if (!bridge) + return -1; + + /* + * Make sure it is a Winbond 553 and is an ISA bridge. + */ + if (bridge->vendor != PCI_VENDOR_ID_WINBOND || + bridge->device != PCI_DEVICE_ID_WINBOND_83C553 || + bridge->class >> 8 != PCI_CLASS_BRIDGE_ISA) + return -1; + + /* + * We need to find function 0's revision, not function 1 + */ + pci_read_config_byte(bridge, PCI_REVISION_ID, &rev); + + return rev; +} + +/* + * Enable the PCI device + * + * --BenH: It's arch fixup code that should enable channels that + * have not been enabled by firmware. I decided we can still enable + * channel 0 here at least, but channel 1 has to be enabled by + * firmware or arch code. We still set both to 16 bits mode. + */ +static unsigned int __init init_chipset_sl82c105(struct pci_dev *dev, const char *msg) +{ + u32 val; + + DBG(("init_chipset_sl82c105()\n")); + + pci_read_config_dword(dev, 0x40, &val); + val |= CTRL_P0EN | CTRL_P0F16 | CTRL_P1F16; + pci_write_config_dword(dev, 0x40, val); + + return dev->irq; +} + +static void __init init_dma_sl82c105(ide_hwif_t *hwif, unsigned long dma_base) +{ + unsigned int rev; + u8 dma_state; + + DBG(("init_dma_sl82c105(hwif: ide%d, dma_base: 0x%08x)\n", hwif->index, dma_base)); + + hwif->autodma = 0; + + if (!dma_base) + return; + + dma_state = hwif->INB(dma_base + 2); + rev = sl82c105_bridge_revision(hwif->pci_dev); + if (rev <= 5) { + printk(" %s: Winbond 553 bridge revision %d, BM-DMA disabled\n", + hwif->name, rev); + dma_state &= ~0x60; + } else { + dma_state |= 0x60; + if (!noautodma) + hwif->autodma = 1; + } + hwif->OUTB(dma_state, dma_base + 2); + + ide_setup_dma(hwif, dma_base, 8); +} + +/* + * Initialise the chip + */ + +static void __init init_hwif_sl82c105(ide_hwif_t *hwif) +{ + struct pci_dev *dev = hwif->pci_dev; + u32 val; + + DBG(("init_hwif_sl82c105(hwif: ide%d)\n", hwif->index)); + + hwif->tuneproc = tune_sl82c105; + hwif->selectproc = sl82c105_selectproc; + hwif->resetproc = sl82c105_resetproc; + + /* Default to PIO 0 for fallback unless tuned otherwise, + * we always autotune PIO, this is done before DMA is + * checked, so there is no risk of accidentally disabling + * DMA + */ + hwif->drives[0].pio_speed = XFER_PIO_0; + hwif->drives[0].autotune = 1; + hwif->drives[1].pio_speed = XFER_PIO_1; + hwif->drives[1].autotune = 1; + + pci_read_config_dword(dev, 0x40, &val); + *((u32 *)&hwif->hwif_data) = val; + + if (!hwif->dma_base) + return; + + hwif->atapi_dma = 1; + hwif->mwdma_mask = 0x07; + hwif->swdma_mask = 0x07; + +#ifdef CONFIG_BLK_DEV_IDEDMA + hwif->ide_dma_check = &sl82c105_check_drive; + hwif->ide_dma_on = &sl82c105_ide_dma_on; + hwif->ide_dma_off_quietly = &sl82c105_ide_dma_off_quietly; + hwif->ide_dma_lostirq = &sl82c105_ide_dma_lost_irq; + hwif->dma_start = &sl82c105_ide_dma_start; + hwif->ide_dma_timeout = &sl82c105_ide_dma_timeout; + + if (!noautodma) + hwif->autodma = 1; + hwif->drives[0].autodma = hwif->autodma; + hwif->drives[1].autodma = hwif->autodma; +#endif /* CONFIG_BLK_DEV_IDEDMA */ +} + +static ide_pci_device_t sl82c105_chipset __devinitdata = { + .name = "W82C105", + .init_chipset = init_chipset_sl82c105, + .init_hwif = init_hwif_sl82c105, + .init_dma = init_dma_sl82c105, + .channels = 2, + .autodma = NOAUTODMA, + .enablebits = {{0x40,0x01,0x01}, {0x40,0x10,0x10}}, + .bootable = ON_BOARD, +}; + +static int __devinit sl82c105_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + return ide_setup_pci_device(dev, &sl82c105_chipset); +} + +static struct pci_device_id sl82c105_pci_tbl[] = { + { PCI_VENDOR_ID_WINBOND, PCI_DEVICE_ID_WINBOND_82C105, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, sl82c105_pci_tbl); + +static struct pci_driver driver = { + .name = "W82C105_IDE", + .id_table = sl82c105_pci_tbl, + .probe = sl82c105_init_one, +}; + +static int sl82c105_ide_init(void) +{ + return ide_pci_register_driver(&driver); +} + +module_init(sl82c105_ide_init); + +MODULE_DESCRIPTION("PCI driver module for W82C105 IDE"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/pci/slc90e66.c b/drivers/ide/pci/slc90e66.c new file mode 100644 index 0000000..7fbf363 --- /dev/null +++ b/drivers/ide/pci/slc90e66.c @@ -0,0 +1,273 @@ +/* + * linux/drivers/ide/pci/slc90e66.c Version 0.11 September 11, 2002 + * + * Copyright (C) 2000-2002 Andre Hedrick <andre@linux-ide.org> + * + * This a look-a-like variation of the ICH0 PIIX4 Ultra-66, + * but this keeps the ISA-Bridge and slots alive. + * + */ + +#include <linux/config.h> +#include <linux/types.h> +#include <linux/module.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 <linux/init.h> + +#include <asm/io.h> + +static u8 slc90e66_ratemask (ide_drive_t *drive) +{ + u8 mode = 2; + + if (!eighty_ninty_three(drive)) + mode = min(mode, (u8)1); + return mode; +} + +static u8 slc90e66_dma_2_pio (u8 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 useful if drive is not registered in CMOS for any reason). + */ +static void slc90e66_tune_drive (ide_drive_t *drive, u8 pio) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + int is_slave = (&hwif->drives[1] == drive); + int master_port = hwif->channel ? 0x42 : 0x40; + int slave_port = 0x44; + unsigned long flags; + u16 master_data; + u8 slave_data; + /* ISP RTC */ + u8 timings[][2] = { { 0, 0 }, + { 0, 0 }, + { 1, 0 }, + { 2, 1 }, + { 2, 3 }, }; + + pio = ide_get_best_pio_mode(drive, pio, 5, NULL); + spin_lock_irqsave(&ide_lock, flags); + pci_read_config_word(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(dev, slave_port, &slave_data); + slave_data = slave_data & (hwif->channel ? 0x0f : 0xf0); + slave_data = slave_data | (((timings[pio][0] << 2) | timings[pio][1]) << (hwif->channel ? 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); + } + pci_write_config_word(dev, master_port, master_data); + if (is_slave) + pci_write_config_byte(dev, slave_port, slave_data); + spin_unlock_irqrestore(&ide_lock, flags); +} + +static int slc90e66_tune_chipset (ide_drive_t *drive, u8 xferspeed) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + u8 maslave = hwif->channel ? 0x42 : 0x40; + u8 speed = ide_rate_filter(slc90e66_ratemask(drive), xferspeed); + int sitre = 0, a_speed = 7 << (drive->dn * 4); + int u_speed = 0, u_flag = 1 << drive->dn; + u16 reg4042, reg44, reg48, reg4a; + + 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); + + switch(speed) { +#ifdef CONFIG_BLK_DEV_IDEDMA + case XFER_UDMA_4: u_speed = 4 << (drive->dn * 4); break; + case XFER_UDMA_3: u_speed = 3 << (drive->dn * 4); break; + case XFER_UDMA_2: u_speed = 2 << (drive->dn * 4); break; + case XFER_UDMA_1: u_speed = 1 << (drive->dn * 4); break; + case XFER_UDMA_0: u_speed = 0 << (drive->dn * 4); break; + case XFER_MW_DMA_2: + case XFER_MW_DMA_1: + case XFER_SW_DMA_2: break; +#endif /* CONFIG_BLK_DEV_IDEDMA */ + case XFER_PIO_4: + case XFER_PIO_3: + case XFER_PIO_2: + case XFER_PIO_0: break; + default: return -1; + } + + if (speed >= XFER_UDMA_0) { + if (!(reg48 & u_flag)) + pci_write_config_word(dev, 0x48, reg48|u_flag); + /* FIXME: (reg4a & a_speed) ? */ + if ((reg4a & u_speed) != u_speed) { + pci_write_config_word(dev, 0x4a, reg4a & ~a_speed); + pci_read_config_word(dev, 0x4a, ®4a); + pci_write_config_word(dev, 0x4a, reg4a|u_speed); + } + } else { + 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); + } + + slc90e66_tune_drive(drive, slc90e66_dma_2_pio(speed)); + return (ide_config_drive_speed(drive, speed)); +} + +#ifdef CONFIG_BLK_DEV_IDEDMA +static int slc90e66_config_drive_for_dma (ide_drive_t *drive) +{ + u8 speed = ide_dma_speed(drive, slc90e66_ratemask(drive)); + + if (!(speed)) { + u8 tspeed = ide_get_best_pio_mode(drive, 255, 5, NULL); + speed = slc90e66_dma_2_pio(XFER_PIO_0 + tspeed); + } + + (void) slc90e66_tune_chipset(drive, speed); + return ide_dma_enable(drive); +} + +static int slc90e66_config_drive_xfer_rate (ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + struct hd_driveid *id = drive->id; + + drive->init_speed = 0; + + if (id && (id->capability & 1) && drive->autodma) { + + if (ide_use_dma(drive)) { + if (slc90e66_config_drive_for_dma(drive)) + return hwif->ide_dma_on(drive); + } + + goto fast_ata_pio; + + } else if ((id->capability & 8) || (id->field_valid & 2)) { +fast_ata_pio: + hwif->tuneproc(drive, 5); + return hwif->ide_dma_off_quietly(drive); + } + /* IORDY not supported */ + return 0; +} +#endif /* CONFIG_BLK_DEV_IDEDMA */ + +static void __init init_hwif_slc90e66 (ide_hwif_t *hwif) +{ + u8 reg47 = 0; + u8 mask = hwif->channel ? 0x01 : 0x02; /* bit0:Primary */ + + hwif->autodma = 0; + + if (!hwif->irq) + hwif->irq = hwif->channel ? 15 : 14; + + hwif->speedproc = &slc90e66_tune_chipset; + hwif->tuneproc = &slc90e66_tune_drive; + + pci_read_config_byte(hwif->pci_dev, 0x47, ®47); + + if (!hwif->dma_base) { + hwif->drives[0].autotune = 1; + hwif->drives[1].autotune = 1; + return; + } + + hwif->atapi_dma = 1; + hwif->ultra_mask = 0x1f; + hwif->mwdma_mask = 0x07; + hwif->swdma_mask = 0x07; + +#ifdef CONFIG_BLK_DEV_IDEDMA + if (!(hwif->udma_four)) + /* bit[0(1)]: 0:80, 1:40 */ + hwif->udma_four = (reg47 & mask) ? 0 : 1; + + hwif->ide_dma_check = &slc90e66_config_drive_xfer_rate; + if (!noautodma) + hwif->autodma = 1; + hwif->drives[0].autodma = hwif->autodma; + hwif->drives[1].autodma = hwif->autodma; +#endif /* !CONFIG_BLK_DEV_IDEDMA */ +} + +static ide_pci_device_t slc90e66_chipset __devinitdata = { + .name = "SLC90E66", + .init_hwif = init_hwif_slc90e66, + .channels = 2, + .autodma = AUTODMA, + .enablebits = {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, + .bootable = ON_BOARD, +}; + +static int __devinit slc90e66_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + return ide_setup_pci_device(dev, &slc90e66_chipset); +} + +static struct pci_device_id slc90e66_pci_tbl[] = { + { PCI_VENDOR_ID_EFAR, PCI_DEVICE_ID_EFAR_SLC90E66_1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, slc90e66_pci_tbl); + +static struct pci_driver driver = { + .name = "SLC90e66_IDE", + .id_table = slc90e66_pci_tbl, + .probe = slc90e66_init_one, +}; + +static int slc90e66_ide_init(void) +{ + return ide_pci_register_driver(&driver); +} + +module_init(slc90e66_ide_init); + +MODULE_AUTHOR("Andre Hedrick"); +MODULE_DESCRIPTION("PCI driver module for SLC90E66 IDE"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/pci/triflex.c b/drivers/ide/pci/triflex.c new file mode 100644 index 0000000..a1df2bf --- /dev/null +++ b/drivers/ide/pci/triflex.c @@ -0,0 +1,188 @@ +/* + * triflex.c + * + * IDE Chipset driver for the Compaq TriFlex IDE controller. + * + * Known to work with the Compaq Workstation 5x00 series. + * + * Copyright (C) 2002 Hewlett-Packard Development Group, L.P. + * Author: Torben Mathiasen <torben.mathiasen@hp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Loosely based on the piix & svwks drivers. + * + * Documentation: + * Not publically available. + */ + +#include <linux/config.h> +#include <linux/types.h> +#include <linux/module.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 <linux/init.h> + +static int triflex_tune_chipset(ide_drive_t *drive, u8 xferspeed) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + u8 channel_offset = hwif->channel ? 0x74 : 0x70; + u16 timing = 0; + u32 triflex_timings = 0; + u8 unit = (drive->select.b.unit & 0x01); + u8 speed = ide_rate_filter(0, xferspeed); + + pci_read_config_dword(dev, channel_offset, &triflex_timings); + + switch(speed) { + case XFER_MW_DMA_2: + timing = 0x0103; + break; + case XFER_MW_DMA_1: + timing = 0x0203; + break; + case XFER_MW_DMA_0: + timing = 0x0808; + break; + case XFER_SW_DMA_2: + case XFER_SW_DMA_1: + case XFER_SW_DMA_0: + timing = 0x0f0f; + break; + case XFER_PIO_4: + timing = 0x0202; + break; + case XFER_PIO_3: + timing = 0x0204; + break; + case XFER_PIO_2: + timing = 0x0404; + break; + case XFER_PIO_1: + timing = 0x0508; + break; + case XFER_PIO_0: + timing = 0x0808; + break; + default: + return -1; + } + + triflex_timings &= ~(0xFFFF << (16 * unit)); + triflex_timings |= (timing << (16 * unit)); + + pci_write_config_dword(dev, channel_offset, triflex_timings); + + return (ide_config_drive_speed(drive, speed)); +} + +static void triflex_tune_drive(ide_drive_t *drive, u8 pio) +{ + int use_pio = ide_get_best_pio_mode(drive, pio, 4, NULL); + (void) triflex_tune_chipset(drive, (XFER_PIO_0 + use_pio)); +} + +static int triflex_config_drive_for_dma(ide_drive_t *drive) +{ + int speed = ide_dma_speed(drive, 0); /* No ultra speeds */ + + if (!speed) { + u8 pspeed = ide_get_best_pio_mode(drive, 255, 4, NULL); + speed = XFER_PIO_0 + pspeed; + } + + (void) triflex_tune_chipset(drive, speed); + return ide_dma_enable(drive); +} + +static int triflex_config_drive_xfer_rate(ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + struct hd_driveid *id = drive->id; + + if ((id->capability & 1) && drive->autodma) { + if (ide_use_dma(drive)) { + if (triflex_config_drive_for_dma(drive)) + return hwif->ide_dma_on(drive); + } + } + + hwif->tuneproc(drive, 255); + return hwif->ide_dma_off_quietly(drive); +} + +static void __init init_hwif_triflex(ide_hwif_t *hwif) +{ + hwif->tuneproc = &triflex_tune_drive; + hwif->speedproc = &triflex_tune_chipset; + + hwif->atapi_dma = 1; + hwif->mwdma_mask = 0x07; + hwif->swdma_mask = 0x07; + hwif->ide_dma_check = &triflex_config_drive_xfer_rate; + + if (!noautodma) + hwif->autodma = 1; + hwif->drives[0].autodma = hwif->autodma; + hwif->drives[1].autodma = hwif->autodma; +} + +static ide_pci_device_t triflex_device __devinitdata = { + .name = "TRIFLEX", + .init_hwif = init_hwif_triflex, + .channels = 2, + .autodma = AUTODMA, + .enablebits = {{0x80, 0x01, 0x01}, {0x80, 0x02, 0x02}}, + .bootable = ON_BOARD, +}; + +static int __devinit triflex_init_one(struct pci_dev *dev, + const struct pci_device_id *id) +{ + return ide_setup_pci_device(dev, &triflex_device); +} + +static struct pci_device_id triflex_pci_tbl[] = { + { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_TRIFLEX_IDE, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, triflex_pci_tbl); + +static struct pci_driver driver = { + .name = "TRIFLEX_IDE", + .id_table = triflex_pci_tbl, + .probe = triflex_init_one, +}; + +static int triflex_ide_init(void) +{ + return ide_pci_register_driver(&driver); +} + +module_init(triflex_ide_init); + +MODULE_AUTHOR("Torben Mathiasen"); +MODULE_DESCRIPTION("PCI driver module for Compaq Triflex IDE"); +MODULE_LICENSE("GPL"); + + diff --git a/drivers/ide/pci/trm290.c b/drivers/ide/pci/trm290.c new file mode 100644 index 0000000..8b5eea5 --- /dev/null +++ b/drivers/ide/pci/trm290.c @@ -0,0 +1,369 @@ +/* + * linux/drivers/ide/pci/trm290.c Version 1.02 Mar. 18, 2000 + * + * Copyright (c) 1997-1998 Mark Lord + * May be copied or modified under the terms of the GNU General Public License + * + * June 22, 2004 - get rid of check_region + * Jesper Juhl <juhl-lkml@dif.dk> + * + */ + +/* + * 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/config.h> +#include <linux/types.h> +#include <linux/module.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); + u16 reg = 0; + unsigned long flags; + + /* select PIO or DMA */ + reg = use_dma ? (0x21 | 0x82) : (0x21 & ~0x82); + + local_irq_save(flags); + + if (reg != hwif->select_data) { + hwif->select_data = reg; + /* set PIO/DMA */ + hwif->OUTB(0x51|(hwif->channel<<3), hwif->config_data+1); + hwif->OUTW(reg & 0xff, hwif->config_data); + } + + /* enable IRQ if not probing */ + if (drive->present) { + reg = hwif->INW(hwif->config_data + 3); + reg &= 0x13; + reg &= ~(1 << hwif->channel); + hwif->OUTW(reg, hwif->config_data+3); + } + + local_irq_restore(flags); +} + +static void trm290_selectproc (ide_drive_t *drive) +{ + trm290_prepare_drive(drive, drive->using_dma); +} + +#ifdef CONFIG_BLK_DEV_IDEDMA +static void trm290_ide_dma_exec_cmd(ide_drive_t *drive, u8 command) +{ + ide_hwif_t *hwif = HWIF(drive); + + if (HWGROUP(drive)->handler != NULL) /* paranoia check */ + BUG(); + ide_set_handler(drive, &ide_dma_intr, WAIT_CMD, NULL); + /* issue cmd to drive */ + hwif->OUTB(command, IDE_COMMAND_REG); +} + +static int trm290_ide_dma_setup(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + struct request *rq = hwif->hwgroup->rq; + unsigned int count, rw; + + if (rq_data_dir(rq)) { +#ifdef TRM290_NO_DMA_WRITES + /* always use PIO for writes */ + trm290_prepare_drive(drive, 0); /* select PIO xfer */ + return 1; +#endif + rw = 1; + } else + rw = 2; + + if (!(count = ide_build_dmatable(drive, rq))) { + /* try PIO instead of DMA */ + trm290_prepare_drive(drive, 0); /* select PIO xfer */ + return 1; + } + /* select DMA xfer */ + trm290_prepare_drive(drive, 1); + hwif->OUTL(hwif->dmatable_dma|rw, hwif->dma_command); + drive->waiting_for_dma = 1; + /* start DMA */ + hwif->OUTW((count * 2) - 1, hwif->dma_status); + return 0; +} + +static void trm290_ide_dma_start(ide_drive_t *drive) +{ +} + +static int trm290_ide_dma_end (ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + u16 status = 0; + + drive->waiting_for_dma = 0; + /* purge DMA mappings */ + ide_destroy_dmatable(drive); + status = hwif->INW(hwif->dma_status); + return (status != 0x00ff); +} + +static int trm290_ide_dma_test_irq (ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + u16 status = 0; + + status = hwif->INW(hwif->dma_status); + return (status == 0x00ff); +} +#endif /* CONFIG_BLK_DEV_IDEDMA */ + +/* + * Invoked from ide-dma.c at boot time. + */ +static void __devinit init_hwif_trm290(ide_hwif_t *hwif) +{ + unsigned int cfgbase = 0; + unsigned long flags; + u8 reg = 0; + struct pci_dev *dev = hwif->pci_dev; + + hwif->no_lba48 = 1; + hwif->chipset = ide_trm290; + cfgbase = pci_resource_start(dev, 4); + if ((dev->class & 5) && cfgbase) { + hwif->config_data = cfgbase; + printk(KERN_INFO "TRM290: chip config base at 0x%04lx\n", + hwif->config_data); + } else { + hwif->config_data = 0x3df0; + printk(KERN_INFO "TRM290: using default config base at 0x%04lx\n", + hwif->config_data); + } + + local_irq_save(flags); + /* put config reg into first byte of hwif->select_data */ + hwif->OUTB(0x51|(hwif->channel<<3), hwif->config_data+1); + /* select PIO as default */ + hwif->select_data = 0x21; + hwif->OUTB(hwif->select_data, hwif->config_data); + /* get IRQ info */ + reg = hwif->INB(hwif->config_data+3); + /* mask IRQs for both ports */ + reg = (reg & 0x10) | 0x03; + hwif->OUTB(reg, hwif->config_data+3); + local_irq_restore(flags); + + if ((reg & 0x10)) + /* legacy mode */ + hwif->irq = hwif->channel ? 15 : 14; + else if (!hwif->irq && hwif->mate && hwif->mate->irq) + /* sharing IRQ with mate */ + hwif->irq = hwif->mate->irq; + + ide_setup_dma(hwif, (hwif->config_data + 4) ^ (hwif->channel ? 0x0080 : 0x0000), 3); + +#ifdef CONFIG_BLK_DEV_IDEDMA + hwif->dma_setup = &trm290_ide_dma_setup; + hwif->dma_exec_cmd = &trm290_ide_dma_exec_cmd; + hwif->dma_start = &trm290_ide_dma_start; + hwif->ide_dma_end = &trm290_ide_dma_end; + hwif->ide_dma_test_irq = &trm290_ide_dma_test_irq; +#endif /* CONFIG_BLK_DEV_IDEDMA */ + + hwif->selectproc = &trm290_selectproc; + hwif->autodma = 0; /* play it safe for now */ + hwif->drives[0].autodma = hwif->autodma; + hwif->drives[1].autodma = hwif->autodma; +#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 + */ + u16 new, old, compat = hwif->channel ? 0x374 : 0x3f4; + static u16 next_offset = 0; + u8 old_mask; + + hwif->OUTB(0x54|(hwif->channel<<3), hwif->config_data+1); + old = hwif->INW(hwif->config_data); + old &= ~1; + old_mask = hwif->INB(old+2); + if (old != compat && old_mask == 0xff) { + /* leave lower 10 bits untouched */ + compat += (next_offset += 0x400); + hwif->io_ports[IDE_CONTROL_OFFSET] = compat + 2; + hwif->OUTW(compat|1, hwif->config_data); + new = hwif->INW(hwif->config_data); + printk(KERN_INFO "%s: control basereg workaround: " + "old=0x%04x, new=0x%04x\n", + hwif->name, old, new & ~1); + } + } +#endif +} + +static ide_pci_device_t trm290_chipset __devinitdata = { + .name = "TRM290", + .init_hwif = init_hwif_trm290, + .channels = 2, + .autodma = NOAUTODMA, + .bootable = ON_BOARD, +}; + +static int __devinit trm290_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + return ide_setup_pci_device(dev, &trm290_chipset); +} + +static struct pci_device_id trm290_pci_tbl[] = { + { PCI_VENDOR_ID_TEKRAM, PCI_DEVICE_ID_TEKRAM_DC290, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, trm290_pci_tbl); + +static struct pci_driver driver = { + .name = "TRM290_IDE", + .id_table = trm290_pci_tbl, + .probe = trm290_init_one, +}; + +static int trm290_ide_init(void) +{ + return ide_pci_register_driver(&driver); +} + +module_init(trm290_ide_init); + +MODULE_AUTHOR("Mark Lord"); +MODULE_DESCRIPTION("PCI driver module for Tekram TRM290 IDE"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/pci/via82cxxx.c b/drivers/ide/pci/via82cxxx.c new file mode 100644 index 0000000..069dbff --- /dev/null +++ b/drivers/ide/pci/via82cxxx.c @@ -0,0 +1,656 @@ +/* + * + * Version 3.38 + * + * VIA IDE driver for Linux. Supported southbridges: + * + * vt82c576, vt82c586, vt82c586a, vt82c586b, vt82c596a, vt82c596b, + * vt82c686, vt82c686a, vt82c686b, vt8231, vt8233, vt8233c, vt8233a, + * vt8235, vt8237 + * + * Copyright (c) 2000-2002 Vojtech Pavlik + * + * Based on the work of: + * Michel Aubry + * Jeff Garzik + * Andre Hedrick + * + * Documentation: + * Obsolete device documentation publically available from via.com.tw + * Current device documentation available under NDA only + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/ioport.h> +#include <linux/blkdev.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/ide.h> +#include <asm/io.h> + +#ifdef CONFIG_PPC_MULTIPLATFORM +#include <asm/processor.h> +#endif + +#include "ide-timing.h" + +#define DISPLAY_VIA_TIMINGS + +#define VIA_IDE_ENABLE 0x40 +#define VIA_IDE_CONFIG 0x41 +#define VIA_FIFO_CONFIG 0x43 +#define VIA_MISC_1 0x44 +#define VIA_MISC_2 0x45 +#define VIA_MISC_3 0x46 +#define VIA_DRIVE_TIMING 0x48 +#define VIA_8BIT_TIMING 0x4e +#define VIA_ADDRESS_SETUP 0x4c +#define VIA_UDMA_TIMING 0x50 + +#define VIA_UDMA 0x007 +#define VIA_UDMA_NONE 0x000 +#define VIA_UDMA_33 0x001 +#define VIA_UDMA_66 0x002 +#define VIA_UDMA_100 0x003 +#define VIA_UDMA_133 0x004 +#define VIA_BAD_PREQ 0x010 /* Crashes if PREQ# till DDACK# set */ +#define VIA_BAD_CLK66 0x020 /* 66 MHz clock doesn't work correctly */ +#define VIA_SET_FIFO 0x040 /* Needs to have FIFO split set */ +#define VIA_NO_UNMASK 0x080 /* Doesn't work with IRQ unmasking on */ +#define VIA_BAD_ID 0x100 /* Has wrong vendor ID (0x1107) */ +#define VIA_BAD_AST 0x200 /* Don't touch Address Setup Timing */ + +/* + * VIA SouthBridge chips. + */ + +static struct via_isa_bridge { + char *name; + u16 id; + u8 rev_min; + u8 rev_max; + u16 flags; +} via_isa_bridges[] = { + { "vt8237", PCI_DEVICE_ID_VIA_8237, 0x00, 0x2f, VIA_UDMA_133 | VIA_BAD_AST }, + { "vt8235", PCI_DEVICE_ID_VIA_8235, 0x00, 0x2f, VIA_UDMA_133 | VIA_BAD_AST }, + { "vt8233a", PCI_DEVICE_ID_VIA_8233A, 0x00, 0x2f, VIA_UDMA_133 | VIA_BAD_AST }, + { "vt8233c", PCI_DEVICE_ID_VIA_8233C_0, 0x00, 0x2f, VIA_UDMA_100 }, + { "vt8233", PCI_DEVICE_ID_VIA_8233_0, 0x00, 0x2f, VIA_UDMA_100 }, + { "vt8231", PCI_DEVICE_ID_VIA_8231, 0x00, 0x2f, VIA_UDMA_100 }, + { "vt82c686b", PCI_DEVICE_ID_VIA_82C686, 0x40, 0x4f, VIA_UDMA_100 }, + { "vt82c686a", PCI_DEVICE_ID_VIA_82C686, 0x10, 0x2f, VIA_UDMA_66 }, + { "vt82c686", PCI_DEVICE_ID_VIA_82C686, 0x00, 0x0f, VIA_UDMA_33 | VIA_BAD_CLK66 }, + { "vt82c596b", PCI_DEVICE_ID_VIA_82C596, 0x10, 0x2f, VIA_UDMA_66 }, + { "vt82c596a", PCI_DEVICE_ID_VIA_82C596, 0x00, 0x0f, VIA_UDMA_33 | VIA_BAD_CLK66 }, + { "vt82c586b", PCI_DEVICE_ID_VIA_82C586_0, 0x47, 0x4f, VIA_UDMA_33 | VIA_SET_FIFO }, + { "vt82c586b", PCI_DEVICE_ID_VIA_82C586_0, 0x40, 0x46, VIA_UDMA_33 | VIA_SET_FIFO | VIA_BAD_PREQ }, + { "vt82c586b", PCI_DEVICE_ID_VIA_82C586_0, 0x30, 0x3f, VIA_UDMA_33 | VIA_SET_FIFO }, + { "vt82c586a", PCI_DEVICE_ID_VIA_82C586_0, 0x20, 0x2f, VIA_UDMA_33 | VIA_SET_FIFO }, + { "vt82c586", PCI_DEVICE_ID_VIA_82C586_0, 0x00, 0x0f, VIA_UDMA_NONE | VIA_SET_FIFO }, + { "vt82c576", PCI_DEVICE_ID_VIA_82C576, 0x00, 0x2f, VIA_UDMA_NONE | VIA_SET_FIFO | VIA_NO_UNMASK }, + { "vt82c576", PCI_DEVICE_ID_VIA_82C576, 0x00, 0x2f, VIA_UDMA_NONE | VIA_SET_FIFO | VIA_NO_UNMASK | VIA_BAD_ID }, + { NULL } +}; + +static struct via_isa_bridge *via_config; +static unsigned int via_80w; +static unsigned int via_clock; +static char *via_dma[] = { "MWDMA16", "UDMA33", "UDMA66", "UDMA100", "UDMA133" }; + +/* + * VIA /proc entry. + */ + +#if defined(DISPLAY_VIA_TIMINGS) && defined(CONFIG_PROC_FS) + +#include <linux/stat.h> +#include <linux/proc_fs.h> + +static u8 via_proc = 0; +static unsigned long via_base; +static struct pci_dev *bmide_dev, *isa_dev; + +static char *via_control3[] = { "No limit", "64", "128", "192" }; + +#define via_print(format, arg...) p += sprintf(p, format "\n" , ## arg) +#define via_print_drive(name, format, arg...)\ + p += sprintf(p, name); for (i = 0; i < 4; i++) p += sprintf(p, format, ## arg); p += sprintf(p, "\n"); + + +/** + * via_get_info - generate via /proc file + * @buffer: buffer for data + * @addr: set to start of data to use + * @offset: current file offset + * @count: size of read + * + * Fills in buffer with the debugging/configuration information for + * the VIA chipset tuning and attached drives + */ + +static int via_get_info(char *buffer, char **addr, off_t offset, int count) +{ + int speed[4], cycle[4], setup[4], active[4], recover[4], den[4], + uen[4], udma[4], umul[4], active8b[4], recover8b[4]; + struct pci_dev *dev = bmide_dev; + unsigned int v, u, i; + int len; + u16 c, w; + u8 t, x; + char *p = buffer; + + via_print("----------VIA BusMastering IDE Configuration" + "----------------"); + + via_print("Driver Version: 3.38"); + via_print("South Bridge: VIA %s", + via_config->name); + + pci_read_config_byte(isa_dev, PCI_REVISION_ID, &t); + pci_read_config_byte(dev, PCI_REVISION_ID, &x); + via_print("Revision: ISA %#x IDE %#x", t, x); + via_print("Highest DMA rate: %s", + via_dma[via_config->flags & VIA_UDMA]); + + via_print("BM-DMA base: %#lx", via_base); + via_print("PCI clock: %d.%dMHz", + via_clock / 1000, via_clock / 100 % 10); + + pci_read_config_byte(dev, VIA_MISC_1, &t); + via_print("Master Read Cycle IRDY: %dws", + (t & 64) >> 6); + via_print("Master Write Cycle IRDY: %dws", + (t & 32) >> 5); + via_print("BM IDE Status Register Read Retry: %s", + (t & 8) ? "yes" : "no"); + + pci_read_config_byte(dev, VIA_MISC_3, &t); + via_print("Max DRDY Pulse Width: %s%s", + via_control3[(t & 0x03)], (t & 0x03) ? " PCI clocks" : ""); + + via_print("-----------------------Primary IDE" + "-------Secondary IDE------"); + via_print("Read DMA FIFO flush: %10s%20s", + (t & 0x80) ? "yes" : "no", (t & 0x40) ? "yes" : "no"); + via_print("End Sector FIFO flush: %10s%20s", + (t & 0x20) ? "yes" : "no", (t & 0x10) ? "yes" : "no"); + + pci_read_config_byte(dev, VIA_IDE_CONFIG, &t); + via_print("Prefetch Buffer: %10s%20s", + (t & 0x80) ? "yes" : "no", (t & 0x20) ? "yes" : "no"); + via_print("Post Write Buffer: %10s%20s", + (t & 0x40) ? "yes" : "no", (t & 0x10) ? "yes" : "no"); + + pci_read_config_byte(dev, VIA_IDE_ENABLE, &t); + via_print("Enabled: %10s%20s", + (t & 0x02) ? "yes" : "no", (t & 0x01) ? "yes" : "no"); + + c = inb(via_base + 0x02) | (inb(via_base + 0x0a) << 8); + via_print("Simplex only: %10s%20s", + (c & 0x80) ? "yes" : "no", (c & 0x8000) ? "yes" : "no"); + + via_print("Cable Type: %10s%20s", + (via_80w & 1) ? "80w" : "40w", (via_80w & 2) ? "80w" : "40w"); + + via_print("-------------------drive0----drive1" + "----drive2----drive3-----"); + + pci_read_config_byte(dev, VIA_ADDRESS_SETUP, &t); + pci_read_config_dword(dev, VIA_DRIVE_TIMING, &v); + pci_read_config_word(dev, VIA_8BIT_TIMING, &w); + + if (via_config->flags & VIA_UDMA) + pci_read_config_dword(dev, VIA_UDMA_TIMING, &u); + else u = 0; + + for (i = 0; i < 4; i++) { + + setup[i] = ((t >> ((3 - i) << 1)) & 0x3) + 1; + recover8b[i] = ((w >> ((1 - (i >> 1)) << 3)) & 0xf) + 1; + active8b[i] = ((w >> (((1 - (i >> 1)) << 3) + 4)) & 0xf) + 1; + active[i] = ((v >> (((3 - i) << 3) + 4)) & 0xf) + 1; + recover[i] = ((v >> ((3 - i) << 3)) & 0xf) + 1; + udma[i] = ((u >> ((3 - i) << 3)) & 0x7) + 2; + umul[i] = ((u >> (((3 - i) & 2) << 3)) & 0x8) ? 1 : 2; + uen[i] = ((u >> ((3 - i) << 3)) & 0x20); + den[i] = (c & ((i & 1) ? 0x40 : 0x20) << ((i & 2) << 2)); + + speed[i] = 2 * via_clock / (active[i] + recover[i]); + cycle[i] = 1000000 * (active[i] + recover[i]) / via_clock; + + if (!uen[i] || !den[i]) + continue; + + switch (via_config->flags & VIA_UDMA) { + + case VIA_UDMA_33: + speed[i] = 2 * via_clock / udma[i]; + cycle[i] = 1000000 * udma[i] / via_clock; + break; + + case VIA_UDMA_66: + speed[i] = 4 * via_clock / (udma[i] * umul[i]); + cycle[i] = 500000 * (udma[i] * umul[i]) / via_clock; + break; + + case VIA_UDMA_100: + speed[i] = 6 * via_clock / udma[i]; + cycle[i] = 333333 * udma[i] / via_clock; + break; + + case VIA_UDMA_133: + speed[i] = 8 * via_clock / udma[i]; + cycle[i] = 250000 * udma[i] / via_clock; + break; + } + } + + via_print_drive("Transfer Mode: ", "%10s", + den[i] ? (uen[i] ? "UDMA" : "DMA") : "PIO"); + + via_print_drive("Address Setup: ", "%8dns", + 1000000 * setup[i] / via_clock); + via_print_drive("Cmd Active: ", "%8dns", + 1000000 * active8b[i] / via_clock); + via_print_drive("Cmd Recovery: ", "%8dns", + 1000000 * recover8b[i] / via_clock); + via_print_drive("Data Active: ", "%8dns", + 1000000 * active[i] / via_clock); + via_print_drive("Data Recovery: ", "%8dns", + 1000000 * recover[i] / via_clock); + via_print_drive("Cycle Time: ", "%8dns", + cycle[i]); + via_print_drive("Transfer Rate: ", "%4d.%dMB/s", + speed[i] / 1000, speed[i] / 100 % 10); + + /* hoping it is less than 4K... */ + len = (p - buffer) - offset; + *addr = buffer + offset; + + return len > count ? count : len; +} + +#endif /* DISPLAY_VIA_TIMINGS && CONFIG_PROC_FS */ + +/** + * via_set_speed - write timing registers + * @dev: PCI device + * @dn: device + * @timing: IDE timing data to use + * + * via_set_speed writes timing values to the chipset registers + */ + +static void via_set_speed(struct pci_dev *dev, u8 dn, struct ide_timing *timing) +{ + u8 t; + + if (~via_config->flags & VIA_BAD_AST) { + pci_read_config_byte(dev, VIA_ADDRESS_SETUP, &t); + t = (t & ~(3 << ((3 - dn) << 1))) | ((FIT(timing->setup, 1, 4) - 1) << ((3 - dn) << 1)); + pci_write_config_byte(dev, VIA_ADDRESS_SETUP, t); + } + + pci_write_config_byte(dev, VIA_8BIT_TIMING + (1 - (dn >> 1)), + ((FIT(timing->act8b, 1, 16) - 1) << 4) | (FIT(timing->rec8b, 1, 16) - 1)); + + pci_write_config_byte(dev, VIA_DRIVE_TIMING + (3 - dn), + ((FIT(timing->active, 1, 16) - 1) << 4) | (FIT(timing->recover, 1, 16) - 1)); + + switch (via_config->flags & VIA_UDMA) { + case VIA_UDMA_33: t = timing->udma ? (0xe0 | (FIT(timing->udma, 2, 5) - 2)) : 0x03; break; + case VIA_UDMA_66: t = timing->udma ? (0xe8 | (FIT(timing->udma, 2, 9) - 2)) : 0x0f; break; + case VIA_UDMA_100: t = timing->udma ? (0xe0 | (FIT(timing->udma, 2, 9) - 2)) : 0x07; break; + case VIA_UDMA_133: t = timing->udma ? (0xe0 | (FIT(timing->udma, 2, 9) - 2)) : 0x07; break; + default: return; + } + + pci_write_config_byte(dev, VIA_UDMA_TIMING + (3 - dn), t); +} + +/** + * via_set_drive - configure transfer mode + * @drive: Drive to set up + * @speed: desired speed + * + * via_set_drive() computes timing values configures the drive and + * the chipset to a desired transfer mode. It also can be called + * by upper layers. + */ + +static int via_set_drive(ide_drive_t *drive, u8 speed) +{ + ide_drive_t *peer = HWIF(drive)->drives + (~drive->dn & 1); + struct ide_timing t, p; + unsigned int T, UT; + + if (speed != XFER_PIO_SLOW) + ide_config_drive_speed(drive, speed); + + T = 1000000000 / via_clock; + + switch (via_config->flags & VIA_UDMA) { + case VIA_UDMA_33: UT = T; break; + case VIA_UDMA_66: UT = T/2; break; + case VIA_UDMA_100: UT = T/3; break; + case VIA_UDMA_133: UT = T/4; break; + default: UT = T; + } + + ide_timing_compute(drive, speed, &t, T, UT); + + if (peer->present) { + ide_timing_compute(peer, peer->current_speed, &p, T, UT); + ide_timing_merge(&p, &t, &t, IDE_TIMING_8BIT); + } + + via_set_speed(HWIF(drive)->pci_dev, drive->dn, &t); + + if (!drive->init_speed) + drive->init_speed = speed; + drive->current_speed = speed; + + return 0; +} + +/** + * via82cxxx_tune_drive - PIO setup + * @drive: drive to set up + * @pio: mode to use (255 for 'best possible') + * + * A callback from the upper layers for PIO-only tuning. + */ + +static void via82cxxx_tune_drive(ide_drive_t *drive, u8 pio) +{ + if (pio == 255) { + via_set_drive(drive, + ide_find_best_mode(drive, XFER_PIO | XFER_EPIO)); + return; + } + + via_set_drive(drive, XFER_PIO_0 + min_t(u8, pio, 5)); +} + +/** + * via82cxxx_ide_dma_check - set up for DMA if possible + * @drive: IDE drive to set up + * + * Set up the drive for the highest supported speed considering the + * driver, controller and cable + */ + +static int via82cxxx_ide_dma_check (ide_drive_t *drive) +{ + u16 w80 = HWIF(drive)->udma_four; + + u16 speed = ide_find_best_mode(drive, + XFER_PIO | XFER_EPIO | XFER_SWDMA | XFER_MWDMA | + (via_config->flags & VIA_UDMA ? XFER_UDMA : 0) | + (w80 && (via_config->flags & VIA_UDMA) >= VIA_UDMA_66 ? XFER_UDMA_66 : 0) | + (w80 && (via_config->flags & VIA_UDMA) >= VIA_UDMA_100 ? XFER_UDMA_100 : 0) | + (w80 && (via_config->flags & VIA_UDMA) >= VIA_UDMA_133 ? XFER_UDMA_133 : 0)); + + via_set_drive(drive, speed); + + if (drive->autodma && (speed & XFER_MODE) != XFER_PIO) + return HWIF(drive)->ide_dma_on(drive); + return HWIF(drive)->ide_dma_off_quietly(drive); +} + +/** + * init_chipset_via82cxxx - initialization handler + * @dev: PCI device + * @name: Name of interface + * + * The initialization callback. Here we determine the IDE chip type + * and initialize its drive independent registers. + */ + +static unsigned int __init init_chipset_via82cxxx(struct pci_dev *dev, const char *name) +{ + struct pci_dev *isa = NULL; + u8 t, v; + unsigned int u; + int i; + + /* + * Find the ISA bridge to see how good the IDE is. + */ + + for (via_config = via_isa_bridges; via_config->id; via_config++) + if ((isa = pci_find_device(PCI_VENDOR_ID_VIA + + !!(via_config->flags & VIA_BAD_ID), + via_config->id, NULL))) { + + pci_read_config_byte(isa, PCI_REVISION_ID, &t); + if (t >= via_config->rev_min && + t <= via_config->rev_max) + break; + } + + if (!via_config->id) { + printk(KERN_WARNING "VP_IDE: Unknown VIA SouthBridge, disabling DMA.\n"); + return -ENODEV; + } + + /* + * Check 80-wire cable presence and setup Clk66. + */ + + switch (via_config->flags & VIA_UDMA) { + + case VIA_UDMA_66: + /* Enable Clk66 */ + pci_read_config_dword(dev, VIA_UDMA_TIMING, &u); + pci_write_config_dword(dev, VIA_UDMA_TIMING, u|0x80008); + for (i = 24; i >= 0; i -= 8) + if (((u >> (i & 16)) & 8) && + ((u >> i) & 0x20) && + (((u >> i) & 7) < 2)) { + /* + * 2x PCI clock and + * UDMA w/ < 3T/cycle + */ + via_80w |= (1 << (1 - (i >> 4))); + } + break; + + case VIA_UDMA_100: + pci_read_config_dword(dev, VIA_UDMA_TIMING, &u); + for (i = 24; i >= 0; i -= 8) + if (((u >> i) & 0x10) || + (((u >> i) & 0x20) && + (((u >> i) & 7) < 4))) { + /* BIOS 80-wire bit or + * UDMA w/ < 60ns/cycle + */ + via_80w |= (1 << (1 - (i >> 4))); + } + break; + + case VIA_UDMA_133: + pci_read_config_dword(dev, VIA_UDMA_TIMING, &u); + for (i = 24; i >= 0; i -= 8) + if (((u >> i) & 0x10) || + (((u >> i) & 0x20) && + (((u >> i) & 7) < 6))) { + /* BIOS 80-wire bit or + * UDMA w/ < 60ns/cycle + */ + via_80w |= (1 << (1 - (i >> 4))); + } + break; + + } + + /* Disable Clk66 */ + if (via_config->flags & VIA_BAD_CLK66) { + /* Would cause trouble on 596a and 686 */ + pci_read_config_dword(dev, VIA_UDMA_TIMING, &u); + pci_write_config_dword(dev, VIA_UDMA_TIMING, u & ~0x80008); + } + + /* + * Check whether interfaces are enabled. + */ + + pci_read_config_byte(dev, VIA_IDE_ENABLE, &v); + + /* + * Set up FIFO sizes and thresholds. + */ + + pci_read_config_byte(dev, VIA_FIFO_CONFIG, &t); + + /* Disable PREQ# till DDACK# */ + if (via_config->flags & VIA_BAD_PREQ) { + /* Would crash on 586b rev 41 */ + t &= 0x7f; + } + + /* Fix FIFO split between channels */ + if (via_config->flags & VIA_SET_FIFO) { + t &= (t & 0x9f); + switch (v & 3) { + case 2: t |= 0x00; break; /* 16 on primary */ + case 1: t |= 0x60; break; /* 16 on secondary */ + case 3: t |= 0x20; break; /* 8 pri 8 sec */ + } + } + + pci_write_config_byte(dev, VIA_FIFO_CONFIG, t); + + /* + * Determine system bus clock. + */ + + via_clock = system_bus_clock() * 1000; + + switch (via_clock) { + case 33000: via_clock = 33333; break; + case 37000: via_clock = 37500; break; + case 41000: via_clock = 41666; break; + } + + if (via_clock < 20000 || via_clock > 50000) { + printk(KERN_WARNING "VP_IDE: User given PCI clock speed " + "impossible (%d), using 33 MHz instead.\n", via_clock); + printk(KERN_WARNING "VP_IDE: Use ide0=ata66 if you want " + "to assume 80-wire cable.\n"); + via_clock = 33333; + } + + /* + * Print the boot message. + */ + + pci_read_config_byte(isa, PCI_REVISION_ID, &t); + printk(KERN_INFO "VP_IDE: VIA %s (rev %02x) IDE %s " + "controller on pci%s\n", + via_config->name, t, + via_dma[via_config->flags & VIA_UDMA], + pci_name(dev)); + + /* + * Setup /proc/ide/via entry. + */ + +#if defined(DISPLAY_VIA_TIMINGS) && defined(CONFIG_PROC_FS) + if (!via_proc) { + via_base = pci_resource_start(dev, 4); + bmide_dev = dev; + isa_dev = isa; + ide_pci_create_host_proc("via", via_get_info); + via_proc = 1; + } +#endif /* DISPLAY_VIA_TIMINGS && CONFIG_PROC_FS */ + return 0; +} + +static void __init init_hwif_via82cxxx(ide_hwif_t *hwif) +{ + int i; + + hwif->autodma = 0; + + hwif->tuneproc = &via82cxxx_tune_drive; + hwif->speedproc = &via_set_drive; + + +#if defined(CONFIG_PPC_MULTIPLATFORM) && defined(CONFIG_PPC32) + if(_machine == _MACH_chrp && _chrp_type == _CHRP_Pegasos) { + hwif->irq = hwif->channel ? 15 : 14; + } +#endif + + for (i = 0; i < 2; i++) { + hwif->drives[i].io_32bit = 1; + hwif->drives[i].unmask = (via_config->flags & VIA_NO_UNMASK) ? 0 : 1; + hwif->drives[i].autotune = 1; + hwif->drives[i].dn = hwif->channel * 2 + i; + } + + if (!hwif->dma_base) + return; + + hwif->atapi_dma = 1; + hwif->ultra_mask = 0x7f; + hwif->mwdma_mask = 0x07; + hwif->swdma_mask = 0x07; + + if (!hwif->udma_four) + hwif->udma_four = (via_80w >> hwif->channel) & 1; + hwif->ide_dma_check = &via82cxxx_ide_dma_check; + if (!noautodma) + hwif->autodma = 1; + hwif->drives[0].autodma = hwif->autodma; + hwif->drives[1].autodma = hwif->autodma; +} + +static ide_pci_device_t via82cxxx_chipset __devinitdata = { + .name = "VP_IDE", + .init_chipset = init_chipset_via82cxxx, + .init_hwif = init_hwif_via82cxxx, + .channels = 2, + .autodma = NOAUTODMA, + .enablebits = {{0x40,0x02,0x02}, {0x40,0x01,0x01}}, + .bootable = ON_BOARD, +}; + +static int __devinit via_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + return ide_setup_pci_device(dev, &via82cxxx_chipset); +} + +static struct pci_device_id via_pci_tbl[] = { + { PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C576_1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, via_pci_tbl); + +static struct pci_driver driver = { + .name = "VIA_IDE", + .id_table = via_pci_tbl, + .probe = via_init_one, +}; + +static int via_ide_init(void) +{ + return ide_pci_register_driver(&driver); +} + +module_init(via_ide_init); + +MODULE_AUTHOR("Vojtech Pavlik, Michel Aubry, Jeff Garzik, Andre Hedrick"); +MODULE_DESCRIPTION("PCI driver module for VIA IDE"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/ppc/mpc8xx.c b/drivers/ide/ppc/mpc8xx.c new file mode 100644 index 0000000..b80c613 --- /dev/null +++ b/drivers/ide/ppc/mpc8xx.c @@ -0,0 +1,855 @@ +/* + * linux/drivers/ide/ppc/ide-m8xx.c + * + * Copyright (C) 2000, 2001 Wolfgang Denk, wd@denx.de + * Modified for direct IDE interface + * by Thomas Lange, thomas@corelatus.com + * Modified for direct IDE interface on 8xx without using the PCMCIA + * controller + * by Steven.Scholz@imc-berlin.de + * Moved out of arch/ppc/kernel/m8xx_setup.c, other minor cleanups + * by Mathew Locke <mattl@mvista.com> + */ + +#include <linux/config.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/stddef.h> +#include <linux/unistd.h> +#include <linux/ptrace.h> +#include <linux/slab.h> +#include <linux/user.h> +#include <linux/a.out.h> +#include <linux/tty.h> +#include <linux/major.h> +#include <linux/interrupt.h> +#include <linux/reboot.h> +#include <linux/init.h> +#include <linux/ioport.h> +#include <linux/ide.h> +#include <linux/bootmem.h> + +#include <asm/mpc8xx.h> +#include <asm/mmu.h> +#include <asm/processor.h> +#include <asm/residual.h> +#include <asm/io.h> +#include <asm/pgtable.h> +#include <asm/ide.h> +#include <asm/8xx_immap.h> +#include <asm/machdep.h> +#include <asm/irq.h> + +static int identify (volatile u8 *p); +static void print_fixed (volatile u8 *p); +static void print_funcid (int func); +static int check_ide_device (unsigned long base); + +static void ide_interrupt_ack (void *dev); +static void m8xx_ide_tuneproc(ide_drive_t *drive, u8 pio); + +typedef struct ide_ioport_desc { + unsigned long base_off; /* Offset to PCMCIA memory */ + unsigned long reg_off[IDE_NR_PORTS]; /* controller register offsets */ + int irq; /* IRQ */ +} ide_ioport_desc_t; + +ide_ioport_desc_t ioport_dsc[MAX_HWIFS] = { +#ifdef IDE0_BASE_OFFSET + { IDE0_BASE_OFFSET, + { + IDE0_DATA_REG_OFFSET, + IDE0_ERROR_REG_OFFSET, + IDE0_NSECTOR_REG_OFFSET, + IDE0_SECTOR_REG_OFFSET, + IDE0_LCYL_REG_OFFSET, + IDE0_HCYL_REG_OFFSET, + IDE0_SELECT_REG_OFFSET, + IDE0_STATUS_REG_OFFSET, + IDE0_CONTROL_REG_OFFSET, + IDE0_IRQ_REG_OFFSET, + }, + IDE0_INTERRUPT, + }, +#ifdef IDE1_BASE_OFFSET + { IDE1_BASE_OFFSET, + { + IDE1_DATA_REG_OFFSET, + IDE1_ERROR_REG_OFFSET, + IDE1_NSECTOR_REG_OFFSET, + IDE1_SECTOR_REG_OFFSET, + IDE1_LCYL_REG_OFFSET, + IDE1_HCYL_REG_OFFSET, + IDE1_SELECT_REG_OFFSET, + IDE1_STATUS_REG_OFFSET, + IDE1_CONTROL_REG_OFFSET, + IDE1_IRQ_REG_OFFSET, + }, + IDE1_INTERRUPT, + }, +#endif /* IDE1_BASE_OFFSET */ +#endif /* IDE0_BASE_OFFSET */ +}; + +ide_pio_timings_t ide_pio_clocks[6]; +int hold_time[6] = {30, 20, 15, 10, 10, 10 }; /* PIO Mode 5 with IORDY (nonstandard) */ + +/* + * Warning: only 1 (ONE) PCMCIA slot supported here, + * which must be correctly initialized by the firmware (PPCBoot). + */ +static int _slot_ = -1; /* will be read from PCMCIA registers */ + +/* Make clock cycles and always round up */ +#define PCMCIA_MK_CLKS( t, T ) (( (t) * ((T)/1000000) + 999U ) / 1000U ) + + + +/* + * IDE stuff. + */ +static int +m8xx_ide_default_irq(unsigned long base) +{ +#ifdef CONFIG_BLK_DEV_MPC8xx_IDE + if (base >= MAX_HWIFS) + return 0; + + printk("[%d] m8xx_ide_default_irq %d\n",__LINE__,ioport_dsc[base].irq); + + return (ioport_dsc[base].irq); +#else + return 9; +#endif +} + +static unsigned long +m8xx_ide_default_io_base(int index) +{ + return index; +} + +#define M8XX_PCMCIA_CD2(slot) (0x10000000 >> (slot << 4)) +#define M8XX_PCMCIA_CD1(slot) (0x08000000 >> (slot << 4)) + +/* + * The TQM850L hardware has two pins swapped! Grrrrgh! + */ +#ifdef CONFIG_TQM850L +#define __MY_PCMCIA_GCRX_CXRESET PCMCIA_GCRX_CXOE +#define __MY_PCMCIA_GCRX_CXOE PCMCIA_GCRX_CXRESET +#else +#define __MY_PCMCIA_GCRX_CXRESET PCMCIA_GCRX_CXRESET +#define __MY_PCMCIA_GCRX_CXOE PCMCIA_GCRX_CXOE +#endif + +#if defined(CONFIG_BLK_DEV_MPC8xx_IDE) && defined(CONFIG_IDE_8xx_PCCARD) +#define PCMCIA_SCHLVL IDE0_INTERRUPT /* Status Change Interrupt Level */ +static int pcmcia_schlvl = PCMCIA_SCHLVL; +#endif + +/* + * See include/linux/ide.h for definition of hw_regs_t (p, base) + */ + +/* + * m8xx_ide_init_hwif_ports for a direct IDE interface _using_ + */ +#if defined(CONFIG_IDE_8xx_PCCARD) || defined(CONFIG_IDE_8xx_DIRECT) +static void +m8xx_ide_init_hwif_ports(hw_regs_t *hw, unsigned long data_port, + unsigned long ctrl_port, int *irq) +{ + unsigned long *p = hw->io_ports; + int i; + + typedef struct { + ulong br; + ulong or; + } pcmcia_win_t; + volatile pcmcia_win_t *win; + volatile pcmconf8xx_t *pcmp; + + uint *pgcrx; + u32 pcmcia_phy_base; + u32 pcmcia_phy_end; + static unsigned long pcmcia_base = 0; + unsigned long base; + + *p = 0; + if (irq) + *irq = 0; + + pcmp = (pcmconf8xx_t *)(&(((immap_t *)IMAP_ADDR)->im_pcmcia)); + + if (!pcmcia_base) { + /* + * Read out PCMCIA registers. Since the reset values + * are undefined, we sure hope that they have been + * set up by firmware + */ + + /* Scan all registers for valid settings */ + pcmcia_phy_base = 0xFFFFFFFF; + pcmcia_phy_end = 0; + /* br0 is start of brX and orX regs */ + win = (pcmcia_win_t *) \ + (&(((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pbr0)); + for (i = 0; i < 8; i++) { + if (win->or & 1) { /* This bank is marked as valid */ + if (win->br < pcmcia_phy_base) { + pcmcia_phy_base = win->br; + } + if ((win->br + PCMCIA_MEM_SIZE) > pcmcia_phy_end) { + pcmcia_phy_end = win->br + PCMCIA_MEM_SIZE; + } + /* Check which slot that has been defined */ + _slot_ = (win->or >> 2) & 1; + + } /* Valid bank */ + win++; + } /* for */ + + printk ("PCMCIA slot %c: phys mem %08x...%08x (size %08x)\n", + 'A' + _slot_, + pcmcia_phy_base, pcmcia_phy_end, + pcmcia_phy_end - pcmcia_phy_base); + + pcmcia_base=(unsigned long)ioremap(pcmcia_phy_base, + pcmcia_phy_end-pcmcia_phy_base); + +#ifdef DEBUG + printk ("PCMCIA virt base: %08lx\n", pcmcia_base); +#endif + /* Compute clock cycles for PIO timings */ + for (i=0; i<6; ++i) { + bd_t *binfo = (bd_t *)__res; + + hold_time[i] = + PCMCIA_MK_CLKS (hold_time[i], + binfo->bi_busfreq); + ide_pio_clocks[i].setup_time = + PCMCIA_MK_CLKS (ide_pio_timings[i].setup_time, + binfo->bi_busfreq); + ide_pio_clocks[i].active_time = + PCMCIA_MK_CLKS (ide_pio_timings[i].active_time, + binfo->bi_busfreq); + ide_pio_clocks[i].cycle_time = + PCMCIA_MK_CLKS (ide_pio_timings[i].cycle_time, + binfo->bi_busfreq); +#if 0 + printk ("PIO mode %d timings: %d/%d/%d => %d/%d/%d\n", + i, + ide_pio_clocks[i].setup_time, + ide_pio_clocks[i].active_time, + ide_pio_clocks[i].hold_time, + ide_pio_clocks[i].cycle_time, + ide_pio_timings[i].setup_time, + ide_pio_timings[i].active_time, + ide_pio_timings[i].hold_time, + ide_pio_timings[i].cycle_time); +#endif + } + } + + if (data_port >= MAX_HWIFS) + return; + + if (_slot_ == -1) { + printk ("PCMCIA slot has not been defined! Using A as default\n"); + _slot_ = 0; + } + +#ifdef CONFIG_IDE_8xx_PCCARD + +#ifdef DEBUG + printk ("PIPR = 0x%08X slot %c ==> mask = 0x%X\n", + pcmp->pcmc_pipr, + 'A' + _slot_, + M8XX_PCMCIA_CD1(_slot_) | M8XX_PCMCIA_CD2(_slot_) ); +#endif /* DEBUG */ + + if (pcmp->pcmc_pipr & (M8XX_PCMCIA_CD1(_slot_)|M8XX_PCMCIA_CD2(_slot_))) { + printk ("No card in slot %c: PIPR=%08x\n", + 'A' + _slot_, (u32) pcmp->pcmc_pipr); + return; /* No card in slot */ + } + + check_ide_device (pcmcia_base); + +#endif /* CONFIG_IDE_8xx_PCCARD */ + + base = pcmcia_base + ioport_dsc[data_port].base_off; +#ifdef DEBUG + printk ("base: %08x + %08x = %08x\n", + pcmcia_base, ioport_dsc[data_port].base_off, base); +#endif + + for (i = 0; i < IDE_NR_PORTS; ++i) { +#ifdef DEBUG + printk ("port[%d]: %08x + %08x = %08x\n", + i, + base, + ioport_dsc[data_port].reg_off[i], + i, base + ioport_dsc[data_port].reg_off[i]); +#endif + *p++ = base + ioport_dsc[data_port].reg_off[i]; + } + + if (irq) { +#ifdef CONFIG_IDE_8xx_PCCARD + unsigned int reg; + + *irq = ioport_dsc[data_port].irq; + if (_slot_) + pgcrx = &((immap_t *) IMAP_ADDR)->im_pcmcia.pcmc_pgcrb; + else + pgcrx = &((immap_t *) IMAP_ADDR)->im_pcmcia.pcmc_pgcra; + + reg = *pgcrx; + reg |= mk_int_int_mask (pcmcia_schlvl) << 24; + reg |= mk_int_int_mask (pcmcia_schlvl) << 16; + *pgcrx = reg; +#else /* direct connected IDE drive, i.e. external IRQ, not the PCMCIA irq */ + *irq = ioport_dsc[data_port].irq; +#endif /* CONFIG_IDE_8xx_PCCARD */ + } + + /* register routine to tune PIO mode */ + ide_hwifs[data_port].tuneproc = m8xx_ide_tuneproc; + + hw->ack_intr = (ide_ack_intr_t *) ide_interrupt_ack; + /* Enable Harddisk Interrupt, + * and make it edge sensitive + */ + /* (11-18) Set edge detect for irq, no wakeup from low power mode */ + ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_siel |= + (0x80000000 >> ioport_dsc[data_port].irq); + +#ifdef CONFIG_IDE_8xx_PCCARD + /* Make sure we don't get garbage irq */ + ((immap_t *) IMAP_ADDR)->im_pcmcia.pcmc_pscr = 0xFFFF; + + /* Enable falling edge irq */ + pcmp->pcmc_per = 0x100000 >> (16 * _slot_); +#endif /* CONFIG_IDE_8xx_PCCARD */ +} /* m8xx_ide_init_hwif_ports() using 8xx internal PCMCIA interface */ +#endif /* CONFIG_IDE_8xx_PCCARD || CONFIG_IDE_8xx_DIRECT */ + +/* + * m8xx_ide_init_hwif_ports for a direct IDE interface _not_ using + * MPC8xx's internal PCMCIA interface + */ +#if defined(CONFIG_IDE_EXT_DIRECT) +void m8xx_ide_init_hwif_ports (hw_regs_t *hw, + unsigned long data_port, unsigned long ctrl_port, int *irq) +{ + unsigned long *p = hw->io_ports; + int i; + + u32 ide_phy_base; + u32 ide_phy_end; + static unsigned long ide_base = 0; + unsigned long base; + + *p = 0; + if (irq) + *irq = 0; + + if (!ide_base) { + + /* TODO: + * - add code to read ORx, BRx + */ + ide_phy_base = CFG_ATA_BASE_ADDR; + ide_phy_end = CFG_ATA_BASE_ADDR + 0x200; + + printk ("IDE phys mem : %08x...%08x (size %08x)\n", + ide_phy_base, ide_phy_end, + ide_phy_end - ide_phy_base); + + ide_base=(unsigned long)ioremap(ide_phy_base, + ide_phy_end-ide_phy_base); + +#ifdef DEBUG + printk ("IDE virt base: %08lx\n", ide_base); +#endif + } + + if (data_port >= MAX_HWIFS) + return; + + base = ide_base + ioport_dsc[data_port].base_off; +#ifdef DEBUG + printk ("base: %08x + %08x = %08x\n", + ide_base, ioport_dsc[data_port].base_off, base); +#endif + + for (i = 0; i < IDE_NR_PORTS; ++i) { +#ifdef DEBUG + printk ("port[%d]: %08x + %08x = %08x\n", + i, + base, + ioport_dsc[data_port].reg_off[i], + i, base + ioport_dsc[data_port].reg_off[i]); +#endif + *p++ = base + ioport_dsc[data_port].reg_off[i]; + } + + if (irq) { + /* direct connected IDE drive, i.e. external IRQ */ + *irq = ioport_dsc[data_port].irq; + } + + /* register routine to tune PIO mode */ + ide_hwifs[data_port].tuneproc = m8xx_ide_tuneproc; + + hw->ack_intr = (ide_ack_intr_t *) ide_interrupt_ack; + /* Enable Harddisk Interrupt, + * and make it edge sensitive + */ + /* (11-18) Set edge detect for irq, no wakeup from low power mode */ + ((immap_t *) IMAP_ADDR)->im_siu_conf.sc_siel |= + (0x80000000 >> ioport_dsc[data_port].irq); +} /* m8xx_ide_init_hwif_ports() for CONFIG_IDE_8xx_DIRECT */ + +#endif /* CONFIG_IDE_8xx_DIRECT */ + + +/* -------------------------------------------------------------------- */ + + +/* PCMCIA Timing */ +#ifndef PCMCIA_SHT +#define PCMCIA_SHT(t) ((t & 0x0F)<<16) /* Strobe Hold Time */ +#define PCMCIA_SST(t) ((t & 0x0F)<<12) /* Strobe Setup Time */ +#define PCMCIA_SL(t) ((t==32) ? 0 : ((t & 0x1F)<<7)) /* Strobe Length */ +#endif + + +/* Calculate PIO timings */ +static void +m8xx_ide_tuneproc(ide_drive_t *drive, u8 pio) +{ + ide_pio_data_t d; +#if defined(CONFIG_IDE_8xx_PCCARD) || defined(CONFIG_IDE_8xx_DIRECT) + volatile pcmconf8xx_t *pcmp; + ulong timing, mask, reg; +#endif + + pio = ide_get_best_pio_mode(drive, pio, 4, &d); + +#if 1 + printk("%s[%d] %s: best PIO mode: %d\n", + __FILE__,__LINE__,__FUNCTION__, pio); +#endif + +#if defined(CONFIG_IDE_8xx_PCCARD) || defined(CONFIG_IDE_8xx_DIRECT) + pcmp = (pcmconf8xx_t *)(&(((immap_t *)IMAP_ADDR)->im_pcmcia)); + + mask = ~(PCMCIA_SHT(0xFF) | PCMCIA_SST(0xFF) | PCMCIA_SL(0xFF)); + + timing = PCMCIA_SHT(hold_time[pio] ) + | PCMCIA_SST(ide_pio_clocks[pio].setup_time ) + | PCMCIA_SL (ide_pio_clocks[pio].active_time) + ; + +#if 1 + printk ("Setting timing bits 0x%08lx in PCMCIA controller\n", timing); +#endif + if ((reg = pcmp->pcmc_por0 & mask) != 0) + pcmp->pcmc_por0 = reg | timing; + + if ((reg = pcmp->pcmc_por1 & mask) != 0) + pcmp->pcmc_por1 = reg | timing; + + if ((reg = pcmp->pcmc_por2 & mask) != 0) + pcmp->pcmc_por2 = reg | timing; + + if ((reg = pcmp->pcmc_por3 & mask) != 0) + pcmp->pcmc_por3 = reg | timing; + + if ((reg = pcmp->pcmc_por4 & mask) != 0) + pcmp->pcmc_por4 = reg | timing; + + if ((reg = pcmp->pcmc_por5 & mask) != 0) + pcmp->pcmc_por5 = reg | timing; + + if ((reg = pcmp->pcmc_por6 & mask) != 0) + pcmp->pcmc_por6 = reg | timing; + + if ((reg = pcmp->pcmc_por7 & mask) != 0) + pcmp->pcmc_por7 = reg | timing; + +#elif defined(CONFIG_IDE_EXT_DIRECT) + + printk("%s[%d] %s: not implemented yet!\n", + __FILE__,__LINE__,__FUNCTION__); +#endif /* defined(CONFIG_IDE_8xx_PCCARD) || defined(CONFIG_IDE_8xx_PCMCIA */ +} + +static void +ide_interrupt_ack (void *dev) +{ +#ifdef CONFIG_IDE_8xx_PCCARD + u_int pscr, pipr; + +#if (PCMCIA_SOCKETS_NO == 2) + u_int _slot_; +#endif + + /* get interrupt sources */ + + pscr = ((volatile immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pscr; + pipr = ((volatile immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pipr; + + /* + * report only if both card detect signals are the same + * not too nice done, + * we depend on that CD2 is the bit to the left of CD1... + */ + + if(_slot_==-1){ + printk("PCMCIA slot has not been defined! Using A as default\n"); + _slot_=0; + } + + if(((pipr & M8XX_PCMCIA_CD2(_slot_)) >> 1) ^ + (pipr & M8XX_PCMCIA_CD1(_slot_)) ) { + printk ("card detect interrupt\n"); + } + /* clear the interrupt sources */ + ((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pscr = pscr; + +#else /* ! CONFIG_IDE_8xx_PCCARD */ + /* + * Only CONFIG_IDE_8xx_PCCARD is using the interrupt of the + * MPC8xx's PCMCIA controller, so there is nothing to be done here + * for CONFIG_IDE_8xx_DIRECT and CONFIG_IDE_EXT_DIRECT. + * The interrupt is handled somewhere else. -- Steven + */ +#endif /* CONFIG_IDE_8xx_PCCARD */ +} + + + +/* + * CIS Tupel codes + */ +#define CISTPL_NULL 0x00 +#define CISTPL_DEVICE 0x01 +#define CISTPL_LONGLINK_CB 0x02 +#define CISTPL_INDIRECT 0x03 +#define CISTPL_CONFIG_CB 0x04 +#define CISTPL_CFTABLE_ENTRY_CB 0x05 +#define CISTPL_LONGLINK_MFC 0x06 +#define CISTPL_BAR 0x07 +#define CISTPL_PWR_MGMNT 0x08 +#define CISTPL_EXTDEVICE 0x09 +#define CISTPL_CHECKSUM 0x10 +#define CISTPL_LONGLINK_A 0x11 +#define CISTPL_LONGLINK_C 0x12 +#define CISTPL_LINKTARGET 0x13 +#define CISTPL_NO_LINK 0x14 +#define CISTPL_VERS_1 0x15 +#define CISTPL_ALTSTR 0x16 +#define CISTPL_DEVICE_A 0x17 +#define CISTPL_JEDEC_C 0x18 +#define CISTPL_JEDEC_A 0x19 +#define CISTPL_CONFIG 0x1a +#define CISTPL_CFTABLE_ENTRY 0x1b +#define CISTPL_DEVICE_OC 0x1c +#define CISTPL_DEVICE_OA 0x1d +#define CISTPL_DEVICE_GEO 0x1e +#define CISTPL_DEVICE_GEO_A 0x1f +#define CISTPL_MANFID 0x20 +#define CISTPL_FUNCID 0x21 +#define CISTPL_FUNCE 0x22 +#define CISTPL_SWIL 0x23 +#define CISTPL_END 0xff + +/* + * CIS Function ID codes + */ +#define CISTPL_FUNCID_MULTI 0x00 +#define CISTPL_FUNCID_MEMORY 0x01 +#define CISTPL_FUNCID_SERIAL 0x02 +#define CISTPL_FUNCID_PARALLEL 0x03 +#define CISTPL_FUNCID_FIXED 0x04 +#define CISTPL_FUNCID_VIDEO 0x05 +#define CISTPL_FUNCID_NETWORK 0x06 +#define CISTPL_FUNCID_AIMS 0x07 +#define CISTPL_FUNCID_SCSI 0x08 + +/* + * Fixed Disk FUNCE codes + */ +#define CISTPL_IDE_INTERFACE 0x01 + +#define CISTPL_FUNCE_IDE_IFACE 0x01 +#define CISTPL_FUNCE_IDE_MASTER 0x02 +#define CISTPL_FUNCE_IDE_SLAVE 0x03 + +/* First feature byte */ +#define CISTPL_IDE_SILICON 0x04 +#define CISTPL_IDE_UNIQUE 0x08 +#define CISTPL_IDE_DUAL 0x10 + +/* Second feature byte */ +#define CISTPL_IDE_HAS_SLEEP 0x01 +#define CISTPL_IDE_HAS_STANDBY 0x02 +#define CISTPL_IDE_HAS_IDLE 0x04 +#define CISTPL_IDE_LOW_POWER 0x08 +#define CISTPL_IDE_REG_INHIBIT 0x10 +#define CISTPL_IDE_HAS_INDEX 0x20 +#define CISTPL_IDE_IOIS16 0x40 + + +/* -------------------------------------------------------------------- */ + + +#define MAX_TUPEL_SZ 512 +#define MAX_FEATURES 4 + +static int check_ide_device (unsigned long base) +{ + volatile u8 *ident = NULL; + volatile u8 *feature_p[MAX_FEATURES]; + volatile u8 *p, *start; + int n_features = 0; + u8 func_id = ~0; + u8 code, len; + unsigned short config_base = 0; + int found = 0; + int i; + +#ifdef DEBUG + printk ("PCMCIA MEM: %08lX\n", base); +#endif + start = p = (volatile u8 *) base; + + while ((p - start) < MAX_TUPEL_SZ) { + + code = *p; p += 2; + + if (code == 0xFF) { /* End of chain */ + break; + } + + len = *p; p += 2; +#ifdef DEBUG_PCMCIA + { volatile u8 *q = p; + printk ("\nTuple code %02x length %d\n\tData:", + code, len); + + for (i = 0; i < len; ++i) { + printk (" %02x", *q); + q+= 2; + } + } +#endif /* DEBUG_PCMCIA */ + switch (code) { + case CISTPL_VERS_1: + ident = p + 4; + break; + case CISTPL_FUNCID: + func_id = *p; + break; + case CISTPL_FUNCE: + if (n_features < MAX_FEATURES) + feature_p[n_features++] = p; + break; + case CISTPL_CONFIG: + config_base = (*(p+6) << 8) + (*(p+4)); + default: + break; + } + p += 2 * len; + } + + found = identify (ident); + + if (func_id != ((u8)~0)) { + print_funcid (func_id); + + if (func_id == CISTPL_FUNCID_FIXED) + found = 1; + else + return (1); /* no disk drive */ + } + + for (i=0; i<n_features; ++i) { + print_fixed (feature_p[i]); + } + + if (!found) { + printk ("unknown card type\n"); + return (1); + } + + /* set level mode irq and I/O mapped device in config reg*/ + *((u8 *)(base + config_base)) = 0x41; + + return (0); +} + +/* ------------------------------------------------------------------------- */ + +static void print_funcid (int func) +{ + switch (func) { + case CISTPL_FUNCID_MULTI: + printk (" Multi-Function"); + break; + case CISTPL_FUNCID_MEMORY: + printk (" Memory"); + break; + case CISTPL_FUNCID_SERIAL: + printk (" Serial Port"); + break; + case CISTPL_FUNCID_PARALLEL: + printk (" Parallel Port"); + break; + case CISTPL_FUNCID_FIXED: + printk (" Fixed Disk"); + break; + case CISTPL_FUNCID_VIDEO: + printk (" Video Adapter"); + break; + case CISTPL_FUNCID_NETWORK: + printk (" Network Adapter"); + break; + case CISTPL_FUNCID_AIMS: + printk (" AIMS Card"); + break; + case CISTPL_FUNCID_SCSI: + printk (" SCSI Adapter"); + break; + default: + printk (" Unknown"); + break; + } + printk (" Card\n"); +} + +/* ------------------------------------------------------------------------- */ + +static void print_fixed (volatile u8 *p) +{ + if (p == NULL) + return; + + switch (*p) { + case CISTPL_FUNCE_IDE_IFACE: + { u8 iface = *(p+2); + + printk ((iface == CISTPL_IDE_INTERFACE) ? " IDE" : " unknown"); + printk (" interface "); + break; + } + case CISTPL_FUNCE_IDE_MASTER: + case CISTPL_FUNCE_IDE_SLAVE: + { u8 f1 = *(p+2); + u8 f2 = *(p+4); + + printk ((f1 & CISTPL_IDE_SILICON) ? " [silicon]" : " [rotating]"); + + if (f1 & CISTPL_IDE_UNIQUE) + printk (" [unique]"); + + printk ((f1 & CISTPL_IDE_DUAL) ? " [dual]" : " [single]"); + + if (f2 & CISTPL_IDE_HAS_SLEEP) + printk (" [sleep]"); + + if (f2 & CISTPL_IDE_HAS_STANDBY) + printk (" [standby]"); + + if (f2 & CISTPL_IDE_HAS_IDLE) + printk (" [idle]"); + + if (f2 & CISTPL_IDE_LOW_POWER) + printk (" [low power]"); + + if (f2 & CISTPL_IDE_REG_INHIBIT) + printk (" [reg inhibit]"); + + if (f2 & CISTPL_IDE_HAS_INDEX) + printk (" [index]"); + + if (f2 & CISTPL_IDE_IOIS16) + printk (" [IOis16]"); + + break; + } + } + printk ("\n"); +} + +/* ------------------------------------------------------------------------- */ + + +#define MAX_IDENT_CHARS 64 +#define MAX_IDENT_FIELDS 4 + +static u8 *known_cards[] = { + "ARGOSY PnPIDE D5", + NULL +}; + +static int identify (volatile u8 *p) +{ + u8 id_str[MAX_IDENT_CHARS]; + u8 data; + u8 *t; + u8 **card; + int i, done; + + if (p == NULL) + return (0); /* Don't know */ + + t = id_str; + done =0; + + for (i=0; i<=4 && !done; ++i, p+=2) { + while ((data = *p) != '\0') { + if (data == 0xFF) { + done = 1; + break; + } + *t++ = data; + if (t == &id_str[MAX_IDENT_CHARS-1]) { + done = 1; + break; + } + p += 2; + } + if (!done) + *t++ = ' '; + } + *t = '\0'; + while (--t > id_str) { + if (*t == ' ') + *t = '\0'; + else + break; + } + printk ("Card ID: %s\n", id_str); + + for (card=known_cards; *card; ++card) { + if (strcmp(*card, id_str) == 0) { /* found! */ + return (1); + } + } + + return (0); /* don't know */ +} + +void m8xx_ide_init(void) +{ + ppc_ide_md.default_irq = m8xx_ide_default_irq; + ppc_ide_md.default_io_base = m8xx_ide_default_io_base; + ppc_ide_md.ide_init_hwif = m8xx_ide_init_hwif_ports; +} diff --git a/drivers/ide/ppc/pmac.c b/drivers/ide/ppc/pmac.c new file mode 100644 index 0000000..6dc273a --- /dev/null +++ b/drivers/ide/ppc/pmac.c @@ -0,0 +1,2208 @@ +/* + * linux/drivers/ide/ide-pmac.c + * + * Support for IDE interfaces on PowerMacs. + * These IDE interfaces are memory-mapped and have a DBDMA channel + * for doing DMA. + * + * Copyright (C) 1998-2003 Paul Mackerras & Ben. Herrenschmidt + * + * 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/ide/ide-dma.c: + * + * Copyright (c) 1995-1998 Mark Lord + * + * TODO: - Use pre-calculated (kauai) timing tables all the time and + * get rid of the "rounded" tables used previously, so we have the + * same table format for all controllers and can then just have one + * big table + * + */ +#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 <linux/notifier.h> +#include <linux/reboot.h> +#include <linux/pci.h> +#include <linux/adb.h> +#include <linux/pmu.h> +#include <linux/scatterlist.h> + +#include <asm/prom.h> +#include <asm/io.h> +#include <asm/dbdma.h> +#include <asm/ide.h> +#include <asm/pci-bridge.h> +#include <asm/machdep.h> +#include <asm/pmac_feature.h> +#include <asm/sections.h> +#include <asm/irq.h> + +#ifndef CONFIG_PPC64 +#include <asm/mediabay.h> +#endif + +#include "ide-timing.h" + +#undef IDE_PMAC_DEBUG + +#define DMA_WAIT_TIMEOUT 50 + +typedef struct pmac_ide_hwif { + unsigned long regbase; + int irq; + int kind; + int aapl_bus_id; + unsigned cable_80 : 1; + unsigned mediabay : 1; + unsigned broken_dma : 1; + unsigned broken_dma_warn : 1; + struct device_node* node; + struct macio_dev *mdev; + u32 timings[4]; + volatile u32 __iomem * *kauai_fcr; +#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC + /* Those fields are duplicating what is in hwif. We currently + * can't use the hwif ones because of some assumptions that are + * beeing done by the generic code about the kind of dma controller + * and format of the dma table. This will have to be fixed though. + */ + volatile struct dbdma_regs __iomem * dma_regs; + struct dbdma_cmd* dma_table_cpu; +#endif + +} pmac_ide_hwif_t; + +static pmac_ide_hwif_t pmac_ide[MAX_HWIFS] __pmacdata; +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 */ + controller_un_ata6, /* UniNorth2 ATA-6 */ + controller_k2_ata6, /* K2 ATA-6 */ + controller_sh_ata6, /* Shasta ATA-6 */ +}; + +static const char* model_name[] = { + "OHare ATA", /* OHare based */ + "Heathrow ATA", /* Heathrow/Paddington */ + "KeyLargo ATA-3", /* KeyLargo ATA-3 (MDMA only) */ + "KeyLargo ATA-4", /* KeyLargo ATA-4 (UDMA/66) */ + "UniNorth ATA-6", /* UniNorth2 ATA-6 (UDMA/100) */ + "K2 ATA-6", /* K2 ATA-6 (UDMA/100) */ + "Shasta ATA-6", /* Shasta ATA-6 (UDMA/133) */ +}; + +/* + * Extra registers, both 32-bit little-endian + */ +#define IDE_TIMING_CONFIG 0x200 +#define IDE_INTERRUPT 0x300 + +/* Kauai (U2) ATA has different register setup */ +#define IDE_KAUAI_PIO_CONFIG 0x200 +#define IDE_KAUAI_ULTRA_CONFIG 0x210 +#define IDE_KAUAI_POLL_CONFIG 0x220 + +/* + * Timing configuration register definitions + */ + +/* Number of IDE_SYSCLK_NS ticks, argument is in nanoseconds */ +#define SYSCLK_TICKS(t) (((t) + IDE_SYSCLK_NS - 1) / IDE_SYSCLK_NS) +#define SYSCLK_TICKS_66(t) (((t) + IDE_SYSCLK_66_NS - 1) / IDE_SYSCLK_66_NS) +#define IDE_SYSCLK_NS 30 /* 33Mhz cell */ +#define IDE_SYSCLK_66_NS 15 /* 66Mhz cell */ + +/* 133Mhz cell, found in shasta. + * See comments about 100 Mhz Uninorth 2... + * Note that PIO_MASK and MDMA_MASK seem to overlap + */ +#define TR_133_PIOREG_PIO_MASK 0xff000fff +#define TR_133_PIOREG_MDMA_MASK 0x00fff800 +#define TR_133_UDMAREG_UDMA_MASK 0x0003ffff +#define TR_133_UDMAREG_UDMA_EN 0x00000001 + +/* 100Mhz cell, found in Uninorth 2. I don't have much infos about + * this one yet, it appears as a pci device (106b/0033) on uninorth + * internal PCI bus and it's clock is controlled like gem or fw. It + * appears to be an evolution of keylargo ATA4 with a timing register + * extended to 2 32bits registers and a similar DBDMA channel. Other + * registers seem to exist but I can't tell much about them. + * + * So far, I'm using pre-calculated tables for this extracted from + * the values used by the MacOS X driver. + * + * The "PIO" register controls PIO and MDMA timings, the "ULTRA" + * register controls the UDMA timings. At least, it seems bit 0 + * of this one enables UDMA vs. MDMA, and bits 4..7 are the + * cycle time in units of 10ns. Bits 8..15 are used by I don't + * know their meaning yet + */ +#define TR_100_PIOREG_PIO_MASK 0xff000fff +#define TR_100_PIOREG_MDMA_MASK 0x00fff000 +#define TR_100_UDMAREG_UDMA_MASK 0x0000ffff +#define TR_100_UDMAREG_UDMA_EN 0x00000001 + + +/* 66Mhz cell, found in KeyLargo. Can do ultra mode 0 to 2 on + * 40 connector cable and to 4 on 80 connector one. + * Clock unit is 15ns (66Mhz) + * + * 3 Values can be programmed: + * - Write data setup, which appears to match the cycle time. They + * also call it DIOW setup. + * - Ready to pause time (from spec) + * - Address setup. That one is weird. I don't see where exactly + * it fits in UDMA cycles, I got it's name from an obscure piece + * of commented out code in Darwin. They leave it to 0, we do as + * well, despite a comment that would lead to think it has a + * min value of 45ns. + * Apple also add 60ns to the write data setup (or cycle time ?) on + * reads. + */ +#define TR_66_UDMA_MASK 0xfff00000 +#define TR_66_UDMA_EN 0x00100000 /* Enable Ultra mode for DMA */ +#define TR_66_UDMA_ADDRSETUP_MASK 0xe0000000 /* Address setup */ +#define TR_66_UDMA_ADDRSETUP_SHIFT 29 +#define TR_66_UDMA_RDY2PAUS_MASK 0x1e000000 /* Ready 2 pause time */ +#define TR_66_UDMA_RDY2PAUS_SHIFT 25 +#define TR_66_UDMA_WRDATASETUP_MASK 0x01e00000 /* Write data setup time */ +#define TR_66_UDMA_WRDATASETUP_SHIFT 21 +#define TR_66_MDMA_MASK 0x000ffc00 +#define TR_66_MDMA_RECOVERY_MASK 0x000f8000 +#define TR_66_MDMA_RECOVERY_SHIFT 15 +#define TR_66_MDMA_ACCESS_MASK 0x00007c00 +#define TR_66_MDMA_ACCESS_SHIFT 10 +#define TR_66_PIO_MASK 0x000003ff +#define TR_66_PIO_RECOVERY_MASK 0x000003e0 +#define TR_66_PIO_RECOVERY_SHIFT 5 +#define TR_66_PIO_ACCESS_MASK 0x0000001f +#define TR_66_PIO_ACCESS_SHIFT 0 + +/* 33Mhz cell, found in OHare, Heathrow (& Paddington) and KeyLargo + * Can do pio & mdma modes, clock unit is 30ns (33Mhz) + * + * The access time and recovery time can be programmed. Some older + * Darwin code base limit OHare to 150ns cycle time. I decided to do + * the same here fore safety against broken old hardware ;) + * The HalfTick bit, when set, adds half a clock (15ns) to the access + * time and removes one from recovery. It's not supported on KeyLargo + * implementation afaik. The E bit appears to be set for PIO mode 0 and + * is used to reach long timings used in this mode. + */ +#define TR_33_MDMA_MASK 0x003ff800 +#define TR_33_MDMA_RECOVERY_MASK 0x001f0000 +#define TR_33_MDMA_RECOVERY_SHIFT 16 +#define TR_33_MDMA_ACCESS_MASK 0x0000f800 +#define TR_33_MDMA_ACCESS_SHIFT 11 +#define TR_33_MDMA_HALFTICK 0x00200000 +#define TR_33_PIO_MASK 0x000007ff +#define TR_33_PIO_E 0x00000400 +#define TR_33_PIO_RECOVERY_MASK 0x000003e0 +#define TR_33_PIO_RECOVERY_SHIFT 5 +#define TR_33_PIO_ACCESS_MASK 0x0000001f +#define TR_33_PIO_ACCESS_SHIFT 0 + +/* + * Interrupt register definitions + */ +#define IDE_INTR_DMA 0x80000000 +#define IDE_INTR_DEVICE 0x40000000 + +/* + * FCR Register on Kauai. Not sure what bit 0x4 is ... + */ +#define KAUAI_FCR_UATA_MAGIC 0x00000004 +#define KAUAI_FCR_UATA_RESET_N 0x00000002 +#define KAUAI_FCR_UATA_ENABLE 0x00000001 + +#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC + +/* Rounded Multiword DMA timings + * + * I gave up finding a generic formula for all controller + * types and instead, built tables based on timing values + * used by Apple in Darwin's implementation. + */ +struct mdma_timings_t { + int accessTime; + int recoveryTime; + int cycleTime; +}; + +struct mdma_timings_t mdma_timings_33[] __pmacdata = +{ + { 240, 240, 480 }, + { 180, 180, 360 }, + { 135, 135, 270 }, + { 120, 120, 240 }, + { 105, 105, 210 }, + { 90, 90, 180 }, + { 75, 75, 150 }, + { 75, 45, 120 }, + { 0, 0, 0 } +}; + +struct mdma_timings_t mdma_timings_33k[] __pmacdata = +{ + { 240, 240, 480 }, + { 180, 180, 360 }, + { 150, 150, 300 }, + { 120, 120, 240 }, + { 90, 120, 210 }, + { 90, 90, 180 }, + { 90, 60, 150 }, + { 90, 30, 120 }, + { 0, 0, 0 } +}; + +struct mdma_timings_t mdma_timings_66[] __pmacdata = +{ + { 240, 240, 480 }, + { 180, 180, 360 }, + { 135, 135, 270 }, + { 120, 120, 240 }, + { 105, 105, 210 }, + { 90, 90, 180 }, + { 90, 75, 165 }, + { 75, 45, 120 }, + { 0, 0, 0 } +}; + +/* KeyLargo ATA-4 Ultra DMA timings (rounded) */ +struct { + int addrSetup; /* ??? */ + int rdy2pause; + int wrDataSetup; +} kl66_udma_timings[] __pmacdata = +{ + { 0, 180, 120 }, /* Mode 0 */ + { 0, 150, 90 }, /* 1 */ + { 0, 120, 60 }, /* 2 */ + { 0, 90, 45 }, /* 3 */ + { 0, 90, 30 } /* 4 */ +}; + +/* UniNorth 2 ATA/100 timings */ +struct kauai_timing { + int cycle_time; + u32 timing_reg; +}; + +static struct kauai_timing kauai_pio_timings[] __pmacdata = +{ + { 930 , 0x08000fff }, + { 600 , 0x08000a92 }, + { 383 , 0x0800060f }, + { 360 , 0x08000492 }, + { 330 , 0x0800048f }, + { 300 , 0x080003cf }, + { 270 , 0x080003cc }, + { 240 , 0x0800038b }, + { 239 , 0x0800030c }, + { 180 , 0x05000249 }, + { 120 , 0x04000148 } +}; + +static struct kauai_timing kauai_mdma_timings[] __pmacdata = +{ + { 1260 , 0x00fff000 }, + { 480 , 0x00618000 }, + { 360 , 0x00492000 }, + { 270 , 0x0038e000 }, + { 240 , 0x0030c000 }, + { 210 , 0x002cb000 }, + { 180 , 0x00249000 }, + { 150 , 0x00209000 }, + { 120 , 0x00148000 }, + { 0 , 0 }, +}; + +static struct kauai_timing kauai_udma_timings[] __pmacdata = +{ + { 120 , 0x000070c0 }, + { 90 , 0x00005d80 }, + { 60 , 0x00004a60 }, + { 45 , 0x00003a50 }, + { 30 , 0x00002a30 }, + { 20 , 0x00002921 }, + { 0 , 0 }, +}; + +static struct kauai_timing shasta_pio_timings[] __pmacdata = +{ + { 930 , 0x08000fff }, + { 600 , 0x0A000c97 }, + { 383 , 0x07000712 }, + { 360 , 0x040003cd }, + { 330 , 0x040003cd }, + { 300 , 0x040003cd }, + { 270 , 0x040003cd }, + { 240 , 0x040003cd }, + { 239 , 0x040003cd }, + { 180 , 0x0400028b }, + { 120 , 0x0400010a } +}; + +static struct kauai_timing shasta_mdma_timings[] __pmacdata = +{ + { 1260 , 0x00fff000 }, + { 480 , 0x00820800 }, + { 360 , 0x00820800 }, + { 270 , 0x00820800 }, + { 240 , 0x00820800 }, + { 210 , 0x00820800 }, + { 180 , 0x00820800 }, + { 150 , 0x0028b000 }, + { 120 , 0x001ca000 }, + { 0 , 0 }, +}; + +static struct kauai_timing shasta_udma133_timings[] __pmacdata = +{ + { 120 , 0x00035901, }, + { 90 , 0x000348b1, }, + { 60 , 0x00033881, }, + { 45 , 0x00033861, }, + { 30 , 0x00033841, }, + { 20 , 0x00033031, }, + { 15 , 0x00033021, }, + { 0 , 0 }, +}; + + +static inline u32 +kauai_lookup_timing(struct kauai_timing* table, int cycle_time) +{ + int i; + + for (i=0; table[i].cycle_time; i++) + if (cycle_time > table[i+1].cycle_time) + return table[i].timing_reg; + return 0; +} + +/* allow up to 256 DBDMA commands per xfer */ +#define MAX_DCMDS 256 + +/* + * Wait 1s for disk to answer on IDE bus after a hard reset + * of the device (via GPIO/FCR). + * + * Some devices seem to "pollute" the bus even after dropping + * the BSY bit (typically some combo drives slave on the UDMA + * bus) after a hard reset. Since we hard reset all drives on + * KeyLargo ATA66, we have to keep that delay around. I may end + * up not hard resetting anymore on these and keep the delay only + * for older interfaces instead (we have to reset when coming + * from MacOS...) --BenH. + */ +#define IDE_WAKEUP_DELAY (1*HZ) + +static void pmac_ide_setup_dma(pmac_ide_hwif_t *pmif, ide_hwif_t *hwif); +static int pmac_ide_build_dmatable(ide_drive_t *drive, struct request *rq); +static int pmac_ide_tune_chipset(ide_drive_t *drive, u8 speed); +static void pmac_ide_tuneproc(ide_drive_t *drive, u8 pio); +static void pmac_ide_selectproc(ide_drive_t *drive); +static void pmac_ide_kauai_selectproc(ide_drive_t *drive); + +#endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */ + +/* + * Below is the code for blinking the laptop LED along with hard + * disk activity. + */ + +#ifdef CONFIG_BLK_DEV_IDE_PMAC_BLINK + +/* Set to 50ms minimum led-on time (also used to limit frequency + * of requests sent to the PMU + */ +#define PMU_HD_BLINK_TIME (HZ/50) + +static struct adb_request pmu_blink_on, pmu_blink_off; +static spinlock_t pmu_blink_lock; +static unsigned long pmu_blink_stoptime; +static int pmu_blink_ledstate; +static struct timer_list pmu_blink_timer; +static int pmu_ide_blink_enabled; + + +static void +pmu_hd_blink_timeout(unsigned long data) +{ + unsigned long flags; + + spin_lock_irqsave(&pmu_blink_lock, flags); + + /* We may have been triggered again in a racy way, check + * that we really want to switch it off + */ + if (time_after(pmu_blink_stoptime, jiffies)) + goto done; + + /* Previous req. not complete, try 100ms more */ + if (pmu_blink_off.complete == 0) + mod_timer(&pmu_blink_timer, jiffies + PMU_HD_BLINK_TIME); + else if (pmu_blink_ledstate) { + pmu_request(&pmu_blink_off, NULL, 4, 0xee, 4, 0, 0); + pmu_blink_ledstate = 0; + } +done: + spin_unlock_irqrestore(&pmu_blink_lock, flags); +} + +static void +pmu_hd_kick_blink(void *data, int rw) +{ + unsigned long flags; + + pmu_blink_stoptime = jiffies + PMU_HD_BLINK_TIME; + wmb(); + mod_timer(&pmu_blink_timer, pmu_blink_stoptime); + /* Fast path when LED is already ON */ + if (pmu_blink_ledstate == 1) + return; + spin_lock_irqsave(&pmu_blink_lock, flags); + if (pmu_blink_on.complete && !pmu_blink_ledstate) { + pmu_request(&pmu_blink_on, NULL, 4, 0xee, 4, 0, 1); + pmu_blink_ledstate = 1; + } + spin_unlock_irqrestore(&pmu_blink_lock, flags); +} + +static int +pmu_hd_blink_init(void) +{ + struct device_node *dt; + const char *model; + + /* Currently, I only enable this feature on KeyLargo based laptops, + * older laptops may support it (at least heathrow/paddington) but + * I don't feel like loading those venerable old machines with so + * much additional interrupt & PMU activity... + */ + if (pmu_get_model() != PMU_KEYLARGO_BASED) + return 0; + + dt = find_devices("device-tree"); + if (dt == NULL) + return 0; + model = (const char *)get_property(dt, "model", NULL); + if (model == NULL) + return 0; + if (strncmp(model, "PowerBook", strlen("PowerBook")) != 0 && + strncmp(model, "iBook", strlen("iBook")) != 0) + return 0; + + pmu_blink_on.complete = 1; + pmu_blink_off.complete = 1; + spin_lock_init(&pmu_blink_lock); + init_timer(&pmu_blink_timer); + pmu_blink_timer.function = pmu_hd_blink_timeout; + + return 1; +} + +#endif /* CONFIG_BLK_DEV_IDE_PMAC_BLINK */ + +/* + * N.B. this can't be an initfunc, because the media-bay task can + * call ide_[un]register at any time. + */ +void __pmac +pmac_ide_init_hwif_ports(hw_regs_t *hw, + unsigned long data_port, unsigned long 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; + hw->io_ports[IDE_CONTROL_OFFSET] = ctrl_port; + 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; +} + +#define PMAC_IDE_REG(x) ((void __iomem *)(IDE_DATA_REG+(x))) + +/* + * Apply the timings of the proper unit (master/slave) to the shared + * timing register when selecting that unit. This version is for + * ASICs with a single timing register + */ +static void __pmac +pmac_ide_selectproc(ide_drive_t *drive) +{ + pmac_ide_hwif_t* pmif = (pmac_ide_hwif_t *)HWIF(drive)->hwif_data; + + if (pmif == NULL) + return; + + if (drive->select.b.unit & 0x01) + writel(pmif->timings[1], PMAC_IDE_REG(IDE_TIMING_CONFIG)); + else + writel(pmif->timings[0], PMAC_IDE_REG(IDE_TIMING_CONFIG)); + (void)readl(PMAC_IDE_REG(IDE_TIMING_CONFIG)); +} + +/* + * Apply the timings of the proper unit (master/slave) to the shared + * timing register when selecting that unit. This version is for + * ASICs with a dual timing register (Kauai) + */ +static void __pmac +pmac_ide_kauai_selectproc(ide_drive_t *drive) +{ + pmac_ide_hwif_t* pmif = (pmac_ide_hwif_t *)HWIF(drive)->hwif_data; + + if (pmif == NULL) + return; + + if (drive->select.b.unit & 0x01) { + writel(pmif->timings[1], PMAC_IDE_REG(IDE_KAUAI_PIO_CONFIG)); + writel(pmif->timings[3], PMAC_IDE_REG(IDE_KAUAI_ULTRA_CONFIG)); + } else { + writel(pmif->timings[0], PMAC_IDE_REG(IDE_KAUAI_PIO_CONFIG)); + writel(pmif->timings[2], PMAC_IDE_REG(IDE_KAUAI_ULTRA_CONFIG)); + } + (void)readl(PMAC_IDE_REG(IDE_KAUAI_PIO_CONFIG)); +} + +/* + * Force an update of controller timing values for a given drive + */ +static void __pmac +pmac_ide_do_update_timings(ide_drive_t *drive) +{ + pmac_ide_hwif_t* pmif = (pmac_ide_hwif_t *)HWIF(drive)->hwif_data; + + if (pmif == NULL) + return; + + if (pmif->kind == controller_sh_ata6 || + pmif->kind == controller_un_ata6 || + pmif->kind == controller_k2_ata6) + pmac_ide_kauai_selectproc(drive); + else + pmac_ide_selectproc(drive); +} + +static void +pmac_outbsync(ide_drive_t *drive, u8 value, unsigned long port) +{ + u32 tmp; + + writeb(value, (void __iomem *) port); + tmp = readl(PMAC_IDE_REG(IDE_TIMING_CONFIG)); +} + +/* + * Send the SET_FEATURE IDE command to the drive and update drive->id with + * the new state. We currently don't use the generic routine as it used to + * cause various trouble, especially with older mediabays. + * This code is sometimes triggering a spurrious interrupt though, I need + * to sort that out sooner or later and see if I can finally get the + * common version to work properly in all cases + */ +static int __pmac +pmac_ide_do_setfeature(ide_drive_t *drive, u8 command) +{ + ide_hwif_t *hwif = HWIF(drive); + int result = 1; + + disable_irq_nosync(hwif->irq); + udelay(1); + SELECT_DRIVE(drive); + SELECT_MASK(drive, 0); + udelay(1); + /* Get rid of pending error state */ + (void) hwif->INB(IDE_STATUS_REG); + /* Timeout bumped for some powerbooks */ + if (wait_for_ready(drive, 2000)) { + /* Timeout bumped for some powerbooks */ + printk(KERN_ERR "%s: pmac_ide_do_setfeature disk not ready " + "before SET_FEATURE!\n", drive->name); + goto out; + } + udelay(10); + hwif->OUTB(drive->ctl | 2, IDE_CONTROL_REG); + hwif->OUTB(command, IDE_NSECTOR_REG); + hwif->OUTB(SETFEATURES_XFER, IDE_FEATURE_REG); + hwif->OUTBSYNC(drive, WIN_SETFEATURES, IDE_COMMAND_REG); + udelay(1); + /* Timeout bumped for some powerbooks */ + result = wait_for_ready(drive, 2000); + hwif->OUTB(drive->ctl, IDE_CONTROL_REG); + if (result) + printk(KERN_ERR "%s: pmac_ide_do_setfeature disk not ready " + "after SET_FEATURE !\n", drive->name); +out: + SELECT_MASK(drive, 0); + if (result == 0) { + drive->id->dma_ultra &= ~0xFF00; + drive->id->dma_mword &= ~0x0F00; + drive->id->dma_1word &= ~0x0F00; + switch(command) { + 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; + } + } + enable_irq(hwif->irq); + return result; +} + +/* + * Old tuning functions (called on hdparm -p), sets up drive PIO timings + */ +static void __pmac +pmac_ide_tuneproc(ide_drive_t *drive, u8 pio) +{ + ide_pio_data_t d; + u32 *timings; + unsigned accessTicks, recTicks; + unsigned accessTime, recTime; + pmac_ide_hwif_t* pmif = (pmac_ide_hwif_t *)HWIF(drive)->hwif_data; + + if (pmif == NULL) + return; + + /* which drive is it ? */ + timings = &pmif->timings[drive->select.b.unit & 0x01]; + + pio = ide_get_best_pio_mode(drive, pio, 4, &d); + + switch (pmif->kind) { + case controller_sh_ata6: { + /* 133Mhz cell */ + u32 tr = kauai_lookup_timing(shasta_pio_timings, d.cycle_time); + if (tr == 0) + return; + *timings = ((*timings) & ~TR_133_PIOREG_PIO_MASK) | tr; + break; + } + case controller_un_ata6: + case controller_k2_ata6: { + /* 100Mhz cell */ + u32 tr = kauai_lookup_timing(kauai_pio_timings, d.cycle_time); + if (tr == 0) + return; + *timings = ((*timings) & ~TR_100_PIOREG_PIO_MASK) | tr; + break; + } + case controller_kl_ata4: + /* 66Mhz cell */ + recTime = d.cycle_time - ide_pio_timings[pio].active_time + - ide_pio_timings[pio].setup_time; + recTime = max(recTime, 150U); + accessTime = ide_pio_timings[pio].active_time; + accessTime = max(accessTime, 150U); + accessTicks = SYSCLK_TICKS_66(accessTime); + accessTicks = min(accessTicks, 0x1fU); + recTicks = SYSCLK_TICKS_66(recTime); + recTicks = min(recTicks, 0x1fU); + *timings = ((*timings) & ~TR_66_PIO_MASK) | + (accessTicks << TR_66_PIO_ACCESS_SHIFT) | + (recTicks << TR_66_PIO_RECOVERY_SHIFT); + break; + default: { + /* 33Mhz cell */ + int ebit = 0; + recTime = d.cycle_time - ide_pio_timings[pio].active_time + - ide_pio_timings[pio].setup_time; + recTime = max(recTime, 150U); + accessTime = ide_pio_timings[pio].active_time; + accessTime = max(accessTime, 150U); + accessTicks = SYSCLK_TICKS(accessTime); + accessTicks = min(accessTicks, 0x1fU); + accessTicks = max(accessTicks, 4U); + recTicks = SYSCLK_TICKS(recTime); + recTicks = min(recTicks, 0x1fU); + recTicks = max(recTicks, 5U) - 4; + if (recTicks > 9) { + recTicks--; /* guess, but it's only for PIO0, so... */ + ebit = 1; + } + *timings = ((*timings) & ~TR_33_PIO_MASK) | + (accessTicks << TR_33_PIO_ACCESS_SHIFT) | + (recTicks << TR_33_PIO_RECOVERY_SHIFT); + if (ebit) + *timings |= TR_33_PIO_E; + break; + } + } + +#ifdef IDE_PMAC_DEBUG + printk(KERN_ERR "%s: Set PIO timing for mode %d, reg: 0x%08x\n", + drive->name, pio, *timings); +#endif + + if (drive->select.all == HWIF(drive)->INB(IDE_SELECT_REG)) + pmac_ide_do_update_timings(drive); +} + +#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC + +/* + * Calculate KeyLargo ATA/66 UDMA timings + */ +static int __pmac +set_timings_udma_ata4(u32 *timings, u8 speed) +{ + unsigned rdyToPauseTicks, wrDataSetupTicks, addrTicks; + + if (speed > XFER_UDMA_4) + return 1; + + rdyToPauseTicks = SYSCLK_TICKS_66(kl66_udma_timings[speed & 0xf].rdy2pause); + wrDataSetupTicks = SYSCLK_TICKS_66(kl66_udma_timings[speed & 0xf].wrDataSetup); + addrTicks = SYSCLK_TICKS_66(kl66_udma_timings[speed & 0xf].addrSetup); + + *timings = ((*timings) & ~(TR_66_UDMA_MASK | TR_66_MDMA_MASK)) | + (wrDataSetupTicks << TR_66_UDMA_WRDATASETUP_SHIFT) | + (rdyToPauseTicks << TR_66_UDMA_RDY2PAUS_SHIFT) | + (addrTicks <<TR_66_UDMA_ADDRSETUP_SHIFT) | + TR_66_UDMA_EN; +#ifdef IDE_PMAC_DEBUG + printk(KERN_ERR "ide_pmac: Set UDMA timing for mode %d, reg: 0x%08x\n", + speed & 0xf, *timings); +#endif + + return 0; +} + +/* + * Calculate Kauai ATA/100 UDMA timings + */ +static int __pmac +set_timings_udma_ata6(u32 *pio_timings, u32 *ultra_timings, u8 speed) +{ + struct ide_timing *t = ide_timing_find_mode(speed); + u32 tr; + + if (speed > XFER_UDMA_5 || t == NULL) + return 1; + tr = kauai_lookup_timing(kauai_udma_timings, (int)t->udma); + if (tr == 0) + return 1; + *ultra_timings = ((*ultra_timings) & ~TR_100_UDMAREG_UDMA_MASK) | tr; + *ultra_timings = (*ultra_timings) | TR_100_UDMAREG_UDMA_EN; + + return 0; +} + +/* + * Calculate Shasta ATA/133 UDMA timings + */ +static int __pmac +set_timings_udma_shasta(u32 *pio_timings, u32 *ultra_timings, u8 speed) +{ + struct ide_timing *t = ide_timing_find_mode(speed); + u32 tr; + + if (speed > XFER_UDMA_6 || t == NULL) + return 1; + tr = kauai_lookup_timing(shasta_udma133_timings, (int)t->udma); + if (tr == 0) + return 1; + *ultra_timings = ((*ultra_timings) & ~TR_133_UDMAREG_UDMA_MASK) | tr; + *ultra_timings = (*ultra_timings) | TR_133_UDMAREG_UDMA_EN; + + return 0; +} + +/* + * Calculate MDMA timings for all cells + */ +static int __pmac +set_timings_mdma(ide_drive_t *drive, int intf_type, u32 *timings, u32 *timings2, + u8 speed, int drive_cycle_time) +{ + int cycleTime, accessTime = 0, recTime = 0; + unsigned accessTicks, recTicks; + struct mdma_timings_t* tm = NULL; + int i; + + /* Get default cycle time for mode */ + switch(speed & 0xf) { + case 0: cycleTime = 480; break; + case 1: cycleTime = 150; break; + case 2: cycleTime = 120; break; + default: + return 1; + } + /* Adjust for drive */ + if (drive_cycle_time && drive_cycle_time > cycleTime) + cycleTime = drive_cycle_time; + /* OHare limits according to some old Apple sources */ + if ((intf_type == controller_ohare) && (cycleTime < 150)) + cycleTime = 150; + /* Get the proper timing array for this controller */ + switch(intf_type) { + case controller_sh_ata6: + case controller_un_ata6: + case controller_k2_ata6: + break; + case controller_kl_ata4: + tm = mdma_timings_66; + break; + case controller_kl_ata3: + tm = mdma_timings_33k; + break; + default: + tm = mdma_timings_33; + break; + } + if (tm != NULL) { + /* Lookup matching access & recovery times */ + i = -1; + for (;;) { + if (tm[i+1].cycleTime < cycleTime) + break; + i++; + } + if (i < 0) + return 1; + cycleTime = tm[i].cycleTime; + accessTime = tm[i].accessTime; + recTime = tm[i].recoveryTime; + +#ifdef IDE_PMAC_DEBUG + printk(KERN_ERR "%s: MDMA, cycleTime: %d, accessTime: %d, recTime: %d\n", + drive->name, cycleTime, accessTime, recTime); +#endif + } + switch(intf_type) { + case controller_sh_ata6: { + /* 133Mhz cell */ + u32 tr = kauai_lookup_timing(shasta_mdma_timings, cycleTime); + if (tr == 0) + return 1; + *timings = ((*timings) & ~TR_133_PIOREG_MDMA_MASK) | tr; + *timings2 = (*timings2) & ~TR_133_UDMAREG_UDMA_EN; + } + case controller_un_ata6: + case controller_k2_ata6: { + /* 100Mhz cell */ + u32 tr = kauai_lookup_timing(kauai_mdma_timings, cycleTime); + if (tr == 0) + return 1; + *timings = ((*timings) & ~TR_100_PIOREG_MDMA_MASK) | tr; + *timings2 = (*timings2) & ~TR_100_UDMAREG_UDMA_EN; + } + break; + case controller_kl_ata4: + /* 66Mhz cell */ + accessTicks = SYSCLK_TICKS_66(accessTime); + accessTicks = min(accessTicks, 0x1fU); + accessTicks = max(accessTicks, 0x1U); + recTicks = SYSCLK_TICKS_66(recTime); + recTicks = min(recTicks, 0x1fU); + recTicks = max(recTicks, 0x3U); + /* Clear out mdma bits and disable udma */ + *timings = ((*timings) & ~(TR_66_MDMA_MASK | TR_66_UDMA_MASK)) | + (accessTicks << TR_66_MDMA_ACCESS_SHIFT) | + (recTicks << TR_66_MDMA_RECOVERY_SHIFT); + break; + case controller_kl_ata3: + /* 33Mhz cell on KeyLargo */ + accessTicks = SYSCLK_TICKS(accessTime); + accessTicks = max(accessTicks, 1U); + accessTicks = min(accessTicks, 0x1fU); + accessTime = accessTicks * IDE_SYSCLK_NS; + recTicks = SYSCLK_TICKS(recTime); + recTicks = max(recTicks, 1U); + recTicks = min(recTicks, 0x1fU); + *timings = ((*timings) & ~TR_33_MDMA_MASK) | + (accessTicks << TR_33_MDMA_ACCESS_SHIFT) | + (recTicks << TR_33_MDMA_RECOVERY_SHIFT); + break; + default: { + /* 33Mhz cell on others */ + int halfTick = 0; + int origAccessTime = accessTime; + int origRecTime = recTime; + + accessTicks = SYSCLK_TICKS(accessTime); + accessTicks = max(accessTicks, 1U); + accessTicks = min(accessTicks, 0x1fU); + accessTime = accessTicks * IDE_SYSCLK_NS; + recTicks = SYSCLK_TICKS(recTime); + recTicks = max(recTicks, 2U) - 1; + recTicks = min(recTicks, 0x1fU); + recTime = (recTicks + 1) * IDE_SYSCLK_NS; + if ((accessTicks > 1) && + ((accessTime - IDE_SYSCLK_NS/2) >= origAccessTime) && + ((recTime - IDE_SYSCLK_NS/2) >= origRecTime)) { + halfTick = 1; + accessTicks--; + } + *timings = ((*timings) & ~TR_33_MDMA_MASK) | + (accessTicks << TR_33_MDMA_ACCESS_SHIFT) | + (recTicks << TR_33_MDMA_RECOVERY_SHIFT); + if (halfTick) + *timings |= TR_33_MDMA_HALFTICK; + } + } +#ifdef IDE_PMAC_DEBUG + printk(KERN_ERR "%s: Set MDMA timing for mode %d, reg: 0x%08x\n", + drive->name, speed & 0xf, *timings); +#endif + return 0; +} +#endif /* #ifdef CONFIG_BLK_DEV_IDEDMA_PMAC */ + +/* + * Speedproc. This function is called by the core to set any of the standard + * timing (PIO, MDMA or UDMA) to both the drive and the controller. + * You may notice we don't use this function on normal "dma check" operation, + * our dedicated function is more precise as it uses the drive provided + * cycle time value. We should probably fix this one to deal with that too... + */ +static int __pmac +pmac_ide_tune_chipset (ide_drive_t *drive, byte speed) +{ + int unit = (drive->select.b.unit & 0x01); + int ret = 0; + pmac_ide_hwif_t* pmif = (pmac_ide_hwif_t *)HWIF(drive)->hwif_data; + u32 *timings, *timings2; + + if (pmif == NULL) + return 1; + + timings = &pmif->timings[unit]; + timings2 = &pmif->timings[unit+2]; + + switch(speed) { +#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC + case XFER_UDMA_6: + if (pmif->kind != controller_sh_ata6) + return 1; + case XFER_UDMA_5: + if (pmif->kind != controller_un_ata6 && + pmif->kind != controller_k2_ata6 && + pmif->kind != controller_sh_ata6) + return 1; + case XFER_UDMA_4: + case XFER_UDMA_3: + if (HWIF(drive)->udma_four == 0) + return 1; + case XFER_UDMA_2: + case XFER_UDMA_1: + case XFER_UDMA_0: + if (pmif->kind == controller_kl_ata4) + ret = set_timings_udma_ata4(timings, speed); + else if (pmif->kind == controller_un_ata6 + || pmif->kind == controller_k2_ata6) + ret = set_timings_udma_ata6(timings, timings2, speed); + else if (pmif->kind == controller_sh_ata6) + ret = set_timings_udma_shasta(timings, timings2, speed); + else + ret = 1; + break; + case XFER_MW_DMA_2: + case XFER_MW_DMA_1: + case XFER_MW_DMA_0: + ret = set_timings_mdma(drive, pmif->kind, timings, timings2, speed, 0); + break; + case XFER_SW_DMA_2: + case XFER_SW_DMA_1: + case XFER_SW_DMA_0: + return 1; +#endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */ + case XFER_PIO_4: + case XFER_PIO_3: + case XFER_PIO_2: + case XFER_PIO_1: + case XFER_PIO_0: + pmac_ide_tuneproc(drive, speed & 0x07); + break; + default: + ret = 1; + } + if (ret) + return ret; + + ret = pmac_ide_do_setfeature(drive, speed); + if (ret) + return ret; + + pmac_ide_do_update_timings(drive); + drive->current_speed = speed; + + return 0; +} + +/* + * Blast some well known "safe" values to the timing registers at init or + * wakeup from sleep time, before we do real calculation + */ +static void __pmac +sanitize_timings(pmac_ide_hwif_t *pmif) +{ + unsigned int value, value2 = 0; + + switch(pmif->kind) { + case controller_sh_ata6: + value = 0x0a820c97; + value2 = 0x00033031; + break; + case controller_un_ata6: + case controller_k2_ata6: + value = 0x08618a92; + value2 = 0x00002921; + break; + case controller_kl_ata4: + value = 0x0008438c; + break; + case controller_kl_ata3: + value = 0x00084526; + break; + case controller_heathrow: + case controller_ohare: + default: + value = 0x00074526; + break; + } + pmif->timings[0] = pmif->timings[1] = value; + pmif->timings[2] = pmif->timings[3] = value2; +} + +unsigned long __pmac +pmac_ide_get_base(int index) +{ + return pmac_ide[index].regbase; +} + +int __pmac +pmac_ide_check_base(unsigned long base) +{ + int ix; + + for (ix = 0; ix < MAX_HWIFS; ++ix) + if (base == pmac_ide[ix].regbase) + return ix; + return -1; +} + +int __pmac +pmac_ide_get_irq(unsigned long base) +{ + int ix; + + for (ix = 0; ix < MAX_HWIFS; ++ix) + if (base == pmac_ide[ix].regbase) + return pmac_ide[ix].irq; + return 0; +} + +static int ide_majors[] __pmacdata = { 3, 22, 33, 34, 56, 57 }; + +dev_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; +} + +/* Suspend call back, should be called after the child devices + * have actually been suspended + */ +static int +pmac_ide_do_suspend(ide_hwif_t *hwif) +{ + pmac_ide_hwif_t *pmif = (pmac_ide_hwif_t *)hwif->hwif_data; + + /* We clear the timings */ + pmif->timings[0] = 0; + pmif->timings[1] = 0; + +#ifdef CONFIG_BLK_DEV_IDE_PMAC_BLINK + /* Note: This code will be called for every hwif, thus we'll + * try several time to stop the LED blinker timer, but that + * should be harmless + */ + if (pmu_ide_blink_enabled) { + unsigned long flags; + + /* Make sure we don't hit the PMU blink */ + spin_lock_irqsave(&pmu_blink_lock, flags); + if (pmu_blink_ledstate) + del_timer(&pmu_blink_timer); + pmu_blink_ledstate = 0; + spin_unlock_irqrestore(&pmu_blink_lock, flags); + } +#endif /* CONFIG_BLK_DEV_IDE_PMAC_BLINK */ + + /* The media bay will handle itself just fine */ + if (pmif->mediabay) + return 0; + + /* Kauai has bus control FCRs directly here */ + if (pmif->kauai_fcr) { + u32 fcr = readl(pmif->kauai_fcr); + fcr &= ~(KAUAI_FCR_UATA_RESET_N | KAUAI_FCR_UATA_ENABLE); + writel(fcr, pmif->kauai_fcr); + } + + /* Disable the bus on older machines and the cell on kauai */ + ppc_md.feature_call(PMAC_FTR_IDE_ENABLE, pmif->node, pmif->aapl_bus_id, + 0); + + return 0; +} + +/* Resume call back, should be called before the child devices + * are resumed + */ +static int +pmac_ide_do_resume(ide_hwif_t *hwif) +{ + pmac_ide_hwif_t *pmif = (pmac_ide_hwif_t *)hwif->hwif_data; + + /* Hard reset & re-enable controller (do we really need to reset ? -BenH) */ + if (!pmif->mediabay) { + ppc_md.feature_call(PMAC_FTR_IDE_RESET, pmif->node, pmif->aapl_bus_id, 1); + ppc_md.feature_call(PMAC_FTR_IDE_ENABLE, pmif->node, pmif->aapl_bus_id, 1); + msleep(10); + ppc_md.feature_call(PMAC_FTR_IDE_RESET, pmif->node, pmif->aapl_bus_id, 0); + msleep(jiffies_to_msecs(IDE_WAKEUP_DELAY)); + + /* Kauai has it different */ + if (pmif->kauai_fcr) { + u32 fcr = readl(pmif->kauai_fcr); + fcr |= KAUAI_FCR_UATA_RESET_N | KAUAI_FCR_UATA_ENABLE; + writel(fcr, pmif->kauai_fcr); + } + } + + /* Sanitize drive timings */ + sanitize_timings(pmif); + + return 0; +} + +/* + * Setup, register & probe an IDE channel driven by this driver, this is + * called by one of the 2 probe functions (macio or PCI). Note that a channel + * that ends up beeing free of any device is not kept around by this driver + * (it is kept in 2.4). This introduce an interface numbering change on some + * rare machines unfortunately, but it's better this way. + */ +static int +pmac_ide_setup_device(pmac_ide_hwif_t *pmif, ide_hwif_t *hwif) +{ + struct device_node *np = pmif->node; + int *bidp, i; + + pmif->cable_80 = 0; + pmif->broken_dma = pmif->broken_dma_warn = 0; + if (device_is_compatible(np, "shasta-ata")) + pmif->kind = controller_sh_ata6; + else if (device_is_compatible(np, "kauai-ata")) + pmif->kind = controller_un_ata6; + else if (device_is_compatible(np, "K2-UATA")) + pmif->kind = controller_k2_ata6; + else if (device_is_compatible(np, "keylargo-ata")) { + if (strcmp(np->name, "ata-4") == 0) + pmif->kind = controller_kl_ata4; + else + pmif->kind = controller_kl_ata3; + } else if (device_is_compatible(np, "heathrow-ata")) + pmif->kind = controller_heathrow; + else { + pmif->kind = controller_ohare; + pmif->broken_dma = 1; + } + + bidp = (int *)get_property(np, "AAPL,bus-id", NULL); + pmif->aapl_bus_id = bidp ? *bidp : 0; + + /* Get cable type from device-tree */ + if (pmif->kind == controller_kl_ata4 || pmif->kind == controller_un_ata6 + || pmif->kind == controller_k2_ata6 + || pmif->kind == controller_sh_ata6) { + char* cable = get_property(np, "cable-type", NULL); + if (cable && !strncmp(cable, "80-", 3)) + pmif->cable_80 = 1; + } + /* G5's seem to have incorrect cable type in device-tree. Let's assume + * they have a 80 conductor cable, this seem to be always the case unless + * the user mucked around + */ + if (device_is_compatible(np, "K2-UATA") || + device_is_compatible(np, "shasta-ata")) + pmif->cable_80 = 1; + + /* On Kauai-type controllers, we make sure the FCR is correct */ + if (pmif->kauai_fcr) + writel(KAUAI_FCR_UATA_MAGIC | + KAUAI_FCR_UATA_RESET_N | + KAUAI_FCR_UATA_ENABLE, pmif->kauai_fcr); + + pmif->mediabay = 0; + + /* Make sure we have sane timings */ + sanitize_timings(pmif); + +#ifndef CONFIG_PPC64 + /* XXX FIXME: Media bay stuff need re-organizing */ + if (np->parent && np->parent->name + && strcasecmp(np->parent->name, "media-bay") == 0) { +#ifdef CONFIG_PMAC_PBOOK + media_bay_set_ide_infos(np->parent, pmif->regbase, pmif->irq, hwif->index); +#endif /* CONFIG_PMAC_PBOOK */ + pmif->mediabay = 1; + if (!bidp) + pmif->aapl_bus_id = 1; + } else if (pmif->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 + */ + ppc_md.feature_call(PMAC_FTR_IDE_ENABLE, np, 0, 1); + } else +#endif + { + /* This is necessary to enable IDE when net-booting */ + ppc_md.feature_call(PMAC_FTR_IDE_RESET, np, pmif->aapl_bus_id, 1); + ppc_md.feature_call(PMAC_FTR_IDE_ENABLE, np, pmif->aapl_bus_id, 1); + msleep(10); + ppc_md.feature_call(PMAC_FTR_IDE_RESET, np, pmif->aapl_bus_id, 0); + msleep(jiffies_to_msecs(IDE_WAKEUP_DELAY)); + } + + /* Setup MMIO ops */ + default_hwif_mmiops(hwif); + hwif->OUTBSYNC = pmac_outbsync; + + /* Tell common code _not_ to mess with resources */ + hwif->mmio = 2; + hwif->hwif_data = pmif; + pmac_ide_init_hwif_ports(&hwif->hw, pmif->regbase, 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] || pmif->mediabay; + hwif->hold = pmif->mediabay; + hwif->udma_four = pmif->cable_80; + hwif->drives[0].unmask = 1; + hwif->drives[1].unmask = 1; + hwif->tuneproc = pmac_ide_tuneproc; + if (pmif->kind == controller_un_ata6 + || pmif->kind == controller_k2_ata6 + || pmif->kind == controller_sh_ata6) + hwif->selectproc = pmac_ide_kauai_selectproc; + else + hwif->selectproc = pmac_ide_selectproc; + hwif->speedproc = pmac_ide_tune_chipset; + +#ifdef CONFIG_BLK_DEV_IDE_PMAC_BLINK + pmu_ide_blink_enabled = pmu_hd_blink_init(); + + if (pmu_ide_blink_enabled) + hwif->led_act = pmu_hd_kick_blink; +#endif + + printk(KERN_INFO "ide%d: Found Apple %s controller, bus ID %d%s, irq %d\n", + hwif->index, model_name[pmif->kind], pmif->aapl_bus_id, + pmif->mediabay ? " (mediabay)" : "", hwif->irq); + +#ifdef CONFIG_PMAC_PBOOK + if (pmif->mediabay && check_media_bay_by_base(pmif->regbase, MB_CD) == 0) + hwif->noprobe = 0; +#endif /* CONFIG_PMAC_PBOOK */ + + hwif->sg_max_nents = MAX_DCMDS; + +#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC + /* has a DBDMA controller channel */ + if (pmif->dma_regs) + pmac_ide_setup_dma(pmif, hwif); +#endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */ + + /* We probe the hwif now */ + probe_hwif_init(hwif); + + /* The code IDE code will have set hwif->present if we have devices attached, + * if we don't, the discard the interface except if we are on a media bay slot + */ + if (!hwif->present && !pmif->mediabay) { + printk(KERN_INFO "ide%d: Bus empty, interface released.\n", + hwif->index); + default_hwif_iops(hwif); + for (i = IDE_DATA_OFFSET; i <= IDE_CONTROL_OFFSET; ++i) + hwif->io_ports[i] = 0; + hwif->chipset = ide_unknown; + hwif->noprobe = 1; + return -ENODEV; + } + + return 0; +} + +/* + * Attach to a macio probed interface + */ +static int __devinit +pmac_ide_macio_attach(struct macio_dev *mdev, const struct of_match *match) +{ + void __iomem *base; + unsigned long regbase; + int irq; + ide_hwif_t *hwif; + pmac_ide_hwif_t *pmif; + int i, rc; + + i = 0; + while (i < MAX_HWIFS && (ide_hwifs[i].io_ports[IDE_DATA_OFFSET] != 0 + || pmac_ide[i].node != NULL)) + ++i; + if (i >= MAX_HWIFS) { + printk(KERN_ERR "ide-pmac: MacIO interface attach with no slot\n"); + printk(KERN_ERR " %s\n", mdev->ofdev.node->full_name); + return -ENODEV; + } + + pmif = &pmac_ide[i]; + hwif = &ide_hwifs[i]; + + if (mdev->ofdev.node->n_addrs == 0) { + printk(KERN_WARNING "ide%d: no address for %s\n", + i, mdev->ofdev.node->full_name); + return -ENXIO; + } + + /* Request memory resource for IO ports */ + if (macio_request_resource(mdev, 0, "ide-pmac (ports)")) { + printk(KERN_ERR "ide%d: can't request mmio resource !\n", i); + return -EBUSY; + } + + /* 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. That works well enough for the single case + * where that happens though... + */ + if (macio_irq_count(mdev) == 0) { + printk(KERN_WARNING "ide%d: no intrs for device %s, using 13\n", + i, mdev->ofdev.node->full_name); + irq = 13; + } else + irq = macio_irq(mdev, 0); + + base = ioremap(macio_resource_start(mdev, 0), 0x400); + regbase = (unsigned long) base; + + hwif->pci_dev = mdev->bus->pdev; + hwif->gendev.parent = &mdev->ofdev.dev; + + pmif->mdev = mdev; + pmif->node = mdev->ofdev.node; + pmif->regbase = regbase; + pmif->irq = irq; + pmif->kauai_fcr = NULL; +#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC + if (macio_resource_count(mdev) >= 2) { + if (macio_request_resource(mdev, 1, "ide-pmac (dma)")) + printk(KERN_WARNING "ide%d: can't request DMA resource !\n", i); + else + pmif->dma_regs = ioremap(macio_resource_start(mdev, 1), 0x1000); + } else + pmif->dma_regs = NULL; +#endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */ + dev_set_drvdata(&mdev->ofdev.dev, hwif); + + rc = pmac_ide_setup_device(pmif, hwif); + if (rc != 0) { + /* The inteface is released to the common IDE layer */ + dev_set_drvdata(&mdev->ofdev.dev, NULL); + iounmap(base); + if (pmif->dma_regs) + iounmap(pmif->dma_regs); + memset(pmif, 0, sizeof(*pmif)); + macio_release_resource(mdev, 0); + if (pmif->dma_regs) + macio_release_resource(mdev, 1); + } + + return rc; +} + +static int +pmac_ide_macio_suspend(struct macio_dev *mdev, u32 state) +{ + ide_hwif_t *hwif = (ide_hwif_t *)dev_get_drvdata(&mdev->ofdev.dev); + int rc = 0; + + if (state != mdev->ofdev.dev.power.power_state && state >= 2) { + rc = pmac_ide_do_suspend(hwif); + if (rc == 0) + mdev->ofdev.dev.power.power_state = state; + } + + return rc; +} + +static int +pmac_ide_macio_resume(struct macio_dev *mdev) +{ + ide_hwif_t *hwif = (ide_hwif_t *)dev_get_drvdata(&mdev->ofdev.dev); + int rc = 0; + + if (mdev->ofdev.dev.power.power_state != 0) { + rc = pmac_ide_do_resume(hwif); + if (rc == 0) + mdev->ofdev.dev.power.power_state = 0; + } + + return rc; +} + +/* + * Attach to a PCI probed interface + */ +static int __devinit +pmac_ide_pci_attach(struct pci_dev *pdev, const struct pci_device_id *id) +{ + ide_hwif_t *hwif; + struct device_node *np; + pmac_ide_hwif_t *pmif; + void __iomem *base; + unsigned long rbase, rlen; + int i, rc; + + np = pci_device_to_OF_node(pdev); + if (np == NULL) { + printk(KERN_ERR "ide-pmac: cannot find MacIO node for Kauai ATA interface\n"); + return -ENODEV; + } + i = 0; + while (i < MAX_HWIFS && (ide_hwifs[i].io_ports[IDE_DATA_OFFSET] != 0 + || pmac_ide[i].node != NULL)) + ++i; + if (i >= MAX_HWIFS) { + printk(KERN_ERR "ide-pmac: PCI interface attach with no slot\n"); + printk(KERN_ERR " %s\n", np->full_name); + return -ENODEV; + } + + pmif = &pmac_ide[i]; + hwif = &ide_hwifs[i]; + + if (pci_enable_device(pdev)) { + printk(KERN_WARNING "ide%i: Can't enable PCI device for %s\n", + i, np->full_name); + return -ENXIO; + } + pci_set_master(pdev); + + if (pci_request_regions(pdev, "Kauai ATA")) { + printk(KERN_ERR "ide%d: Cannot obtain PCI resources for %s\n", + i, np->full_name); + return -ENXIO; + } + + hwif->pci_dev = pdev; + hwif->gendev.parent = &pdev->dev; + pmif->mdev = NULL; + pmif->node = np; + + rbase = pci_resource_start(pdev, 0); + rlen = pci_resource_len(pdev, 0); + + base = ioremap(rbase, rlen); + pmif->regbase = (unsigned long) base + 0x2000; +#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC + pmif->dma_regs = base + 0x1000; +#endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */ + pmif->kauai_fcr = base; + pmif->irq = pdev->irq; + + pci_set_drvdata(pdev, hwif); + + rc = pmac_ide_setup_device(pmif, hwif); + if (rc != 0) { + /* The inteface is released to the common IDE layer */ + pci_set_drvdata(pdev, NULL); + iounmap(base); + memset(pmif, 0, sizeof(*pmif)); + pci_release_regions(pdev); + } + + return rc; +} + +static int +pmac_ide_pci_suspend(struct pci_dev *pdev, u32 state) +{ + ide_hwif_t *hwif = (ide_hwif_t *)pci_get_drvdata(pdev); + int rc = 0; + + if (state != pdev->dev.power.power_state && state >= 2) { + rc = pmac_ide_do_suspend(hwif); + if (rc == 0) + pdev->dev.power.power_state = state; + } + + return rc; +} + +static int +pmac_ide_pci_resume(struct pci_dev *pdev) +{ + ide_hwif_t *hwif = (ide_hwif_t *)pci_get_drvdata(pdev); + int rc = 0; + + if (pdev->dev.power.power_state != 0) { + rc = pmac_ide_do_resume(hwif); + if (rc == 0) + pdev->dev.power.power_state = 0; + } + + return rc; +} + +static struct of_match pmac_ide_macio_match[] = +{ + { + .name = "IDE", + .type = OF_ANY_MATCH, + .compatible = OF_ANY_MATCH + }, + { + .name = "ATA", + .type = OF_ANY_MATCH, + .compatible = OF_ANY_MATCH + }, + { + .name = OF_ANY_MATCH, + .type = "ide", + .compatible = OF_ANY_MATCH + }, + { + .name = OF_ANY_MATCH, + .type = "ata", + .compatible = OF_ANY_MATCH + }, + {}, +}; + +static struct macio_driver pmac_ide_macio_driver = +{ + .name = "ide-pmac", + .match_table = pmac_ide_macio_match, + .probe = pmac_ide_macio_attach, + .suspend = pmac_ide_macio_suspend, + .resume = pmac_ide_macio_resume, +}; + +static struct pci_device_id pmac_ide_pci_match[] = { + { PCI_VENDOR_ID_APPLE, PCI_DEVIEC_ID_APPLE_UNI_N_ATA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_IPID_ATA100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_K2_ATA100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_SH_ATA, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, +}; + +static struct pci_driver pmac_ide_pci_driver = { + .name = "ide-pmac", + .id_table = pmac_ide_pci_match, + .probe = pmac_ide_pci_attach, + .suspend = pmac_ide_pci_suspend, + .resume = pmac_ide_pci_resume, +}; +MODULE_DEVICE_TABLE(pci, pmac_ide_pci_match); + +void __init +pmac_ide_probe(void) +{ + if (_machine != _MACH_Pmac) + return; + +#ifdef CONFIG_BLK_DEV_IDE_PMAC_ATA100FIRST + pci_register_driver(&pmac_ide_pci_driver); + macio_register_driver(&pmac_ide_macio_driver); +#else + macio_register_driver(&pmac_ide_macio_driver); + pci_register_driver(&pmac_ide_pci_driver); +#endif +} + +#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC + +/* + * pmac_ide_build_dmatable builds the DBDMA command list + * for a transfer and sets the DBDMA channel to point to it. + */ +static int __pmac +pmac_ide_build_dmatable(ide_drive_t *drive, struct request *rq) +{ + struct dbdma_cmd *table; + int i, count = 0; + ide_hwif_t *hwif = HWIF(drive); + pmac_ide_hwif_t* pmif = (pmac_ide_hwif_t *)hwif->hwif_data; + volatile struct dbdma_regs __iomem *dma = pmif->dma_regs; + struct scatterlist *sg; + int wr = (rq_data_dir(rq) == WRITE); + + /* DMA table is already aligned */ + table = (struct dbdma_cmd *) pmif->dma_table_cpu; + + /* Make sure DMA controller is stopped (necessary ?) */ + writel((RUN|PAUSE|FLUSH|WAKE|DEAD) << 16, &dma->control); + while (readl(&dma->status) & RUN) + udelay(1); + + hwif->sg_nents = i = ide_build_sglist(drive, rq); + + if (!i) + return 0; + + /* Build DBDMA commands list */ + sg = hwif->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); + + if (pmif->broken_dma && cur_addr & (L1_CACHE_BYTES - 1)) { + if (pmif->broken_dma_warn == 0) { + printk(KERN_WARNING "%s: DMA on non aligned address," + "switching to PIO on Ohare chipset\n", drive->name); + pmif->broken_dma_warn = 1; + } + goto use_pio_instead; + } + while (cur_len) { + unsigned int tc = (cur_len < 0xfe00)? cur_len: 0xfe00; + + if (count++ >= MAX_DCMDS) { + printk(KERN_WARNING "%s: DMA table too small\n", + drive->name); + goto use_pio_instead; + } + st_le16(&table->command, wr? OUTPUT_MORE: INPUT_MORE); + st_le16(&table->req_count, tc); + st_le32(&table->phy_addr, cur_addr); + table->cmd_dep = 0; + table->xfer_status = 0; + table->res_count = 0; + cur_addr += tc; + cur_len -= tc; + ++table; + } + sg++; + i--; + } + + /* convert the last command to an input/output last command */ + if (count) { + st_le16(&table[-1].command, wr? OUTPUT_LAST: INPUT_LAST); + /* add the stop command to the end of the list */ + memset(table, 0, sizeof(struct dbdma_cmd)); + st_le16(&table->command, DBDMA_STOP); + mb(); + writel(hwif->dmatable_dma, &dma->cmdptr); + return 1; + } + + printk(KERN_DEBUG "%s: empty DMA table?\n", drive->name); + use_pio_instead: + pci_unmap_sg(hwif->pci_dev, + hwif->sg_table, + hwif->sg_nents, + hwif->sg_dma_direction); + return 0; /* revert to PIO for this request */ +} + +/* Teardown mappings after DMA has completed. */ +static void __pmac +pmac_ide_destroy_dmatable (ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + struct pci_dev *dev = HWIF(drive)->pci_dev; + struct scatterlist *sg = hwif->sg_table; + int nents = hwif->sg_nents; + + if (nents) { + pci_unmap_sg(dev, sg, nents, hwif->sg_dma_direction); + hwif->sg_nents = 0; + } +} + +/* + * Pick up best MDMA timing for the drive and apply it + */ +static int __pmac +pmac_ide_mdma_enable(ide_drive_t *drive, u16 mode) +{ + ide_hwif_t *hwif = HWIF(drive); + pmac_ide_hwif_t* pmif = (pmac_ide_hwif_t *)hwif->hwif_data; + int drive_cycle_time; + struct hd_driveid *id = drive->id; + u32 *timings, *timings2; + u32 timing_local[2]; + int ret; + + /* which drive is it ? */ + timings = &pmif->timings[drive->select.b.unit & 0x01]; + timings2 = &pmif->timings[(drive->select.b.unit & 0x01) + 2]; + + /* Check if drive provide explicit cycle time */ + if ((id->field_valid & 2) && (id->eide_dma_time)) + drive_cycle_time = id->eide_dma_time; + else + drive_cycle_time = 0; + + /* Copy timings to local image */ + timing_local[0] = *timings; + timing_local[1] = *timings2; + + /* Calculate controller timings */ + ret = set_timings_mdma( drive, pmif->kind, + &timing_local[0], + &timing_local[1], + mode, + drive_cycle_time); + if (ret) + return 0; + + /* Set feature on drive */ + printk(KERN_INFO "%s: Enabling MultiWord DMA %d\n", drive->name, mode & 0xf); + ret = pmac_ide_do_setfeature(drive, mode); + if (ret) { + printk(KERN_WARNING "%s: Failed !\n", drive->name); + return 0; + } + + /* Apply timings to controller */ + *timings = timing_local[0]; + *timings2 = timing_local[1]; + + /* Set speed info in drive */ + drive->current_speed = mode; + if (!drive->init_speed) + drive->init_speed = mode; + + return 1; +} + +/* + * Pick up best UDMA timing for the drive and apply it + */ +static int __pmac +pmac_ide_udma_enable(ide_drive_t *drive, u16 mode) +{ + ide_hwif_t *hwif = HWIF(drive); + pmac_ide_hwif_t* pmif = (pmac_ide_hwif_t *)hwif->hwif_data; + u32 *timings, *timings2; + u32 timing_local[2]; + int ret; + + /* which drive is it ? */ + timings = &pmif->timings[drive->select.b.unit & 0x01]; + timings2 = &pmif->timings[(drive->select.b.unit & 0x01) + 2]; + + /* Copy timings to local image */ + timing_local[0] = *timings; + timing_local[1] = *timings2; + + /* Calculate timings for interface */ + if (pmif->kind == controller_un_ata6 + || pmif->kind == controller_k2_ata6) + ret = set_timings_udma_ata6( &timing_local[0], + &timing_local[1], + mode); + else if (pmif->kind == controller_sh_ata6) + ret = set_timings_udma_shasta( &timing_local[0], + &timing_local[1], + mode); + else + ret = set_timings_udma_ata4(&timing_local[0], mode); + if (ret) + return 0; + + /* Set feature on drive */ + printk(KERN_INFO "%s: Enabling Ultra DMA %d\n", drive->name, mode & 0x0f); + ret = pmac_ide_do_setfeature(drive, mode); + if (ret) { + printk(KERN_WARNING "%s: Failed !\n", drive->name); + return 0; + } + + /* Apply timings to controller */ + *timings = timing_local[0]; + *timings2 = timing_local[1]; + + /* Set speed info in drive */ + drive->current_speed = mode; + if (!drive->init_speed) + drive->init_speed = mode; + + return 1; +} + +/* + * Check what is the best DMA timing setting for the drive and + * call appropriate functions to apply it. + */ +static int __pmac +pmac_ide_dma_check(ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + ide_hwif_t *hwif = HWIF(drive); + pmac_ide_hwif_t* pmif = (pmac_ide_hwif_t *)hwif->hwif_data; + int enable = 1; + int map; + drive->using_dma = 0; + + if (drive->media == ide_floppy) + enable = 0; + if (((id->capability & 1) == 0) && !__ide_dma_good_drive(drive)) + enable = 0; + if (__ide_dma_bad_drive(drive)) + enable = 0; + + if (enable) { + short mode; + + map = XFER_MWDMA; + if (pmif->kind == controller_kl_ata4 + || pmif->kind == controller_un_ata6 + || pmif->kind == controller_k2_ata6 + || pmif->kind == controller_sh_ata6) { + map |= XFER_UDMA; + if (pmif->cable_80) { + map |= XFER_UDMA_66; + if (pmif->kind == controller_un_ata6 || + pmif->kind == controller_k2_ata6 || + pmif->kind == controller_sh_ata6) + map |= XFER_UDMA_100; + if (pmif->kind == controller_sh_ata6) + map |= XFER_UDMA_133; + } + } + mode = ide_find_best_mode(drive, map); + if (mode & XFER_UDMA) + drive->using_dma = pmac_ide_udma_enable(drive, mode); + else if (mode & XFER_MWDMA) + drive->using_dma = pmac_ide_mdma_enable(drive, mode); + hwif->OUTB(0, IDE_CONTROL_REG); + /* Apply settings to controller */ + pmac_ide_do_update_timings(drive); + } + return 0; +} + +/* + * Prepare a DMA transfer. We build the DMA table, adjust the timings for + * a read on KeyLargo ATA/66 and mark us as waiting for DMA completion + */ +static int __pmac +pmac_ide_dma_setup(ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + pmac_ide_hwif_t* pmif = (pmac_ide_hwif_t *)hwif->hwif_data; + struct request *rq = HWGROUP(drive)->rq; + u8 unit = (drive->select.b.unit & 0x01); + u8 ata4; + + if (pmif == NULL) + return 1; + ata4 = (pmif->kind == controller_kl_ata4); + + if (!pmac_ide_build_dmatable(drive, rq)) { + ide_map_sg(drive, rq); + return 1; + } + + /* Apple adds 60ns to wrDataSetup on reads */ + if (ata4 && (pmif->timings[unit] & TR_66_UDMA_EN)) { + writel(pmif->timings[unit] + (!rq_data_dir(rq) ? 0x00800000UL : 0), + PMAC_IDE_REG(IDE_TIMING_CONFIG)); + (void)readl(PMAC_IDE_REG(IDE_TIMING_CONFIG)); + } + + drive->waiting_for_dma = 1; + + return 0; +} + +static void __pmac +pmac_ide_dma_exec_cmd(ide_drive_t *drive, u8 command) +{ + /* issue cmd to drive */ + ide_execute_command(drive, command, &ide_dma_intr, 2*WAIT_CMD, NULL); +} + +/* + * Kick the DMA controller into life after the DMA command has been issued + * to the drive. + */ +static void __pmac +pmac_ide_dma_start(ide_drive_t *drive) +{ + pmac_ide_hwif_t* pmif = (pmac_ide_hwif_t *)HWIF(drive)->hwif_data; + volatile struct dbdma_regs __iomem *dma; + + dma = pmif->dma_regs; + + writel((RUN << 16) | RUN, &dma->control); + /* Make sure it gets to the controller right now */ + (void)readl(&dma->control); +} + +/* + * After a DMA transfer, make sure the controller is stopped + */ +static int __pmac +pmac_ide_dma_end (ide_drive_t *drive) +{ + pmac_ide_hwif_t* pmif = (pmac_ide_hwif_t *)HWIF(drive)->hwif_data; + volatile struct dbdma_regs __iomem *dma; + u32 dstat; + + if (pmif == NULL) + return 0; + dma = pmif->dma_regs; + + drive->waiting_for_dma = 0; + dstat = readl(&dma->status); + writel(((RUN|WAKE|DEAD) << 16), &dma->control); + pmac_ide_destroy_dmatable(drive); + /* verify good dma status. we don't check for ACTIVE beeing 0. We should... + * in theory, but with ATAPI decices doing buffer underruns, that would + * cause us to disable DMA, which isn't what we want + */ + return (dstat & (RUN|DEAD)) != RUN; +} + +/* + * Check out that the interrupt we got was for us. We can't always know this + * for sure with those Apple interfaces (well, we could on the recent ones but + * that's not implemented yet), on the other hand, we don't have shared interrupts + * so it's not really a problem + */ +static int __pmac +pmac_ide_dma_test_irq (ide_drive_t *drive) +{ + pmac_ide_hwif_t* pmif = (pmac_ide_hwif_t *)HWIF(drive)->hwif_data; + volatile struct dbdma_regs __iomem *dma; + unsigned long status, timeout; + + if (pmif == NULL) + return 0; + dma = pmif->dma_regs; + + /* We have to things to deal with here: + * + * - The dbdma won't stop if the command was started + * but completed with an error without transferring all + * datas. This happens when bad blocks are met during + * a multi-block transfer. + * + * - The dbdma fifo hasn't yet finished flushing to + * to system memory when the disk interrupt occurs. + * + */ + + /* If ACTIVE is cleared, the STOP command have passed and + * transfer is complete. + */ + status = readl(&dma->status); + if (!(status & ACTIVE)) + return 1; + if (!drive->waiting_for_dma) + printk(KERN_WARNING "ide%d, ide_dma_test_irq \ + called while not waiting\n", HWIF(drive)->index); + + /* If dbdma didn't execute the STOP command yet, the + * active bit is still set. We consider that we aren't + * sharing interrupts (which is hopefully the case with + * those controllers) and so we just try to flush the + * channel for pending data in the fifo + */ + udelay(1); + writel((FLUSH << 16) | FLUSH, &dma->control); + timeout = 0; + for (;;) { + udelay(1); + status = readl(&dma->status); + if ((status & FLUSH) == 0) + break; + if (++timeout > 100) { + printk(KERN_WARNING "ide%d, ide_dma_test_irq \ + timeout flushing channel\n", HWIF(drive)->index); + break; + } + } + return 1; +} + +static int __pmac +pmac_ide_dma_host_off (ide_drive_t *drive) +{ + return 0; +} + +static int __pmac +pmac_ide_dma_host_on (ide_drive_t *drive) +{ + return 0; +} + +static int __pmac +pmac_ide_dma_lostirq (ide_drive_t *drive) +{ + pmac_ide_hwif_t* pmif = (pmac_ide_hwif_t *)HWIF(drive)->hwif_data; + volatile struct dbdma_regs __iomem *dma; + unsigned long status; + + if (pmif == NULL) + return 0; + dma = pmif->dma_regs; + + status = readl(&dma->status); + printk(KERN_ERR "ide-pmac lost interrupt, dma status: %lx\n", status); + return 0; +} + +/* + * Allocate the data structures needed for using DMA with an interface + * and fill the proper list of functions pointers + */ +static void __init +pmac_ide_setup_dma(pmac_ide_hwif_t *pmif, ide_hwif_t *hwif) +{ + /* We won't need pci_dev if we switch to generic consistent + * DMA routines ... + */ + if (hwif->pci_dev == NULL) + return; + /* + * 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. + */ + pmif->dma_table_cpu = (struct dbdma_cmd*)pci_alloc_consistent( + hwif->pci_dev, + (MAX_DCMDS + 2) * sizeof(struct dbdma_cmd), + &hwif->dmatable_dma); + if (pmif->dma_table_cpu == NULL) { + printk(KERN_ERR "%s: unable to allocate DMA command list\n", + hwif->name); + return; + } + + hwif->ide_dma_off_quietly = &__ide_dma_off_quietly; + hwif->ide_dma_on = &__ide_dma_on; + hwif->ide_dma_check = &pmac_ide_dma_check; + hwif->dma_setup = &pmac_ide_dma_setup; + hwif->dma_exec_cmd = &pmac_ide_dma_exec_cmd; + hwif->dma_start = &pmac_ide_dma_start; + hwif->ide_dma_end = &pmac_ide_dma_end; + hwif->ide_dma_test_irq = &pmac_ide_dma_test_irq; + hwif->ide_dma_host_off = &pmac_ide_dma_host_off; + hwif->ide_dma_host_on = &pmac_ide_dma_host_on; + hwif->ide_dma_timeout = &__ide_dma_timeout; + hwif->ide_dma_lostirq = &pmac_ide_dma_lostirq; + + hwif->atapi_dma = 1; + switch(pmif->kind) { + case controller_sh_ata6: + hwif->ultra_mask = pmif->cable_80 ? 0x7f : 0x07; + hwif->mwdma_mask = 0x07; + hwif->swdma_mask = 0x00; + break; + case controller_un_ata6: + case controller_k2_ata6: + hwif->ultra_mask = pmif->cable_80 ? 0x3f : 0x07; + hwif->mwdma_mask = 0x07; + hwif->swdma_mask = 0x00; + break; + case controller_kl_ata4: + hwif->ultra_mask = pmif->cable_80 ? 0x1f : 0x07; + hwif->mwdma_mask = 0x07; + hwif->swdma_mask = 0x00; + break; + default: + hwif->ultra_mask = 0x00; + hwif->mwdma_mask = 0x07; + hwif->swdma_mask = 0x00; + break; + } +} + +#endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */ diff --git a/drivers/ide/setup-pci.c b/drivers/ide/setup-pci.c new file mode 100644 index 0000000..e501675 --- /dev/null +++ b/drivers/ide/setup-pci.c @@ -0,0 +1,901 @@ +/* + * linux/drivers/ide/setup-pci.c Version 1.10 2002/08/19 + * + * Copyright (c) 1998-2000 Andre Hedrick <andre@linux-ide.org> + * + * Copyright (c) 1995-1998 Mark Lord + * May be copied or modified under the terms of the GNU General Public License + * + * Recent Changes + * Split the set up function into multiple functions + * Use pci_set_master + * Fix misreporting of I/O v MMIO problems + * Initial fixups for simplex devices + */ + +/* + * This module provides support for automatic detection and + * configuration of all PCI IDE interfaces present in a system. + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/timer.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/ide.h> +#include <linux/dma-mapping.h> + +#include <asm/io.h> +#include <asm/irq.h> + + +/** + * ide_match_hwif - match a PCI IDE against an ide_hwif + * @io_base: I/O base of device + * @bootable: set if its bootable + * @name: name of device + * + * Match a PCI IDE port against an entry in ide_hwifs[], + * based on io_base port if possible. Return the matching hwif, + * or a new hwif. If we find an error (clashing, out of devices, etc) + * return NULL + * + * FIXME: we need to handle mmio matches here too + */ + +static ide_hwif_t *ide_match_hwif(unsigned long io_base, u8 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_forced) + 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(KERN_ERR "%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(KERN_ERR "%s: too many IDE interfaces, no room in table\n", name); + return NULL; +} + +/** + * ide_setup_pci_baseregs - place a PCI IDE controller native + * @dev: PCI device of interface to switch native + * @name: Name of interface + * + * We attempt to place the PCI interface into PCI native mode. If + * we succeed the BARs are ok and the controller is in PCI mode. + * Returns 0 on success or an errno code. + * + * FIXME: if we program the interface and then fail to set the BARS + * we don't switch it back to legacy mode. Do we actually care ?? + */ + +static int ide_setup_pci_baseregs (struct pci_dev *dev, const char *name) +{ + u8 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(KERN_INFO "%s: device not capable of full " + "native PCI mode\n", name); + return -EOPNOTSUPP; + } + 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(KERN_ERR "%s: rewrite of PROGIF failed, wanted " + "0x%04x, got 0x%04x\n", + name, progif|5, progif); + return -EOPNOTSUPP; + } + } + return 0; +} + +#ifdef CONFIG_BLK_DEV_IDEDMA_PCI + +#ifdef CONFIG_BLK_DEV_IDEDMA_FORCED +/* + * Long lost data from 2.0.34 that is now in 2.0.39 + * + * This was used in ./drivers/block/triton.c to do DMA Base address setup + * when PnP failed. Oh the things we forget. I believe this was part + * of SFF-8038i that has been withdrawn from public access... :-(( + */ +#define DEFAULT_BMIBA 0xe800 /* in case BIOS did not init it */ +#define DEFAULT_BMCRBA 0xcc00 /* VIA's default value */ +#define DEFAULT_BMALIBA 0xd400 /* ALI's default value */ +#endif /* CONFIG_BLK_DEV_IDEDMA_FORCED */ + +/** + * ide_get_or_set_dma_base - setup BMIBA + * @hwif: Interface + * + * Fetch the DMA Bus-Master-I/O-Base-Address (BMIBA) from PCI space: + * If need be we set up the DMA base. Where a device has a partner that + * is already in DMA mode we check and enforce IDE simplex rules. + */ + +static unsigned long ide_get_or_set_dma_base (ide_hwif_t *hwif) +{ + unsigned long dma_base = 0; + struct pci_dev *dev = hwif->pci_dev; + +#ifdef CONFIG_BLK_DEV_IDEDMA_FORCED + int second_chance = 0; + +second_chance_to_dma: +#endif /* CONFIG_BLK_DEV_IDEDMA_FORCED */ + + if (hwif->mmio) + return hwif->dma_base; + + if (hwif->mate && hwif->mate->dma_base) { + dma_base = hwif->mate->dma_base - (hwif->channel ? 0 : 8); + } else { + dma_base = pci_resource_start(dev, 4); + if (!dma_base) { + printk(KERN_ERR "%s: dma_base is invalid\n", + hwif->cds->name); + } + } + +#ifdef CONFIG_BLK_DEV_IDEDMA_FORCED + /* FIXME - should use pci_assign_resource surely */ + if ((!dma_base) && (!second_chance)) { + unsigned long set_bmiba = 0; + second_chance++; + switch(dev->vendor) { + case PCI_VENDOR_ID_AL: + set_bmiba = DEFAULT_BMALIBA; break; + case PCI_VENDOR_ID_VIA: + set_bmiba = DEFAULT_BMCRBA; break; + case PCI_VENDOR_ID_INTEL: + set_bmiba = DEFAULT_BMIBA; break; + default: + return dma_base; + } + pci_write_config_dword(dev, 0x20, set_bmiba|1); + goto second_chance_to_dma; + } +#endif /* CONFIG_BLK_DEV_IDEDMA_FORCED */ + + if (dma_base) { + u8 simplex_stat = 0; + dma_base += hwif->channel ? 8 : 0; + + switch(dev->device) { + case PCI_DEVICE_ID_AL_M5219: + case PCI_DEVICE_ID_AL_M5229: + case PCI_DEVICE_ID_AMD_VIPER_7409: + case PCI_DEVICE_ID_CMD_643: + case PCI_DEVICE_ID_SERVERWORKS_CSB5IDE: + simplex_stat = hwif->INB(dma_base + 2); + hwif->OUTB((simplex_stat&0x60),(dma_base + 2)); + simplex_stat = hwif->INB(dma_base + 2); + if (simplex_stat & 0x80) { + printk(KERN_INFO "%s: simplex device: " + "DMA forced\n", + hwif->cds->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. + */ + simplex_stat = hwif->INB(dma_base + 2); + if (simplex_stat & 0x80) { + /* simplex device? */ +/* + * At this point we haven't probed the drives so we can't make the + * appropriate decision. Really we should defer this problem + * until we tune the drive then try to grab DMA ownership if we want + * to be the DMA end. This has to be become dynamic to handle hot + * plug. + */ + if (hwif->mate && hwif->mate->dma_base) { + printk(KERN_INFO "%s: simplex device: " + "DMA disabled\n", + hwif->cds->name); + dma_base = 0; + } + } + } + } + return dma_base; +} +#endif /* CONFIG_BLK_DEV_IDEDMA_PCI */ + +void ide_setup_pci_noise (struct pci_dev *dev, ide_pci_device_t *d) +{ + printk(KERN_INFO "%s: IDE controller at PCI slot %s\n", + d->name, pci_name(dev)); +} + +EXPORT_SYMBOL_GPL(ide_setup_pci_noise); + + +/** + * ide_pci_enable - do PCI enables + * @dev: PCI device + * @d: IDE pci device data + * + * Enable the IDE PCI device. We attempt to enable the device in full + * but if that fails then we only need BAR4 so we will enable that. + * + * Returns zero on success or an error code + */ + +static int ide_pci_enable(struct pci_dev *dev, ide_pci_device_t *d) +{ + int ret; + + if (pci_enable_device(dev)) { + ret = pci_enable_device_bars(dev, 1 << 4); + if (ret < 0) { + printk(KERN_WARNING "%s: (ide_setup_pci_device:) " + "Could not enable device.\n", d->name); + goto out; + } + printk(KERN_WARNING "%s: BIOS configuration fixed.\n", d->name); + } + + /* + * assume all devices can do 32-bit dma for now. we can add a + * dma mask field to the ide_pci_device_t if we need it (or let + * lower level driver set the dma mask) + */ + ret = pci_set_dma_mask(dev, DMA_32BIT_MASK); + if (ret < 0) { + printk(KERN_ERR "%s: can't set dma mask\n", d->name); + goto out; + } + + /* FIXME: Temporary - until we put in the hotplug interface logic + Check that the bits we want are not in use by someone else. */ + ret = pci_request_region(dev, 4, "ide_tmp"); + if (ret < 0) + goto out; + + pci_release_region(dev, 4); +out: + return ret; +} + +/** + * ide_pci_configure - configure an unconfigured device + * @dev: PCI device + * @d: IDE pci device data + * + * Enable and configure the PCI device we have been passed. + * Returns zero on success or an error code. + */ + +static int ide_pci_configure(struct pci_dev *dev, ide_pci_device_t *d) +{ + u16 pcicmd = 0; + /* + * PnP BIOS was *supposed* to have setup this device, 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 (ide_setup_pci_baseregs(dev, d->name) || pci_write_config_word(dev, PCI_COMMAND, pcicmd|PCI_COMMAND_IO)) + { + printk(KERN_INFO "%s: device disabled (BIOS)\n", d->name); + return -ENODEV; + } + if (pci_read_config_word(dev, PCI_COMMAND, &pcicmd)) { + printk(KERN_ERR "%s: error accessing PCI regs\n", d->name); + return -EIO; + } + if (!(pcicmd & PCI_COMMAND_IO)) { + printk(KERN_ERR "%s: unable to enable IDE controller\n", d->name); + return -ENXIO; + } + return 0; +} + +/** + * ide_pci_check_iomem - check a register is I/O + * @dev: pci device + * @d: ide_pci_device + * @bar: bar number + * + * Checks if a BAR is configured and points to MMIO space. If so + * print an error and return an error code. Otherwise return 0 + */ + +static int ide_pci_check_iomem(struct pci_dev *dev, ide_pci_device_t *d, int bar) +{ + ulong flags = pci_resource_flags(dev, bar); + + /* Unconfigured ? */ + if (!flags || pci_resource_len(dev, bar) == 0) + return 0; + + /* I/O space */ + if(flags & PCI_BASE_ADDRESS_IO_MASK) + return 0; + + /* Bad */ + printk(KERN_ERR "%s: IO baseregs (BIOS) are reported " + "as MEM, report to " + "<andre@linux-ide.org>.\n", d->name); + return -EINVAL; +} + +/** + * ide_hwif_configure - configure an IDE interface + * @dev: PCI device holding interface + * @d: IDE pci data + * @mate: Paired interface if any + * + * Perform the initial set up for the hardware interface structure. This + * is done per interface port rather than per PCI device. There may be + * more than one port per device. + * + * Returns the new hardware interface structure, or NULL on a failure + */ + +static ide_hwif_t *ide_hwif_configure(struct pci_dev *dev, ide_pci_device_t *d, ide_hwif_t *mate, int port, int irq) +{ + unsigned long ctl = 0, base = 0; + ide_hwif_t *hwif; + + if ((d->flags & IDEPCI_FLAG_ISA_PORTS) == 0) { + /* Possibly we should fail if these checks report true */ + ide_pci_check_iomem(dev, d, 2*port); + ide_pci_check_iomem(dev, d, 2*port+1); + + ctl = pci_resource_start(dev, 2*port+1); + base = pci_resource_start(dev, 2*port); + if ((ctl && !base) || (base && !ctl)) { + printk(KERN_ERR "%s: inconsistent baseregs (BIOS) " + "for port %d, skipping\n", d->name, port); + return NULL; + } + } + if (!ctl) + { + /* Use default values */ + ctl = port ? 0x374 : 0x3f4; + base = port ? 0x170 : 0x1f0; + } + if ((hwif = ide_match_hwif(base, d->bootable, d->name)) == NULL) + return NULL; /* no room in ide_hwifs[] */ + if (hwif->io_ports[IDE_DATA_OFFSET] != base || + hwif->io_ports[IDE_CONTROL_OFFSET] != (ctl | 2)) { + memset(&hwif->hw, 0, sizeof(hwif->hw)); +#ifndef IDE_ARCH_OBSOLETE_INIT + ide_std_init_ports(&hwif->hw, base, (ctl | 2)); + hwif->hw.io_ports[IDE_IRQ_OFFSET] = 0; +#else + ide_init_hwif_ports(&hwif->hw, base, (ctl | 2), NULL); +#endif + 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->cds = (struct ide_pci_device_s *) d; + hwif->channel = port; + + if (!hwif->irq) + hwif->irq = irq; + if (mate) { + hwif->mate = mate; + mate->mate = hwif; + } + return hwif; +} + +/** + * ide_hwif_setup_dma - configure DMA interface + * @dev: PCI device + * @d: IDE pci data + * @hwif: Hardware interface we are configuring + * + * Set up the DMA base for the interface. Enable the master bits as + * necessary and attempt to bring the device DMA into a ready to use + * state + */ + +#ifndef CONFIG_BLK_DEV_IDEDMA_PCI +static void ide_hwif_setup_dma(struct pci_dev *dev, ide_pci_device_t *d, ide_hwif_t *hwif) +{ +} +#else +static void ide_hwif_setup_dma(struct pci_dev *dev, ide_pci_device_t *d, ide_hwif_t *hwif) +{ + u16 pcicmd; + pci_read_config_word(dev, PCI_COMMAND, &pcicmd); + + if ((d->autodma == AUTODMA) || + ((dev->class >> 8) == PCI_CLASS_STORAGE_IDE && + (dev->class & 0x80))) { + unsigned long dma_base = ide_get_or_set_dma_base(hwif); + if (dma_base && !(pcicmd & PCI_COMMAND_MASTER)) { + /* + * Set up BM-DMA capability + * (PnP BIOS should have done this) + */ + /* default DMA off if we had to configure it here */ + hwif->autodma = 0; + pci_set_master(dev); + if (pci_read_config_word(dev, PCI_COMMAND, &pcicmd) || !(pcicmd & PCI_COMMAND_MASTER)) { + printk(KERN_ERR "%s: %s error updating PCICMD\n", + hwif->name, d->name); + dma_base = 0; + } + } + if (dma_base) { + if (d->init_dma) { + d->init_dma(hwif, dma_base); + } else { + ide_setup_dma(hwif, dma_base, 8); + } + } else { + printk(KERN_INFO "%s: %s Bus-Master DMA disabled " + "(BIOS)\n", hwif->name, d->name); + } + } +} + +#ifndef CONFIG_IDEDMA_PCI_AUTO +#warning CONFIG_IDEDMA_PCI_AUTO=n support is obsolete, and will be removed soon. +#endif + +#endif /* CONFIG_BLK_DEV_IDEDMA_PCI*/ + +/** + * ide_setup_pci_controller - set up IDE PCI + * @dev: PCI device + * @d: IDE PCI data + * @noisy: verbose flag + * @config: returned as 1 if we configured the hardware + * + * Set up the PCI and controller side of the IDE interface. This brings + * up the PCI side of the device, checks that the device is enabled + * and enables it if need be + */ + +static int ide_setup_pci_controller(struct pci_dev *dev, ide_pci_device_t *d, int noisy, int *config) +{ + int ret; + u32 class_rev; + u16 pcicmd; + + if (noisy) + ide_setup_pci_noise(dev, d); + + ret = ide_pci_enable(dev, d); + if (ret < 0) + goto out; + + ret = pci_read_config_word(dev, PCI_COMMAND, &pcicmd); + if (ret < 0) { + printk(KERN_ERR "%s: error accessing PCI regs\n", d->name); + goto out; + } + if (!(pcicmd & PCI_COMMAND_IO)) { /* is device disabled? */ + ret = ide_pci_configure(dev, d); + if (ret < 0) + goto out; + *config = 1; + printk(KERN_INFO "%s: device enabled (Linux)\n", d->name); + } + + pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev); + class_rev &= 0xff; + if (noisy) + printk(KERN_INFO "%s: chipset revision %d\n", d->name, class_rev); +out: + return ret; +} + +/** + * ide_pci_setup_ports - configure ports/devices on PCI IDE + * @dev: PCI device + * @d: IDE pci device info + * @pciirq: IRQ line + * @index: ata index to update + * + * Scan the interfaces attached to this device and do any + * necessary per port setup. Attach the devices and ask the + * generic DMA layer to do its work for us. + * + * Normally called automaticall from do_ide_pci_setup_device, + * but is also used directly as a helper function by some controllers + * where the chipset setup is not the default PCI IDE one. + */ + +void ide_pci_setup_ports(struct pci_dev *dev, ide_pci_device_t *d, int pciirq, ata_index_t *index) +{ + int port; + int at_least_one_hwif_enabled = 0; + ide_hwif_t *hwif, *mate = NULL; + static int secondpdc = 0; + u8 tmp; + + index->all = 0xf0f0; + + /* + * Set up the IDE ports + */ + + for (port = 0; port <= 1; ++port) { + ide_pci_enablebit_t *e = &(d->enablebits[port]); + + /* + * If this is a Promise FakeRaid controller, + * the 2nd controller will be marked as + * disabled while it is actually there and enabled + * by the bios for raid purposes. + * Skip the normal "is it enabled" test for those. + */ + if ((d->flags & IDEPCI_FLAG_FORCE_PDC) && + (secondpdc++==1) && (port==1)) + goto controller_ok; + + if (e->reg && (pci_read_config_byte(dev, e->reg, &tmp) || + (tmp & e->mask) != e->val)) + continue; /* port not enabled */ +controller_ok: + + if (d->channels <= port) + break; + + if ((hwif = ide_hwif_configure(dev, d, mate, port, pciirq)) == NULL) + continue; + + /* setup proper ancestral information */ + hwif->gendev.parent = &dev->dev; + + if (hwif->channel) { + index->b.high = hwif->index; + } else { + index->b.low = hwif->index; + } + + + if (d->init_iops) + d->init_iops(hwif); + + if (d->autodma == NODMA) + goto bypass_legacy_dma; + + if(d->init_setup_dma) + d->init_setup_dma(dev, d, hwif); + else + ide_hwif_setup_dma(dev, d, hwif); +bypass_legacy_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(KERN_INFO "%s: neither IDE port enabled (BIOS)\n", d->name); +} + +EXPORT_SYMBOL_GPL(ide_pci_setup_ports); + +/* + * 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 int do_ide_setup_pci_device(struct pci_dev *dev, ide_pci_device_t *d, + ata_index_t *index, u8 noisy) +{ + static ata_index_t ata_index = { .b = { .low = 0xff, .high = 0xff } }; + int tried_config = 0; + int pciirq, ret; + + ret = ide_setup_pci_controller(dev, d, noisy, &tried_config); + if (ret < 0) + goto out; + + /* + * Can we trust the reported IRQ? + */ + pciirq = dev->irq; + + /* Is it an "IDE storage" device in non-PCI mode? */ + if ((dev->class >> 8) == PCI_CLASS_STORAGE_IDE && (dev->class & 5) != 5) { + if (noisy) + printk(KERN_INFO "%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. + */ + ret = d->init_chipset ? d->init_chipset(dev, d->name) : 0; + if (ret < 0) + goto out; + pciirq = ret; + } else if (tried_config) { + if (noisy) + printk(KERN_INFO "%s: will probe irqs later\n", d->name); + pciirq = 0; + } else if (!pciirq) { + if (noisy) + printk(KERN_WARNING "%s: bad irq (%d): will probe later\n", + d->name, pciirq); + pciirq = 0; + } else { + if (d->init_chipset) { + ret = d->init_chipset(dev, d->name); + if (ret < 0) + goto out; + } + if (noisy) +#ifdef __sparc__ + printk(KERN_INFO "%s: 100%% native mode on irq %s\n", + d->name, __irq_itoa(pciirq)); +#else + printk(KERN_INFO "%s: 100%% native mode on irq %d\n", + d->name, pciirq); +#endif + } + + /* FIXME: silent failure can happen */ + + *index = ata_index; + ide_pci_setup_ports(dev, d, pciirq, index); +out: + return ret; +} + +int ide_setup_pci_device(struct pci_dev *dev, ide_pci_device_t *d) +{ + ata_index_t index_list; + int ret; + + ret = do_ide_setup_pci_device(dev, d, &index_list, 1); + if (ret < 0) + goto out; + + if ((index_list.b.low & 0xf0) != 0xf0) + probe_hwif_init_with_fixup(&ide_hwifs[index_list.b.low], d->fixup); + if ((index_list.b.high & 0xf0) != 0xf0) + probe_hwif_init_with_fixup(&ide_hwifs[index_list.b.high], d->fixup); + + create_proc_ide_interfaces(); +out: + return ret; +} + +EXPORT_SYMBOL_GPL(ide_setup_pci_device); + +int ide_setup_pci_devices(struct pci_dev *dev1, struct pci_dev *dev2, + ide_pci_device_t *d) +{ + struct pci_dev *pdev[] = { dev1, dev2 }; + ata_index_t index_list[2]; + int ret, i; + + for (i = 0; i < 2; i++) { + ret = do_ide_setup_pci_device(pdev[i], d, index_list + i, !i); + /* + * FIXME: Mom, mom, they stole me the helper function to undo + * do_ide_setup_pci_device() on the first device! + */ + if (ret < 0) + goto out; + } + + for (i = 0; i < 2; i++) { + u8 idx[2] = { index_list[i].b.low, index_list[i].b.high }; + int j; + + for (j = 0; j < 2; j++) { + if ((idx[j] & 0xf0) != 0xf0) + probe_hwif_init(ide_hwifs + idx[j]); + } + } + + create_proc_ide_interfaces(); +out: + return ret; +} + +EXPORT_SYMBOL_GPL(ide_setup_pci_devices); + +/* + * Module interfaces + */ + +static int pre_init = 1; /* Before first ordered IDE scan */ +static LIST_HEAD(ide_pci_drivers); + +/* + * ide_register_pci_driver - attach IDE driver + * @driver: pci driver + * + * Registers a driver with the IDE layer. The IDE layer arranges that + * boot time setup is done in the expected device order and then + * hands the controllers off to the core PCI code to do the rest of + * the work. + * + * The driver_data of the driver table must point to an ide_pci_device_t + * describing the interface. + * + * Returns are the same as for pci_register_driver + */ + +int ide_pci_register_driver(struct pci_driver *driver) +{ + if(!pre_init) + return pci_module_init(driver); + list_add_tail(&driver->node, &ide_pci_drivers); + return 0; +} + +EXPORT_SYMBOL_GPL(ide_pci_register_driver); + +/** + * ide_unregister_pci_driver - unregister an IDE driver + * @driver: driver to remove + * + * Unregister a currently installed IDE driver. Returns are the same + * as for pci_unregister_driver + */ + +void ide_pci_unregister_driver(struct pci_driver *driver) +{ + if(!pre_init) + pci_unregister_driver(driver); + else + list_del(&driver->node); +} + +EXPORT_SYMBOL_GPL(ide_pci_unregister_driver); + +/** + * ide_scan_pcidev - find an IDE driver for a device + * @dev: PCI device to check + * + * Look for an IDE driver to handle the device we are considering. + * This is only used during boot up to get the ordering correct. After + * boot up the pci layer takes over the job. + */ + +static int __init ide_scan_pcidev(struct pci_dev *dev) +{ + struct list_head *l; + struct pci_driver *d; + + list_for_each(l, &ide_pci_drivers) + { + d = list_entry(l, struct pci_driver, node); + if(d->id_table) + { + const struct pci_device_id *id = pci_match_device(d->id_table, dev); + if(id != NULL) + { + if(d->probe(dev, id) >= 0) + { + dev->driver = d; + return 1; + } + } + } + } + return 0; +} + +/** + * ide_scan_pcibus - perform the initial IDE driver scan + * @scan_direction: set for reverse order scanning + * + * Perform the initial bus rather than driver ordered scan of the + * PCI drivers. After this all IDE pci handling becomes standard + * module ordering not traditionally ordered. + */ + +void __init ide_scan_pcibus (int scan_direction) +{ + struct pci_dev *dev = NULL; + struct pci_driver *d; + struct list_head *l, *n; + + pre_init = 0; + if (!scan_direction) { + while ((dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) { + ide_scan_pcidev(dev); + } + } else { + while ((dev = pci_find_device_reverse(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) { + ide_scan_pcidev(dev); + } + } + + /* + * Hand the drivers over to the PCI layer now we + * are post init. + */ + + list_for_each_safe(l, n, &ide_pci_drivers) + { + list_del(l); + d = list_entry(l, struct pci_driver, node); + pci_register_driver(d); + } +} |